/*jsl:option explicit*/
/*jsl:import ../common/core.js*/
function getAbsX(c) {
	var x=c.offsetLeft;
	while (c.offsetParent) { c=c.offsetParent; x+=c.offsetLeft; }
	return(x);
}
function getAbsY(c) {
	var y=c.offsetTop;
	while (c.offsetParent) { c=c.offsetParent; y+=c.offsetTop; }
	return(y);
}

function getMouseXY(ev) {
///*@IE@*/	var isStrictMode = document.compatMode && document.compatMode != 'BackCompat' ? true : false;
///*@IE@*/	var scrollX = isStrictMode ? document.documentElement.scrollLeft : document.body.scrollLeft;
///*@IE@*/	var scrollY	= isStrictMode ? document.documentElement.scrollTop : document.body.scrollTop;
///*@IE@*/	return {x:window.event.clientX + scrollX,y:window.event.clientY + scrollY};
/*jsl:ignore*/
	return {x:ev.pageX,y:ev.pageY};
/*jsl:end*/
}

function getPageCoords(el) {
  var x0=el.offsetLeft;
  var y0=el.offsetTop;
  el=el.offsetParent;
  while (el) {
    x0+=el.offsetLeft-el.scrollLeft;
    y0+=el.offsetTop-el.scrollTop;
    el=el.offsetParent;
  }
///*@IE@*/  x0+=document.body.scrollLeft;
  x0+=window.pageXOffset;
///*@IE@*/  y0+=document.body.scrollTop;
  y0+=window.pageYOffset;
  return {x:x0,y:y0};
} 

function getRelMouseXY(ev,el) {
	var m=getMouseXY(ev);
	var c=getPageCoords(el);
	return {x:m.x-c.x,y:m.y-c.y};
}

function getCurrentStyle (element, cssPropertyName) {
  if (window.getComputedStyle) {
    return window.getComputedStyle(element, '').getPropertyValue(cssPropertyName.replace(/([A-Z])/g, "-$1").toLowerCase());
  } else if (element.currentStyle) {
    return element.currentStyle[cssPropertyName];
  } else {
    return '';
  }
}

// return the target element of en event
//function getTarget(ev) {
//	var t;
//	if (!ev) var ev = window.event;
//	if (ev.target) t=ev.target;
//	else if (e.srcElement) t=ev.srcElement;
//	return(t.nodeType == 3)?t.parentNode:t;
//}


// =============================================================================
// Drag
// =============================================================================
// Creates a Dragging Controller
//
// opt:
//   init   : (optional) callback- callen when dragging starts- function init(x0,y0,ev)
//   move   : callback- called when mouse moves- function move(dx,dy,ev,exit) 
var dragactive=null;
var dragpos;
document.onmousemove=function(ev) {
		dragpos=getMouseXY(ev);
		if(dragactive)dragactive.move(ev||window.event);
		return false;
	};
function TDrag(opt) {
	var me=this;
	var x0,y0;
	
	this.start=function(ev) {
			if(dragactive)dragactive.stop(ev);
			dragactive=me;
			x0=dragpos.x;
			y0=dragpos.y;
			if(opt.init)opt.init(x0,y0,ev);
			document.onmouseup=me.stop;
			ev=ev||window.event;
			if(ev)ev.stopPropagation();
///*@IE@*/			if(ev)ev.cancelBubble=true;
			return false;
		};
	this.move=function(ev) {
			opt.move(dragpos.x-x0,dragpos.y-y0,ev||window.event);
			x0=dragpos.x;
			y0=dragpos.y;
			return false;
		};
	this.stop=function(ev) {
			if(dragactive==me){
				document.onmouseup='';
				dragactive=null;
				opt.move(dragpos.x-x0,dragpos.y-y0,ev||window.event,true);
			}
			return false;
		};
}

// =============================================================================
// Mouse Wheel
// =============================================================================
// Creates a Mouse Wheel Controller
//
// div  : panel <DIV>, which shal be sensitive for mouse wheel events
// move : callback, called, when mouse wheel has been used- function move(delta,evt)
//
function TWheel(div,move) {
	var me=this;
	
	if (div.addEventListener){
		div.addEventListener('DOMMouseScroll', wheel, false); 
		div.addEventListener('mousewheel', wheel, false); 
	} else div.onmousewheel=wheel;
		
	function wheel(evt) {
	  evt=evt||window.event;
	  var d=0;
	  if (evt.wheelDelta) { 
	  	// IE/Opera
	    d=-evt.wheelDelta/15;
	    if (window.opera)d=-d;
	  } else if (evt.detail) { 
	  	// Mozilla
	    d=evt.detail*3;
	  }
	  if(d&&move)move(d,evt);
	  if (evt.preventDefault)evt.preventDefault();
		evt.returnValue = false;
		return false;
	}	
}


// =============================================================================
// Drag'n'Drop
// =============================================================================
// Creates a drag'n'drop Controller
//
function TDragDrop(opt) {
	var me=this;
	var startXY;
	var t=[];
	var ddrag=new TDrag({move:dmove});
	var dcopy=null;
	var dxy,dval;
	this.addTarget=function(div,f) {
			var p={target:div,drop:f};
			t.push(p);
		};
	this.removeTarget=function(div) {
			for(var i=0;i<t.length;i++)
				if(t[i].target==div){
					t.splice(i,1);
					return;
				}
		};
	this.reset=function() {
			t=[];
		};
	function getTarget(x,y) {
			for(var i=0; i<t.length; i++) {
				var d=t[i].target;
				var o=getPageCoords(d);
				o.x1=o.x+d.offsetWidth;
				o.y1=o.y+d.offsetHeight;
				if((x>=o.x) && (x<o.x1) && (y>=o.y) && (y<o.y1)) return(t[i]);
			}
			return(null);
		}
	this.grip={x:10,y:10};
	this.start=function(val,evt) {
			dval=val;										// save value for later (dropping)
			evt = evt || window.event;
      startXY=getMouseXY(evt);
      // Kopie formatieren
			var oElement=evt.target;
      var elxy = getPageCoords(oElement);
      if(opt.render){
	      dcopy=opt.render(val,startXY.x-elxy.x,startXY.y-elxy.y);
	    	dxy={x:me.grip.x,y:me.grip.y};
	    	me.grip={x:10,y:10};
      } else {
	      dcopy=oElement.cloneNode(true);
	      dxy={
	          x:Math.abs(elxy.x-startXY.x - oElement.parentNode.scrollLeft),
	          y:Math.abs(elxy.y-startXY.y - oElement.parentNode.scrollTop)
	        };
      	dcopy.style.width=getCurrentStyle(oElement, 'width');
      	dcopy.style.height=getCurrentStyle(oElement, 'height');
      }
      dcopy.style.position='absolute';
      dcopy.style.display='none';
      document.body.appendChild(dcopy);
      ddrag.start();
    };
  function dmove(dx,dy,evt,exit) {
    // Eventhandler für MouseMove definieren
    if(!dcopy) {return;}
    var c=getMouseXY(evt);
    if(exit) {
    	// dropped
      if(dist(c,startXY)>5){
        var target = getTarget(c.x,c.y);
        if(target != null) target.drop(dval); 
        else if(opt.dragfail)opt.dragfail(dval);
			}
      // Kopie wieder entfernen
      // und Daten setzen in Formularfelder
      if(opt.finished)opt.finished(dval);
      document.body.removeChild(dcopy);
      dcopy=null;     	
    } else if(dist(c,startXY)>=1){
    	// move element
      dcopy.style.display='block';
      dcopy.style.left=c.x-dxy.x;
	    dcopy.style.top =c.y-dxy.y;
	  }
  }
  function dist(a,b){
  	return(Math.sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)));
  }
}


// =============================================================================
// Formular
// =============================================================================
// Creates and maintains a user input form
//
function TForm() {
	var f={};
	
	this.setValues=function(json,init) {
		for (var o in json) {
			var fo=o.toLowerCase();
			if(f[fo]) {
					if(f[fo].length){
						for (var i=0; i<f[fo].length; i++) f[fo][i].setValue(json[o],init);
					} else f[fo].setValue(json[o],init);
				}
		}
	};
	this.toString=function() {
		var r='';
		for (var o in f) {
			r=r+'&'+o+'=';
			if (f[o].length) {
				var s='';
				for (var i=0; i<f[o].length; i++) {
					if (f[o][i].isChecked()) s=s+'|'+f[o][i].getValue();
				}
				r=r+encodeURIComponent(s.substr(1));
			} else {
				r=r+encodeURIComponent(f[o].getValue());
			}
		}
		return(r.substr(1));	
	};
	// getValue of a component
	// if the component is an array of components, and {j} is defined
	// the result is concatenated
	this.getValue=function(o,j) {
			var c=f[o.toLowerCase()];
			if(c&&(c.length>1)) {
					var r=[];
					for(var i=0;i<c.length;i++){
						var v=c[i].getValue();
						if(v)r.push(v);
					}
					if(j&&(r.length>0))r=r.join(j);
					return r;
			} else return(c.getValue());
		};
	this.$=function(o) {return(f[o.toLowerCase()]);};
	this.add=function(comp) {
			var n=comp.getName().toLowerCase();
			if (f[n]) {
				// field with that name already exists -> create array!
				if (!f[n].length) f[n]=new Array(f[n]);
				f[n].push(comp);
			} else f[n]=comp;
			return(comp);
		};
	// remove - removes components from the form
	this.remove=function(o) {
			if(f[o]) delete f[o];
		};
	this.reset=function(init) {
			for (var o in f) {
				if (f[o].length) {
					for (var i=0; i<f[o].length; i++) f[o][i].reset(init);
				} else if(f[o].reset)f[o].reset(init);
			}
		};
}

