
/**
 * JavaScript Object Oriented Cross-Browser control wrapper library.
 *
 * Some stuff is taken from:
 *    http://www.quirksmode.org
 *    http://evolt.org
 *    http://www.faqts.com/knowledge_base/view.phtml/aid/13562
 *    http://www.howtocreate.co.uk/tutorials/javascript/browserwindow
 */

window.jooxbrLoaded = false; // we gonna load for long

//----- Auxilary functions -----

function def( val, defval )
{
	return val == null ? defval : val;
};

function rng( val, minval /* = null */, maxval /* = null */ )
{
	if( minval != null && maxval != null && maxval < minval )
	{
		maxval = minval;
	}
	if( minval != null && val < minval )
	{
		val = minval;
	}
	if( maxval != null && val > maxval )
	{
		val = maxval;
	}
	return val;
};

/**
 * Returns javascript object for the element with the specified id and of the specified parent window
 *
 * @param id {String}
 * @param parent {Object}		Window reference
 * @return HTMLElement
 */
function el( id, parent )
{
	if( typeof( id ) == 'object' )
	{
		return id;
	}
	parent = def( parent, window );
	if( parent.document.getElementById )
	{
		return parent.document.getElementById( id );
	}
	else if( parent.document.all )
	{
		return parent.document.all[ id ];
	}
	else if( parent.document.layers )
	{
		var out = parent.document.layers[ id ];
		out.style = out;
		return out;
	}
	return null;
};

/**
 * Alias to the el() function
 */
var _ = el;

function els( name, parent )
{
	parent = def( parent, window );
	return parent.document.getElementsByName( name );
};

/**
 * Returns control object wrapped in corresponding class. Supports object caching
 * to speed up consequent ctl() calls with the same id.
 *
 * @param {String or Object} control		HTML object id or object reference
 * @param {String} [type]						Forced contorl type or null to detect automatically
 * @return CHTMLObject						Actually one of the derivates of the CHTMLObject
 */
function ctl( control, type )
{
	if( typeof( control ) == 'string' || typeof( control ) == 'number' ) // object id passed
	{
		var controlDOM = el( control );
		if( controlDOM == null )
		{
			throw new Error( 'No object exists with the id \'' + control + '\'!' );
		}
		control = controlDOM;
	}
	if( control == null )
	{
		throw new Error( 'Null object supplied!' );
	}
	var objClass = Aux.getCtlClass( type == null ? Aux.getCtlType( control ) : type );
	if( String( control.id ) != '' && type == null /* cache only objects with type autodetection */ )
	{
		if( Aux.ctlCache[ control.id ] == null || Aux.ctlCache[ control.id ].dom() != control /* case when we have 2+ controls with same id */ )
		{
			Aux.ctlCache[ control.id ] = new objClass( control );
		}
		return Aux.ctlCache[ control.id ];
	}
	return new objClass( control );
};

var $ = ctl;

var CAux = function()
{

	this.ctlCache = {};
	
	this.triggerUnsupported = function( features )
	{
		if( window.jooxbrUnsupportedHandler )
		{
			window.jooxbrUnsupportedHander( features );
		}
	};

	/**
	* Returns type of the supplied control object
	*
	* @param ctl Object
	* @return String
	*/
	this.getCtlType = function( ctl )
	{
	    var nodeName = (ctl.nodeName!=null)?ctl.nodeName.toLowerCase():'';	
		switch(nodeName)
		{
			case 'input':
				switch( ctl.type.toLowerCase() )
				{
					case 'checkbox':
					case 'check':
						return 'check';

					case 'radio':
						return 'radio';

					case 'reset':
					case 'submit':
					case 'button':
						return 'button';

					case 'text':
					case 'password':
					case 'hidden':
						return 'textfield';
						
					case 'file':
						return 'file';
				}
			break;

			case 'textarea':
				return 'textfield';

			case 'select':
				if( ctl.size == '1' )
				{
					return 'combo';
				}
				else
				{
					return 'list';
				}
			break;

			case 'button':
				return 'button';
				
			case 'img':
				return 'image';
		}
		return nodeName;
	};

	this.getCtlClass = function( type )
	{
		switch( type )
		{
			case 'button':
				return CButton;

			case 'list':
			case 'combo':
				return CList;

			case 'check':
			case 'radio':
				return CCheckBox;

			case 'textfield':
				return CTextfield;

			case 'file':
				return CFile;

			case 'image':
				return CImage;
			
			default:
				return CHTMLObject;
		}
	};

};
var Aux = new CAux();

//----------------------------------------------------------------------------------------------------------

function dumpHelper( obj, level, out, maxLevel, showTypes )
{
	var pre = '';
	for( var i = 0; i < level; i++ )
	{
		pre += '   ';
	}
	for( var el in obj )
	{
		if( typeof( obj[ el ] ) == 'object' && level + 1 <= maxLevel )
		{
			return dumpHelper( obj[ el ], level + 1, out + pre + el + ( showTypes ? '[ ' + typeof( obj[ el ] ) + ' ]' : '' ) + ' :\n', maxLevel, showTypes );
		}
		else
		{
			out += pre + el + ( showTypes ? '[ ' + typeof( obj[ el ] ) + ' ]' : '' ) + ' = ' + obj[ el ] + '\n';
		}
	}
	return out;
};

