Liberate {,ui/}field/
parent
4cc240e977
commit
9a1dd337eb
|
@ -0,0 +1,60 @@
|
||||||
|
/**
|
||||||
|
* Field representing bucket value
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 LoVullo Associates, Inc.
|
||||||
|
*
|
||||||
|
* This file is part of liza.
|
||||||
|
*
|
||||||
|
* liza is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var Class = require( 'easejs' ).Class,
|
||||||
|
Field = require( './Field' );
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = Class( 'BucketField' )
|
||||||
|
.implement( Field )
|
||||||
|
.extend(
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Field name
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
'private _name': '',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Field index
|
||||||
|
* @type {string}'
|
||||||
|
*/
|
||||||
|
'private _index': 0,
|
||||||
|
|
||||||
|
|
||||||
|
__construct: function( name, index )
|
||||||
|
{
|
||||||
|
this._name = ''+name;
|
||||||
|
this._index = +index;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'public getName': function()
|
||||||
|
{
|
||||||
|
return this._name;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'public getIndex': function()
|
||||||
|
{
|
||||||
|
return this._index;
|
||||||
|
}
|
||||||
|
} );
|
|
@ -0,0 +1,31 @@
|
||||||
|
/**
|
||||||
|
* Field representation
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 LoVullo Associates, Inc.
|
||||||
|
*
|
||||||
|
* This file is part of liza.
|
||||||
|
*
|
||||||
|
* liza is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
var Interface = require( 'easejs' ).Interface;
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = Interface( 'Field',
|
||||||
|
{
|
||||||
|
'public getName': [],
|
||||||
|
|
||||||
|
'public getIndex': []
|
||||||
|
} );
|
|
@ -0,0 +1,300 @@
|
||||||
|
/**
|
||||||
|
* Field represented by DOM element
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 LoVullo Associates, Inc.
|
||||||
|
*
|
||||||
|
* This file is part of liza.
|
||||||
|
*
|
||||||
|
* liza is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var Class = require( 'easejs' ).Class,
|
||||||
|
Field = require( '../../field/Field' ),
|
||||||
|
|
||||||
|
EventEmitter = require( 'events' ).EventEmitter;
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = Class( 'DomField' )
|
||||||
|
.implement( Field )
|
||||||
|
.extend( EventEmitter,
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Wrapped field
|
||||||
|
* @type {Field}
|
||||||
|
*/
|
||||||
|
'private _field': null,
|
||||||
|
|
||||||
|
'private _element': null,
|
||||||
|
|
||||||
|
'private _idPrefix': 'q_',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Currently active styles
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
'private _styles': {},
|
||||||
|
|
||||||
|
|
||||||
|
__construct: function( field, element )
|
||||||
|
{
|
||||||
|
if ( !( Class.isA( Field, field ) ) )
|
||||||
|
{
|
||||||
|
throw TypeError( "Invalid field provided" );
|
||||||
|
}
|
||||||
|
|
||||||
|
this._field = field;
|
||||||
|
this._element = element;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'public proxy getName': '_field',
|
||||||
|
'public proxy getIndex': '_field',
|
||||||
|
|
||||||
|
|
||||||
|
'private _getElement': function( callback )
|
||||||
|
{
|
||||||
|
// if the provided root is a function, then it should be lazily laoded
|
||||||
|
if ( this._element === 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' )
|
||||||
|
{
|
||||||
|
var _self = this,
|
||||||
|
f = this._element;
|
||||||
|
|
||||||
|
// 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 )
|
||||||
|
{
|
||||||
|
queue.push( c );
|
||||||
|
};
|
||||||
|
|
||||||
|
// attempt to retrieve our element from the DOM
|
||||||
|
f( function( element )
|
||||||
|
{
|
||||||
|
if ( !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.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_self._element = element;
|
||||||
|
callback( element );
|
||||||
|
|
||||||
|
// if we have any queued requests, process them when we're not
|
||||||
|
// busy
|
||||||
|
var c;
|
||||||
|
while ( c = queue.shift() )
|
||||||
|
{
|
||||||
|
setTimeout( function()
|
||||||
|
{
|
||||||
|
// return the element to the queued callback
|
||||||
|
c( element );
|
||||||
|
}, 25 );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we already have the element; immediately return it
|
||||||
|
callback( this._element );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'private _hasStyle': function( style )
|
||||||
|
{
|
||||||
|
return !!this._styles[ style.getId() ];
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'private _flagStyle': function( style, flag )
|
||||||
|
{
|
||||||
|
this._styles[ style.getId() ] = !!flag;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'public applyStyle': function( style )
|
||||||
|
{
|
||||||
|
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 )
|
||||||
|
{
|
||||||
|
style.applyStyle.apply(
|
||||||
|
style,
|
||||||
|
[ _self.__inst, root, _self.getContainingRow() ].concat( sargs )
|
||||||
|
);
|
||||||
|
} );
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'public revokeStyle': function( style )
|
||||||
|
{
|
||||||
|
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 )
|
||||||
|
{
|
||||||
|
style.revokeStyle( _self.__inst, root, _self.getContainingRow() );
|
||||||
|
} );
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a field into an id that may be used to query the DOM
|
||||||
|
*
|
||||||
|
* @return {string} expected id of element on the DOM
|
||||||
|
*/
|
||||||
|
'protected resolveId': function()
|
||||||
|
{
|
||||||
|
return this.doResolveId(
|
||||||
|
this._field.getName(),
|
||||||
|
this._field.getIndex()
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a field into an id that may be used to query the DOM
|
||||||
|
*
|
||||||
|
* This may be overridden by a subtype to alter the resolution logic. The
|
||||||
|
* name and index are passed to the method to ensure that the field itself
|
||||||
|
* remains encapsulated.
|
||||||
|
*
|
||||||
|
* @param {string} name field name
|
||||||
|
* @param {number} index field index
|
||||||
|
*
|
||||||
|
* @return {string} expected id of element on the DOM
|
||||||
|
*/
|
||||||
|
'virtual protected doResolveId': function( name, index )
|
||||||
|
{
|
||||||
|
return ( this._idPrefix + name + '_' + index );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: move me
|
||||||
|
'protected getContainingRow': function()
|
||||||
|
{
|
||||||
|
var dd = this.getParent( this._element, 'dd' ),
|
||||||
|
dt = ( dd ) ? this.getPrecedingSibling( dd, 'dt' ) : null;
|
||||||
|
|
||||||
|
return ( dt )
|
||||||
|
? [ dd, dt ]
|
||||||
|
: [ this.getParent( this._element ) ];
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'protected getParent': function( element, type )
|
||||||
|
{
|
||||||
|
var parent = element.parentElement;
|
||||||
|
|
||||||
|
if ( parent === null )
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else if ( !type )
|
||||||
|
{
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// nodeName is in caps
|
||||||
|
if ( type.toUpperCase() === parent.nodeName )
|
||||||
|
{
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, keep looking
|
||||||
|
return this.getParent( parent, type );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'protected getPrecedingSibling': function( element, type )
|
||||||
|
{
|
||||||
|
return this.getSibling( element, type, -1 );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'protected getFollowingSibling': function( element, type )
|
||||||
|
{
|
||||||
|
return this.getSibling( element, type, 1 );
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'protected getSibling': function( element, type, direction )
|
||||||
|
{
|
||||||
|
// if no direction was provided, then search in both
|
||||||
|
if ( !direction )
|
||||||
|
{
|
||||||
|
return ( this.getSibling( element, type, -1 )
|
||||||
|
|| this.getSibling( element, type, 1 )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the next node relative to the direction
|
||||||
|
var next = element[
|
||||||
|
( direction === -1 ) ? 'previousSibling' : 'nextSibling'
|
||||||
|
];
|
||||||
|
if ( next === null )
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we found our sibling, return it
|
||||||
|
if ( type.toUpperCase() === next.nodeName )
|
||||||
|
{
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.getSibling( next, type, direction );
|
||||||
|
}
|
||||||
|
} );
|
|
@ -0,0 +1,95 @@
|
||||||
|
/**
|
||||||
|
* Creates DomField
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 LoVullo Associates, Inc.
|
||||||
|
*
|
||||||
|
* This file is part of liza.
|
||||||
|
*
|
||||||
|
* liza is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* @needsLove
|
||||||
|
* - Dependencies need to be liberated:
|
||||||
|
* - ElementStyler.
|
||||||
|
* @end needsLove
|
||||||
|
*/
|
||||||
|
|
||||||
|
var Class = require( 'easejs' ).Class,
|
||||||
|
|
||||||
|
BucketField = require( '../../field/BucketField' ),
|
||||||
|
DomField = require( './DomField' );
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = Class( 'DomFieldFactory',
|
||||||
|
{
|
||||||
|
'private _elementStyler': null,
|
||||||
|
|
||||||
|
|
||||||
|
__construct: function( element_styler )
|
||||||
|
{
|
||||||
|
this._elementStyler = element_styler;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a DomField from the given field description
|
||||||
|
*
|
||||||
|
* The provided DomField will wait to access the DOM until an operation
|
||||||
|
* requires it.
|
||||||
|
*
|
||||||
|
* @param {string} name field name
|
||||||
|
* @param {number} index field index
|
||||||
|
*
|
||||||
|
* @param {function(HtmlElement)|HtmlElement} root root element containing
|
||||||
|
* the field (optionally
|
||||||
|
* lazy)
|
||||||
|
*
|
||||||
|
* @return {DomField} generated field
|
||||||
|
*/
|
||||||
|
'public create': function( name, index, root )
|
||||||
|
{
|
||||||
|
var _self = this;
|
||||||
|
|
||||||
|
return DomField(
|
||||||
|
BucketField( name, index ),
|
||||||
|
|
||||||
|
// lazy load on first access
|
||||||
|
function( callback )
|
||||||
|
{
|
||||||
|
// are we lazy?
|
||||||
|
if ( typeof root === 'function' )
|
||||||
|
{
|
||||||
|
// wait to fulfill this request until after the element
|
||||||
|
// becomes available
|
||||||
|
root( function( result )
|
||||||
|
{
|
||||||
|
root = result;
|
||||||
|
c();
|
||||||
|
} );
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// not lazy; continue immediately
|
||||||
|
c();
|
||||||
|
|
||||||
|
function c()
|
||||||
|
{
|
||||||
|
callback( _self._elementStyler.getElementByName(
|
||||||
|
name, index, null, root
|
||||||
|
)[0] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} );
|
Loading…
Reference in New Issue