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
|
||||
* representing a document.
|
||||
* - Dependencies need to be liberated:
|
||||
* - ElementStyler;
|
||||
* - BucketDataValidator.
|
||||
* - Global references (e.g. jQuery) must be removed.
|
||||
* - 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,
|
||||
|
||||
/**
|
||||
* Function used to query for element
|
||||
* @type {function(function(HTMLElement))}
|
||||
*/
|
||||
'private _query': null,
|
||||
|
||||
'private _idPrefix': 'q_',
|
||||
|
||||
/**
|
||||
* Currently active styles
|
||||
* @type {Object}
|
||||
* Cached immediate parent
|
||||
* @type {HTMLElement}
|
||||
*/
|
||||
'private _styles': {},
|
||||
'private _parent': null,
|
||||
|
||||
|
||||
__construct: function( field, element )
|
||||
|
@ -53,8 +59,8 @@ module.exports = Class( 'DomField' )
|
|||
throw TypeError( "Invalid field provided" );
|
||||
}
|
||||
|
||||
this._field = field;
|
||||
this._element = element;
|
||||
this._field = field;
|
||||
this._query = element;
|
||||
},
|
||||
|
||||
|
||||
|
@ -62,77 +68,117 @@ module.exports = Class( 'DomField' )
|
|||
'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 )
|
||||
{
|
||||
// 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
|
||||
// not even invoke the callback
|
||||
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,
|
||||
f = this._element;
|
||||
queue.push( c );
|
||||
};
|
||||
|
||||
// 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._element = function( c )
|
||||
// attempt to retrieve our element from the DOM
|
||||
orig_query( function( element )
|
||||
{
|
||||
var new_element = element || _self._element;
|
||||
|
||||
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
|
||||
f( function( element )
|
||||
// do not even finish; this shit is for real.
|
||||
return;
|
||||
}
|
||||
|
||||
if ( new_element !== _self._element )
|
||||
{
|
||||
if ( !element )
|
||||
{
|
||||
_self._element = null;
|
||||
_self.emit( 'error', Error(
|
||||
"Cannot locate DOM element for field " +
|
||||
_self.getName() + "[" + _self.getIndex() + "]"
|
||||
) );
|
||||
_self.updateElement( new_element );
|
||||
}
|
||||
|
||||
// do not even finish; this shit is for real.
|
||||
return;
|
||||
}
|
||||
// restore original query
|
||||
_self._query = orig_query;
|
||||
|
||||
_self._element = element;
|
||||
callback( element );
|
||||
callback( new_element );
|
||||
|
||||
// if we have any queued requests, process them when we're not
|
||||
// busy
|
||||
var c;
|
||||
while ( c = queue.shift() )
|
||||
// if we have any queued requests, process them when we're not
|
||||
// busy
|
||||
var c;
|
||||
while ( c = queue.shift() )
|
||||
{
|
||||
( function( c )
|
||||
{
|
||||
setTimeout( function()
|
||||
{
|
||||
// return the element to the queued callback
|
||||
c( element );
|
||||
}, 25 );
|
||||
}
|
||||
} );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// we already have the element; immediately return it
|
||||
callback( this._element );
|
||||
} )( c );
|
||||
}
|
||||
} );
|
||||
},
|
||||
|
||||
|
||||
'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() ];
|
||||
},
|
||||
|
||||
|
||||
'private _flagStyle': function( style, flag )
|
||||
{
|
||||
this._styles[ style.getId() ] = !!flag;
|
||||
this._element = new_element;
|
||||
this._parent = new_element.parentElement;
|
||||
},
|
||||
|
||||
|
||||
|
@ -140,23 +186,19 @@ module.exports = Class( 'DomField' )
|
|||
{
|
||||
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
|
||||
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
|
||||
// styling
|
||||
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,
|
||||
[ _self.__inst, root, _self.getContainingRow() ].concat( sargs )
|
||||
|
@ -171,18 +213,14 @@ module.exports = Class( 'DomField' )
|
|||
{
|
||||
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 )
|
||||
{
|
||||
// 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() );
|
||||
} );
|
||||
|
||||
|
@ -222,22 +260,46 @@ module.exports = Class( 'DomField' )
|
|||
},
|
||||
|
||||
|
||||
// TODO: move me
|
||||
// TODO: move me; too many odd exceptions; standardize
|
||||
'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;
|
||||
|
||||
return ( 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;
|
||||
|
||||
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 )
|
||||
{
|
||||
return null;
|
||||
|
@ -247,14 +309,14 @@ module.exports = Class( 'DomField' )
|
|||
return parent;
|
||||
}
|
||||
|
||||
// nodeName is in caps
|
||||
if ( type.toUpperCase() === parent.nodeName )
|
||||
// nodeName might not be in caps
|
||||
if ( type.toUpperCase() === parent.nodeName.toUpperCase() )
|
||||
{
|
||||
return parent;
|
||||
}
|
||||
|
||||
// otherwise, keep looking
|
||||
return this.getParent( parent, type );
|
||||
return this.getElementParent( parent, type );
|
||||
},
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Creates DomField
|
||||
*
|
||||
* Copyright (C) 2015 LoVullo Associates, Inc.
|
||||
* Copyright (C) 2015, 2016 LoVullo Associates, Inc.
|
||||
*
|
||||
* This file is part of liza.
|
||||
*
|
||||
|
@ -17,11 +17,6 @@
|
|||
*
|
||||
* 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
|
||||
* - Dependencies need to be liberated:
|
||||
* - ElementStyler.
|
||||
* @end needsLove
|
||||
*/
|
||||
|
||||
var Class = require( 'easejs' ).Class,
|
||||
|
@ -85,7 +80,7 @@ module.exports = Class( 'DomFieldFactory',
|
|||
|
||||
function c()
|
||||
{
|
||||
callback( _self._elementStyler.getElementByName(
|
||||
callback( _self._elementStyler.getElementByNameLax(
|
||||
name, index, null, root
|
||||
)[0] );
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* General UI logic for groups
|
||||
*
|
||||
* Copyright (C) 2015 LoVullo Associates, Inc.
|
||||
* Copyright (C) 2015, 2016 LoVullo Associates, Inc.
|
||||
*
|
||||
* This file is part of liza.
|
||||
*
|
||||
|
@ -144,23 +144,45 @@ module.exports = Class( 'GroupUi' )
|
|||
*/
|
||||
'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
|
||||
*
|
||||
* @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
|
||||
* @todo three of the below parameters might be able to be removed by
|
||||
* using context instead; the separate context is transitional
|
||||
* (refactoring).
|
||||
*
|
||||
* @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}
|
||||
*/
|
||||
'public __construct': function( group, $content, styler, jquery )
|
||||
'public __construct': function(
|
||||
group, $content, styler, jquery, context, na_styler
|
||||
)
|
||||
{
|
||||
this.group = group;
|
||||
this.$content = $content;
|
||||
this.styler = styler;
|
||||
this._jquery = jquery;
|
||||
this.group = group;
|
||||
this.$content = $content;
|
||||
this.styler = styler;
|
||||
this._jquery = jquery;
|
||||
this.context = context;
|
||||
this._naStyler = na_styler;
|
||||
},
|
||||
|
||||
|
||||
|
@ -787,14 +809,8 @@ module.exports = Class( 'GroupUi' )
|
|||
|
||||
'virtual protected doHideField': function( field, index )
|
||||
{
|
||||
var $elements = this.getFieldElements( field, index );
|
||||
|
||||
$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', '' );
|
||||
} );
|
||||
this.context.getFieldByName( field, index )
|
||||
.applyStyle( this._naStyler );
|
||||
},
|
||||
|
||||
|
||||
|
@ -815,12 +831,8 @@ module.exports = Class( 'GroupUi' )
|
|||
|
||||
'virtual protected doShowField': function( field, index )
|
||||
{
|
||||
var $elements = this.getFieldElements( field, index );
|
||||
|
||||
$elements.find( '.hidden' ).andSelf()
|
||||
.stop( true, true )
|
||||
.removeClass( 'hidden' )
|
||||
.slideDown( 500 );
|
||||
this.context.getFieldByName( field, index )
|
||||
.revokeStyle( this._naStyler );
|
||||
},
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Group tabbed UI
|
||||
*
|
||||
* Copyright (C) 2015 LoVullo Associates, Inc.
|
||||
* Copyright (C) 2015, 2016 LoVullo Associates, Inc.
|
||||
*
|
||||
* This file is part of liza.
|
||||
*
|
||||
|
@ -373,23 +373,11 @@ module.exports = Class( 'TabbedGroupUi' )
|
|||
{
|
||||
_self.doHideField( field, index, true );
|
||||
}, 25 );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var $elements = this.getFieldElements( field, index );
|
||||
|
||||
$elements.stop( true, true );
|
||||
|
||||
if ( this.isOnVisibleTab( field, index ) )
|
||||
{
|
||||
$elements.slideUp( 500, function()
|
||||
{
|
||||
$( this ).addClass( 'hidden' );
|
||||
} );
|
||||
}
|
||||
else
|
||||
{
|
||||
$elements.hide().addClass( 'hidden' );
|
||||
}
|
||||
this.__super( field, index );
|
||||
},
|
||||
|
||||
|
||||
|
@ -405,26 +393,11 @@ module.exports = Class( 'TabbedGroupUi' )
|
|||
{
|
||||
_self.doShowField( field, index, true );
|
||||
}, 25 );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var $elements = this.getFieldElements( 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();
|
||||
}
|
||||
this.__super( field, index );
|
||||
},
|
||||
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
* - References to "quote" should be replaced with generic terminology
|
||||
* representing a document.
|
||||
* - Dependencies need to be liberated:
|
||||
* - ElementStyler;
|
||||
* - BucketDataValidator.
|
||||
* - Global references (e.g. jQuery) must be removed.
|
||||
* - jQuery must be eliminated.
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
* @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 (?)
|
||||
|
|
|
@ -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