function dump( obj, maxLevel /* = 2 */, showTypes /* = false */ )
{
	maxLevel = def( maxLevel, 2 );
	showTypes = def( showTypes, false );
	if( obj == null )
	{
		alert( 'Null object supplied!' );
		return;
	}
	return dumpHelper( obj, 0, '', maxLevel, showTypes );
};

//----------------------------------------------------------------------------------------------------------

Array.prototype.contains = function( val )
{
	for( var el in this )
	{
		if( this[ el ] == val )
		{
			return true;
		}
	}
	return false;
};

String.prototype.ltrim = function()
{
	var str = String( this );
	for( var i = 0; i < str.length && ( str.charAt( i ) == ' ' || str.charAt( i ) == '\t' || str.charAt( i ) == '\n' || str.charAt( i ) == '\r' ); i++ );
	if( i )
	{
		str = str.substr( i );
	}
	return str;
};

String.prototype.rtrim = function()
{
	var str = String( this );
	for( var i = str.length - 1; i >= 0 && ( str.charAt( i ) == ' ' || str.charAt( i ) == '\t' || str.charAt( i ) == '\n' || str.charAt( i ) == '\r' ); i-- );
	if( i != str.length - 1 )
	{
		str = str.substring( 0, i + 1 );
	}
	return str;
};

/**
 * Removes whitespace from the start and the end of the passed string
 * 
 * @return {String}
 */
String.prototype.trim = function()
{
	return this.ltrim().rtrim();
};

var Callback = function( obj, method, args )
{

	// constructor {
		this.obj = obj;
		if( typeof( method ) == 'string' )
		{
			if( obj == null )
			{
				this.method = window[ method ];
			}
			else
			{
				this.method = obj[ method ];
			}
		}
		else
		{
			this.method = method;
		}
		this.args = args;
		this.intervalId = 0;
		this.timeoutId = 0;
	// }

	this.call = function( /* arg1, arg2, ... */ )
	{
		if( this.method == null )
		{
			return null;
		}
		var args = [];
		if( this.args.length )
		{
			args = this.args;
		}
		if( arguments.length )
		{
			args = [];
			for( var i = 1; i < arguments.length; i++ )
			{
				args.push( arguments[ i ] );
			}
			for( var i = 0; i < args.length; i++ )
			{
				if( args[ i ].length == 1 && typeof( args[ i ][ 0 ] ) == 'string' && args[ i ][ 0 ] == '%%args%%' ) // it somewhat strictier than it should be, but I don't wanna replace incorrect parameter
				{
					args = args.slice( 0, i ).concat( this.args ).concat( args.slice( i + 1 ) );
					break;
				}
			}
		}
		return this.method.apply( obj, args );
	};

	this.callInterval = function( interval /*, arg1, arg2, ... */ )
	{
		var args = [];
		for( var i = 1; i < arguments.length; i++ )
		{
			args.push( arguments[ i ] );
		}
		this.intervalId = setInterval( this.onInterval, interval, this, args );
	};

	this.isIntervalActive = function()
	{
		return this.intervalId != 0;
	};

	this.clearCallInterval = function()
	{
		clearInterval( this.intervalId );
		this.intervalId = 0;
	};

	this.onInterval = function( obj, args )
	{
		obj.call.apply( obj, args );
	};

	this.callTimeout = function( timeout /*, arg1, arg2, ... */ )
	{
		var args = [];
		for( var i = 1; i < arguments.length; i++ )
		{
			args.push( arguments[ i ] );
		}
		this.timeoutId = setTimeout( this.onTimeout, timeout, this, args );
	};

	this.isTimeoutActive = function()
	{
		return this.timeoutId != 0;
	};

	this.clearCallTimeout = function()
	{
		clearTimeout( this.timeoutId );
		this.timeoutId = 0;
	};

	this.onTimeout = function( obj, args )
	{
		obj.clearCallTimeout();
		obj.call.apply( obj, args );
	};

};

//----------------------------------------------------------------------------------------------------------

var Browser = {
	  agent : navigator.userAgent
 	, _lagent : navigator.userAgent.toLowerCase()
};
Browser.isGecko = Browser._lagent.indexOf( 'gecko' ) != -1;
Browser.isIE = Browser._lagent.indexOf( 'msie' ) != -1;
Browser.isOpera = Browser._lagent.indexOf( 'opera' ) != -1;

//----------------------------------------------------------------------------------------------------------

var CDOMNode = function( node )
{

	// constructor {
		this._node = def( node );
	// }

	this.childNodes = function()
	{
		var out = [];
		for( var i = 0; i < this._node.childNodes.length; i++ )
		{
			if( this._node.childNodes[ i ].nodeType == 1 )
			{
				out.push( this._node.childNodes[ i ] );
			}
		}
		return out;
	};

};