// =============================================================================
// TGroup
// =============================================================================
// TGroup is a virtual container component for grouping other UI elements 
// (TCheck/TButton) into one serialized component.
// opts:
//   name: name of component (getName) 
//   separator: (optional) serialization separator (default: '|')
//   onchange: callback function, called when one of the contained components changes
//   form: (optional) formular to register with
//   radio: (flag,optional) if set, only one checkbox can be checked at a time
//   allownull: (flag,optional) if NOT set, at least one option has to be selected
//
// using "setValue":
//   the string submitted with setValue can contain any number of values, separated by
//   separators (defined in "opts.separator") referring to the "value" properties of
//   the groups components. The first character of the string may be "+", "-", or "!"
//   meaning, that only specified components shall be switched on (+), off (-) or 
//   inverted (!), all other components shall not be touched.
function TGroup(opts) {
	var me=this;
	var c=[];								// list of contained components
	if(!opts)opts={};
	if(!opts.separator)opts.separator='|';
	
	function listener(s) {
		if(opts.radio){
			for(var i=0;i<c.length;i++)
				if(c[i]!=s)c[i].setChecked(false,true);
		}
		if(!opts.allownull&&(me.getValue()==''))s.setChecked(true,true);
		if(opts.onchange)opts.onchange(me);
	}
	
	this.add=function(ui) {
			c.push(ui);
			ui.gname=opts.separator+ui.getValue(true)+opts.separator;
			ui.setListener(listener);
		};
		
	// clear : removes all attached components
	this.clear=function() {
			for(var i=0;i<c.length;i++)c[i].setListener(null);
			c=[];
		};
	this.getName=function() {
			return(opts.name);
		};
	this.getValue=function() {
			var r=[];
			for(var i=0;i<c.length;i++)
				if(c[i].isChecked())
					r.push(c[i].getValue());
			return(r.length>0)?r.join(opts.separator):'';
		};
	this.setValue=function(v,init) {
			var v1=''+v;
			switch(v1.charAt(0)) {
				case '+': // switch specified checks on
									if(v1=='+') {
										for(var i=0;i<c.length;i++)c[i].setChecked(true,true);
									} else {
										v1=new String(opts.separator+v.substr(1)+opts.separator);
										for(var i=0;i<c.length;i++){
											var ci=c[i];
											if(v1.indexOf(ci.gname)>-1)
												ci.setChecked(true,true);
										}
									}
									break;
				case '-': // switch specified checks off
									if(v1=='-') {
										for(var i=0;i<c.length;i++)c[i].setChecked(false,true);
									} else {
										v1=new String(opts.separator+v.substr(1)+opts.separator);
										for(var i=0;i<c.length;i++){
											var ci=c[i];
											if(v1.indexOf(ci.gname)>-1)
												ci.setChecked(false,true);
										}
									}
									break;
				case '!': // invert specified checks
									if(v1=='!') {
										for(var i=0;i<c.length;i++)c[i].setChecked(!c[i].isChecked(),true);
									} else {
										v1=new String(opts.separator+v.substr(1)+opts.separator);
										for(var i=0;i<c.length;i++){
											var ci=c[i];
											if(v1.indexOf(ci.gname)>-1)
												ci.setChecked(!ci.isChecked(),true);
										}
									}
									break;
				default:  // hard set values according argument
									v1=new String(opts.separator+v+opts.separator);
									for(var i=0;i<c.length;i++) {
										c[i].setChecked(v1.indexOf(c[i].gname)>-1,true);
									}
			}
			// after a setValue no validation is performed !!!
			if(!init&&opts.onchange)opts.onchange(me);
		};
	this.reset=function(init) {
			if(opts.allownull)me.setValue('',init);
			else if(c.length>0)me.setValue(c[0].getValue(true),init);
		};
	if(opts.form)opts.form.add(me);
}

// =============================================================================
// Component Focus Handler
// =============================================================================
// every component should provide the interface function "focus" and "blur"
// "focus" shall be called, if a Component shall get the input focus;
//         the Component must call "setFocus(component)" to announce its new state
//				 {component} can be null if the Component does not provide a "blur" function
// "blur"  will be called, when the Component looses its focus
// when a component is activated by the user by clicking onto a Components
// controlled area, its own "focus" function shall be called
//
var fComp=null;
function setFocus(c) {
	if((c!=fComp)&&fComp&&fComp.blur)fComp.blur(c);
	fComp=c;
}

// =============================================================================
// Slider
// =============================================================================
// Creates and maintains a slider
//

function TSlider(opts,par,ifo) {
	var me=this;
	var opt=opts;
	opt.val=opts.init;

	var dragV=-1;
	var dx0;

	var con=$DIV({css:'Slider'});
	var tit=null;
	var mo=false;						// MouseOver flag

	var main,sli,knob;			// container for slider graphics
	
	var dc=new TDrag({init:dragstart,move:drag});

	if (opt.title) {
		tit=$DIV({css:'title',width:(34+((opt.values.length-1)*14))});
		if (opt.titleHeight) tit.style.height=opt.titleHeight;
		if (opt.titleWidth) {
			tit.style.width=opt.titleWidth;
			con.style.width=opt.titleWidth;
		}
		tit.innerHTML=opt.title;
		con.appendChild(tit);
		con.onmouseover=function(){
				mo=true;
				tit.style.width=main.offsetWidth;
				tit.innerHTML=opt.titles[(dragV>-1)?dragV:opt.val];				
				tit.className='titleVal';
			};
		con.onmouseout=function(){
				mo=false;
				tit.innerHTML=opt.title;
				tit.className='title';
				if(opt.titleWidth)tit.style.width=opt.titleWidth;
			};
	}
	main=$DIV({css:'main'});
	con.appendChild(main);
	sli=$DIV({css:'grid',width:(2+((opt.values.length-1)*14))});
	if(typeof(opt.val)!='number')opt.val=0;
	knob=$DIV({css:'knob',top:0,left:(-6+(opt.val*14)),title:(opt.titles)?opt.titles[opt.init]:''});
	knob.onmousedown=dc.start;
	sli.appendChild(knob);

	this.change=function(delta) { _setIndex(opt.val+delta);	};
	this.setIndex=_setIndex;
	function _setIndex(v,init) {
		if ((v<opt.values.length) && (v>=0) && (opt.val!=v)) {
			opt.val=v;
			knob.style.left=-6+(v*14);
			if(opt.titles) knob.title=opt.titles[v];
			if(!init&&opt.onchange)opt.onchange(me);
			if(mo)tit.innerHTML=opt.titles[v];
		} else knob.style.left=-6+(opt.val*14);
	}
	this.setValue=function(val,init) {
		for (var i=0; i<opt.values.length; i++) if (opt.values[i]==val) { _setIndex(i,init); return; }
	};
	this.getValue=function() {return(opt.values[opt.val]);};
	this.getIndex=function() {return(opt.val);};
	this.getName=function() {return(opt.name);};
	this.reset=function() {_setIndex(opt.init);};
	
	function drag(dx,dy,evt,exit) {
		dx0+=dx;
		var v=opt.val + Math.floor((dx0+7)/14);
		if ((v<opt.values.length) && (v>=0)) {
			knob.style.left=-6+(v*14);
			dragV=v;
			if(mo)tit.innerHTML=opt.titles[v];
		}
		if(exit){
			_setIndex(dragV);
			dragV=-1;
		}
	}
	function dragstart(x0,y0,evt) {
		setFocus(null);
		dx0=0;		
		dragV=opt.val;
	}

	var dec=$IMG('img/ui/sliminus.png',{link:this.change.bind(this,-1)});
	var inc=$IMG('img/ui/sliplus.png',{link:this.change.bind(this,1)});
	
	main.appendChild(dec);
	main.appendChild(sli);
	main.appendChild(inc);
		
	if(opts.parent)opts.parent.appendChild(con); else if(par)par.appendChild(con);
	if(opts.form)opts.form.add(me); else if(ifo)ifo.add(me);
	this.Component=con;	
}


// Check
// =============================================================================
// opts.title    : string; title of component (used as label)
// opts.layout   : ['left','right','none'] defines, where the label is placed
// opts.name     : name of component (used for form processing)
// opts.value    : value of component (returns this value on getValue when checked)
// opts.offvalue : value taken, if not checked (optional- default='')
// opts.onchange : call back function, called when content has been changed by user
// opts.margin*  : margin style definitions for the component
// opts.titleWidth: width of label
// opts.checked  : boolean; default value
// par (optional): parent container (DIV) the component is added to 
// ifo (optional): Formular component, this component is automatically added to
function TCheck(opts,par,ifo) {
	var me=this;
	var opt=opts;
	this.type='check';

	var main=$DIV({css:'Check'});
	if(typeof(opts.enabled)=='undefined')opts.enabled=true;
	if(typeof(opts.checked)=='undefined')opts.checked=false;
	var but=$DIV({css:(opt.checked)?'checked':'unchecked'});
	for (var o in opt) 
		switch(o) {
			case 'margin':
			case 'marginTop':
			case 'marginBottom':
			case 'marginLeft':
			case 'marginRight':main.style[o]=opt[o]; break;
			case 'titleWidth':
			case 'value':
			case 'title':
			case 'name':
			case 'layout':
			case 'onchange': break;
			default: but.style[o]=opt[o];
		}	
	switch (opt.layout) {
		case 'left':
								var t=$DIV({css:'titleLeft'});
								if(opt.titleWidth) t.style.width=opt.titleWidth;
								t.innerHTML=opt.title;
								main.appendChild(t);
								main.appendChild(but);
								break;
		case 'right':
								var t=$DIV({css:'titleRight'});
								if(opt.titleWidth) t.style.width=opt.titleWidth;
								t.innerHTML=opt.title;
								main.appendChild(but);
								main.appendChild(t);
								break;
		case 'top':
								var t=$DIV({css:'titleRight'});
								if(opt.titleWidth) t.style.width=opt.titleWidth;
								if(opt.titleHeight) t.style.height=opt.titleHeight;
								t.innerHTML=opt.title;
								main.appendChild(t);
								main.appendChild($DIV({clear:'both'}));
								main.appendChild(but);
								break;
		default:
								main.appendChild(but);
								break;
	}

	but.onclick=function() {
		setFocus(null);
		if(opts.enabled) {
			opts.checked=!opts.checked;
			but.className=(opt.checked)?'checked':'unchecked';
			if (opts.onchange) opts.onchange(me);
		}
	};
	this.isChecked=function() {return(opt.checked);};
	this.setChecked=function(v,init) {
		opt.checked=v;
		but.className=(opt.checked)?'checked':'unchecked';
		if(!init&&opts.onchange)opts.onchange(me);
	};
	this.setEnabled=function(b){
			if(b) {
///*@IE@*/				main.style.filter='alpha(opacity:100)';
				main.style.opacity=1;
				opts.enabled=true;
			} else {
///*@IE@*/				main.style.filter='alpha(opacity:20)';
				main.style.opacity=0.2;
				opts.enabled=false;
			}
		};
	this.getValue=function(b) {return((b||opt.checked)?opt.value:(opt.offvalue?opt.offvalue:''));};
	this.setValue=_setValue;
	function _setValue(v,init) {
		if(!v) {
			opt.checked=false;
		} else {
			v=''+v;
			switch(v.charAt(0)) {
				case '+':	if((v.length==1)||(('|'+v.substr(1)+'|').indexOf('|'+opt.value+'|')>=0))opt.checked=true;
									break;
				case '-':	if((v.length==1)||(('|'+v.substr(1)+'|').indexOf('|'+opt.value+'|')>=0))opt.checked=false;
									break;
				case '!':	if((v.length==1)||(('|'+v.substr(1)+'|').indexOf('|'+opt.value+'|')>=0))opt.checked=!opt.checked;
									break;
				default :	opt.checked=(('|'+v+'|').indexOf('|'+opt.value+'|')>=0);
			}
		}
		but.className=((opt.checked)?'checked':'unchecked');
		if(!init&&opts.onchange)opts.onchange(me);
	}
	this.getName=function() {return(opt.name);};
	this.reset=function(init) {_setValue('',init);};
	this.setListener=function(cb){
			opts.onchange=cb;
		};
	if(opt.form)opt.form.add(me); else if(ifo)ifo.add(me);
	if(opt.group)opt.group.add(me);
	if(opt.parent)opt.parent.appendChild(main); else if(par)par.appendChild(main);
	
	this.Component=main;
}


