/* 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);