//----------------------------------------------------------------------------------------------------------

var CEvent = function( evt )
{

	// constructor {
		this._evt = def( evt, window.event );
	// }

	/**
	 * Returns the reference to the control that fired the event.
	 *
	 * @return Object
	 */
	this.targetDOM = function()
	{
		var out = null;
		if( this._evt.target )
		{
			out = this._evt.target;
		}
		else if( this._evt.srcElement)
		{
			out = this._evt.srcElement;
		}
		if( out && out.nodeType == 3 ) // fix Safari bug
		{
			return out.parentNode;
		}
		return out;
	};

	this.target = function()
	{
		return ctl( this.targetDOM() );
	};

	this.fromElementDOM = function()
	{
		if( this._evt.relatedTarget )
		{
			return this._evt.relatedTarget;
		}
		else if( this._evt.fromElement )
		{
			return this._evt.fromElement;
		}
		return null;
	};

	this.fromElement = function()
	{
		var obj = this.fromElementDOM();
		if( obj )
		{
			return ctl( obj );
		}
		return null;
	};

	this.toElementDOM = function()
	{
		if( this._evt.relatedTarget )
		{
			return this._evt.relatedTarget;
		}
		else if( this._evt.toElement )
		{
			return this._evt.toElement;
		}
		return null;
	};

	this.toElement = function()
	{
		var obj = this.toElementDOM();
		if( obj )
		{
			return ctl( obj );
		}
		return null;
	};

	/**
	 * clientX/Y -- relative to the control that fired the event
	 */
	this.clientX = function()
	{
/*		if( this._evt.offsetX )
		{
			return this._evt.offsetX;
		}*/
		return this.pageX() - this.target().pageX();
	};

	this.clientY = function()
	{
/*		if( this._evt.offsetY )
		{
			return this._evt.offsetY;
		}*/
		return this.pageY() - this.target().pageY();
	};

	/**
	 * viewX/Y -- relative to the current viewport of the browser (maybe be not cross-browser)
	 */
	this.viewX = function()
	{
		return this._evt.clientX;
	};

	this.viewY = function()
	{
		return this._evt.clientY;
	};

	/**
	 * screenX/Y -- relative to the whole screen
	 */
	this.screenX = function()
	{
		return this._evt.screenX;
	};

	this.screenY = function()
	{
		return this._evt.screenY;
	};

	/**
	 * pageX/Y -- relative to the document origin disregarding any scrolling
	 */
	this.pageX = function()
	{
		if( this._evt.pageX )
		{
			return this._evt.pageX;
		}
		else if( this._evt.clientX )
		{
			return this._evt.clientX + Window.scrollLeft() - ( document.documentElement.clientLeft || 0 );
		}
		return 0;
	};

	this.pageY = function()
	{
		if( this._evt.pageY )
		{
			return this._evt.pageY;
		}
		else if( this._evt.clientY )
		{
			return this._evt.clientY + Window.scrollTop() - ( document.documentElement.clientTop || 0 );
		}
		return 0;
	};

	this.keyCode = function()
	{
		if( this._evt.keyCode)
		{
			return this._evt.keyCode;
		}
		else if( this._evt.which )
		{
			return this._evt.which;
		}
		return 0;
	};

	this.keyChar = function()
	{
		return String.fromCharCode( this.keyCode() );
	};

	this.leftButton = function()
	{
		if( this._evt.which )
		{
			return this._evt.which == 1;
		}
		else if( this._evt.button )
		{
			if( Browser.isGecko )
			{
				return this._evt.button == 0;
			}
			else
			{
				return Boolean( this._evt.button & 1 );
			}
		}
		return false;
	};

	this.rightButton = function()
	{
		if( this._evt.which )
		{
			return this._evt.which == 3;
		}
		else if( this._evt.button )
		{
			return Boolean( this._evt.button & 2 );
		}
		return false;
	};

	this.middleButton = function()
	{
		if( this._evt.which )
		{
			return this._evt.which == 2;
		}
		else if( this._evt.button )
		{
			if( Browser.isGecko )
			{
				return this._evt.button == 1;
			}
			else
			{
				return Boolean( this._evt.button & 4 );
			}
		}
		return false;
	};

	this.mouseButtons = function()
	{
		return this.leftButton() * 1 | this.rightButton() * 2 | this.middleButton() * 4;
	};

};

//----------------------------------------------------------------------------------------------------------