// TTextArea
// =============================================================================
// opts.readonly : boolean; if false, field cannot be edited
// opts.title    : string; title of component (used as label)
// opts.layout   : ['top','none'] defines, where the label is placed
// opts.titleHeight: height of label (for layout='top')
// opts.titleWidth: width of label (for layout='left')
// opts.width    : width of input
// opts.height   : height of input
// opts.name     : name of component (used for form processing)
// opts.onchange : call back function, called when content has been changed by user
// opts.margin*  : margin style definitions for the component
// opts.value    : default value
// opts.parent   : parent container (DIV) the component is added to 
// opts.form     : Formular component, this component is automatically added to
function TTextArea(opts) {
	var me=this;
	this.type='area';
	var main=$DIV({css:'TextFieldArea',width:opts.width,height:opts.height});
	var inp=document.createElement('textarea');
	inp.style.width=opts.width-4;
	inp.style.height=opts.height-8;
	var frm=[
			$DIV({css:'frameEl',width:2,height:4}),
			$DIV({css:'frameEl',backgroundRepeat:'repeat-x',width:(opts.width-4),height:4}),
			$DIV({css:'frameEl',width:2,height:4,clear:'right'}),
			$DIV({css:'frameEl',backgroundRepeat:'repeat-y',width:2,height:(opts.height-8)}),
			$DIV({css:'frameEl',backgroundRepeat:'repeat-y',width:2,height:(opts.height-8),clear:'right'}),
			$DIV({css:'frameEl',width:2,height:4}),
			$DIV({css:'frameEl',backgroundRepeat:'repeat-x',width:(opts.width-4),height:4}),
			$DIV({css:'frameEl',width:2,height:4})
		];
	function setFrame(f) {
		if(f)f='_f'; else f='';
		for(var i=0;i<8;i++)frm[i].style.backgroundImage='url(img/ui/area'+i+f+'.png)';
	}
	setFrame();
	main.appendChild(frm[0]);
	main.appendChild(frm[1]);
	main.appendChild(frm[2]);
	main.appendChild(frm[3]);
	main.appendChild(inp);
	main.appendChild(frm[4]);
	main.appendChild(frm[5]);
	main.appendChild(frm[6]);
	main.appendChild(frm[7]);
	for (var o in opts) 
		switch(o) {
			case 'name':inp.name=opts[o]; break;
			case 'margin':
			case 'marginLeft':
///*@IE@*/								inp.style.marginLeft=-opts[o]+2;
												/*jsl:fallthru*/
			case 'marginTop':
			case 'marginBottom':
			case 'marginRight':main.style[o]=opts[o]; break;
			case 'value':inp.value=opts[o]; break;
		}
	
	inp.onfocus=function() {
			setFocus(me);
			Core.setKeyEnabled(false);
			setFrame(true);
		};
	inp.onblur=function() {
			setFocus(null);
		};
	this.focus=function() {
			inp.focus();
		};
	this.blur=function() {
			Core.setKeyEnabled(true);
			setFrame();
		};
	
	this.Component=main;
	this.setValue=function(val,init) {inp.value=val;if(!init&&opts.onchange)opts.onchange(me);};
	this.getValue=function() {return(inp.value);};
	this.getName=function() {return(opts.name);};
	inp.onchange=function() {if(opts.onchange)opts.onchange(me);};
	this.reset=function() {inp.value=opts.value;};
	if(opts.parent)opts.parent.appendChild(main);
	if(opts.form)opts.form.add(me);
}


// TextField
// =============================================================================
// opts.readonly : boolean; if false, field cannot be edited
// opts.calendar : boolean; if true, a calendar is shown on activation
// opts.title    : string; title of component (used as label)
// opts.layout   : ['left','top','none'] defines, where the label is placed
// opts.titleHeight: height of label (for layout='top')
// opts.titleWidth: width of label (for layout='left')
// opts.width    : width of input
// opts.name     : name of component (used for form processing)
// opts.onchange : call back function, called when content has been changed by user
// opts.margin*  : margin style definitions for the component
// opts.value    : default value
// opts.style    : if 'big' a bigger font is used (title should be empty)
// par (optional): parent container (DIV) the component is added to 
// ifo (optional): Formular component, this component is automatically added to
function TTextField(opts) {
	var me=this;
	this.type='text';
	var opt=opts;
	var inp,cal=null;
	switch(opt.style){
		case 'area':
						opt.css='TextFieldBig';
						inp=document.createElement('textarea');
						break;
		case 'bigarea':
						opt.css='TextFieldBig2';
						inp=document.createElement('textarea');
						break;
		case 'big':
						opt.css='TextFieldBig';
						inp=document.createElement('input');
						break;
		default:
						opt.css='TextField';
						inp=document.createElement('input');
	}
	var main=$DIV({css:opt.css,width:(6+opt.width)});
	for (var o in opt) 
		switch(o) {
			case 'title': inp.title=opt[o]; break;
			case 'name':inp.name=opt[o]; break;
			case 'hide':inp.type='password';break;
			case 'readonly':
			case 'margin':
			case 'marginTop':
			case 'marginBottom':
			case 'marginLeft':
			case 'marginRight':main.style[o]=opt[o]; break;
			case 'value':inp.value=opt[o]; break;
			case 'layout':
			case 'stlye':
			case 'onchange':
			case 'onkeyup': break;
			default: inp.style[o]=opt[o];
		}
	switch(opt.layout) {
		case 'top':	// 2-line layout (title first line, input field second line)
								var t=$DIV({css:'titleTop'});
								if(opt.titleHeight) { t.style.height=opt.titleHeight; main.style.height=((opt.style=='bigarea')?51:16)+opt.titleHeight; }
								if(opt.titleWidth) { main.style.width=opt.titleWidth; }
								t.innerHTML=opt.title;
								main.appendChild(t);
								break;
		case 'left': // 1-line layout (title left)
								main.style.width=6+opt.width+opt.titleWidth;
								var t=$DIV({css:'titleLeft'});
								if(opt.titleWidth) t.style.width=opt.titleWidth;
								t.innerHTML=opt.title;
								main.appendChild(t);
								break;
	}
	_setReadOnly(opt.readonly);

	main.appendChild($DIV({css:'leftBorder'}));
	main.appendChild(inp);
	main.appendChild($DIV({css:'rightBorder'}));

	this.Component=main;
	this.setValue=function(val,init) {inp.value=val;if(!init&&opt.onchange)opt.onchange(me);};
	this.getValue=function() {return(inp.value);};
	this.getName=function() {return(opt.name);};
	function _changed() {if(opt.onchange)opt.onchange(me);}
	function _keyup() {if(opt.onkeyup)opt.onkeyup(me);}
	this.reset=function() {inp.value='';};
	this.setReadOnly=_setReadOnly;
	function _setReadOnly(b) {
		if ((b) && (b==true)) inp.readOnly=true; else{
			// active input field -> prepare
			if (opt.onchange) inp.onchange=_changed;
			if (opt.onkeyup) inp.onkeyup=_keyup;
			inp.onfocus=function() {
					setFocus(me);
					Core.setKeyEnabled(false);
					if(opt.css=='TextField')main.className='TextFieldFocus';
					if(opt.calendar&&!cal.isOpen){
						cal.setDate(Core.parseDate(inp.value));
						cal.open(inp);
					}
				};
//			inp.onblur=function() {
//					setFocus(null);
//				};
			inp.readOnly=false;
		}
	}
	this.setDate=function(val) {inp.value=val;_changed();};
	this.focus=function() { 
			inp.focus(); 
		};
	this.blur=function() {
			Core.setKeyEnabled(true);
			if(opt.css=='TextField')main.className='TextField';
			if(opt.calendar)cal.close(true);		
		};
	
	if(opt.calendar)cal=new TCalendar(me.setDate.bind(me));
	if(opt.parent)opt.parent.appendChild(main);
	if(opt.form)opt.form.add(me);
}


// DropDown
// =============================================================================
function TDropDown(opts,par,ifo) {
	var me=this;
	this.type='dropdown';
	var opt=opts;
	var main=$DIV({css:'TextField',width:(18+opt.width)});
	var men=new Array();							// array of options (menu items)

	var m=document.getElementsByTagName('BODY')[0];
	var dd=$DIV({css:'DropDownList',width:(12+opt.width),display:'none'});
	m.appendChild(dd);

	var inp=document.createElement('input');
	inp.onclick=function() { setFocus(me); };
	for (var o in opt) 
		switch(o) {
			case 'title': inp.title=opt[o]; break;
			case 'name':inp.name=opt[o]; break;
			case 'hide':inp.type='password';break;
			case 'readonly':
			case 'margin':
			case 'marginTop':
			case 'marginBottom':
			case 'marginLeft':
			case 'marginRight':main.style[o]=opt[o]; break;
			case 'layout':
			case 'options':
			case 'onchange': break;
			default: inp.style[o]=opt[o];
		}
	switch(opt.layout) {
		case 'top':	// 2-line layout (title first line, input field second line)
								var t=$DIV({css:'titleTop'});
								if(opt.titleHeight) { t.style.height=opt.titleHeight; main.style.height=16+opt.titleHeight; }
								t.innerHTML=opt.title;
								main.appendChild(t);
								break;
		case 'left': // 1-line layout (title left)
								var t=$DIV({css:'titleLeft'});
								if(opt.titleWidth) t.style.width=opt.titleWidth;
								t.innerHTML=opt.title;
								main.appendChild(t);
								break;
	}
	
	var sel=$DIV({css:'rightSelBorder'});
	if (opt.onlist) {
		sel.onclick=function() {
								setFocus(me);
								if ((dd.style.display=='none') || (me.length==0)) {
									while (dd.firstChild) dd.removeChild(dd.firstChild);
									men=[];
									opt.onlist(me);
									dd.style.left=getAbsX(inp)+2;
									dd.style.top=getAbsY(inp)+inp.offsetHeight+1;
									dd.style.display='block';
								} else dd.style.display='none';
							};
	} else {
		sel.onclick=function() {
								setFocus(me);
								if (dd.style.display=='none') {
									dd.style.left=getAbsX(inp)+2;
									dd.style.top=getAbsY(inp)+inp.offsetHeight+1;
									dd.style.display='block';
								} else dd.style.display='none';
							};
		inp.readOnly=true;
	}
	this.focus=function() {
			setFocus(me);
		};
	this.blur=function() {
			dd.style.display='none';
		};

	main.appendChild($DIV({css:'leftBorder'}));
	main.appendChild(inp);
	main.appendChild(sel);

	this.Component=main;

	this.addOption=function(id,title) {
			var li=$DIV({css:'ddListItem'});
			li.innerHTML=title;
			li.onmouseover=function() {this.className='ddListItemSel';};
			li.onmouseout=function() {this.className='ddListItem';};
			li.onclick=function() {
					dd.style.display='none';
					if(id!=opt.value){
						opt.value=id;
						inp.value=title;
						_changed();
					}
				};
			men[id]=title;
			dd.appendChild(li);		
		};
	this.setValue=function(val,init) {
			if((men[val])&&(val!=opt.value)){
				inp.value=men[val];
				opt.value=val;
				if(!init)_changed();
				return(true);
			} else return(false);
		};
	this.getValue=function() {return(opt.value);};
	this.getText=function() {return(inp.value);};
	this.getName=function() {return(opt.name);};
	function _changed() {if(opt.onchange)opt.onchange(me);}
	this.reset=function() {inp.value='';opt.value=null;};
	this.clear=function() {
			inp.value='';
			opt.value=null;
			men=new Array();
			while (dd.firstChild) dd.removeChild(dd.firstChild);
		};
	
	if(opt.parent)opt.parent.appendChild(main); else if(par)par.appendChild(main);
	if(opt.form)opt.form.add(me); else if(ifo)ifo.add(me);
}

