/* RSence
* Copyright 2006 Riassence Inc.
* http://riassence.com/
*
* You should have received a copy of the GNU General Public License along
* with this software package. If not, contact licensing@riassence.com
*/
/*** = Description
** HView is the foundation class for all views. HView is useful for
** any type of view and control grouping. It is designed for easy extension
** and it's the foundation for HControl and all other controls.
**
** The major differences between HView and HControl is that HView handles
** only visual representation and structurization. In addition to HView's
** features, HControl handles labels, values, events, states and such.
** However, HControl is more complex, so use HView instead whenever you don't
** need the additional features of HControl. HView implements the HMarkupView
** interface for template-related task.
**
** = Usage
** var myAppInstance = HApplication.nu();
** var rect1 = [10, 10, 100, 100];
** var myViewInstance = HView.nu( rect1, myAppInstance );
** var myViewInstance.setStyle('background-color','#ffcc00');
** var rect2 = [10, 10, 70, 70];
** var mySubView1 = HView.nu( rect2, myViewIntance );
** var rect3 [20, 20, 50, 50];
** var mySubView2 = HView.nu( rect3, mySubView1 );
**
***/
var//RSence.Foundation
HView = HClass.extend({
/** Component specific theme path.
**/
themePath: null,
/** True, if the component using absolute positioning.
* False, if the component is using relative positioning.
**/
isAbsolute: true,
/** The display mode to use.
* Defaults to 'block'.
* The other sane alternative is 'inline'.
**/
displayMode: 'block',
/** The visual value of a component, usually a String.
* See +#setLabel+.
**/
label: null,
/** When true, calls the +refreshLabel+ method whenever
* +self.label+ is changed.
**/
refreshOnLabelChange: true,
/** Escapes HTML in the label when true.
**/
escapeLabelHTML: false,
/** True, if the coordinates are right-aligned.
* False, if the coordinates are left-aligned.
* Uses flexRightOffset if true. Defined with 6-item arrays
* for the _rect parameter of setRect or the constructor.
* Can be set directly using the setFlexRight method.
**/
flexRight: false,
/** True, if the coordinates are left-aligned.
* False, if the coordinates are right-aligned.
* Uses the X-coordinate of rect, if true.
* Disabled using 6-item arrays with null x-coordinate
* for the _rect parameter of setRect or the constructor.
* Can be set directly using the setFlexLeft method.
**/
flexLeft: true,
/** True, if the coordinates are top-aligned.
* False, if the coordinates are bottom-aligned.
* Uses the Y-coordinate of rect, if true.
* Disabled using 6-item arrays with null x-coordinate
* for the _rect parameter of setRect or the constructor.
* Can be set directly using the setFlexTop method.
**/
flexTop: true,
/** True, if the coordinates are bottom-aligned.
* False, if the coordinates are top-aligned.
* Uses flexBottomOffset if true. Defined with 6-item arrays
* for the _rect parameter of setRect or the constructor.
* Can be set directly using the setFlexRight method.
**/
flexBottom: false,
/** The amount of pixels to offset from the right edge when
* flexRight is true. Defined with 6-item arrays
* for the _rect parameter of setRect or the constructor.
* Can be set directly using the setFlexRight method.
**/
flexRightOffset: 0,
/** The amount of pixels to offset from the bottom edge when
* flexBottom is true.Defined with 6-item arrays
* for the _rect parameter of setRect or the constructor.
* Can be set directly using the setFlexBottom method.
**/
flexBottomOffset: 0,
/** The drawn flag is false before the component is visually
* drawn, it's true after it's drawn.
**/
drawn: false,
/** The theme the component is constructed with. By default,
* uses the HThemeManager.currentTheme specified at the time
* of construction.
**/
theme: null,
/** The preserveTheme flag prevents the view from being redrawn
* if HThemeManager.currentTheme is changed after the view
* has been drawn. Is true, if theme has been set.
**/
preserveTheme: false,
/** The optimizeWidthOnRefresh flag, when enabled, allows
* automatic width calculation for components that support
* that feature.
**/
optimizeWidthOnRefresh: true,
/** The parent is the +_parent+ supplied to the constructor.
* This is a complete object reference to the parent's namespace.
**/
parent: null,
/** The parents is an array containing parent instances up to
* the root controller level. The root controller is almost
* always an instance of HApplication.
**/
parents: null,
/** The viewId is the unique ID (serial number) of this view.
* This means the view can be looked up globally based on its
* id by using the +HSystem.views+ array.
**/
viewId: null,
/** The appId is the unique ID (serial number) of the app process
* acting as the root controller of the view tree of which this
* view is a member.
* This means the app can be looked up globally based on this
* id by using the +HSystem.apps+ array.
**/
appId: null,
/** The app is the reference of the app process acting as
* the root controller of the view tree of which this view is a
* member.
* This is a complete object reference to the app's namespace.
**/
app: null,
/** The views array contains a list of subviews of this view
* by id. To access the object reference, use the +HSystem.views+
* array with the id.
**/
views: null,
/** The viewsZOrder array contains a list of subviews ordered by
* zIndex. To change the order, use the bringToFront,
* sendToBack, bringForwards, sendBackwards, bringToFrontOf and
* sentToBackOf methods.
**/
viewsZOrder: null,
/** The isHidden flog reflects the visibility of the view.
**/
isHidden: false,
/** The +HRect+ instance bound to +self+ using the +constructor+ or +setRect+.
**/
rect: null,
/** An reference to the options block given as the constructor
* parameter _options.
**/
options: null,
/** The viewDefaults is a HViewDefaults object that is extended
* in the constructor with the options block given. The format of
* it is an Object.
* It's only used when not extended via HControl, see HControl#controlDefaults.
**/
viewDefaults: HViewDefaults,
/** = Description
* Constructs the logic part of a HView.
* The view still needs to be drawn on screen. To do that, call draw after
* subcomponents of the view are initialized.
*
* = Parameters
* +_rect+:: An instance of +HRect+, defines the position and size of views.
* It can be also defined with an array, see below.
* +_parent+:: The parent instance this instance will be contained within.
* A valid parent can be another HView compatible instance,
* an HApplication instance, a HControl or a similar extended
* HView instance. The origin of the +_rect+ is the same as the
* parent's offset. For HApplication instances, the web browser's
* window's left top corner is the origin.
*
* == The +_rect+ dimensions as arrays
* Instead of an instance of +HRect+, dimensions can also be supplied as arrays.
* The array length must be either 4 or 6. If the length is 4, the dimensions are
* specified as follows: +[ x, y, width, height ]+. Note that this is different
* from the construction parameters of +HRect+ that takes the coordinates as two
* points, like: +( left, top, right, bottom )+.
* Arrays with 6 items are a bit more complex (and powerful) as they can specify
* the flexible offsets too.
*
* === The array indexes for a +_rect+ configured as an 4-item array:
* Always left/top aligned, all items must be specified.
* Index:: Description
* +0+:: The X-coordinate (measured from the parent's left edge)
* +1+:: The Y-coordinate (measured from the parent's top edge)
* +2+:: The width.
* +3+:: The height.
*
* === The array indexes a +_rect+ configured as an 6-item array:
* Can be any configuration of left/top/right/bottom alignment and supports
* flexible widths. At least 4 items must be specified.
* Index:: Description
* +0+:: The left-aligned X-coordinate or +null+ if the view is
* right-aligned and using a right-aligned X-coordinate at
* index +4+ as well as the width specified at index +2+.
* +1+:: The top-aligned Y-coordinate or +null+ if the view is
* bottom-aligned and using a right-aligned X-coordinate at
* index +5+ as well as the height specified at index +3+.
* +2+:: The width, if only one X-coordinate specifies the
* position (at indexes +0+ or +4+).
* If both X-coordinates (at indexes +0+ and +4+) are
* specified, the width can be specified with a +null+ for
* automatic (flexible) width. If the width is specified,
* it's used as the minimum width.
* +3+:: The height, if only one Y-coordinate specifies the
* position (at indexes +1+ or +5+).
* If both Y-coordinates (at indexes +1+ and +5+) are
* specified, the height can be specified with a +null+ for
* automatic (flexible) height. if the height is specified,
* it's used as the minimum height.
* +4+:: The right-aligned X-coordinate or +null+ if the view is
* left-aligned and using a left-aligned X-coordinate at
* index +0+ as well as the width specified at index +2+.
* +5+:: The bottom-aligned Y-coordinate or +null+ if the view is
* top-aligned and using a top-aligned X-coordinate at
* index +1+ as well as the height specified at index +3+.
* == Usage examples of +_rect+:
* Specified as two instances of +HPoint+,
* x: 23, y: 75, width: 200, height: 100:
* HRect.nu( HPoint.nu( 23, 75 ), HPoint.nu( 223, 175 ) )
*
* The same as above, but without +HPoint+ instances:
* HRect.nu( 23, 75, 223, 175 )
*
* The same as above, but with an array as the constructor
* parameter for +HRect+:
* HRect.nu( [ 23, 75, 223, 175 ] )
*
* The same as above, but with an array instead of a +HRect+ instance:
* [ 23, 75, 200, 100 ]
*
* The same as above, but with a 6-item array:
* [ 23, 75, 200, 100, null, null ]
*
* The same as above, but aligned to the right instead of left:
* [ null, 75, 200, 100, 23, null ]
*
* The same as above, but aligned to the right/bottom edges:
* [ null, null, 200, 100, 23, 75 ]
*
* The same as above, but aligned to the left/bottom edges:
* [ 23, null, 200, 100, null, 75 ]
*
* Flexible width (based on the parent's dimensions):
* [ 23, 75, null, 100, 23, null ]
*
* Flexible height (based on the parent's dimensions):
* [ 23, 75, 200, null, null, 75 ]
*
* Flexible width and height (based on the parent's dimensions):
* [ 23, 75, null, null, 23, 75 ]
*
* Flexible width and height, but limited to a minimum width
* of 200 and a minimum height of 100 (based on the parent's dimensions):
* [ 23, 75, 200, 100, 23, 75 ]
*
**/
constructor: function(_rect, _parent, _options) {
if( !_options ){
_options = {};
}
if(!this.isinherited){
_options = (this.viewDefaults.extend(_options)).nu(this);
}
this.options = _options;
this.label = _options.label;
// Moved these to the top to ensure safe theming operation
if( _options.theme ){
this.theme = _options.theme;
this.preserveTheme = true;
}
else if(!this.theme){
this.theme = HThemeManager.currentTheme;
this.preserveTheme = false;
}
else {
this.preserveTheme = true;
}
if(_options.visible === false) {
this.isHidden = true;
}
// adds the parentClass as a "super" object
this.parent = _parent;
this.viewId = this.parent.addView(this);
// the parent addView method adds this.parents
this.appId = this.parent.appId;
this.app = HSystem.apps[this.appId];
// subview-ids, index of HView-derived objects that are found in HSystem.views[viewId]
this.views = [];
// Subviews in Z order.
this.viewsZOrder = [];
// Keep the view (and its subviews) hidden until its drawn.
this._createElement();
// Set the geometry
this.setRect(_rect);
this._cachedLeft = _rect.left;
this._cachedTop = _rect.top;
// Additional DOM element bindings are saved into this array so they can be
// deleted from the element manager when the view gets destroyed.
this._domElementBindings = [];
if(!this.isinherited) {
this.draw();
}
},
/** = Description
* When the +_flag+ is true, the view will be aligned to the right.
* The +_px+ offset defines how many pixels from the parent's right
* edge the right edge of this view will be. If both setFlexRight
* and setFlexLeft are set, the width is flexible.
* Use the constructor or setRect instead of calling this method
* directly.
*
* = Parameters
* +_flag+:: Boolean flag (true/false). Enables
* right-alignment when true.
* +_px+:: The amount of pixels to offset from the right
* edge of the parent's right edge.
*
* = Returns
* +self+
**/
setFlexRight: function(_flag,_px){
if(_flag===undefined){_flag=true;}
this.flexRight = _flag;
if(_px===undefined){_px=0;}
this.flexRightOffset = _px;
return this;
},
/** = Description
* When the +_flag+ is true, the view will be aligned to the left (default).
* The +_px+ offset defines how many pixels from the parent's left
* edge the left edge of this view will be. If both setFlexLeft
* and setFlexRight are set, the width is flexible.
* Use the constructor or setRect instead of calling this method
* directly.
*
* = Parameters
* +_flag+:: Boolean flag (true/false). Enables
* left-alignment when true.
* +_px+:: The amount of pixels to offset from the left
* edge of the parent's left edge.
*
* = Returns
* +self+
**/
setFlexLeft: function(_flag,_px){
if(_flag===undefined){_flag=true;}
this.flexLeft = _flag;
if((_px || _px === 0) && this.rect){
this.rect.setLeft(_px);
}
return this;
},
/** = Description
* When the +_flag+ is true, the view will be aligned to the top (default).
* The +_px+ offset defines how many pixels from the parent's top
* edge the top edge of this view will be. If both setFlexTop
* and setFlexBottom are set, the height is flexible.
* Use the constructor or setRect instead of calling this method
* directly.
*
* = Parameters
* +_flag+:: Boolean flag (true/false). Enables
* top-alignment when true.
* +_px+:: The amount of pixels to offset from the top
* edge of the parent's top edge.
*
* = Returns
* +self+
**/
setFlexTop: function(_flag,_px){
if(_flag===undefined){_flag=true;}
this.flexTop = _flag;
if((_px || _px === 0) && this.rect){
this.rect.setTop(_px);
}
return this;
},
/** = Description
* When the +_flag+ is true, the view will be aligned to the bottom.
* The +_px+ offset defines how many pixels from the parent's bottom
* edge the bottom edge of this view will be. If both setFlexBottom
* and setFlexTop are set, the height is flexible.
* Use the constructor or setRect instead of calling this method
* directly.
*
* = Parameters
* +_flag+:: Boolean flag (true/false). Enables
* bottom-alignment when true.
* +_px+:: The amount of pixels to offset from the bottom
* edge of the parent's bottom edge.
*
* = Returns
* +self+
**/
setFlexBottom: function(_flag,_px){
if(_flag===undefined){_flag=true;}
this.flexBottom = _flag;
if(_px===undefined){_px=0;}
this.flexBottomOffset = _px;
return this;
},
/** = Description
* The +_flag+ enables or disables the absolute positioning mode.
* (It's enabled by default). If absolute positioning mode is
* off, the coordinate system has little or no effect.
*
* = Parameters
* +_flag+:: Boolean flag (true/false). Enables
* absolute positioning when true.
* Enables relative positioning when false.
*
* = Returns
* +self+
**/
setAbsolute: function(_flag){
if(_flag===undefined){_flag=true;}
this.isAbsolute = _flag;
return this;
},
/** = Description
* The +_flag+ enables or disables the relative positioning mode.
* (It's disabled by default). If relative positioning mode is
* on, the coordinate system has little or no effect.
*
* = Parameters
* +_flag+:: Boolean flag (true/false). Enables
* absolute relative when true.
* Enables absolute positioning when false.
*
* = Returns
* +self+
**/
setRelative: function(_flag){
if(_flag===undefined){_flag=true;}
this.isAbsolute = (!_flag);
return this;
},
/** = Description
* Used by html theme templates to get the theme-specific full image path.
*
* = Returns
* The full path of the theme-specific gfx path as a string.
**/
getThemeGfxPath: function() {
var _themeName;
if( this.preserveTheme ){
_themeName = this.theme;
} else {
_themeName = HThemeManager.currentTheme;
}
return HThemeManager._componentGfxPath( _themeName, this.componentName, this.themePath );
},
/** = Description
* Used by html theme templates to get the theme-specific full path
* of the _fileName given.
*
* = Returns
* The full path of the file.
**/
getThemeGfxFile: function( _fileName ) {
if( this.preserveTheme ){
_themeName = this.theme;
} else {
_themeName = HThemeManager.currentTheme;
}
return HThemeManager._componentGfxFile( _themeName, this.componentName, this.themePath, _fileName );
},
/** --
* = Description
* The _makeElem method does the ELEM.make call to create
* the
element of the component. It assigns the elemId.
* It's a separate method to ease creating component that require
* other element types.
* ++
**/
_makeElem: function(_parentElemId){
this.elemId = ELEM.make(_parentElemId,'div');
},
/** --
* = Description
* The _setCSS method does the initial styling of the element.
* It's a separate method to ease creating component that require
* other initial styles.
* ++
**/
_setCSS: function(_additional){
var _cssStyle = 'display:none;overflow:hidden;visibility:hidden;';
if(this.isAbsolute){
_cssStyle += 'position:absolute;';
} else {
_cssStyle += 'position:relative;';
}
_cssStyle += _additional;
ELEM.setCSS(this.elemId,_cssStyle);
},
/** --
* = Description
* The _getParentElemId method returns the ELEM ID of the parent.
* ++
**/
_getParentElemId: function(){
var _parentElemId;
// if the parent does not have an element:
if(this.parent.elemId === undefined) {
_parentElemId = 0;
}
// if a subview element is defined in the template, use it:
else if(this.parent.markupElemIds&&this.parent.markupElemIds['subview']){
_parentElemId = this.parent.markupElemIds['subview'];
}
// otherwise, use main elemId
else {
_parentElemId = this.parent.elemId;
}
return _parentElemId;
},
/** --
* = Description
* The _createElement method calls the methods required to initialize the
* main DOM element of the view.
* ++
**/
_createElement: function() {
if(!this.elemId) {
this._makeElem(this._getParentElemId());
this._setCSS('');
// Theme name == CSS class name
if(this.preserveTheme){
ELEM.addClassName( this.elemId, this.theme );
}
else {
ELEM.addClassName( this.elemId, HThemeManager.currentTheme );
}
}
},
/** = Description
* The +drawRect+ method refreshes the dimensions of the view.
* It needs to be called to affect changes in the rect.
* It enables the drawn flag.
*
* = Returns
* +self+
*
**/
drawRect: function() {
if (this.parent && this.rect.isValid) {
var _this = this,
_elemId = _this.elemId,
_styl = ELEM.setStyle,
_rect = _this.rect;
_styl( _elemId, 'left', _this.flexLeft?(_rect.left+'px'):'auto', true);
_styl( _elemId, 'top', _this.flexTop?(_rect.top+'px'):'auto', true);
_styl( _elemId, 'right', _this.flexRight?(_this.flexRightOffset+'px'):'auto', true);
_styl( _elemId, 'bottom', _this.flexBottom?(_this.flexBottomOffset+'px'):'auto', true);
_styl( _elemId, 'width', (_this.flexLeft&&_this.flexRight)?'auto':(_rect.width+'px'), true);
_styl( _elemId, 'height', (_this.flexTop&&_this.flexBottom)?'auto':(_rect.height+'px'), true);
// Show the rectangle once it gets created, unless visibility was set to
// hidden in the constructor.
if(_this.isHidden === undefined || _this.isHidden === false) {
_styl( _elemId, 'visibility', 'inherit', true);
}
_styl( _elemId, 'display', _this.displayMode, true);
_this._updateZIndex();
if (_this._cachedLeft !== _rect.left || _this._cachedTop !== _rect.top) {
_this.invalidatePositionCache();
_this._cachedLeft = _rect.left;
_this._cachedTop = _rect.top;
}
_this.drawn = true;
}
return this;
},
/** --
* This method updates the z-index property of the children of self.
* It's essentially a wrapper for HSystem.updateZIndexOfChildren passed
* with the viewId of self.
* ++
**/
_updateZIndex: function() {
HSystem.updateZIndexOfChildren(this.viewId);
},
/** --
* This method updates the z-index property of the siblings of self.
* It's essentially a wrapper for HSystem.updateZIndexOfChildren passed
* with the parent's viewId of self.
* ++
**/
_updateZIndexAllSiblings: function() {
HSystem.updateZIndexOfChildren(this.parent.viewId);
},
/** = Description
* The higher level draw wrapper for drawRect, drawMarkup and drawSubviews.
* Finally calls refresh.
*
* = Returns
* +self+
*
**/
draw: function() {
var _isDrawn = this.drawn;
this.drawRect();
if(!_isDrawn){
this.firstDraw();
if(this['componentName']!==undefined){
this.drawMarkup();
}
this.drawSubviews();
if(this.options.style){
var
_style = this.options.style,
_styleItem, _styleKey, _styleValue, i = 0;
for(;i<_style.length;i++){
_styleItem = _style[i];
_styleKey = _styleItem[0];
_styleValue = _styleItem[1];
this.setStyle(_styleKey,_styleValue);
}
}
if(this.options.html){
this.setHTML(this.options.html);
}
if(!this.isHidden){
this.show();
}
}
this.refresh();
return this;
},
/** = Description
* Called once, before the layout of the view is initially drawn.
* Doesn't do anything by itself, but provides an extension point.
*
**/
firstDraw: function(){
},
/** = Description
* Called once, when the layout of the view is initially drawn.
* Doesn't do anything by itself, but provides an extension point for making
* subviews.
*
**/
drawSubviews: function(){
},
/** --
* Loads the markup from theme manager. If this.preserveTheme is set to true,
* the this.theme is used for loading the markup. Otherwise the currently
* active theme is used.
* ++
**/
_loadMarkup: function() {
var _themeName, _markup;
if (this.preserveTheme) {
_themeName = this.theme;
}
else {
_themeName = HThemeManager.currentTheme;
}
_markup = HThemeManager.getMarkup( _themeName, this.componentName, this.themePath );
if(_markup === false){
console.log('Warning: Markup template for "'+this.componentName+'" using theme "'+_themeName+'" not loaded.');
}
this.markup = _markup;
return (_markup !== false);
},
/** = Description
* Replaces the contents of the view's DOM element with html from the theme specific html file.
*
* = Returns
* +self+
**/
markupElemNames: ['bg', 'label', 'state', 'control', 'value', 'subview'],
drawMarkup: function() {
ELEM.setStyle(this.elemId, 'display', 'none', true);
// continue processing from here on:
var _markupStatus = this._loadMarkup();
this.bindMarkupVariables();
ELEM.setHTML(this.elemId, this.markup);
this.markupElemIds = {};
for(var i=0; i < this.markupElemNames.length; i++ ) {
var _partName = this.markupElemNames[ i ],
_elemName = _partName + this.elemId,
_htmlIdMatch = ' id="' + _elemName + '"';
if( this.markup.indexOf( _htmlIdMatch ) !== -1 ) {
this.markupElemIds[ _partName ] = this.bindDomElement( _elemName );
}
}
ELEM.setStyle(this.elemId, 'display', this.displayMode );
return this;
},
/** = Description
* Replaces the contents of the view's DOM element with custom html.
*
* = Parameters
* +_html+:: The HTML (string-formatted) to replace the content with.
*
* = Returns
* +self+
*
**/
setHTML: function( _html ) {
ELEM.setHTML( this.elemId, _html );
return this;
},
/** = Description
* Wrapper for setHTML, sets escaped html, if tags and such are present.
*
* = Parameters
* +_text+:: The text to set. If it contains any html, it's escaped.
*
* = Returns
* +self+
**/
setText: function( _text ) {
return this.setHTML( this.escapeHTML( _text ) );
},
/** = Description
* Method to escape HTML from text.
*
* Converts < to < and > to > and & to &
*
* = Parameters
* +_html+:: The html to escape.
*
* = Returns
* A string with the html escaped.
**/
escapeHTML: function( _html ) {
return _html.replace(/&/gmi, '&').replace(/>/gmi, '>').replace(/ instance with identical values to this component's rect.
*
**/
bounds: function() {
// Could be cached.
var _bounds = new HRect(this.rect);
_bounds.right -= _bounds.left;
_bounds.left = 0;
_bounds.bottom -= _bounds.top;
_bounds.top = 0;
return _bounds;
},
/** = Description
* This method resizes the view, without moving its left and top sides.
* It adds horizontal coordinate units to the width and vertical units to
* the height of the view.
* Since a View's frame rectangle must be aligned on screen pixels, only
* integral values should be passed to this method. Values with
* fractional components will be rounded to the nearest whole integer.
* If the View is attached to a window, this method causes its parent view
* to be updated, so the View is immediately displayed in its new size. If it
* doesn't have a parent or isn't attached to a window, this method
* merely alter its frame and bounds rectangle.
*
* = Parameters
* +_horizonal+:: Horizonal units to add to the width (negative units subtract)
* +_vertical+:: Vertical units to add to the height (negative units subtract)
*
* = Returns
* +self+
*
**/
resizeBy: function(_horizontal, _vertical) {
var _rect = this.rect;
_rect.right += _horizontal;
_rect.bottom += _vertical;
_rect.updateSecondaryValues();
this.drawRect();
return this;
},
/** = Description
* This method makes the view width units wide
* and height units high. This method adjust the right and bottom
* components of the frame rectangle accordingly.
* Since a View's frame rectangle must be aligned on screen pixels, only
* integral values should be passed to this method. Values with
* fractional components will be rounded to the nearest whole integer.
* If the View is attached to a window, this method causes its parent view
* to be updated, so the View is immediately displayed in its new size. If it
* doesn't have a parent or isn't attached to a window, this method
* merely alter its frame and bounds rectangle.
*
* = Parameters
* +_width+:: The new width of the view.
* +_height+:: The new height of the view.
*
* = Returns
* +self+
*
**/
resizeTo: function(_width, _height) {
var _rect = this.rect;
_rect.right = _rect.left + _width;
_rect.bottom = _rect.top + _height;
_rect.updateSecondaryValues();
this.drawRect();
return this;
},
/** = Descripion
* This method moves the view to a new coordinate. It adjusts the
* left and top components of the frame rectangle accordingly.
* Since a View's frame rectangle must be aligned on screen pixels, only
* integral values should be passed to this method. Values with
* fractional components will be rounded to the nearest whole integer.
* If the View is attached to a window, this method causes its parent view
* to be updated, so the View is immediately displayed in its new size. If it
* doesn't have a parent or isn't attached to a window, this method
* merely alter its frame and bounds rectangle.
*
* = Parameters
* +_x+:: The new x-coordinate of the view.
* +_y+:: The new y-coordinate of the view.
*
* +_point+:: The new coordinate point of the view.
*
* = Returns
* +self+
*
**/
offsetTo: function() {
this.rect.offsetTo.apply(this.rect, arguments);
this.drawRect();
return this;
},
/** = Description
* Alias method for offsetTo.
*
* = Returns
* +self+
*
**/
moveTo: function() {
this.offsetTo.apply(this, arguments);
return this;
},
/** = Description
* This method re-positions the view without changing its size.
* It adds horizontal coordinate units to the x coordinate and vertical
* units to the y coordinate of the view.
* Since a View's frame rectangle must be aligned on screen pixels, only
* integral values should be passed to this method. Values with
* fractional components will be rounded to the nearest whole integer.
* If the View is attached to a window, this method causes its parent view
* to be updated, so the View is immediately displayed in its new size. If it
* doesn't have a parent or isn't attached to a window, this method
* merely alter its frame and bounds rectangle.
*
* = Parameters
* +_horizonal+:: Horizonal units to change the x coordinate (negative units subtract)
* +_vertical+:: Vertical units to add to change the y coordinate (negative units subtract)
*
* = Returns
* +self+
*
**/
offsetBy: function(_horizontal, _vertical) {
this.rect.offsetBy(_horizontal, _vertical);
this.drawRect();
return this;
},
/** = Description
* Alias method for offsetBy.
*
* = Returns
* +self+
*
**/
moveBy: function() {
this.offsetBy.apply(this, arguments);
return this;
},
/** = Description
* Brings the view to the front by changing its Z-Index.
*
* = Returns
* +self+
*
**/
bringToFront: function() {
if (this.parent) {
var _index = this.zIndex();
this.parent.viewsZOrder.splice(_index, 1);
this.parent.viewsZOrder.push(this.viewId);
this._updateZIndexAllSiblings();
}
return this;
},
/** = Description
* Brings itself to the front of the given view by changing its Z-Index.
* Only works on sibling views.
*
* = Parameters
* +_view+:: The view to bring to the front of.
*
* = Returns
* +self+
*
**/
bringToFrontOf: function(_view){
if(this.parent.viewId === _view.parent.viewId){
this.parent.viewsZOrder.splice( this.zIndex(), 1 ); // removes selfs index from the array
this.parent.viewsZOrder.splice( _view.zIndex()+1, 0, this.viewId); // sets itself in front of to _view
this._updateZIndexAllSiblings();
}
return this;
},
/** = Description
* Sends itself to the back of the given view by changing its Z-Index.
* Only works on sibling views.
*
* = Parameters
* +_view+:: The view to send to the back of.
*
* = Returns
* +self+
*
**/
sendToBackOf: function(_view){
if(this.parent.viewId === _view.parent.viewId){
this.parent.viewsZOrder.splice( this.zIndex(), 1 ); // removes selfs index from the array
this.parent.viewsZOrder.splice( _view.zIndex(), 0, this.viewId); // sets itself in back of to _view
this._updateZIndexAllSiblings();
}
return this;
},
/** = Description
* Sends itself one step backward by changing its Z-Index.
*
* = Returns
* +self+
*
**/
sendBackward: function(){
var _index = this.zIndex();
if(_index!==0){
this.parent.viewsZOrder.splice( _index, 1 ); // removes selfs index from the array
this.parent.viewsZOrder.splice( _index-1, 0, this.viewId); // moves selfs position to one step less than where it was
this._updateZIndexAllSiblings();
}
return this;
},
/** = Description
* Brings itself one step forward by changing its Z-Index.
*
* = Returns
* +self+
*
**/
bringForward: function(){
var _index = this.zIndex();
if(_index!==this.parent.viewsZOrder.length-1){
this.parent.viewsZOrder.splice( _index, 1 ); // removes selfs index from the array
this.parent.viewsZOrder.splice( _index+1, 0, this.viewId); // moves selfs position to one step more than it was
this._updateZIndexAllSiblings();
}
return this;
},
/** = Description
* Sends the view to the back by changing its Z-Index.
*
* = Returns
* +self+
*
**/
sendToBack: function() {
if (this.parent) {
var _index = this.zIndex();
this.parent.viewsZOrder.splice(_index, 1); // removes this index from the arr
this.parent.viewsZOrder.splice(0, 0, this.viewId); // unshifts viewId
this._updateZIndexAllSiblings();
}
return this;
},
/** = Description
* Use this method to get the Z-Index of itself.
*
* = Returns
* The current Z-Index value.
*
**/
zIndex: function() {
if (!this.parent) {
return -1;
}
// Returns the z-order of this item as seen by the parent.
return this.parent.viewsZOrder.indexOf(this.viewId);
},
/** = Description
* Measures the characters encoded in length bytes of the string - or,
* if no length is specified, the entire string up to the null character,
* '0', which terminates it. The return value totals the width of all the
* characters in coordinate units; it's the length of the baseline required
* to draw the string.
*
* = Parameters
* +_string+:: The string to measure.
* +_length+:: Optional, How many characters to count.
* +_elemId+:: Optional, The element ID where the temporary string is created
* in.
* +_wrap+:: Optional boolean value, wrap whitespaces?
* +_extraCss+:: Optional, extra css to add.
*
* = Returns
* The width in pixels required to draw a string in the font.
*
**/
stringSize: function(_string, _length, _elemId, _wrap, _extraCss) {
if (_length || _length === 0) {
_string = _string.substring(0, _length);
}
if (!_elemId && _elemId !== 0) {
_elemId = 0; //this.elemId;
}
if (!_extraCss) {
_extraCss = '';
}
if (!_wrap){
_extraCss += 'white-space:nowrap;';
}
var _stringElem = ELEM.make(_elemId,'span');
ELEM.setCSS(_stringElem, "visibility:hidden;"+_extraCss);
ELEM.setHTML(_stringElem, _string);
// ELEM.flushLoop();
var _visibleSize=ELEM.getVisibleSize(_stringElem);
// console.log('visibleSize',_visibleSize);
ELEM.del(_stringElem);
return [_visibleSize[0]+_visibleSize[0]%2,_visibleSize[1]+_visibleSize[1]%2];
},
/** Returns the string width
**/
stringWidth: function(_string, _length, _elemId, _extraCss){
return this.stringSize(_string, _length, _elemId, false, _extraCss)[0];
},
/** Returns the string height.
**/
stringHeight: function(_string, _length, _elemId, _extraCss){
return this.stringSize(_string, _length, _elemId, true, _extraCss)[1];
},
/** Returns the X coordinate that has the scrolled position calculated.
**/
pageX: function() {
var _x = 0,
_elem = this;
while(_elem) {
if(_elem.elemId && _elem.rect) {
_x += ELEM.get(_elem.elemId).offsetLeft;
_x -= ELEM.get(_elem.elemId).scrollLeft;
}
if(_elem.markupElemIds&&_elem.markupElemIds.subview){
_x += ELEM.get(_elem.markupElemIds.subview).offsetLeft;
_x -= ELEM.get(_elem.markupElemIds.subview).scrollLeft;
}
_elem = _elem.parent;
}
return _x;
},
/** = Description
* Sets the label on a control component: the text that's displayed in
* HControl extensions. Visual functionality is implemented in component
* theme templates and refreshLabel method extensions.
*
* Avoid extending directly, extend +refreshLabel+ instead.
*
* = Parameters
* +_label+:: The text the component should display.
*
* = Returns
* +self+
*
**/
setLabel: function(_label) {
if(this.escapeLabelHTML){
_label = this.escapeHTML( _label );
}
var _this = this,
_differs = (_label !== _this.label);
if(_differs){
_this.label = _label;
_this.options.label = _label;
_this.refresh();
}
return this;
},
/** = Description
* Called when the +self.label+ has been changed. By default
* tries to update the label element defined in the theme of
* the component. Of course, the HControl itself doesn't
* define a theme, so without a theme doesn't do anything.
*
* = Returns
* +self+
*
**/
refreshLabel: function(){
if(this.markupElemIds){
if(this.markupElemIds['label']){
ELEM.setHTML(this.markupElemIds.label,this.label);
}
}
return this;
},
/** Returns the Y coordinate that has the scrolled position calculated.
**/
pageY: function() {
var _y = 0,
_elem = this;
while(_elem) {
if(_elem.elemId && _elem.rect) {
_y += ELEM.get(_elem.elemId).offsetTop;
_y -= ELEM.get(_elem.elemId).scrollTop;
}
if(_elem.markupElemIds&&_elem.markupElemIds.subview){
_y += ELEM.get(_elem.markupElemIds.subview).offsetTop;
_y -= ELEM.get(_elem.markupElemIds.subview).scrollTop;
}
_elem = _elem.parent;
}
return _y;
},
/** Returns the HPoint that has the scrolled position calculated.
**/
pageLocation: function() {
return new HPoint(this.pageX(), this.pageY());
},
/** = Description
* An abstract method that derived classes may implement, if they are able to
* resize themselves so that their content fits nicely inside.
* Similar to pack, might be renamed when components are written to
* be savvy of this feature.
**/
optimizeWidth: function() {
},
/** = Description
* Invalidates event manager's element position cache for this view and its
* subviews. Actual functionality is implemented in HControl.
*
* = Returns
* +self+
*
**/
invalidatePositionCache: function() {
for(var i=0; i -1) {
ELEM.del(_elementId);
this._domElementBindings.splice(_indexOfElementId, 1);
}
}
});
HView.implement(HMarkupView);
HView.implement(HMorphAnimation);