var CWindow = function( wnd )
{

	// constructor {
		this._wnd = def( wnd, window );
	// }

	this.innerWidth = function()
	{
		if( this._wnd.innerWidth ) // Non-IE
		{
			return this._wnd.innerWidth;
		}
		else if( this._wnd.document.documentElement && this._wnd.document.documentElement.clientWidth ) // IE 6+ in 'standards compliant mode'
		{
			return this._wnd.document.documentElement.clientWidth;
		}
		else if( this._wnd.document.body && this._wnd.document.body.clientWidth ) // IE 4 compatible
		{
			return this._wnd.document.body.clientWidth;
		}
		return 0;
	};

	this.innerHeight = function()
	{
		if( this._wnd.innerHeight ) // Non-IE
		{
			return this._wnd.innerHeight;
		}
		else if( this._wnd.document.documentElement && this._wnd.document.documentElement.clientHeight ) // IE 6+ in 'standards compliant mode'
		{
			return this._wnd.document.documentElement.clientHeight;
		}
		else if( this._wnd.document.body && this._wnd.document.body.clientHeight ) // IE 4 compatible
		{
			return this._wnd.document.body.clientHeight;
		}
		return 0;
	};
	
	this.setScrollLeft = function( val )
	{
		this.scrollTo( val, this.scrollTop() );
	}

	this.scrollLeft = function()
	{
		if( this._wnd.pageXOffset ) // Netscape compliant
		{
			return this._wnd.pageXOffset;
		}
		else if( this._wnd.document.body && this._wnd.document.body.scrollLeft ) // DOM compliant
		{
			return this._wnd.document.body.scrollLeft;
 		}
		else if( this._wnd.document.documentElement && this._wnd.document.documentElement.scrollLeft ) // IE6 standards compliant mode
		{
			return this._wnd.document.documentElement.scrollLeft;
		}
		return 0;
	};
	
	this.setScrollTop = function( val )
	{
		this.scrollTo( this.scrollLeft(), val );
	}
	
	this.scrollTop = function()
	{
		if( this._wnd.pageYOffset ) // Netscape compliant
		{
			return this._wnd.pageYOffset;
		}
		else if( this._wnd.document.body && this._wnd.document.body.scrollTop ) // DOM compliant
		{
			return this._wnd.document.body.scrollTop;
 		}
		else if( this._wnd.document.documentElement && this._wnd.document.documentElement.scrollTop ) // IE6 standards compliant mode
		{
			return this._wnd.document.documentElement.scrollTop;
		}
		return 0;
	};
	
	this.scrollTo = function( x, y )
	{
		this._wnd.scrollTo( x, y );
	};

	this.scrollBy = function( x, y )
	{
		this._wnd.scrollBy( x, y );
	}

	this.setLocation = function( url )
	{
		this._wnd.location = url;
	};

	this.location = function()
	{
		return this._wnd.location;
	};

};

var Window = new CWindow();

//----------------------------------------------------------------------------------------------------------

var CDocument = function( doc )
{

	// constructor {
		this._doc = def( doc, window.document );
	// }

	this.write = function( text )
	{
		this._doc.open();
		this._doc.write( text );
	};

	this.writeLine = function( text )
	{
		this.write( text + '<br />\n' );
	};

	this.setTitle = function( text )
	{
		this._doc.title = text;
	};

	this.title = function()
	{
		return this._doc.title;
	};

	this.width = function()
	{
		if( this._doc.scrollWidth )
		{
			return this._doc.scrollWidth;
		}
		else if( this._doc.width )
		{
			return this._doc.width;
		}
		else if( this._doc.body.clientWidth )
		{
			return this._doc.body.clientWidth;
		}
		return 0;
	};

	this.height = function()
	{
		if( this._doc.scrollHeight )
		{
			return this._doc.scrollHeight;
		}
		else if( this._doc.height )
		{
			return this._doc.height;
		}
		else if( this._doc.body.clientHeight )
		{
			return this._doc.body.clientHeight;
		}
		return 0;
	};

};

var Document = new CDocument();

//----------------------------------------------------------------------------------------------------------

var CScreen = function()
{
	
	// constructor {
		this._dpix = 96;
		this._dpiy = 96;
	// }
	
	this.initDPI = function( testObj )
	{
		testObj = ctl( testObj );
		testObj._elem.style.position = 'absolute';
		testObj._elem.style.left = '1in';
		testObj._elem.style.top = '1in';
		this._dpix = testObj.x();
		this._dpiy = testObj.y();
		testObj.setVisible( false );
	};
	
	this.DPI = function()
	{
		return this.XDPI();
	};
	
	this.XDPI = function()
	{
		if( screen.logicalXDPI )
		{
			return screen.logicalXDPI;
		}
		return this._dpix;
	};
	
	this.YDPI = function()
	{
		if( screen.logicalYDPI )
		{
			return screen.logicalYDPI;
		}
		return this._dpiy;
	};
	
};

var Screen = new CScreen();

//----------------------------------------------------------------------------------------------------------