// =============================================================================
// THeader
// =============================================================================
// Creates a label used as a section heading
//
function THeader(opts) {
	var main=$DIV({html:opts.title});
	switch(opts.style) {
		case 'big' : main.className='LabelBig'; break;
		case 'medium' : main.className='LabelMedium'; break;
		default : main.className='LabelSmall';
	}
	this.Component=main;
	if(opts.parent)opts.parent.appendChild(main);
}

// =============================================================================
// Scroll
// =============================================================================
// Creates and maintains a scrollbar
//

function TScroll(pane,dat) {
	var scr = $DIV({css:'Scrollbar'});
	var bar = $DIV({css:'bar'});
	var itop = $DIV({css:'barTop'});
	var ibody = $DIV({css:'barBody'});
	var ibot = $DIV({css:'barBottom'});
	var ddrag=new TDrag({init:_refresh,move:dmove});
	
	bar.appendChild(itop);
	bar.appendChild(ibody);
	bar.appendChild(ibot);

	scr.appendChild(bar);
	
	this.Component=scr;

	var dragy;
	var dragflag=false;
	var dragposy;

	ibody.onmousedown=ddrag.start;
	itop.onmousedown=ddrag.start;
	ibot.onmousedown=ddrag.start;
	scr.onclick=dragclick;
	scr.onmousedown=function(){return false;};

	// initialize mouse wheel support
	new TWheel(pane,wmove);
	new TWheel(scr,wmove);

	var l0=0;
	function wmove(delta,evt) {
  	var i=(new Date()).getTime();
  	if(i>l0)_refresh();
  	l0=i+500;
  	move(delta);
	}	
	function dmove(dx,dy,evt,exit) {
		if(!exit)move(dy);
	}
	// intermediate meassures
	// m0: 	itop.offsetHeight+ibot.offsetHeight
	// m1:	pane.offsetHeight
	// m2:	scr.offsetHeight
	// m3:	dat.offsetHeight
	// m4:	bar.offsetTop
	// m5:	bar.offsetHeight
	var m0=0,m1,m2,m3=-1,m4,m5;	
	// dragclick
	// callback: click on scrollbar to move list by ~ one item
	function dragclick(evt) {
		setFocus(null);
	  var y = (document.all ? window.event.clientY : evt.pageY)-getAbsY(scr);
	  _refresh();
	  if(y<m4) move(-m5/2);
	  else if(y>(m4+m5)) move(m5/2);
	}
	function move(y) {
	  	m4+=y;
	  	if (m4+m5 > m2) m4=m2-m5;
	  	if (m4<0) m4=0;
	  	bar.style.top=m4;
	  	dat.style.top=-(m3*m4/m2);		
	}
	this.makeVisible=function(y1,y2){
			_refresh();
			var v1=-dat.offsetTop;
			var v2=m1+v1;
			y2+=y1;
			var f=(m2-m5)/(m3-m1);
			if(y2>v2)	move(Math.ceil((y2-v2)*f));
			else if(y1<v1) move(Math.floor((y1-v1)*f));
		};
		
	function _refresh() {
			var h=dat.offsetHeight;
			if(m3==h)return;
			m3=h;
			m5=(m3==0)?(m2-m0):(Math.ceil(m2*m1/m3)-m0);
			if(m5+m0>m2) m5=m2-m0;
			if(m5<1) m5=1;
			ibody.style.height=m5;
			m5+=m0;
			if ((m4+m5)>m2) {
				m4=(m2-m5);
				bar.style.top=m4;
				dat.style.top=(m2==0)?0:-(m3*(m4+m0)/m2);
			}
	}
	this.refresh=function() {
			if(scr.offsetParent)scr.style.height=scr.offsetParent.offsetHeight;
			m0=itop.offsetHeight+ibot.offsetHeight;
			m1=pane.offsetHeight;
			m2=scr.offsetHeight;
			m3=-1;
			m4=bar.offsetTop;
			m5=bar.offsetHeight;
			_refresh();
		};
}

// THScroll
// Horizintal Scroll bar
// -----------------------------------------------------------------------------
// width: size of scrollbar component
// total: value range
// clip: visible range
// onchange : callback, when bar is moved
// onblur : callback, when dragging is finished
//
function THScroll(opts) {
	var me=this;
	var ddrag=new TDrag({move:dmove});
	var clipx,barx,dx0;

	if(!opts.value)opts.value=0;
	opts.clip0=opts.clip;
	
	var scr = $DIV({css:'HScroll',width:opts.width});
	var bar = $DIV({css:'bar'});
	var iright = $DIV({css:'barRight'});
	var ibody = $DIV({css:'barBody'});
	var ileft = $DIV({css:'barLeft'});
	
	bar.appendChild(ileft);
	bar.appendChild(ibody);
	bar.appendChild(iright);

	scr.appendChild(bar);
	
	this.Component=scr;

	_refresh();
			
	bar.onmousedown=ddrag.start;
	scr.onmousedown=function(){return false;};
	scr.onclick=function(ev){
			setFocus(null);
			var p=getMouseXY(ev);
			var v=Math.ceil(opts.clip/opts.total*opts.width)/2;
			if(v<1)v=1;
			var x0=getAbsX(bar);
			if(p.x<x0)return dmove(-v,0,ev,true);
			if(p.x>x0+clipx)return dmove(v,0,ev,true);
			return false;
		};

	function dmove(dx,dy,ev,exit) {
		barx+=dx;
		if(barx<0)barx=0;
		if(barx+clipx>opts.width)barx=opts.width-clipx;
		
		bar.style.left=barx;
		opts.value=barx*dx0;
		if(opts.onchange)opts.onchange(me,opts.value);	
		if(exit&&opts.onblur)opts.onblur(me,opts.value);	
	}
	this.getValue=function() { return(opts.value); };
	this.setValue=function(n,init) {
			barx=dx0?(n/dx0):0;
			if(barx<0)barx=0;
			if(barx+clipx>opts.width)barx=opts.width-clipx;		
			bar.style.left=barx;
			opts.value=barx*dx0;
			if(!init){
				if(opts.onchange)opts.onchange(me,opts.value);	
				if(opts.onblur)opts.onblur(me,opts.value);	
			}
		};
	this.setLength=function(len,clp) {
			opts.total=len;
			if(clp){
				opts.clip=clp;
				opts.clip0=clp;
			} else opts.clip=opts.clip0;
			_refresh();
		};
	function _refresh() {
		if(opts.total>0) {
			opts.clip=(opts.clip>opts.total)?opts.total:opts.clip;
			clipx=Math.ceil(opts.clip/opts.total*opts.width);
			if(clipx<5)clipx=5;
			dx0=(opts.width==clipx)?0:((opts.total-Math.floor(opts.clip))/(opts.width-clipx));
			barx=dx0?(opts.value/dx0):0;
			ibody.style.width=clipx-2;			
			bar.style.left=barx;
			bar.style.visibility='visible';
		} else {
			// no data -> disable scroll bar
			ibody.style.width=opts.width-2;
			bar.style.left=0;
			bar.style.visibility='hidden';
		}
	}
}

// TVScroll
// Vertical Scroll bar
// -----------------------------------------------------------------------------
// height: size of scrollbar component
// total: value range
// clip: visible range
// onchange : callback, when bar is moved
// onblur : callback, when dragging is finished
//
function TVScroll(opts) {
	var me=this;
	var ddrag=new TDrag({move:dmove});
	var clipy,bary,dy0;

	if(!opts.value)opts.value=0;
	opts.clip0=opts.clip;
	
	var scr = $DIV({css:'Scrollbar',height:opts.height});
	var bar = $DIV({css:'bar'});
	var itop = $DIV({css:'barTop'});
	var ibody = $DIV({css:'barBody'});
	var ibot = $DIV({css:'barBottom'});	
	bar.appendChild(itop);
	bar.appendChild(ibody);
	bar.appendChild(ibot);
	scr.appendChild(bar);

	this.Component=scr;

	_refresh();
			
	bar.onmousedown=ddrag.start;
	scr.onmousedown=function(){return false;};
	scr.onclick=function(ev){
			setFocus(null);
			var p=getMouseXY(ev);
			var v=Math.ceil(opts.clip/opts.total*opts.height)/2;
			if(v<1)v=1;
			var y0=getAbsY(bar);
			if(p.y<y0)return dmove(0,-v,ev,true);
			if(p.y>y0+clipy)return dmove(0,v,ev,true);
			return false;
		};

	function dmove(dx,dy,ev,exit) {
		bary+=dy;
		if(bary<0)bary=0;
		if(bary+clipy>opts.height)bary=opts.height-clipy;
		
		bar.style.top=bary;
		opts.value=bary*dy0;
		if(opts.onchange)opts.onchange(me,opts.value);	
		if(exit&&opts.onblur)opts.onblur(me,opts.value);	
	}
	this.getValue=function() { return(opts.value); };
	this.setValue=function(n,init) {
			bary=dy0?(n/dy0):0;
			if(bary<0)bary=0;
			if(bary+clipy>opts.height)bary=opts.height-clipy;		
			bar.style.top=bary;
			opts.value=bary*dy0;
			if(!init){
				if(opts.onchange)opts.onchange(me,opts.value);	
				if(opts.onblur)opts.onblur(me,opts.value);	
			}
		};
	this.setLength=function(len,clp) {
			opts.total=len;
			if(clp){
				opts.clip=clp;
				opts.clip0=clp;
			} else opts.clip=opts.clip0;
			_refresh();
		};
	function _refresh() {
		if(opts.total>0) {
			opts.clip=(opts.clip>opts.total)?opts.total:opts.clip;
			clipy=Math.ceil(opts.clip/opts.total*opts.height);
			if(clipy<5)clipy=5;
			dy0=(opts.height==clipy)?0:((opts.total-Math.floor(opts.clip))/(opts.height-clipy));
			bary=dy0?(opts.value/dy0):0;
			ibody.style.height=clipy-2;			
			bar.style.top=bary;
			bar.style.visibility='visible';
		} else {
			// no data -> disable scroll bar
			ibody.style.height=opts.height-2;
			bar.style.top=0;
			bar.style.visibility='hidden';
		}
	}
}


// =============================================================================
// List
// =============================================================================
// Creates and maintains a list for selection. Every line of the list can 
// contain a number of subitems (columns)
// -----------------------------------------------------------------------------
// width      (o) width of component (default=800)
// height     (m) height of component in pixels
// dataHeight (m) height of one entry in pixels
// dataWidth  (o) size of an entry in pixels (if ommited, visible width is used)
// header     (o) bool., visibility of the header line (default=true)
// selectable (o) bool., selectable list (default=false)
// fontSize   (o) font size of component
// onreload   (m) function, called to request more data entries
// onselect   (o) function, called, when an entry is selected
// onsort     (o) function, called, when the sorting order is changed
// -----------------------------------------------------------------------------
// (o)=optional (m)=mandatory
//
function TDynList(_options) {
	var me=this;
	this.type='list';
	// options for component
	var opts={height:0,width:800,header:true,selectable:false,selected:-1,dataLeft:0,nofocus:false};
	if (_options)	for (var o in _options) opts[o]=_options[o];
	if(opts.fontSize)main.style.fontSize=opts.fontSize;
	if(!opts.dataWidth)opts.dataWidth=opts.width-16;

	var main= $DIV({css:'container',width:opts.width,height:opts.height});
	opts.width-=16;
	var div = $DIV({css:'Liste',width:opts.width,height:opts.height});
	var hdr = $DIV({css:'header',width:opts.dataWidth});
	var tdv = $DIV({css:'Liste',width:opts.width,height:opts.height-hdr.offsetHeight});
	var scr = new TVScroll({height:opts.height,total:0,clip:opts.height/opts.dataHeight,onchange:emove,onblur:eset});	// scroll bar
	var hscr;

	var lst = [];													// array containing the list items as objects
	var col = [];													// array of columns
	var vis=-1;														// first visible element

	
	// GLOBAL CONSTANTs DEFINITION for List
	// =============================================================================
	var sortImgNo = 'img/ui/arrownone.png';	// sort flag - no sort
	var sortImgUp = 'img/ui/arrowup.png';		// sort flag - ascending
	var sortImgDn = 'img/ui/arrowdown.png';	// sort flag - descending
	// =============================================================================

	var sortCol = -1;
	var sortOrd = 1;

	if (!opts.header) hdr.style.display='none';
	
	div.appendChild(hdr);
	div.appendChild(tdv);
	
	main.appendChild(div);
	main.appendChild(scr.Component);
	
	if(opts.dataWidth>opts.width){
		// horizontal clipping -> add horizontal scroll bar
		var d=$DIV({css:'container'});
		d.appendChild(main);
		hscr=new THScroll({width:opts.width,total:opts.dataWidth,clip:opts.width,value:0,onchange: function(c){
						var n=-c.getValue();
						if(vis!=-1)
							for(var i=vis;i<vis+elvis;i++)
								if((i<lst.length)&&lst[i])
									lst[i].row.style.left=n;
						hdr.style.left=n;
						opts.dataLeft=n;
				} });
		d.appendChild(hscr.Component);
		main = d;
	}
		
	this.Component = main;

	var elvis=Math.ceil(opts.height/opts.dataHeight);	// number of elements visible at a time
		
	new TWheel(tdv,function(d) {
			if(vis==-1)return;
			var n=vis+d;
			if(n<0)n=0;
			if(n+elvis>lst.length)n=lst.length-elvis;
			scr.setValue(n);
		});
	
	function emove(c,val) {
		// just move elements
		var n0=Math.floor(val);
		var nx0=-(val-n0)*opts.dataHeight;	// position of first visible element
		// hide invisible elements
		if(vis!=-1)
			for(var i=vis;i<vis+elvis;i++){
				if((i<lst.length)&&lst[i]&&((i<n0)||(i>=(n0+elvis)))){
					lst[i].row.style.display='none';
				}
			}
		// move visible elements
		for(var i=n0;i<n0+elvis;i++) {
			if((i<lst.length)&&lst[i]){
				lst[i].row.style.display='block';
				lst[i].row.style.top=nx0+(i-n0)*opts.dataHeight;
				lst[i].row.style.left=opts.dataLeft;
			}
		}
		vis=n0;
	}
	function eset(c,val) {
		// now reload missing elements
		var vis=Math.floor(val);
		var nx0=-(val-vis)*opts.dataHeight;		// position of first visible element
		for(var n=vis;n<vis+elvis;n++) {
			if((n<lst.length)&&(!lst[n])){
				var robj=opts.onreload(n);

				for (var i=0; i<robj.length; i++) robj[i].content=$content(robj[i].content);

				var r={cells:robj,value:n,row:$DIV({css:(n==opts.selected)?'absselect':((n%2==0)?'absodd':'abseven'),height:opts.dataHeight,top:nx0+(n-vis)*opts.dataHeight,left:opts.dataLeft})};

				var d=r.row;
				d.onmouseover=me.mouseOverEvent.bind(me,r);
				d.onmouseout=me.mouseOutEvent.bind(me,r);
				d.onclick=me.mouseClick.bind(me,r,n);
						
				for (i=0; i<r.cells.length; i++) {
					var cell=$DIV({css:'cell', width:col[i].width, textAlign:col[i].align});
					cell.appendChild(r.cells[i].content);
					d.appendChild(cell);
				}
				d.appendChild($DIV({clear:'left'}));
				tdv.appendChild(d);

				lst[n]=r;
			}
		}
	}

	// setHeader
	// replaces the complete header
	this.setHeader = function(hobjs) {
		for (var i=0; i<hobjs.length; i++) {
			var head=hobjs[i];
			head.width=(head.width>1)?head.width:Math.floor(head.width*opts.dataWidth);
			if(opts.header) {
				head.content=$content(head.content);
				if (!head.align)head.align='left';
				if (typeof(head.sort)=='undefined') head.sort=true;
				// create new cell in header
				head.cell = $DIV({css:'cell', width:head.width,textAlign:head.align,cursor:'pointer'});
				
				// fill cell
				head.cell.onclick=me.sortBy.bind(me,col.length,0);
				head.cell.appendChild(head.content);
				if (head.sort){
					head.sortimg=$IMG(sortImgNo);
					head.cell.appendChild(head.sortimg);
				}
				hdr.appendChild(head.cell);
			}
			col.push(head);
		}
		if(opts.header) {
			hdr.appendChild($DIV({clear:'left'}));
			tdv.style.height=opts.height-hdr.offsetHeight;
		}
	};
	
	this.mouseOverEvent=function(r) {
			r.row.className='absover';
		};
	this.mouseOutEvent=function(r) {
			if((opts.selectable)&&(r.value==opts.selected))r.row.className='absselect';
			else r.row.className=(r.value%2==0)?'absodd':'abseven';
		};
	this.mouseClick=function(r,i) {
			setFocus(null);
			if(opts.selectable){
				unselect();
				r.row.className='absselect';
				opts.selected=i;
				if(opts.onchange)opts.onchange(me,opts.selected);
				if((opts.selected<vis)||(opts.selected>=vis+elvis-1)||(vis==-1))scr.setValue(opts.selected);
			}
		};
	this.getIndex=function(){
			return(opts.selected); 
		};
	this.setIndex=function(v,init){
			unselect();
			opts.selected=((opts.selectable)&&(v>-1)&&(v<lst.length))?v:-1;
			if(opts.selected!=-1) {
				var r=lst[opts.selected];
				if(r)r.row.className='absselect';
				if((opts.selected<vis)||(opts.selected>=vis+elvis-1)||(vis==-1))scr.setValue(opts.selected);
			}
			if((!init)&&(opts.onchange))opts.onchange(me,opts.selected);
		};
	function unselect() {
		// deselect actual selection
		if(opts.selected!=-1){
			var r=lst[opts.selected];
			r.row.className=(r.value%2==0)?'absodd':'abseven';
			opts.selected=-1;
		}
	}
	this.getValue=function(){
			return (opts.selected==-1)?null:lst[opts.selected].value;
		};
	this.setValue=function(v,init){
			var i;
			for(i=0;i<lst.length;i++)if(lst[i]&&(lst[i].value==v))break;
			me.setIndex(i,init);
		};
	this.getLength=function() {
			return(lst.length);
		};

	// setLength
	// set total number of elements in the list
	this.setLength=function(len) {
			// first remove existing entries
			opts.selected=-1;
			var l;
			while (lst.length>0) {
				l=lst.shift();
				if(l)tdv.removeChild(l.row);
			}
			vis=-1;
			// now generate new list
			if(len>0) {
				lst=new Array(len);
				scr.setLength(len);
				scr.setValue(0);									// initiate a reload
			}		
//			if (sortCol!=-1)col[sortCol].sortimg.src=sortImgNo;
//			sortCol = -1;
//			sortOrd = 1;		
		};
	// clear
	// removes all elements from the list
	this.clear = function () {
			scr.setLength(0);
		};
	this.setHeight=function(h){
			main.style.height=h;
			tdv.style.height=(h-hdr.offsetHeight);
			scr.refresh();
		};
		
	// sortBy
	// rearranges the lines, so that they appear sorted by the column specified
	// ord : 1=ascending, -1=descending, or null/0=flip sort order
	this.sortBy = function(column,ord) {
		if (!col[column].sort) return;
		if ((col[sortCol].sortimg)&&(sortCol!=-1))col[sortCol].sortimg.src=sortImgNo;
		sortOrd=(ord)?ord:((sortCol==column)?-sortOrd:1);
		sortCol=column;
		if(col[sortCol].sortimg)col[sortCol].sortimg.src=(sortOrd==1)?sortImgUp:sortImgDn;
		if(opts.onsort)opts.onsort(me,sortCol,sortOrd);		
		me.setLength(lst.length);						// delete cached elements
	};
	this.getWidth=function(i) {
			return(i<col.length)?col[i].width:-1;
		};
	function $content(c) {
		return (typeof(c)=='string')?$TXT(c):c;
	}
}