var CHTMLObject = function( id )
{

	// constructor {
		this.type = 'html object';
		this._elem = null;
		this._label = null;
		if( id != null )
		{
			this._elem = el( id );
		}
	// }

	this.attach = function( id )
	{
		var elem = el( id );
		if( elem == null )
		{
			return false;
		}
		var out = this._elem;
		this._elem = elem;
		return out;
	};

	this.dom = function()
	{
		return this._elem;
	}
	
	this.parentDOM = function()
	{
		return this._elem.parentNode;
	};
	
	this.parent = function()
	{
		return ctl( this.parentDOM() );
	};

	this.ancestorsDOM = function()
	{
		var out = [];
		var curParent = this.parentDOM();
		while( curParent )
		{
			out.push( curParent );
			curParent = curParent.parentNode;
		}
		return out;
	};

	this.ancestors = function()
	{
		var out = this.ancestorsDOM();
		for( var i = 0; i < out.length; i++ )
		{
			out[ i ] = ctl( out[ i ] );
		}
		return out;
	};

	this.isChildOfDOM = function( elem )
	{
		var curParent = this.parentDOM();
		while( curParent )
		{
			if( curParent == elem )
			{
				return true;
			}
			curParent = curParent.parentNode;
		}
		return false;
	};
	
	this.isChildOf = function( ctl )
	{
		return this.isChildOfDOM( ctl._elem );
	};

	this.getLabel = function()
	{
		if( this._label == null )
		{
			if( String( this._elem.id ) != '' )
			{
				var labels = document.getElementsByTagName( 'label' );
				for( var i = 0; i < labels.length; i++ )
				{
					var forCtl = labels[ i ].getAttribute( 'for' );
					if( forCtl == null )
					{
						forCtl = labels[ i ].getAttribute( 'htmlfor' ); // stupid IE
					}
					if( forCtl == this._elem.id )
					{
						this._label = labels[ i ];
						break;
					}
				}
			}
		}
		return this._label;
	};
	
	this.isReallyVisible = function()
	{
		if( !this.visible() )
		{
			return false;
		}
		var prnt = this.parentDOM();
		while( prnt != document )
		{
			prnt_ctl = ctl( prnt );
			prnt = prnt_ctl.parentDOM()
			if( !prnt_ctl.visible() )
			{
				return false;
			}
		}
		return true;
	};

	this.id = function()
	{
		return this._elem.id;
	};

	this.name = function()
	{
		return this._elem.name;
	};

	this.setValue = function( value )
	{
		this._elem.value = value;
	};

	this.value = function()
	{
		return this._elem.value;
	};

	this.focus = function()
	{
		this._elem.focus();
	};

	this.blur = function()
	{
		this._elem.blur();
	};

	this.setInnerHTML = function( html )
	{
		this._elem.innerHTML = html;
	};

	this.innerHTML = function()
	{
		return this._elem.innerHTML;
	};

	this.setVisible = function( visible )
	{
		this._elem.style.display = visible ? '' : 'none';
	};

	this.visible = function()
	{
		return this._elem.style.display != 'none';
	};

	/**
	 * x position relative to the document origin
	 */
	this.setX = function( x )
	{
		this._elem.style.left = Number( x ) + 'px';
	};

	this.x = function()
	{
		if( this.compStyle( 'position' ) == 'absolute' )
		{
			var left = this.compStyle( 'left' );
			if( left.indexOf( 'px' ) != -1 ) // we have left in pixels
			{
				return parseInt( left );
			}
		}
		var curleft = 0;
		var obj = this._elem;
		if( obj.offsetParent )
		{
			// commented out impacts GYRE in IE, according to the documentation, it's not really needed
//			while( obj.offsetParent )
//			{
				curleft += obj.offsetLeft;
//				obj = obj.offsetParent;
//			}
		}
		else if( obj.x )
		{
			return obj.x;
		}
		return curleft;
	};

	this.pageX = function()
	{
		var curleft = 0;
		var obj = this._elem;
		if( obj.offsetParent )
		{
			while( obj.offsetParent )
			{
				curleft += obj.offsetLeft;
				obj = obj.offsetParent;
			}
		}
		else if( obj.x )
		{
			return obj.x;
		}
		return curleft;
	};

	/**
	 * y position relative to the document origin
	 */
	this.setY= function( y )
	{
		this._elem.style.top = Number( y ) + 'px';
	};

	this.y = function()
	{
		if( this.compStyle( 'position' ) == 'absolute' )
		{
			var top = this.compStyle( 'top' );
			if( top.indexOf( 'px' ) != -1 ) // we have top in pixels
			{
				return parseInt( top );
			}
		}
		var curtop = 0;
		var obj = this._elem;
		if( obj.offsetParent )
		{
			// commented out impacts GYRE in IE, according to the documentation, it's not really needed
//			while( obj.offsetParent )
//			{
				curtop += obj.offsetTop;
//				obj = obj.offsetParent;
//			}
		}
		else if( obj.y )
		{
			return obj.y;
		}
		return curtop;
	};

	this.pageY = function()
	{
		var curtop = 0;
		var obj = this._elem;
		if( obj.offsetParent )
		{
			while( obj.offsetParent )
			{
				curtop += obj.offsetTop;
				obj = obj.offsetParent;
			}
		}
		else if( obj.y )
		{
			return obj.y;
		}
		return curtop;
	};

	/**
	 * width
	 */
	this.setWidth = function( width )
	{
		if( width < 0 )
		{
			return;
		}
		this._elem.style.width = Number( width ) + 'px';
	};

	this.width = function()
	{
		if( this._elem.offsetWidth )
		{
			return this._elem.offsetWidth;
		}
		return parseInt( this.compStyle( 'width' ) );
	};

	/**
	 * height
	 */
	this.setHeight = function( height )
	{
		if( height < 0 )
		{
			return;
		}
		if( this._elem.DoResize != null )
			this._elem.DoResize(0, height - Number(this._elem.offsetHeight));
		else
			this._elem.style.height = Number( height ) + 'px';
	};

	this.height = function()
	{
		if( this._elem.offsetHeight )
		{
			return this._elem.offsetHeight;
		}
		return parseInt( this.compStyle( 'height' ) );
	};

	this.setAttr = function( attr, value )
	{
		this._elem.setAttribute( attr, value );
	};

	this.attr = function( attr )
	{
		return this._elem.getAttribute( attr );
	};

	this.setStyle = function( style, value )
	{
		this._elem.style[ style ] = value;
	};

	this.style = function( style )
	{
		return this._elem.style[ style ];
	};
	
	this.compStyle = function( style )
	{
		if( document.defaultView && document.defaultView.getComputedStyle )
		{
			return document.defaultView.getComputedStyle( this._elem, null )[ style ];
		}
		else if( this._elem.currentStyle )
		{
			return this._elem.currentStyle[ style ];
		}
		else
		{
			return this._elem.style[ style ];
		}
	};

};