function TList(_options) {
	var me=this;
	this.type='list';
	var main= $DIV({css:'container'});		// main container
	var div = $DIV({css:'Liste'});				// container for list
	var hdr = $DIV({css:'header'});				// contains the list's header
	var tdv = $DIV({css:'Liste'});				// continer for main table
	var tbl = $DIV({css:'tables'});				// contains the TABLE component
	var scr = new TScroll(tdv,tbl);				// scroll bar
	var hscr=null;												// optioinal horizontal scroll bar
	
	var lst = new Array();								// array containing the list items as objects
	var col = new Array();								// array of columns
	
	// GLOBAL CONSTANTs DEFINITION for List
	// =============================================================================
	var sortImgNo = 'img/ui/arrownone.png';	// sort flag - no sort
	var sortImgUp = 'img/ui/arrowup.png';		// sort flag - ascending
	var sortImgDn = 'img/ui/arrowdown.png';	// sort flag - descending
	// =============================================================================

	// options for component
	var opts={height:0,width:800,header:true,selectable:false,selected:-1};
	if (_options)	for (var o in _options) opts[o]=_options[o];
	if(opts.fontSize)main.style.fontSize=opts.fontSize;

	var sortCol = -1;
	var sortOrd = 1;

	if (opts.height!=0) {
		main.style.height=opts.height;
		tdv.style.height=(opts.height-hdr.offsetHeight);
	}

	main.style.width=opts.width;
	opts.width-=16;
	// width of data field
	var datw = (opts.dataWidth)?opts.dataWidth:opts.width;
	div.style.width=opts.width;
	tdv.style.width=opts.width;
	tbl.style.width=datw;
	hdr.style.width=datw;

	if (!opts.header) hdr.style.display='none';
	
	div.appendChild(hdr);
	tdv.appendChild(tbl);
	div.appendChild(tdv);
	
	main.appendChild(div);
	main.appendChild(scr.Component);
	
	if(datw>opts.width){
		// horizontal clipping -> add horizontal scroll bar
		var d=$DIV({css:'container'});
		d.appendChild(main);
		hscr=new THScroll({width:opts.width,total:datw,clip:opts.width,value:0,onchange: function(c){ tbl.style.left=-c.getValue(); hdr.style.left=-c.getValue(); } });
		d.appendChild(hscr.Component);
		main = d;
	}
		
	this.Component = main;

	// setHeader
	// replaces the complete header
	this.setHeader = function(hobjs) {
		for (var i=0; i<hobjs.length; i++) _addColumn(hobjs[i]);
		if(opts.header) {
			hdr.appendChild($DIV({clear:'left'}));
			if (opts.height!=0)tdv.style.height=(opts.height-hdr.offsetHeight);
			scr.refresh();
		}
	};
	
	// addColumn
	// adds one column to the list
	function _addColumn(head) {
		// conditioning
		head.width=(head.width>1)?head.width:Math.floor(head.width*opts.width);
		if (!head.align) head.align='left';
		if (typeof(head.sort)=='undefined') head.sort=true;

		if(opts.header){
			head.content=$content(head.content);
			// create new cell in header
			head.cell = $DIV({css:'cell', width:head.width,textAlign:head.align,cursor:'pointer'});
		
			// fill cell
			head.cell.onclick=me.sortBy.bind(me,col.length);
			head.cell.appendChild(head.content);
			if (head.sort){
				head.sortimg=$IMG(sortImgNo);
				head.cell.appendChild(head.sortimg);
			}
			hdr.appendChild(head.cell);
		}			
		col.push(head);
	}
	
	// insertRow
	// appends one row to the list
	// ro is an Array of Objects (content,sort)
	this.insertRow = function(robj,val) {
		// convert content fields into proper arrays
		for (var i=0; i<robj.length; i++) robj[i].content=$content(robj[i].content);

		var r=new Object();
		r.cells=robj;
		r.value=val;
		lst.push(r);
		this.updateRow(r);
		return r;
	};

	this.updateRow=function(r) {
		var i,d;
		if (r.row) {
			tbl.appendChild(tbl.removeChild(r.row));
		} else {
			d=$DIV();
			d.onmouseover=this.mouseOverEvent.bind(this,r);
			d.onmouseout=this.mouseOutEvent.bind(this,r);
			d.onclick=this.mouseClick.bind(this,r);
						
			for (i=0; i<r.cells.length; i++) {
				var cell=$DIV({css:'cell', width:col[i].width, textAlign:col[i].align});
				cell.appendChild(r.cells[i].content);
				d.appendChild(cell);
			}
			d.appendChild($DIV({clear:'left'}));
			tbl.appendChild(d);
			r.row=d;
		}
		// update row's classNames in table
		for (i=0; i<lst.length; i++) { 
			lst[i].rowIndex=i; 
			if(lst[i].row.className!='select')lst[i].row.className=(i%2==0)?'odd':'even'; else opts.selected=i;
		}
		return(r);
	};
	this.mouseOverEvent=function(r) {
			r.row.className='over';
		};
	this.mouseOutEvent=function(r) {
			if((opts.selectable)&&(r.rowIndex==opts.selected))r.row.className='select';
			else r.row.className=(r.rowIndex%2==0)?'odd':'even';
		};
	this.mouseClick=function(r) {
			if(!opts.nofocus)setFocus(null);
			if(opts.selectable){
				r.row.className='select';
				opts.selected=r.rowIndex;
				if(opts.onchange)opts.onchange(me,r.rowIndex);
				updateSel();
				scr.makeVisible(r.row.offsetTop,r.row.offsetHeight);
			}
		};
	this.getIndex=function(){
			return(opts.selected); 
		};
	this.setIndex=function(v,init){
			opts.selected=((opts.selectable)&&(v>-1)&&(v<lst.length))?v:-1;
			updateSel();
			if(opts.selected!=-1) {
				var r=lst[opts.selected].row;
				scr.makeVisible(r.offsetTop,r.offsetHeight);
			}
			if((!init)&&(opts.onchange))opts.onchange(me,v);
		};
	this.getValue=function(){
			return (opts.selected==-1)?null:lst[opts.selected].value;
		};
	this.setValue=function(v,init){
			var i;
			for(i=0;i<lst.length;i++)if(lst[i].value==v)break;
			me.setIndex(i,init);
		};
	this.getLength=function() {
			return(lst.length);
		};
	function updateSel(){
		for(var i=0;i<lst.length;i++)
			lst[i].row.className=(i==opts.selected)?'select':((i%2==0)?'odd':'even');
	}

	// clear
	// removes all elements from the list
	this.clear = function () {
		opts.selected=-1;
		var l;
		while (lst.length>0) {
			l=lst.shift();
			tbl.removeChild(l.row);
		}
		scr.refresh();
	};
	this.refresh=function(){
			tdv.style.height=opts.height-hdr.offsetHeight;
			scr.refresh();
		};
	this.setHeight=function(h){
			main.style.height=h;
			tdv.style.height=(h-hdr.offsetHeight);
			scr.refresh();
		};
		
	// sortBy
	// rearranges the lines, so that they appear sorted by the column specified
	this.sortBy = function(column,ord) {
		if (!col[column].sort) return;
		if ((col[sortCol].sortimg)&&(sortCol!=-1))col[sortCol].sortimg.src=sortImgNo;
		sortOrd=(ord)?ord:((sortCol==column)?-sortOrd:1);
		sortCol=column;
		if(col[sortCol].sortimg)col[sortCol].sortimg.src=(sortOrd==1)?sortImgUp:sortImgDn;
		if(opts.onsort)opts.onsort(me,sortCol,sortOrd);		
		me.setLength(lst.length);						// delete cached elements
	};
	this.sortBy = function(column,ord) {
		if (!col[column].sort) return;
		if (sortCol!=-1) col[sortCol].sortimg.src=sortImgNo;
		if (sortCol==column) sortOrd=-sortOrd;
		sortCol=column;
		col[sortCol].sortimg.src=(sortOrd>0)?sortImgUp:sortImgDn;
		lst.sort(_sort);

 		for (var k=0; k<lst.length; k++) this.updateRow(lst[k]);
	};
	this.sortByEx=function(column,order) {
		if (!col[column].sort) return;
		sortOrd=order;
		sortCol=column;
		if(col[sortCol].sortimg)col[sortCol].sortimg.src=(sortOrd>0)?sortImgUp:sortImgDn;
		lst.sort(_sort);

 		for (var k=0; k<lst.length; k++) this.updateRow(lst[k]);
	};
	function _sort(a,b) {
		if (a.cells[sortCol].sort>b.cells[sortCol].sort) return (sortOrd); else return (-sortOrd);
	}
	this.getWidth=function(i) {
			return(i<col.length)?col[i].width:-1;
		};
	function $content(c) {
		return (typeof(c)=='string')?$TXT(c):c;
	}
}


function TOptionList(_opts,_ifo) {
	var opt=_opts;
	var ifo=_ifo;
	opt.header=false;
	var l=new TList(opt);
	l.setHeader([{width:1,content:'', sort:false}]);
	this.Component=l.Component;
	this.addOption=function(_o) {
		_o.name=opt.name;
		_o.layout='right';
		_o.marginTop=1;
		_o.marginBottom=1;
		_o.titleWidth=opt.width-40;
		var c=new TCheck(_o,null,ifo);
		l.insertRow([{content:c.Component}]);
		l.refresh();
	};
	this.addHeader=function(_txt) {
		var v=$DIV({css:'ListeHeader'});
		v.innerHTML=_txt;
		l.insertRow([{content:v}]);
		l.refresh();
	};
	this.refresh=function(){l.refresh();};
}


//
// Value Component
// =============================================================================
function TValue(opts) {
	var me=this;
	var m=$DIV({css:'UIelement'});
	var prec=opts.precision;
	var txt='';
	var vdiv;
	var val;
	var tx;
	if(opts.width)m.style.width=opts.width;
	if (opts.style=='small') {
		vdiv=$DIV({css:'UIelement',fontSize:14});
		txt='<SMALL>'+(opts.title?opts.title:'&nbsp;')+(opts.unit?('['+opts.unit+']'):'')+'</SMALL><BR>';
		m.appendChild(vdiv);
	} else {
		tx=$DIV({css:'UIelement',fontSize:8});
		tx.innerHTML=(opts.title?opts.title:'&nbsp;')+'<BR><BR>'+(opts.unit?opts.unit:'&nbsp;');
		m.appendChild(tx);
		vdiv=$DIV({css:'UIelement',fontSize:(opts.style=='medium')?18:24,fontWeight:900});
		m.appendChild(vdiv);
	}

	this.setValue=_setValue;
	function _setValue(v) { val=v;vdiv.innerHTML=txt+((prec==-1)?v:parseFloat(v).toFixed(prec)); }
	this.getName=function(){return(opts.name);};
	this.getValue=function(){return(val);};
	this.reset=function(){};
	this.setUnit=function(u){
			opts.unit=u;
			if(opts.style=='small'){
				txt='<SMALL>'+(opts.title?opts.title:'&nbsp;')+(opts.unit?('['+opts.unit+']'):'')+'</SMALL><BR>';
			} else {
				tx.innerHTML=(opts.title?opts.title:'&nbsp;')+'<BR><BR>'+opts.unit;
			}
		};
	this.Component=m;

	if(typeof(opts.value)!='undefined')_setValue(opts.value);
	
	if(opts.parent)opts.parent.appendChild(m);
	if(opts.form)opts.form.add(me);
}

//
// DateTime View component
// =============================================================================
function TDateTime(opts) {
	var me=this;
	var m=$DIV({css:'UIelement'});
	var tx=$DIV({css:'UIelement',fontSize:10,fontWeight:900});
	tx.innerHTML='@';
	m.appendChild(tx);
	var tv=$DIV({css:'UIelement',fontSize:14,fontWeight:900});
	var val=0;
	m.appendChild(tv);
	
	this.setValue=function(v) { 
			val=(typeof(v)=='string')?parseInt(v,10):v;
			tv.innerHTML='<SMALL>'+Core.getDateStr(val)+'</SMALL><BR>'+Core.getTimeStr(val);
		};
	this.getValue=function(v) { 
			return val;
		};	
	this.getName=function(){return(opts.name);};
	this.reset=function(){};
	this.Component=m;

	if(opts.value!=null)me.setValue(opts.value);
	if(opts.parent)opts.parent.appendChild(m);
	if(opts.form)opts.form.add(me);
}
		
function TButton(opts) {
	this.type='button';
	var me=this;
	var stl='ui_button';
	
	if(opts.onclick)opts.link=opts.onclick;
	opts.onclick=function() {
			setFocus(null);
			if(opts.link)opts.link(me);
		};
	if(opts.decoration) stl='ui_buttonDeco';
	var m=$DIV({css:stl});
	m.onmouseover=function() { m.className='ui_buttonOver'; };
	m.onmouseout=function() { m.className=stl; };
	var i=$IMG(opts.img,{css:'ui_buttonImg',title:opts.title,link:function(){if(opts.onclick)opts.onclick(me); }});
	m.appendChild(i);
	this.form=null;
	this.file=null;
	if(opts.file){
		// a file upload button
		// required opts: {id},{name},{action}
		// optional: {onchange}

		// virtual target for form response
		var tid='_iframe_'+opts.id;
///*@IE@*/			var f=$DIV({html:'<iframe name="'+tid+'" style="display:none"></iframe>'});
			var f=document.createElement('iframe');
			f.name=tid;
			f.id=tid;
			f.style.display='none';
		m.appendChild(f);
		this.target=f;

		// form for file upload
		var f=document.createElement('form');
		f.action=opts.action;
		f.method='post';
		f.encoding='multipart/form-data';
		f.target=tid;
		m.appendChild(f);
		this.form=f;

		// file upload button
		var fsel=document.createElement('input');
		fsel.type='file';
		fsel.name=opts.name;
		fsel.style.position='absolute';
		fsel.style.marginLeft=0;
		fsel.size='1';
		fsel.style.top=0;
		fsel.style.left=-50;
		fsel.style.height=32;
		if(opts.accept) fsel.accept=opts.accept;  // note: this is often ignored!
///*@IE@*/		fsel.style.filter='alpha(opacity:0)';
		fsel.style.opacity=0;
		f.appendChild(fsel);
		fsel.onchange=function() {
			    var allow=true;
			    if(this.accept){
			    		fmts=this.accept.split(',');
			    		fmttyp=this.value.slice(this.value.length-3).toLowerCase();
			    		var i;
			    		allow=false;
			    		for(i=0;i<fmts.length;i++){
			    			  var imgtyp=fmts[i].split('/');
			    			  allow = allow || (imgtyp.length>1 && imgtyp[1].toLowerCase()==fmttyp);
			    		}
			    		
			    		if(!allow) {
			    				Core.alert("File does not match allowed format: " + this.accept);
			    		}
			    		
			    }
					if(opts.onchange && allow)opts.onchange(me);
				};
		this.file=fsel;
	}
	this.submit=function(callback,timeout,act) {
			function wait() {
				timeout-=1000;
				if (me.target.contentWindow.document.getElementById('SUCCESS')) {
					// SUCCESS
					callback(0);
				} else {
					if((timeout<0)||(me.target.contentWindow.document.getElementById('ERROR'))) callback(-1); else window.setTimeout(wait,1000);
				}
			}

			if(opts.file){
				if(act)me.form.action=act;
				me.form.submit();
				if(!timeout)timeout=1000000;
				if(callback)wait();
			}
		};
	this.setImg=function(img) {i.src=img;};
	this.setEnabled=function(mo) {
///*@IE@*/				i.style.filter=(mo)?'alpha(opacity:100)':'alpha(opacity:15)';
				i.style.opacity=(mo)?1:0.15;
			opts.enabled=mo;
		};
	this.getEnabled=function() { return (opts.enabled); };
	this.setChecked=this.setEnabled;
	this.isChecked=this.getEnabled;
	this.getName=function() { return(opts.name); };
	this.getValue=function() { return(opts.value); };
	this.setListener=function(cb){
			opts.onclick=function(s) {
					s.setChecked(!s.isChecked());
					cb(s);
				};
		};
	this.Component=m;
	if(opts.enabled===false)this.setEnabled(false); else opts.enabled=true;
	if(opts.group)opts.group.add(me);
	if(opts.parent)opts.parent.appendChild(m);
}

function TTextButton(opts) {
	var me=this;
	var m=$DIV({css:'tbMain'});
	var ml=$DIV({css:'tbEdge',backgroundImage:'url(img/ui/tbl_'+opts.style+'.png)'});
	var mm=$DIV({css:'tbTitle',background:'url(img/ui/tbm_'+opts.style+'.png) repeat-x'});
	var mr=$DIV({css:'tbEdge',backgroundImage:'url(img/ui/tbr_'+opts.style+'.png)'});
	m.appendChild(ml);		
	m.appendChild(mm);		
	m.appendChild(mr);		
	if(opts.width) {
		mm.style.width=opts.width-24;
		m.style.width=opts.width;
	}
	if(opts.img)mm.appendChild($IMG(opts.img,{title:opts.title?opts.title:''}));
	if(opts.text)mm.appendChild($HTML(opts.text));
	if(opts.margin)m.style.margin=opts.margin;
	if(opts.marginLeft)m.style.marginLeft=opts.marginLeft;
	if(opts.marginTop)m.style.marginTop=opts.marginTop;
	m.onmouseover=function() { mm.className='tbTitleOver'; };
	m.onmouseout=function() { mm.className='tbTitle'; };
	m.onclick=function() {
			setFocus(null);
			if(opts.onclick)opts.onclick(me);
		};
	this.setTitle=function(t) { mm.innerHTML=t; opts.title=t; };
	this.getName=function() { return(opts.name); };
	this.setValue=function(v) { opts.value=v; };
	this.getValue=function() { return(opts.value); };
	this.Component=m;
	if(opts.parent)opts.parent.appendChild(m);
}

function TActionButton(opts) {
	var me=this;
	var main=$DIV({css:'abMain',marginLeft:opts.marginLeft,marginTop:opts.marginTop});
	if(opts.text){
		var txt=$DIV({css:'txt',html:opts.text});
		main.appendChild(txt);
	}
	var br=new TTextButton({img:'img/ui/rarrow24.png',title:opts.title,width:50,style:opts.style});
	br.Component.style.position='absolute';
	br.Component.style.left=1;
	br.Component.style.top=1;
	main.appendChild(br.Component);

	var dc=new TDrag({init:dstart,move:dmove});
	var ani=new TAnimation();
	br.Component.onmousedown=dc.start;

	function dstart(x0,y0,evt) {
		setFocus(null);
		ani.stop();			// just to be on the safe side
	}
	function butRet() {
		ani.set([[br.Component,'left',1]]);
		ani.animate(20,10,null);
	}
	function dmove(dx,dy,evt,exit) {
		var o=br.Component;
		var x=o.offsetLeft+dx;
		if(x<1)x=1;
		if(x>129)x=129;
		o.style.left=x;
		if(exit){
			if(x==129) {
				if(opts.onclick&&opts.onclick(me))butRet();
			} else butRet();
		}
	}
	this.reset=butRet;
	this.Component=main;	
}

// TOOLBAR Components
// =============================================================================
function TToolbar(opts) {
	var opt=opts;						// Toolbar options (visible)
	if (!opt) opt=new Object();
	var me=this;
	var main=$DIV({css:(opt.style=='big')?'ToolbarBig':'Toolbar'});
	var grp=new Array();		// contains groups
	var tabs=new Array();		// contains tab buttons
	var seltab=-1;

	var trans=false;				// true if animation is running
	if (opt.transparent) {
		main.style.background='transparent';
		main.style.borderColor='transparent';
	}
	if ((opt.visible)&&(opt.visible==true)) main.style.display='block';
		else opt.visible=false;
	if(opt.backgroundImage)main.style.backgroundImage=opt.backgroundImage;
  if(opt.float) main.style.float=opt.float;

	this.isVisible=function() { return(opt.visible); };
	this.Component=main;
	
	this.addGroup=function(opts) {
		if (!opts) opts=new Object();
		opts.css=(opts.decoration||opts.style)?'groupDeco':'group';
		switch(opts.style) {
			case 'blue' : opts.background='url(img/ui/tbbluebkg.png)';
										break;
			case 'brown': opts.background='url(img/ui/tbbrownbkg.png)';
										break;
		}
		var g=$DIV(opts);
		main.appendChild(g);
		grp.push(g);
		return(grp.length-1);
	};
	this.grpComponent=function(i) {
		return(grp[i]);
	};
	this.showGroup=function(g,show) {
		if (isArray(g)) for(var j=0;j<g.length;j++) _showComp(grp[g[j]],show);
			else _showComp(grp[g],show);
	};
	this.addImg=function(g,opts) {
		opts.css='UIelement';
		opts.background='url('+opts.img+') center no-repeat';
		delete opts.img;
		var m=$DIV(opts);
		grp[g].appendChild(m);
		return(m);
	};
	this.addTab=function(tab) {
		tab.idx=tabs.length;
		var t=$DIV({css:'tab'});
		main.appendChild(t);
		
		t.appendChild($DIV({css:'tabLeft'}));
		tab.div=$DIV({css:'tabMain'});
		t.appendChild(tab.div);
		t.appendChild($DIV({css:'tabRight'}));
		t.onclick=function() {
				setFocus(null);
				me.selectTab(tab.idx);
			};
		
		tab.div.appendChild($IMG(tab.img));
		tab.div.appendChild($HTML(tab.title));
		tab.Component=t;
		tabs.push(tab);
		return(tab);
	};
	this.selectedTab=function() { return seltab;};
	this.selectTab=function(i,relative) {
		if(relative) {
			seltab+=(i<0)?-1:1;
			if(seltab>=tabs.length)seltab=0;
			if(seltab<0)seltab=tabs.length-1;
		} else seltab=i;
		for (var j=0; j<tabs.length; j++) {
			tabs[j].Component.className=(seltab==j)?'tabSelect':'tab';
		}
		if(tabs[seltab].link)tabs[seltab].link(seltab);
	};
	this.showTab=function(i,show) {
		if (isArray(i)) for (var j=0;j<i.length;j++){
			if(i[j]<tabs.length)_showComp(tabs[i[j]].Component,show);
		}	else if(i<tabs.length)_showComp(tabs[i].Component,show);
	};
	function _showComp(c,show) {
		switch(show) {
			case 'none': c.style.display='none'; break;
			case 'hide': c.style.visibility='hidden'; break;
			default: c.style.display='block'; c.style.visibility='visible';
		}
	}
	this.addSlider=function(g,opts) {
		var m=$DIV({css:'UIelement'});
		opts.titleHeight=16;
		var s=new TSlider(opts,m);
		grp[g].appendChild(m);
		return(s);
	};
	this.addTextField=function(g,opts) {
		var m=$DIV({css:'UIelement'});
		opts.layout='top';
		opts.titleHeight=14;
		opts.parent=m;
		var s=new TTextField(opts);
		grp[g].appendChild(m);
		return(s);
	};
	this.addDropDown=function(g,opts) {
		var m=$DIV({css:'UIelement'});
		opts.layout='top';
		opts.titleHeight=14;
		var s=new TDropDown(opts,m);
		grp[g].appendChild(m);
		return(s);
	};
	this.addCheck=function(g,opts) {
		if (!opts) opts=new Object();
		opts.css='UIelement';
		var m=$DIV({css:'UIelement'});
		opts.layout='top';
		opts.titleHeight=14;
		var s=new TCheck(opts,m);
		grp[g].appendChild(m);
		return(s);
	};
	this.addButton=function(g,opts) {
		var b=new TButton(opts);
		grp[g].appendChild(b.Component);
		return b;
	};
	this.addTextButton=function(g,opts) {
		var b=new TTextButton(opts);
		grp[g].appendChild(b.Component);
		return b;
	};
	this.addActionButton=function(g,opts) {
		var b=new TActionButton(opts);
		grp[g].appendChild(b.Component);
		return b;
	};
	this.showButton=function(m,show) {
		if (isArray(m)) for (var i=0;i<m.length;i++)_showComp(m[i].Component,show);
		else _showComp(m.Component,show);
	};
	this.addLabel=function(g,opts) {
		if (!opts) opts=new Object();
		switch(opts.style){
			case'big':opts.css='UIelementBig';break;
			case'medium':opts.css='UIelementMedium';break;
			default:opts.css='UIelement';
		}
		var m=$DIV(opts);
		m.innerHTML=opts.title;
		m.setValue=function(v) { m.innerHTML=v; };
		m.getValue=function() { return(m.innerHTML);};
		grp[g].appendChild(m);
		return(m);
	};
	this.addValue=function(g,opts) {
		opts.parent=grp[g];
		var o=new TValue(opts);
		return(o);		
	};
	this.addDateTime=function(g,opts) {
		opts.parent=grp[g];
		var o=new TDateTime(opts);
		return(o);		
	};
	this.addSpacer=function(g,opts) {
		opts.css='UIelement';
		var o=$DIV(opts);
		grp[g].appendChild(o);
		return(o);		
	};
				
	this.show=function() { main.style.display='block';opt.visible=true; };
	this.hide=function() { main.style.display='none';opt.visible=false; };
	this.flip=function() { opt.visible=!opt.visible; main.style.display=opt.visible?'block':'none'; };
}