//----------------------------------------------------------------------------------------------------------

var CTextfield = function( id )
{

	// constructor {
		this.type = 'textfield';
		this._elem = el( id );
	// }

// private:

	this._trans = function( index )
	{
		while( index < 0 ) // convert negative indices
		{
			index = this.text().length + 1 + index;
		}
		return index;
	};

// public:

	/**
	 * Selects the entire text field
	 * Often faster alternative to setSel() with no arguments
	 */
	this.selectAll = function()
	{
		if( this._elem.select )
		{
			this._elem.select();
			this.focus();
		}
		else
		{
			this.setSel();
		}
	};

	/**
	 * Sets selection to the specified bounds
	 * If length is omitted then selection stretches to the end
	 *
	 * @param start Number or null
	 * @param length Number or null
	 */
	this.setSel = function( start /* = 0 */, length /* = textfield length */ )
	{
		start = this._trans( def( start, 0 ) );
		length = def( length, this.text().length );
		if( this._elem.setSelectionRange )
		{
			this.focus();
			this._elem.setSelectionRange( start, start + length );
		}
		else if( this._elem.createTextRange )
		{
			var range = this._elem.createTextRange();
			range.collapse( true );
			range.moveEnd( 'character', start + length );
			range.moveStart( 'character', start );
			range.select();
		}
	};

	this.setSelStart = function( index )
	{
		index = this._trans( index );
		if( this._elem.selectionStart )
		{
			this.focus();
			this._elem.selectionStart = index;
		}
		else if( this._elem.createTextRange )
		{
			var end = this.getSelEnd();
			var range = this._elem.createTextRange();
			range.collapse( true );
			range.moveEnd( 'character', end );
			range.moveStart( 'character', index );
			range.select();
		}
	};

	this.getSelStart = function()
	{
		if( this._elem.selectionStart )
		{
			return this._elem.selectionStart;
		}
		else if( document.selection && document.selection.createRange ) // this section really sucks as IE itself does
		{
			var range = document.selection.createRange().duplicate();
			var offset = 0;
			var actMove;
			while( actMove = range.moveStart( 'character', -1024 ) )
			{
				offset += Math.abs( actMove );
			}
			if( this._elem.tagName.toLowerCase() == 'textarea' ) // in textareas count begins from 1, not 0
			{
				offset--;
			}
			return offset;
		}
		return 0;
	};

	this.setSelEnd = function( index )
	{
		index = this._trans( index );
		if( this._elem.selectionEnd )
		{
			this.focus();
			this._elem.selectionEnd = index;
		}
		else if( document.selection && document.selection.createRange )
		{
			var start = this.getSelStart();
			var range = this._elem.createTextRange();
			range.collapse( true );
			range.moveEnd( 'character', index );
			range.moveStart( 'character', start );
			range.select();
		}
	};

	this.getSelEnd = function()
	{
		if( this._elem.selectionEnd )
		{
			return this._elem.selectionEnd;
		}
		else if( document.selection && document.selection.createRange ) // this section really sucks as IE itself does
		{
			var range = document.selection.createRange().duplicate();
			var offset = 0;
			var actMove;
			while( actMove = range.moveEnd( 'character', -1024 ) )
			{
				offset += Math.abs( actMove );
			}
			return offset;
		}
		return 0;
	};

	this.setCursorPos = function( index )
	{
		this.setCurSel( this._trans( index ), 0 );
	};

	this.replaceSel = function( text )
	{
		if( this._elem.setSelectionRange )
		{
			/*
			    var selectionStart = input.selectionStart;
    var selectionEnd = input.selectionEnd;
    input.value = input.value.substring(0, selectionStart)
                  + replaceString
                  + input.value.substring(selectionEnd);
    if (selectionStart != selectionEnd) // has there been a selection
      setSelectionRange(input, selectionStart, selectionStart + 
replaceString.length);
    else // set caret
      setCaretToPos(input, selectionStart + replaceString.length);
			*/

			var selectionStart = this._elem.selectionStart;
			var selectionEnd = this._elem.selectionEnd;
			window.status = selectionStart + ', ' + selectionEnd;
			this.setText( this.text().substring( 0, selectionStart ) + text + this.text().substring( selectionEnd ) );
			if( selectionStart != selectionEnd )
			{
				this.setSel( selectionStart, text.length );
			}
			else
			{
				this.setCursorPos( selectionStart + text.length );
			}
		}
		else if( document.selection )
		{
			this.focus();
			var range = document.selection.createRange();
			var isCollapsed = range.text == '';
			range.text = text;
			if( !isCollapsed ) // there has been a selection
			{
				// it appears range.select() should select the newly 
				// inserted text but that fails with IE
				range.moveStart( 'character', -text.length );
				range.select();
			}
		}
	};

	this.setText = function( value )
	{
		this._elem.value = String( value );
	};

	this.text = function()
	{
		return String( this._elem.value );
	};

	/**
	 * Returns true if text field contains only whitespace or is of zero length
	 */
	this.isEmpty = function()
	{
		return this.text().trim() == '';
	};

};

// inheritance
CTextfield.prototype = new CHTMLObject();

//----------------------------------------------------------------------------------------------------------

var CFile = function( id )
{
	
	// constructor {
		this.type = 'file';
		this._elem = el( id );
	// }

};

CFile.prototype = new CTextfield();

//----------------------------------------------------------------------------------------------------------

var CButton = function( id )
{

	// constructor {
		this.type = 'button';
		this._elem = el( id );
	// }

};

// inheritance
CButton.prototype = new CHTMLObject();

//----------------------------------------------------------------------------------------------------------

var CList = function( id )
{

	// constructor {
		this.type = 'list';
		this._elem = el( id );
		this._coll = this._elem.options; // shortcut
	// }

	this.addItem = function( caption, value )
	{
		this._coll[ this._coll.length ] = new Option( caption, value );
	};

	this.removeItem = function( value )
	{
		if( value == null )
		{
			value = this.getCurSel();
		}
		if( typeof( value ) == 'string' )
		{
			value = this.getIndexByValue( value );
		}
		if( typeof( value ) == 'number' )
		{
			this._coll[ value ] = null;
		}
	};

	this.getCurSel = function()
	{
		return this._coll.selectedIndex;
	};

	/**
	 * Sets selected state of the select box.
	 *   if value == null, then clears the selection
	 *   if value is a number, then selects item with the specified zero-based index
	 *   if value is a string, then selects item with the the specified value
	 *   returs true on success and false on failure (when no such item exists)
	 *
	 * @param value null, Number or String
	 * @return Boolean
	 */
	this.setValue = function( value )
	{
		if( value == null )
		{
			for( var i = 0; i < this._coll.length; i++ )
			{
				this._coll[ i ].selected = false;
			}
			return true;
		}
		var elem = this.getOption( value );
		if( elem != null )
		{
			elem.selected = true;
			return true;
		}
		return false;
	};

	this.value = function()
	{
		var idx = this.getCurSel();
		if( idx == -1 )
		{
			return null;
		}
		return this._coll[ idx ].value;
	};

	this.caption = function()
	{
		var idx = this.getCurSel();
		if( idx == -1 )
		{
			return null;
		}
		return this._coll[ idx ].text;
	};

	this.getIndexByValue = function( value )
	{
		for( var i = 0; i < this._coll.length; i++ )
		{
			if( this._coll[ i ].value == value )
			{
				return i;
			}
		}
		return null;
	};

	this.getOption = function( elem )
	{
		if( typeof( elem ) == 'string' )
		{
			elem = this.getIndexByValue( elem );
		}
		if( typeof( elem ) == 'number' )
		{
			if( this._coll.length <= 0 || elem >= this._coll.length )
			{
				return null;
			}
			return this._coll[ elem ];
		}
		return elem;
	};

};

// inheritance
CList.prototype = new CHTMLObject();

//----------------------------------------------------------------------------------------------------------

var CCheckBox = function( id )
{

	// constructor {
		this.type = 'checkbox';
		this._elem = el( id );
	// }

	this.setValue = function( value )
	{
		this._elem.value = value;
	};

	this.value = function()
	{
		return this._elem.value;
	};

	this.setChecked = function( check )
	{
		this._elem.checked = check;
	};

	this.checked = function()
	{
		return this._elem.checked;
	};

};

CCheckBox.prototype = new CHTMLObject();

//----------------------------------------------------------------------------------------------------------