function TCalendar(callback, year, month, day) {
		var me=this;
    var MONTHS=['Jan','Feb','Mar',
    						'Apr','May','Jun',
    						'Jul','Aug','Sep',
    						'Oct','Nov','Dec'];
    var DOW=['Sun',
    				 'Mon',
    				 'Tue',
    				 'Wed',
    				 'Thu',
    				 'Fri',
    				 'Sat'];    
		var cDiv,cBody,cTitle;
		var quit=false;

	  if(year && month && day)
	    this.dat = new Date(year, month, day);
	  else
	    this.dat = new Date();
	  
	  var func = callback;
	  this.isOpen = false;
	  var rendered = false;
	  var bodyRendern = false;
		          
		this.open = function(anchor) {
				quit=false;
			  if(!rendered)
			    render();
			  if(!bodyRendern)
			    renderBody();
			  cDiv.style.display = "block";
			  positionAtAnchor(cDiv, anchor, "bottom", "left");
			  me.isOpen = true;
			};

		function dcClose() {
			if(quit){
		  	cDiv.style.display = "none";
		  	me.isOpen = false;
			}
		}
		this.close = function(force) {
				quit=true;
				if(force) dcClose();
				else window.setTimeout(dcClose,250);
			};

		this.setDate = function(d) {
				quit=false;
			  me.dat=d;
			  bodyRendern = false;
			};
		function dcSetDate(d) {
				quit=false;
			  me.dat.setDate(d);
			  bodyRendern = false;
			}

		function positionAtAnchor(element, anchor) {
		  var left = getAbsX(anchor);
		  if(left + element.offsetWidth > document.body.scrollWidth && left - element.offsetWidth > 0)
		    left+=anchor.offsetWidth-element.offsetWidth+1;
		
		  var top = getAbsY(anchor)+anchor.offsetHeight+1;
		  if(top + element.offsetHeight > document.body.scrollHeight && top - element.offsetHeight > 0)
		    top-=element.offsetHeight+anchor.offsetHeight;
		
		  element.style.top = top+"px";
		  element.style.left = left+"px";
		}

		function render() {
		  cDiv = $DIV({css:'dcDiv'});
		  var cTable = document.createElement("TABLE");
		  var cHead = document.createElement("THEAD");
		  var cHeadTR = document.createElement("TR");
		  var cHeadWeekTR = document.createElement("TR");
		  var cBY = document.createElement("TH");
		  var cBM = document.createElement("TH");
		  cTitle = document.createElement("TH");
		  var cFM = document.createElement("TH");
		  var cFY = document.createElement("TH");
		  cBody = document.createElement("TBODY");
		  
		  cTable.className = "dcTable";
		  cTitle.className='TI';
		  cTitle.colSpan = 3;
		  
		  cBY.className = "BF";
		  cBM.className = "BF";
		  cFM.className = "BF";
		  cFY.className = "BF";
		  cBY.innerHTML = "<<";
		  cBM.innerHTML = "<";
		  cFM.innerHTML = ">";
		  cFY.innerHTML = ">>";
		  cBY.onclick = dcBackYearOnClick;
		  cBM.onclick = dcBackMonthOnClick;
		  cFM.onclick = dcForwardMonthOnClick;
		  cFY.onclick = dcForwardYearOnClick;
		  		  
		  //populate week header
		  for(var i = 0; i < 7; i++) {
		    var dowTH = document.createElement("TH");
		    dowTH.innerHTML = DOW[i];
		    cHeadWeekTR.appendChild(dowTH);
		  }
		
		  cHeadTR.appendChild(cBY);
		  cHeadTR.appendChild(cBM);
		  cHeadTR.appendChild(cTitle);
		  cHeadTR.appendChild(cFM);
		  cHeadTR.appendChild(cFY);
		  cHead.appendChild(cHeadTR);
		  cHead.appendChild(cHeadWeekTR);
		  cTable.appendChild(cHead);
		  cTable.appendChild(cBody);
		  cDiv.appendChild(cTable);
		  document.body.appendChild(cDiv);
		  
		  rendered = true;
		}

		function renderBody() {
		  //title 
		  var title = MONTHS[me.dat.getMonth()]+ " " + me.dat.getFullYear();
		  cTitle.innerHTML = title;
		  
		  //clear table body
		  while(cBody.hasChildNodes())
		    cBody.removeChild(cBody.lastChild);
		  
		  //generate new table body
		  var tmpDate = new Date(me.dat.getFullYear(), me.dat.getMonth(), 1);
		
		  var row = document.createElement("TR");
		  cBody.appendChild(row);
		  
		  //blanks
		  for(var i = 0; i < tmpDate.getDay(); i++) {
		    var td = document.createElement("TD");
		    row.appendChild(td);
		  }
		  
		  while(tmpDate.getMonth() == me.dat.getMonth()) {
		    var td = document.createElement("TD");
		    var d = tmpDate.getDate();
		    if(d == me.dat.getDate())
		      td.className = "daySelected";
		    else
		      td.className = "day";
		    
		    td.innerHTML = d;
		    td.onclick = dcSelectDayOnClick;
		    row.appendChild(td);
		    
		    if(tmpDate.getDay() == 6) {
		      row = document.createElement("TR");
		      cBody.appendChild(row);
		    }
		    
		    tmpDate.setDate(d + 1);
		  }
		  
		  //blanks 
		  for(var i = tmpDate.getDay(); i < 7; i++) {
		    var td = document.createElement("TD");
		    row.appendChild(td);
		  }
		  
		  bodyRendern = true;
		}

		// Event handlers -------------------------------------------------------------
		function dcSelectDayOnClick(event) {
			var src=(window.event)?window.event.srcElement:event.currentTarget;
		  dcSetDate(src.innerHTML);
		  if(func)func(Core.getDateStr(me.dat));
		  me.close(true);
		  return false;
		}
		function dcBackYearOnClick(event) {
		  dcSetDate(1);
		  me.dat.setFullYear(me.dat.getFullYear() - 1);
		  renderBody();
		  return false;
		}
		function dcBackMonthOnClick(event) {
		  dcSetDate(1);
		  me.dat.setMonth(me.dat.getMonth() - 1);
		  renderBody();
		  return false;
		}
		function dcForwardMonthOnClick(event) {
		  dcSetDate(1);
		  me.dat.setMonth(me.dat.getMonth() + 1);
		  renderBody();
		  return false;
		}
		function dcForwardYearOnClick(event) {
		  dcSetDate(1);
		  me.dat.setFullYear(me.dat.getFullYear() + 1);
		  renderBody();
		  return false;
		}

}

// Panels
// =============================================================================
// sliding panels on the left side of a container in main area of layout
// requires: width and height parameter
//
function TPanels(opts) {
	var me=this;
	this.type='panel';
	var div=$DIV({css:'uiPanelsMain',width:opts.width-1,height:opts.height});
	this.Component=div;
	this.panel=[];												// array containing panels
	var sel=-1;													// index to currently selected panel
	var ani=new TAnimation();
	
	this.add=function(title,img) {
			sel=me.panel.length;
			var o={
				main:$DIV({css:'uiPanelsPanel',top:sel*18,width:opts.width-1}),
				head:$DIV({css:'uiPanelsHeader',width:opts.width-5,html:title,onclick:me.select.bind(me,sel)}),
				body:$DIV({css:'uiPanelsBody',width:opts.width-1,height:opts.height})
			};
			o.main.appendChild(o.head);
			o.main.appendChild(o.body);
			div.appendChild(o.main);
			me.panel.push(o);
			return(sel);
		};
	
	this.select=function(n,init) {
			if(init!==true)init=false;
			var ns=me.panel.length;
			if((n==sel)||(n>=ns)||(n<0))return;
			var al=[];
			if(n<sel) {
				for(var i=n+1;i<ns;i++)al.push([me.panel[i].main,'top',opts.height-((ns-i)*18)]);
			} else {
				for(var i=sel+1;i<=n;i++)al.push([me.panel[i].main,'top',i*18]);
			}
			ani.set(al);
			ani.animate(10,25,null,init);
			sel=n;
		};
		
	this.selected=function() {
			return(sel);
		};
}

// Animation
// =============================================================================
var animation=true;
function TAnimation(ai) {
	var me=this;
	var t=0;
	var step,e=[];
	this.inProgress=false;
	this.stop=function() {
			if(t)window.clearInterval(t);
			t=0;
			me.inProgress=false;
		};
	this.clear=function() {
			me.stop();
			e=[];
		};
	this.add=function(elem,property,val) {
///*@IE@*/			if(property=='opacity')property='filter';
			e.push({s:elem.style,a:null,p:property,x1:val,o:elem});
		};
	this.set=function(ar) {
			me.clear();
			for(var i=0;i<ar.length;i++)
				me.add(ar[i][0],ar[i][1],ar[i][2]);
		};
	this.animate=function(steps,intv,callback,noanimate){
			if(t)window.clearInterval(t);
			t=0;
			if(animation&&!noanimate){
				// initialize
				for(var i=0;i<e.length;i++){
					var o=e[i];
					switch(o.p){
						case 'left':		o.x0=o.o.offsetLeft; break;
						case 'top':			o.x0=o.o.offsetTop; break;
						case 'width':		o.x0=o.o.offsetWidth; break;
						case 'height':	o.x0=o.o.offsetHeight; break;
						case 'opacity':	o.x0=parseFloat(getCurrentStyle(o.o,'opacity'));break;
///*@IE@*/						case 'filter':	// IE shit
///*@IE@*/														try{
///*@IE@*/															o.x0=o.o.filters.item('alpha').opacity/100;
///*@IE@*/														} catch(ex) {
///*@IE@*/															o.o.style.filter='alpha(opacity=100)';
///*@IE@*/															o.x0=o.o.filters.item('alpha').opacity/100;
///*@IE@*/														}
///*@IE@*/														break;
					}
					o.a=[];
					for(var j=1;j<=steps;j++){
						var v=o.x0+(Math.sqrt((1/steps)*j)*(o.x1-o.x0));
						if(o.p=='filter')v='alpha(opacity:'+(v*100).toFixed(0)+')';
						else if(o.p!='opacity')v=Math.ceil(v);
						o.a.push(v);
					}
				}
				me.inProgress=true;
				step=0;
				t=window.setInterval(
						function(){
							for(var i=0;i<e.length;i++){
								var o=e[i];
								o.s[o.p]=o.a[step];
							}
							step++;
							if(step>=steps){
								me.stop();
								if(callback)callback();
							}
						},intv);
			} else {
				for(var i=0;i<e.length;i++){
					var o=e[i];
					o.s[o.p]=o.x1;
				}
			}
		};
		
	// Initialisation
	// ---------------------------------------------------------------------------
	if(ai)me.set(ai);
}