var CRadioGroup = function( name )
{

	// constructor {
		this.type = 'radiogroup';
		this._coll = els( name ); // internal html elements collection
	// }

// private:

// public:

	this.attach = function( name )
	{
		this._coll = els( name );
	};

	this.getElemByIndex = function( ind )
	{
		if( this._coll.length <= 0 || ind >= this._coll.length )
		{
			return null;
		}
		return this._coll[ ind ];
	};

	this.getElemByValue = function( val )
	{
		for( var i = 0; i < this._coll.length; i++ )
		{
			if( this._coll[ i ].value == val )
			{
				return this._coll[ i ];
			}
		}
		return null;
	};

	this.getElem = function( elem )
	{
		if( typeof( elem ) == 'number' )
		{
			elem = this.getElemByIndex( elem );
		}
		else if( typeof( elem ) == 'string' )
		{
			elem = this.getElemByValue( elem );
		}
		return elem;
	};

	/**
	 * Sets focus to the radio group.
	 *   if element is supplied and is a number, then tries to set focus to the number'th element counting from zero
	 *   if element is a string; tries to focus element with the value == element
	 *   returns true of success and false on failure
	 *
	 * @param null, Number or String
	 * @return Boolean
	 */
	this.focus = function( element )
	{
		var elem = this.getElem( element );
		if( elem != null )
		{
			elem.focus();
			return true;
		}
		return false;
	};

	/**
	 * @return Void
	 */
	this.blur = function()
	{
		for( var i = 0; i < this._coll.length; i++ )
		{
			this._coll[ i ].blur();
		}
	};

	/**
	 * Sets selected state of the radio group.
	 *   if value == null, then clears all the checks
	 *   if value is a number, then checks radio button with the specified zero-based index
	 *   if value is a string, then check radio button with the the same value
	 *   returs true on success and false on failure (when no such button exists)
	 *
	 * @param value null, Number or String
	 * @return Boolean
	 */
	this.setValue = function( value )
	{
		var elem = this.getElem( value );
		if( value == null || elem == null )
		{
			for( var i = 0; i < this._coll.length; i++ )
			{
				this._coll[ i ].checked = false;
			}
			return true;
		}
		elem.checked = true;
		return true;
	};

	/**
	 * Returns value of the currently selected radiobutton or null if nothing is selected
	 *
	 * @return String
	 */
	this.value = function()
	{
		for( var i = 0; i < this._coll.length; i++ )
		{
			if( this._coll[ i ].checked != false )
			{
				return this._coll[ i ].value;
			}
		}
		return null;
	};

	this.setIndex = this.setValue;

	/**
	 * Returns zero based index of the currently selected radio button or -1 if nothing is selected
	 *
	 * @return Number
	 */
	this.index = function()
	{
		for( var i = 0; i < this._coll.length; i++ )
		{
			if( this._coll[ i ].checked != false )
			{
				return i;
			}
		}
		return -1;
	};

};

// inheritance
//CRadioGroup.prototype = new CHTMLObject();

//----------------------------------------------------------------------------------------------------------

var CImage = function( id )
{
	
	// constructor {
		this.type = 'image';
		this._elem = el( id );
		this.over_image ='';
		this.elid ='img';
		this.out_image='';
		if( this._elem == null )
		{
			this._elem = new Image();
		}
	// }
	
	this.setSrc = function( val )
	{
		this._elem.src = val;
	};
	
	this.src = function()
	{
		return this._elem.src;
	};

	this.swap = function( image )
	{
		var tsrc = this.src();
		var nsrc = image.src();
		this.setSrc( image.src() );
		image.setSrc( tsrc );
	};

	this.setBoundBox = function( width, height, zoom /* = false */ )
	{
		zoom = def( zoom, false );
		var ratio = this.width() / this.height();
		if( height != null && ( width == null || width / ratio > height ) )
		{
			if( zoom || !zoom && this.height() > height )
			{
				this.setHeight( height );
				this.setWidth( height * ratio );
			}
		}
		else if( width != null )
		{
			if( zoom || !zoom && this.width() > width )
			{
				this.setHeight( width / ratio );
				this.setWidth( width );
			}
		}
	};
	
	this.resetSize = function( width /* = true */, height /* = true */ )
	{
		width = def( width, true );
		height = def( height, true );
		if( width )
		{
			this.setStyle( 'width', 'auto' );
		}
		if( height )
		{
			this.setStyle( 'height', 'auto' );
		}
	};

	this.width = function()
	{
		return this._elem.width;
	};

	this.height = function()
	{
		return this._elem.height;
	};

};

CImage.prototype = new CHTMLObject();

//----------------------------------------------------------------------------------------------------------

var CStatusBar = function()
{

	this.setText = function( text )
	{
		window.status = text;
	};

	this.text = function()
	{
		return window.status;
	};

	this.restoreText = function()
	{
		if( Browser.isGecko )
		{
			this.setText( '' );
		}
		else // this is crude hack, sorry
		{
			this.setText( 'Done' );
		}
	};

};

var StatusBar = new CStatusBar();

window.jooxbrLoaded = true;
