// ****************************************************************************
// SESSION ID
// ****************************************************************************
var Core=null;
var T24SID='';

var denyPG=true;
///*@IE@*/window.onbeforeunload=beforeUnloadAction;	//window.attachEvent('beforeunload',beforeUnloadAction);
window.addEventListener('beforeunload',beforeUnloadAction,false);
function beforeUnloadAction(ev) {
	if(denyPG){
		var msg='ATTENTION: Do not use the browser\'s buttons for navigation- use the application controls instead !!!';
		if(ev)ev.returnValue=msg;
		return msg;
	}
}

// =============================================================================
// PROTOTYPE (some useful functions from it)
// =============================================================================
function $A(o) {
	  if (!o) return [];
	  if (o.toArray) return o.toArray();
		for (var r=[], i=0, l=o.length; i<l; i++)
			r.push(o[i]);
		return r;
	}
Function.prototype.bind = function() {
	  var me=this, args=$A(arguments), o=args.shift();
	  return function() {
	  	  return me.apply(o, args.concat($A(arguments)));
		  };
	};
function isArray(obj){return((obj)&&(typeof(obj)=='object')&&(typeof(obj.length)!="undefined")&&(typeof(obj)!='string'));}
function clone(t) {
	if(typeof(t)=='object'){
		if(isArray(t)){
			 	var o=new Array();
				for(var p=0;p<t.length;p++)o.push(clone(t[p])); 
		} else {
			 	var o=new Object();
				for(var p in t)o[p]=clone(t[p]);
		}
		return(o); 
	}
	return(t);
}
// binary search in sorted Arrays
// a : Array
// y : Element to find
// compare=function(el1,el2) : reference to compare function; returns 0=equal, <0 = el1<el2, >0 = el1>el2
// nerest : boolean; if true it finds the nearest match
// returns index of element or -1 if no element has been found
function bsearch(a,y,compare,nearest) {
	function _find(x1,x2) {
		var dx=x2-x1;
		if(dx==0) return (nearest||(compare(y,a[x1])==0))?x1:-1;
		if(dx==1) {
			var c1=compare(y,a[x1]);
			if(c1==0)return x1;
			var c2=compare(y,a[x2]);
			if(c2==0)return x2;
			if(!nearest)return -1;
			return (-c2>c1)?x1:x2;
		}
		var n=Math.floor(dx/2)+x1;
		var c1=compare(y,a[n]);
		if(c1==0)return(n);
		else if(c1<0)return _find(x1,n-1);
		else return _find(n+1,x2);
	}
	if(a.length==0)return -1;
	var c=compare(y,a[0]);
	if(c==0)return 0;
	if((c<0)||(a.length==1))return nearest?0:-1;
	var c=compare(y,a[a.length-1]);
	if(c>0)return nearest?(a.length-1):-1;
	if(c==0)return (a.length-1);

	return _find(0,a.length-1);
}
function $TXT(txt) {
	var r;
	if (txt=='') {
		r=$DIV();
		r.innerHTML='&nbsp;';
	} else r=document.createTextNode(txt);
	return(r);
}
function $HTML(html,styles) {
	var r=$DIV(styles);
	r.innerHTML=(html=='')?'&nbsp;':html;
	return(r);
}
function $IMG(url,styles) {
	var r=document.createElement('img');
	r.src=url;
	setStyle(r,styles);
	return(r);
}
function $DIV(styles) {
	var div=document.createElement('div');
	setStyle(div,styles);
	return(div);
}

function setStyle(r,styles) {
	if (typeof(styles)=='object')	
		for (var s in styles) {
			if('id|title|onload|onmousedown|onclick|ondblclick|onmouseover|onmouseout'.indexOf(s)>-1) r[s]=styles[s];
			else
				switch(s) {
					case 'css':r.className=styles[s]; break;
					case 'link':r.onclick=styles[s]; break;
					case 'html':r.innerHTML=styles[s]; break;
					/*jsl:ignore*/
					default: try {r.style[s]=styles[s];} catch (e) {};
					/*jsl:end*/
				}
		}
	return(r);
}

//Core.round() rounds a number x to the required decimal places n
function round(x, n) {
    if (n < 1 || n > 14) return false;
    var e = Math.pow(10, n);
    var k = (Math.round(x * e) / e).toString();
    if (k.indexOf('.') == -1) k += '.';
    k += e.toString().substring(1);
    return k.substring(0, k.indexOf('.') + n + 1);
}

// =============================================================================
// END PROTOTYPE
// =============================================================================



// ****************************************************************************
// LOGIN/LOGOFF/RECORDING
// ****************************************************************************
function login() {
	function loginAnswer() {
		this.Response=function(resp) {
			if ((resp.result!=null)&&(resp.sid!=null)&&(resp.result==0)) {
				T24SID = resp.sid;
				loadPage('$');
			}
			else { Core.alert('Refused to log in'); }
		};
	}
	var f=document.forms[0];
	Core.lock('Authenticating');
	// check if IP info is available
	var loc='';
	if(typeof(geoip_country_code)=='function'){
		loc='{ccode:"'+geoip_country_code();
		loc+='",cname:"'+geoip_country_name();
		loc+='",city:"'+geoip_city();
		loc+='",lat:"'+geoip_latitude();
		loc+='",lon:"'+geoip_longitude();
		loc+='"}';
		loc='&loc='+encodeURIComponent(loc);
	}
	Core.Request('/tom24x/login','cust=' + encodeURIComponent(f.elements['CUST'].value) + '&usr=' + encodeURIComponent(f.elements['USR'].value) + '&pwd=' + encodeURIComponent(f.elements['PWD'].value)+loc,new loginAnswer());
}
function logoff() {
	function logoffAnswer(httpReq) {
		this.Response=function(resp) {
			if ((resp.result!=null)&&(resp.result==0))loadPage('');
			else { Core.alert('Refused'); }
		};
	}
	Core.lock('Loading Page');
	Core.Request('/tom24x/login','sid='+T24SID,new logoffAnswer());
	return false;
}
function finish() {
	function closeAnswer() {
		this.Response=function(resp) {
			if ((resp.result!=null)&&(resp.result==1))loadPage('$');
			else { Core.alert('Refused'); }
		};
	}
	if (Core.mo) {
		Core.lock('Loading Page');
		Core.Request('/tom24x/open','sid='+T24SID,new closeAnswer());
	}
	return false;
}
// ****************************************************************************
// END ** LOGIN/LOGOFF/RECORDING
// ****************************************************************************


// ****************************************************************************
// LOAD A PAGE
// ****************************************************************************
function loadPage(page,proc) {
		denyPG=false;
		function createI(n,v) {
			var c=document.createElement('input');
			c.type="hidden";
			c.name=n;
			c.value=v;
			return c;
		}
		Core.lock('Loading Page');
		var aform=document.createElement('form');
		aform.action="/tom24x/";
		aform.method="post";
		document.getElementById('CentralPanel').appendChild(aform);
		aform.appendChild(createI('p',page));
		if(proc)aform.appendChild(createI('proc',proc));
		aform.appendChild(createI('sid',T24SID));
		// var d=new Date();
		// aform.appendChild(createI('timestamp',Math.floor(d.getTime()/1000)));
	
		aform.submit();
		return false;
}
// ****************************************************************************
// END * LOAD A PAGE
// ****************************************************************************

// ****************************************************************************
// ANALYSIS FUNCTIONS
// ****************************************************************************

// opts=ARR - run arrhythmia analysis
// opts=BEAT|ARR - run beat analysis and then arrhythmia analysis
function analyse (opts) {
	function analyse_finish() {
		this.Response=function(resp) {
		};
	}
	var msg="Are you sure you want to run the analysis?<BR><UL>";
	opts=new String(opts);
	if (opts.indexOf('HardBeatAnalysis')!=-1) msg=msg+'<LI>Beat Analysis (discard changes)</LI>';
	if (opts.indexOf('DoBeatAnalysis')!=-1) msg=msg+'<LI>Beat Analysis</LI>';
	if (opts.indexOf('Beat')!=-1) msg=msg+'<LI>Arrhythmia Analysis</LI>';
	msg=msg+'</UL>';
	Core.confirm(msg,Core.Request.bind(Core,"/tom24x/analyse","a="+opts+"&sid="+T24SID,new analyse_finish()));
}

// opts=modul to activate
function activateAnalysis (opts,txt) {
	function activate_finish(httpRequest) {
		this.Response=function(resp) {
			loadPage('#');
		};
	}
	var msg="Are you sure you want to activate the selected tool?<BR><UL><LI>"+txt+"</LI></UL>";
	Core.confirm(msg,Core.Request.bind(Core,"/tom24x/db","action=activate&t="+opts+"&sid="+T24SID,new activate_finish()));
}

// ****************************************************************************
// END ** ANALYSIS FUNCTIONS
// ****************************************************************************



// ****************************************************************************
// TOM24 - WORKFLOW HANDLING
// ****************************************************************************

function TWorkflow(id) {
	var me=this;
	var ani=new TAnimation();
	
	var _div=document.getElementById(id);			// the workflow panel
	var selp=null;						// selected procedure
	var pClosed=true;					// flag- true when procedure bar is folded in
	var pCnt=0;								// number of registered Procedures
	var mv=0;									// indicates active movement

	var bc=[];

	var mr=$DIV({css:'returnL'}),ml=$DIV({css:'endL'});
	var procs=$DIV({css:'procs'});
	var pclip=$DIV({css:'clipP'});
	var pbut=$DIV({css:'butP'});
	pclip.appendChild(pbut);
	procs.appendChild(pclip);
	procs.appendChild(ml);
	_div.appendChild(procs);
	
	_div.appendChild(mr);
	var ret=$DIV({css:'return'});
	var retBut=$DIV({css:'toolRET'});
	ret.appendChild(retBut);
	_div.appendChild(ret);
	
	function _mv(){
		var o=bc[selp];
		if((mv==0)||(o.r2<=o.r1))return;
		o.r0=o.r0+mv;
		if (o.r0>0) {o.r0=0;mv=0;}
		if (o.r0+o.r2<o.r1) {o.r0=o.r1-o.r2;mv=0;}
		o.div.style.left=o.r0+16;
		if (mv!=0)window.setTimeout(_mv,50);
	}

	var clip=$DIV({css:'clipWF'});
	ml.onmouseover=function(){mv=10;window.setTimeout(_mv,50);};
	ml.onmouseout=function(){mv=0;};
	mr.onmouseover=function(){mv=-10;window.setTimeout(_mv,50);};
	mr.onmouseout=function(){mv=0;};
	_div.appendChild(clip);
	
	this.registerProcedure=function(i,text,title,image) {
			bc[i]={
					div:$DIV({css:'buttonsWF',visibility:'hidden'}),
					r1:640,								// size of container for regular wf buttons
					r2:0,									// actual size reqired for buttons
					r0:0,									// displacement to clipping area
					r3:0,									// right corner of seleced item
					but:$DIV({css:'procWF'})
				};
			bc[i].cnt=pCnt;
			pCnt++;
			bc[i].but.title=title;
			bc[i].but.appendChild($IMG(image));
			bc[i].but.appendChild(document.createElement('br'));
			bc[i].but.appendChild(document.createTextNode(text));
			bc[i].but.onclick=me.selProc.bind(me,i);

			bc[i].but.onmouseover=function(){this.style.color='#ffaa00';};
			bc[i].but.onmouseout=function(){this.style.color='#fcfcfc';};

			pbut.appendChild(bc[i].but);
			clip.appendChild(bc[i].div);
		};
	this.selProc=function(i) {
			if(pClosed){
				ani.set([
									[pclip,'width',mr.offsetLeft+2],
									[clip,'opacity',0],
									[pbut,'left',16]
								]);
			} else {
				if(selp)bc[selp].div.style.visibility='hidden';
				selp=i;
				bc[selp].div.style.visibility='visible';
				bc[selp].div.style.left=bc[selp].r0+16;				
				ani.set([
									[pclip,'width',64],
									[clip,'opacity',1],
									[pbut,'left',-64*bc[selp].cnt]
								]);
			}
			ani.animate(30,20,null);
			pClosed=!pClosed;
		};
	this.registerPage=function(text,title,image,action,proc,sel) {
			if(proc==-1){
				// RETURN button
				mr.style.left=720;
				ret.style.left=736;
				retBut.title=title;
				retBut.onclick=action;

				retBut.onmouseover=function(){this.style.color='#ffaa00';this.style.fontWeight='bold';};
				retBut.onmouseout=function(){this.style.color='#fcfcfc';this.style.fontWeight='normal';};

				retBut.appendChild($IMG(image));
				retBut.appendChild(document.createElement('br'));
				retBut.appendChild(document.createTextNode(text));
				return;			
			}
			var a=$DIV({css:(sel?'toolWFs':'toolWF')});
			if(!sel){
				a.onmouseover=function(){this.style.color='#ffaa00';this.style.fontWeight='bold';};
				a.onmouseout=function(){this.style.color='#7889a5';this.style.fontWeight='normal';};
			}
			a.title=title;
			a.onclick=action; // Function('return '+action);
			if(text.length>12){
				a.style.fontSize='9px';
				a.style.padding=1;
				if(!sel) {
a.style.height=52;
a.style.width=70;
				}
			}
			a.appendChild($IMG(image,{display:'block',cssFloat:'left',styleFloat:'left',clear:'right',paddingLeft:20}));
			a.appendChild($DIV({display:'block',cssFloat:'left',styleFloat:'left',clear:'both',width:'100%',textAlign:'center',html:text}));
			bc[proc].r2+=75;
			if(sel){
				bc[proc].r2+=5;
				selp=proc;
				bc[proc].div.style.visibility='visible';
				bc[proc].r2+=7;bc[proc].r3=bc[proc].r2;
				pbut.style.left=-64*bc[proc].cnt;
			}
			bc[proc].div.style.width=bc[proc].r2;
			bc[proc].div.appendChild(a);
			if(bc[proc].r2>bc[proc].r1){
				// center selection
				if (bc[proc].r3>0) {
					bc[proc].r0=(bc[proc].r1/2)+41-bc[proc].r3;
					if (bc[proc].r0>0)bc[proc].r0=0;
					if (bc[proc].r0+bc[proc].r2<bc[proc].r1)bc[proc].r0=bc[proc].r1-bc[proc].r2;
					bc[proc].div.style.left=bc[proc].r0+16;				
				}
			}
		};
}

// ****************************************************************************
// END - WORKFLOW HANDLING
// ****************************************************************************


// ****************************************************************************
// TOM24 - STATUS & KEEP ALIVE CONTROL
// ****************************************************************************
function TState(sid) {
	var me=this;
	var _color = new Array('#9BFAD1','#FFAA00','#E0E0E0','#FAD19B','#E0E0E0');
	var _msg = new Array('READY','PROCESSING','LOCKED','LOADING','WAIT');
	var _state=-1;
	var _wait=0;
	var _wait2=true;		// used to flag a change in the (local) state 3 (wait)
	var _pgReload=false;
	var _bReld;					// manual page reload button
	var sbox;						// parent container
	var _pb;						// container for progress bar
	var _pbimg;					// image of progressbar
	var	_bAna;					// reanalysis button
	var ls=[];					// array of listeners
	var anndiv;					// container for announcement message
	var annid=0;				// currently displayed announcement id

	this.addListener=function(func) {
				ls.push(func);
			};

	// if autoreaload is true the page will automatically reloaded when a "reload" request is received
	this.autoreload=true;

	// ==========================================================================
	// loadImg
	// auxillary function to load images under controlled conditions
	// ==========================================================================
	var waitImg=Array();
	this.loadImg=function(imgurl){
		var i=$IMG(imgurl);
		waitImg.push(i);
		_wait=waitImg.length;
		_set(3);
		if (waitImg.length==1) window.setTimeout(checkload,500);
		return 'url('+imgurl+')';
	};
	function checkload(){
		while (waitImg[0].complete) {
			waitImg.shift();
			_wait=waitImg.length;
			if (_wait==0) return;
		}
		window.setTimeout(checkload,500);
	}

	function _set(new_status) {
		var i;
	
		if (_wait>0) {
			// processing locally
			if(_wait2) {
				_wait2=false;
				var c=_color[3];
				sbox.setColor(sbox,c);
				sbox.title.innerHTML = _msg[3];
			}
			_pb.style.display = (_state==1)?'block':'none';
			
			if (new_status==3) _state=new_status;
			return;
		} else _wait2=true;
			
		if (new_status!=_state) {
			_state=new_status;
			var c=_color[_state];
			sbox.setColor(sbox,c);
			sbox.title.innerHTML = _msg[_state];
			_pb.style.display = (_state==1)?'block':'none';
		}
	}

	function annMsgAnswer() {
		this.Response=function(resp) {
			if(resp.length==1) {
				anndiv.innerHTML=resp[0].msg;
				annid=resp[0].id;
			}
		};
	}

	this.Response=function(ans) {
//		if ((ans.uid==null)||(ans.pgid!=Core.pgid)) {
//			loadPage('#');
//			return;
//		}
		if(ls.length!=0) {
			for(var i=0;i<ls.length;i++)ls[i](ans);
		}
		if (ans.state!=null) {
			_set (ans.state);
			if (ans.state==1) {
				var percent = ans.progress;
				_pbimg.style.width = Math.floor(1.8*percent)+"px";
			}
			if ((ans.state!=1) && ans.dirty) {
				_bAna.onclick = function(){ return analyse(ans.dirtyChans);};
				_bAna.style.display = 'block';
			} else _bAna.style.display='none';
			if (ans.reload) {
				if(me.autoreload) {
					loadPage('#');
					_pgReload=true;
				} else _bReld.style.display = 'block';
			} else _bReld.style.display='none';
		} else {
			if (ans.uid>-1)_set(0);
		}
		if(ans.msg!=annid) {
			// announcement broadcast has changed!!!
			if(ans.msg){
				Core.Request('/tom24x/sysinfo','act=list&id='+ans.msg+'&sid='+T24SID,new annMsgAnswer());
				anndiv.style.visibility='visible';
			} else {
				anndiv.innerHTML='';
				anndiv.style.visibility='hidden';
				annid=0;
			}
		}
		window.setTimeout(_request,(ans.state==1)?1000:2000);
	};
	function _request() {
		Core.Request ('/tom24x/status','sid='+T24SID+(_pgReload?'&reload=1':'')+(Core?('&pos='+Core.time):''),me,0);
		_pgReload=false;
	}
	
	sbox=Core.DIV('','',3,{background:'#E0E0E0',position:20});
	
	_pb=document.createElement('DIV');_pb.className='progressbar';_pb.style.display='none';
	sbox.main.appendChild(_pb);
	_pbimg=$IMG('img/common/pb.png',{width:0,height:10});
	_pb.appendChild(_pbimg);
	_bAna=new TTextButton({text:'ANALYZE !',style:'red',width:176,marginLeft:8,marginTop:8});
	_bAna=_bAna.Component;
	_bAna.style.display='none';
	_bReld=new TTextButton({text:'Reload Page !',style:'red',width:176,marginLeft:8,marginTop:8});
	_bReld=_bReld.Component;
	_bReld.style.display='none';
	_bReld.onclick = function(){
			loadPage('#');
			_pgReload=true;
		};
	sbox.main.appendChild(_bAna);
	sbox.main.appendChild(_bReld);
	
	anndiv=$DIV({display:'block',cssFloat:'left',styleFloat:'left',position:'relative',width:190,marginTop:4,visibility:'hidden',color:'#400000',fontSize:'9pt',fontWeight:'bold',borderTop:'2px solid #7889a5'});
	sbox.main.appendChild(anndiv);
		
	if (sid) {
		_set (4);
		window.setTimeout(_request,1000);
	}
}
// ****************************************************************************
// END : TOM24 - STATUS & KEEP ALIVE CONTROL
// ****************************************************************************



// ****************************************************************************
// KEY MAPPING HELP SYSTEM
// ****************************************************************************
function THelp() {
	var me=this;
	var _help=Core.DIV('','HELP',3,{background:'#e8e8e8',position:30});
	var txt='';
	_help.parent.style.display='none';		
	_help.main.style.height=200;	
	var hclip = $DIV({css:'hclip'});
	_help.main.appendChild(hclip);
	var hmain = $DIV({css:'hmain'});
	hclip.appendChild(hmain);
	
	var scr = new TScroll(hclip,hmain);				// scroll bar
	_help.main.appendChild(scr.Component);

	var hlp_l=$DIV({css:'txt'});
	hmain.appendChild(hlp_l);

	this.toggle=function() {
		if (_help.parent.style.display=='block') _help.parent.style.display='none';
		else {
			_help.parent.style.display='block';
			scr.refresh();
		}
	};
	this.setText=function(shrt,desc,hlp) {
		hlp_l.innerHTML='<CENTER><BIG><B>'+shrt.toUpperCase()+'</BIG><BR>'+((desc!='')?desc+'</B></CENTER><BR>':'')+hlp+'<BR>';
	};
	this.addKey=function(k,t) {
		var d=$DIV({css:'keys'});
		var i=0;
		if (k.indexOf('c')>-1) {
			d.appendChild($IMG('img/common/k_ctrl.png',{css:'keyimg'}));
			i++;
		}
		if (k.indexOf('s')>-1) {
			d.appendChild($IMG('img/common/k_shift.png',{css:'keyimg'}));
			i++;
		}
		if (k.indexOf('a')>-1) {
			d.appendChild($IMG('img/common/k_alt.png',{css:'keyimg'}));
			i++;
		}
		k=k.substr(0,k.length-i);
		switch(k) {
			case '-' : d.appendChild($IMG('img/common/k_horiz.png',{css:'keyimg'})); break;
			case '|' : d.appendChild($IMG('img/common/k_vert.png',{css:'keyimg'})); break;
			case '+' : d.appendChild($IMG('img/common/k_cursor.png',{css:'keyimg'})); break;
			case '%' : d.appendChild($IMG('img/common/k_pgupdn.png',{css:'keyimg'})); break;
			case '~' : d.appendChild($IMG('img/common/k_p1end.png',{css:'keyimg'})); break;
			default : d.appendChild($IMG('img/common/k'+k+'.png',{css:'keyimg'}));
		}
		var d1=$DIV({display:'block',overflow:'hidden',height:'auto',width:176});
		d1.appendChild(d);
		d1.appendChild($DIV({css:'keytxt',html:t}));
		hmain.appendChild(d1);
	};
}
// ****************************************************************************
// END: KEY MAPPING HELP SYSTEM
// ****************************************************************************


// ****************************************************************************
// TOOL BOX SYSTEM
// ****************************************************************************
// The ToolBox component has a similar callback interface as a system component
//
function TToolBox() {
	var me=this;
	var box=Core.DIV('','TOOL BOX',3,{background:'#CDC5B0',position:40});
	
	var rForm=null;
	var rTbl={};
	function rRequest(t) {
		if(rForm==null)return;
		for (var tbl in rTbl) {
			if((t<rTbl[tbl].start)||(t>rTbl[tbl].end)) {
				Core.Request('/tom24x/result','sid='+T24SID+'&n=*&t='+Core.time+'&diary='+tbl,me,1,tbl);
				rTbl[tbl].start=t;
				rTbl[tbl].end=t;
			}
		}
	}
	function setSpecPerc(k,r,v1,v2) {
		var o=rForm.$(k+'.'+v1+'%');
		if(!o)return;
		if(r&&r[v1]&&r[v2]){
			o.setUnit('%');
			o.setValue(100*parseFloat(r[v1].val)/parseFloat(r[v2].val));
		} else o.setValue(-1);
	}
	this.Response=function(resp,c,k) {
			var o;
			switch(c) {
				case 1:	// results
								if(rForm){
									if(resp[0]) {
										rTbl[k]=resp[0];
										for(var n in resp[0]) {
											o=rForm.$(k+'.'+n);
											if(o){
												if(resp[0][n].unit)o.setUnit(resp[0][n].unit);
												o.setValue(resp[0][n].val);
											}
										}
									}
									// handle some special cases
									setSpecPerc(k,resp[0],'ULF','TOTPWR');
									setSpecPerc(k,resp[0],'VLF','TOTPWR');
									setSpecPerc(k,resp[0],'LF','TOTPWR');
									setSpecPerc(k,resp[0],'HF','TOTPWR');
								}
								break;
			}
		};
	
	var _time=null;
	function _setTime(t) {
		if(_time)_time.setValue(t);
		rRequest(t);
	}
	
	var _hs;
	function _setRes(r) {
		if(_hs)_hs.setValue(r);
	}
	function _changeTrendMag() {
		Core.setRes('*',_hs.getValue());
	}
	
	var _hr,_minhr,_minhrx,_maxhr,_maxhrx,acthr;
	function _respHR(r) {
		acthr=r;
		if(_hr)_hr.setValue(r.hr.hr);
		if(_minhr)_minhr.setValue(r.minhr.hr);
		if(_minhrx)_minhrx.setValue(r.minhr.xval);
		if(_maxhr)_maxhr.setValue(r.maxhr.hr);
		if(_maxhrx)_maxhrx.setValue(r.maxhr.xval);
	}
	this.jump=function(w){
			switch(w) {
				case 'MINHR' : if(acthr)Core.setTime(null,acthr.minhr.xval); break;
				case 'MAXHR' : if(acthr)Core.setTime(null,acthr.maxhr.xval); break;
			}
		};
	
	this.click=function(cmd) {
			for(var i=0;i<cmd.length;i++) {
				var m=cmd[i].split('.');
				if(m.length==2)m.push('0');
				Core.Module[m[0]].cmd(parseInt(m[1],10),parseInt(m[2],10));
			}
		};
	
	this.init=function(tools) {
			var b,bs;
			var hmain=box.main;

			for(var i=0;i<tools.length;i++){
				if(isArray(tools[i])) {
					bs=new TToolbar({visible:true});
					hmain.appendChild(bs.Component);
					b=bs.addGroup({decoration:false});
					bs.addSpacer(b,{});
					for(var j=0;j<tools[i].length;j++){
							switch(tools[i][j].feature){
								case 'TIME': // displays current date/time of ECG cursor
														_time=bs.addDateTime(b,{value:0});
														me.setTime=_setTime;
														break;
														
								case 'HR':	// displays heart rate at current ECG position
														_hr=bs.addValue(b,{title:'HR',unit:'bpm',precision:1});
														me.ResponseHR=_respHR;
														break;

								case 'MINHR':	// displays minimum heart rate & time of occurance
														bs.addImg(b,{img:'img/trends/minhr.png',width:24});
														_minhr=bs.addValue(b,{title:'HR',width:45,style:'small',precision:1});
														_minhrx=bs.addDateTime(b,{value:0});
														bs.addButton(b,{img:'img/trends/go24.png',link:me.jump.bind(me,'MINHR')});
														me.ResponseHR=_respHR;
														break;
														
								case 'MAXHR':	// displays minimum heart rate & time of occurance
														bs.addImg(b,{img:'img/trends/maxhr.png',width:24});
														_maxhr=bs.addValue(b,{title:'HR',width:45,style:'small',precision:1});
														_maxhrx=bs.addDateTime(b,{value:0});
														bs.addButton(b,{img:'img/trends/go24.png',link:me.jump.bind(me,'MAXHR')});
														me.ResponseHR=_respHR;
														break;
														
								case 'HSCALE': // trend time axis scale
														_hs=bs.addSlider(b,{title:'Trend Magnification',values:[480,240,120,60,30,15,5],titles:['4 days','2 days','1 day','12 hours','6 hours','3 hours','1 hour'],init:2,onchange:_changeTrendMag});
														me.setRes=_setRes;
														break;
								
								case 'RESULT': // a value from the result interface
														if(rForm==null)rForm=new TForm();
														bs.addValue(b,{title:tools[i][j].name,style:tools[i][j].style,unit:'',precision:(tools[i][j].precision?tools[i][j].precision:0),name:tools[i][j].table+'.'+tools[i][j].item,form:rForm});
														rTbl[tools[i][j].table]={start:-1,end:-1};
														break;

								case 'SPACE': // a spacer defined by {width}
														bs.addSpacer(b,{width:tools[i][j].width});
														break;
														
								case 'SEPARATOR': // a separator
														b=bs.addGroup({decoration:true});
														break;					
														
								default:		// a command button
														if(!isArray(tools[i][j].cmd))tools[i][j].cmd=[tools[i][j].cmd];
														if(typeof tools[i][j].title=='undefined')tools[i][j].title=0;
														if(typeof tools[i][j].title=='number') {
															var m=tools[i][j].cmd[tools[i][j].title].split('.');
															tools[i][j].title=Core.Module[m[0]].help[parseInt(m[1],10)];
														}
														bs.addButton(b,{title:tools[i][j].title,img:tools[i][j].img,link:me.click.bind(me,tools[i][j].cmd)});
								
							}
					}
				} else {
						// a title component
						b=$DIV({css:'SectionTitle'});
						b.appendChild($DIV({html:tools[i].title}));
						hmain.appendChild(b);
						hmain.appendChild($DIV({clear:'both'}));
				}
			}		
		};
	this.setVisible=function(s){
			box.parent.style.display=s?'block':'none';
		};
}
// ****************************************************************************
// END: TOOL BOX SYSTEM
// ****************************************************************************



// ============================================================================
// Core (object)
// ============================================================================
// This is the central object required for:
// (*) navigation events dispatcher
// (*) key event dispatcher (and normalisation across browsers)
function TCore(session,usr,cus,ur,cPg,cPgId,cPr,mo,time,region,res) {
	// private
	var me=this;
	var _modules=Array();							// array or registered modules
	var _massoc=Object();							// associative array to resolve module names
	// key handler
	var _keyActive=-1;								// index of activated modul
	var _keyEnabled=true;							// flag - process key event when true
	var _keyMap=new Array();
	var _tmpMap=new Array();
	var hlp=null;											// help object
	var tbx=null;											// toolbox object
	var wf=null;											// workflow object
	// automatic initialisation
	var init={};
	// class interface
	var urc=decodeUR(ur);							// user rights as | delimited list
	this.cid=cus;											// Customer ID
	this.uid=usr;											// User ID
	this.mo=mo;												// the whole recording object

	// create the "task bar" for inactive modules
	var tbar=document.getElementById("TaskBar");

	var recsup='';
	if(mo) {
		// recording attachet
		// convert to JS/UTC 1.1.1970
		mo.mdatef-=2209161600;
		if(!mo.annonym)mo.patdobf-=2209161600;
		this.reclen=mo.mlen;							// recording length in seconds
		this.recstart=mo.mdatef;					// start of recording (UTC)

		this.datasup=mo.recsup;						// data support
		recsup=new String(mo.recorder);	// recorder support
	}

	this.time=time;										// actual position (ECG) in recording
	this.region=region;								// begin of viewing region
	this.resolution=res;							// resolution
	checkRegion();
	this.day=-1;											// default selection (for multiday recordings)
	this.Module=new Array();
	this.pgid=cPgId;
	this.page=cPg;
	this.proc=cPr;
	
	var lsp=new Array();							// list of left side panels
	
	var _imgFlippedDown = 'img/common/h1fup.png';
	var _imgFlippedUp = 'img/common/h1fdown.png';

	this.State=null;
	T24SID=session;
	if(!T24SID)denyPG=false;

	// look for main container
	var main=document.getElementById('CentralPanel');

	// Here are some special response routines for XML answers used in more
	// than one module	
	// ==========================================================================
	var HttpReq;
  if (window.XMLHttpRequest) {
  	HttpReq=function() { return new window.XMLHttpRequest(); };
  } else {
		if (window.ActiveXObject) { // IE
			var hr;
			try {
	      hr = new ActiveXObject("Msxml2.XMLHTTP");
		  	HttpReq=function() { return new ActiveXObject("Msxml2.XMLHTTP"); };
	    } catch (e) {
		  	HttpReq=function() { return new ActiveXObject("Microsoft.XMLHTTP"); };
	  	}
	  	delete hr;
  	}
	}

	this.Response=function(r,c,p1) {
		for (var i=0; i<_modules.length; i++) {
//			if(_modules[i].isActive && _modules[i][c]) _modules[i][c](r,c,p1);
			if(_modules[i][c])_modules[i][c](r,c,p1);
		}
		if(tbx && tbx[c])tbx[c](r);
	};
	
	this.Request=function(addr,post,cobj,p1,p2) {
		var hr=HttpReq();
		
	  if (!hr) { Core.alert('Giving up : Cannot create an XMLHTTP instance'); return false; }
		
	  hr.onreadystatechange = function() { 
	  		if (hr&&(hr.readyState == 4)&&(hr.status==200)) {
	  			if(hr.getResponseHeader('Content-Type').indexOf('application/json')!=-1)hr=eval('('+hr.responseText+')');
	  			if((hr.error)&&(hr.error==-2)) {
	  				// lock timed out -> try again !!!
	  				me.Request(addr,post,cobj,p1,p2);
	  			} else {
	  				// successful -> feed back result to calling object
	  				cobj.Response(hr,p1,p2);
	  			}
	  		}
	  	};
  	hr.open('POST', addr, true);
  	hr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
		hr.send(post);
		return true;
	};

	// =============================================================================
	// WORKFLOW HANDLER
	// =============================================================================
	this.setWorkflow=function(pages,procs) {
				wf=new TWorkflow('Workflow');
				if(mo!=null)
						wf.registerPage('Close','Close Recording and return to Administration','img/common/back_f2.png',Function('return finish()'),-1,false);
//				else
//						wf.registerPage('Log-off','Exit Session','img/common/exit32.png',Function('return finish()'),-1,false);
				// now register Procedures and Pages
				for(var i in procs){
					wf.registerProcedure(i,procs[i][0],procs[i][1],procs[i][2]);
					var s=procs[i][3];
					for(var j=0; j<s.length; j++){
						var p=pages[s[j]];
						if(p)wf.registerPage(p[0],p[1],p[2],Function('loadPage("#'+s[j]+'","'+i+'")'),i,((s[j]==cPg)&&(i==cPr)));
					}
				}
		};
	
	// =============================================================================
	// MODAL MESSAGE BOX
	// =============================================================================
	var lockmsg=$DIV({css:'txtLock'});
	var lockimg=$DIV({css:'imgWAIT'});
	var locker=document.getElementById('Locker');
	var lockm=document.getElementById('Message');
	var locki=$DIV({css:'inner'});
	locki.appendChild(lockimg);
	locki.appendChild(lockmsg);
	lockm.appendChild(locki);

	var lockb=$DIV({css:'buttons',display:'none'});
	var lockok=new TTextButton({text:'OK',style:'green',width:120,marginLeft:20});
	var lockno=new TTextButton({text:'Cancel',style:'red',width:120,marginLeft:120});
	lockb.appendChild(lockno.Component);
	lockb.appendChild(lockok.Component);
	lockm.appendChild(lockb);

	function showLock(){
///*@IE@*/		var i=document.body.offsetWidth;
		var i=window.innerWidth;
		var d=main.offsetLeft+main.offsetWidth+26;
		locker.style.width=(i<d)?d:i;
///*@IE@*/		i=document.body.offsetHeight;
		i=window.innerHeight;
		d=main.offsetTop+main.offsetHeight;
		locker.style.height=(i<d)?d:i;
		locker.style.display='block';
	}
	this.lock=function(msg) {
			lockb.style.display='none';
			lockok.Component.style.visibility='hidden';
			lockno.Component.style.visibility='hidden';
			if (msg) {
				lockmsg.className='txtLock';
				lockmsg.innerHTML=msg;
			} else lockmsg.style.display='none';
				
			lockm.style.display='block';
			lockm.style.left=main.offsetLeft+(main.offsetWidth-lockm.offsetWidth)/2;
///*@IE@*/			lockm.style.top=main.offsetTop+150+document.body.scrollTop;
			lockm.style.top=main.offsetTop+150+window.pageYOffset;

			showLock();
		};
	this.unlock=function(func) {
			me.uncaptureKey('13');
			me.uncaptureKey('27');
			document.getElementById('Message').style.display='none';
			locker.style.display='none';
			lockok.Component.style.visibility='hidden';
			lockno.Component.style.visibility='hidden';
			if(func)return func();
			lockimg.className='imgWAIT';
			return(true);
		};
	this.alert=function(msg) {
			lockb.style.display='block';
			lockimg.className='imgABOUT';
			lockmsg.className='txtMsg';
			lockmsg.innerHTML=msg;
				
			lockm.style.display='block';
			lockm.style.left=main.offsetLeft+(main.offsetWidth-lockm.offsetWidth)/2;
///*@IE@*/			lockm.style.top=main.offsetTop+150+document.body.scrollTop;
			lockm.style.top=main.offsetTop+150+window.pageYOffset;
			
			lockno.Component.style.visibility='hidden';
			lockok.Component.style.visibility='visible';
			lockok.Component.onclick=me.unlock.bind(me,null);
			me.captureKey('13',me.unlock.bind(me,null));
			showLock();
		};
	this.confirm=function(msg,fok,fno) {
			lockb.style.display='block';
			lockimg.className='imgHELP';
			lockmsg.className='txtMsg';
			lockmsg.innerHTML=msg;
				
			lockm.style.display='block';
			lockm.style.left=main.offsetLeft+(main.offsetWidth-lockm.offsetWidth)/2;
///*@IE@*/			lockm.style.top=main.offsetTop+150+document.body.scrollTop;
			lockm.style.top=main.offsetTop+150+window.pageYOffset;

			lockok.Component.style.visibility='visible';
			lockok.Component.onclick=me.unlock.bind(me,fok);
			me.captureKey('13',me.unlock.bind(me,fok));

			lockno.Component.style.visibility='visible';
			lockno.Component.onclick=me.unlock.bind(me,fno);
			me.captureKey('27',me.unlock.bind(me,fno));

			showLock();
		};

	

	// ===========================================================================
	// CORE INITIALISATION
	// ===========================================================================
	
	// initialize system keys
	_keyMap['112']=function(){hlp.toggle();return true;};
	_keyMap['116c']=function(){_reload();return true;};

	this.setHelp=function(s,d,h) {
		if(!hlp)hlp=new THelp();
		hlp.setText(s,d,h);
	};

	// ---------------------------------------------------------------------------
	// DATE/TIME functions
	// ---------------------------------------------------------------------------
	var fDt={ord:'MDY',
					 sep:'/',
					 fmt:'M/D/Y',
					 sfmt:'M/D',
					 reg:'(\\d+)\\s*\\/\\s*(\\d+)\\s*\\/\\s*(\\d+)'};
	var fTm={ord:'HNS',
					 sep:'-',
					 fmt:'H:N:S',
					 reg:'(\\d+)\\s*:\\s*(\\d+)\\s*:\\s*(\\d+)'};

	function cvtFmt(f,d) {
		var r='';
		for (var i=0; i<f.length; i++) {
			switch(f.substr(i,1)) {
				case 'D': r=r+zeroPad(d.getUTCDate(),2);break;
				case 'M': r=r+zeroPad(d.getUTCMonth()+1,2);break;
				case 'Y': r=r+zeroPad(d.getUTCFullYear(),4);break;
				case 'H': r=r+zeroPad(d.getUTCHours(),2);break;
				case 'N': r=r+zeroPad(d.getUTCMinutes(),2);break;
				case 'S': r=r+zeroPad(d.getUTCSeconds(),2);break;
				default : r=r+f.substr(i,1);
			}
		}
		return r;
	}
	this.getDateStr=function(d,sh) {
		if((typeof(d)=='undefined')||(d==null))d=me.time;
		if(typeof(d)=='number')d=new Date(1000*(me.recstart+d));
		return sh?cvtFmt(fDt.sfmt,d):cvtFmt(fDt.fmt,d);
	};
	this.getTimeStr=function(d) {
		if((typeof(d)=='undefined')||(d==null))d=me.time;
		if(d==null)d=me.time;
		if(typeof(d)=='number')d=new Date(1000*(me.recstart+d));
		return cvtFmt(fTm.fmt,d);
	};
	this.parseDate=function(dat) {
			var d=new Date();
			// append year of recording if at last position (optional)
			if (fDt.ord.substr(fDt.length-1,1)=='Y') dat=dat+fDt.sep+d.getUTCFullYear();
			var reg=new RegExp(fDt.reg);
			var t=reg.exec(dat);
			if (t.length==4) {
				for (var i=0; i<fDt.ord.length; i++) {
					switch(fDt.ord.substr(i,1)) {
						case 'D': d.setDate(parseInt(t[i+1],10));break;
						case 'M': d.setMonth(parseInt(t[i+1],10)-1);break;
						case 'Y': d.setFullYear(parseInt(t[i+1],10));break;
					}
				}	
			}	
			return(d);
		};
	this.setTimeStr=function(callerid,dat,tim) {
		var d=new Date(1000*me.recstart);
		var dt={day:0,mon:0,yr:0,hr:0,mi:0,sec:0};
		// append year of recording if at last position (optional)
		if (fDt.ord.substr(fDt.length-1,1)=='Y') dat=dat+fDt.sep+d.getUTCFullYear();
		tim=tim+fTm.sep+'0';
		var reg=new RegExp(fDt.reg);
		var t=reg.exec(dat);
		if (t.length!=4) {_setTime(callerid,me.time);return(false);}
		for (var i=0; i<fDt.ord.length; i++) {
			switch(fDt.ord.substr(i,1)) {
				case 'D': dt.day=parseInt(t[i+1],10);break;
				case 'M': dt.mon=parseInt(t[i+1],10);break;
				case 'Y': dt.yr=parseInt(t[i+1],10);break;
			}
		}		

		reg=new RegExp(fTm.reg);
		t=reg.exec(tim);
		if (t.length!=4) {_setTime(callerid,me.time);return(false);}
		for (i=0; i<fTm.ord.length; i++) {
			switch(fTm.ord.substr(i,1)) {
				case 'H': dt.hr=parseInt(t[i+1],10);break;
				case 'N': dt.mi=parseInt(t[i+1],10);break;
				case 'S': dt.sec=parseInt(t[i+1],10);break;
			}
		}		
		
		if (dt.yr<100) dt.yr+=2000;
		var start=(Date.UTC(dt.yr,dt.mon-1,dt.day,dt.hr,dt.mi,dt.sec)/1000)-me.recstart;
		if ((start<0) && (start>-60)) start=0;		// only difference < 1min at beginning -> ignore
		if ((start>me.reclen) || (start<0)) {_setTime(callerid,me.time);return(false);}
		_setTime(callerid,start);
		return(true);
	};
	this.fmtTime=function(t,fmt){
			var s='';
			if(!fmt)fmt='DHNS';
			if(t>86400){
				if(fmt.indexOf('D')>-1)s=Math.floor(t/86400).toFixed(0)+'d ';
				t=t%86400;
			}
			if(t>3600){
				if(fmt.indexOf('H')>-1)s=s+Math.floor(t/3600).toFixed(0)+'h ';
				t=t%3600;
			} else if(s!='')s=s+'0h ';
			if(t>60){
				if(fmt.indexOf('N')>-1)s=s+Math.floor(t/60).toFixed(0)+'m ';
				t=t%60;
			} else if(s!='')s=s+'0m ';
			if(fmt.indexOf('S')>-1)s+=t.toFixed(0)+'s';
			return(s);
		};

	// _register
	// register a modul
	// ==========================================================================
	this.register=function(obj) {
		// register module
		if(obj.id) {
			_massoc[obj.id]=new Array(_modules.length,obj);
			_modules.push(obj);
			this.Module[obj.id]=obj;
		}
	};
	function reloadResp() {
		var s='sid='+T24SID;
		if (init.Record&&me.mo) me.Response(me.mo,'ResponseRecord');
		if (init.Excl) me.Request('/tom24x/channel',s+'&c=INHIBITION&a=list',me,'ResponseExcl');
		if (init.Noise) me.Request('/tom24x/info',s+'&a=list&c=NOISE',me,'ResponseNoise');
		if (init.HR) me.Request('/tom24x/hr',s+'&a=get&xval='+me.time,me,'ResponseHR');
		if (init.ST) me.Request('/tom24x/info',s+'&a=list&c=ST',me,'ResponseST');
		if (init.PEvt) me.Request('/tom24x/channel',s+'&a=list&c=pevent',me,'ResponsePatEv');
		if (init.Strip) me.Request('/tom24x/ecgstrip',s+'&a=list',me,'ResponseStrip');
		if (init.Evt) me.Request("/tom24x/EvtStrips",s,me,'ResponseEvt');
	}
	// DIV
	// create a new DIV within the requested context
	// context 0=main panel 
	//         1=main panel with minimize button
	//         2=right panel
	var wins={};
	this.DIV=function(id,title,context,opts) {
			if(!context)context=0;
			if(!opts)opts={};
			switch(context){
				case 0:	// add container in central place
								var m=document.getElementById("Main");
								var h=null,h1=null;
								if(!opts.notitle) {
									h=$DIV({css:'ModulTitle',id:'TITLE_'+id});
									var htx=$DIV();
									htx.innerHTML=title;
									h.appendChild(htx);
									// flip button
									h.appendChild($IMG(_imgFlippedDown,{id:'flipImg_'+id,onclick:function(){_toggleActive(id);}}));
									h.appendChild($IMG(_imgFlippedDown,{visibility:'hidden'}));
									m.appendChild(h);

									h1=$DIV({css:'ModulTask'});
									var htx=$DIV({display:'block',styleFloat:'left',cssFloat:'left'});
									htx.innerHTML=title;
									h1.appendChild(htx);
									// flip button
									h1.appendChild($IMG(_imgFlippedUp,{onclick:function(){_toggleActive(id);}}));
									tbar.appendChild(h1);
								}
				
								var o={
										tit:h,
										tbd:h1,
										div:$DIV({css:'container',id:id})
									};
								m.appendChild(o.div);
								wins[id]=o;
								
								return(o.div);
								
				case 2:	// add component in right panel
								var h=$DIV({css:'ModulTitle'});
								if(opts&&opts.margin)h.style.marginTop=opts.margin;
								var htx=$DIV();
								htx.innerHTML=title;
								h.appendChild(htx);
								var div=$DIV({css:'container'});
								var p=document.getElementById("RightPanel");
								p.appendChild(h);
								p.appendChild(div);
								p.style.visibility='visible';
								h.style.width=p.offsetWidth-2;
								div.style.width=p.offsetWidth-2;
								return(div);
								
				case 3: // add component in left panel
								var o={
										parent:$DIV({css:'HelpBox',background:opts.background}),
										title:$DIV({css:'hpar',height:'auto',html:title}),
										main:$DIV({css:'hpar',height:'auto'}),
										frame:new Array(8),
										pos:opts.position,
										setColor:function(obj,c){
												obj.parent.style.background=c;
												for(var i=0;i<8;i++)obj.frame[i].style.background=c;
											}
									};
					 			var b=document.createElement('B');b.className='rtop';
								o.frame[0]=document.createElement('B');o.frame[0].className='r1';o.frame[0].style.background=opts.background;b.appendChild(o.frame[0]);
								o.frame[1]=document.createElement('B');o.frame[1].className='r2';o.frame[1].style.background=opts.background;b.appendChild(o.frame[1]);
								o.frame[2]=document.createElement('B');o.frame[2].className='r3';o.frame[2].style.background=opts.background;b.appendChild(o.frame[2]);
								o.frame[3]=document.createElement('B');o.frame[3].className='r4';o.frame[3].style.background=opts.background;b.appendChild(o.frame[3]);
								o.parent.appendChild(b);
								o.parent.appendChild(o.title);
								o.parent.appendChild(o.main);
								var b=document.createElement('B');b.className='rtop';
								o.frame[4]=document.createElement('B');o.frame[4].className='r4';o.frame[4].style.background=opts.background;b.appendChild(o.frame[4]);
								o.frame[5]=document.createElement('B');o.frame[5].className='r3';o.frame[5].style.background=opts.background;b.appendChild(o.frame[5]);
								o.frame[6]=document.createElement('B');o.frame[6].className='r2';o.frame[6].style.background=opts.background;b.appendChild(o.frame[6]);
								o.frame[7]=document.createElement('B');o.frame[7].className='r1';o.frame[7].style.background=opts.background;b.appendChild(o.frame[7]);
								o.parent.appendChild(b);

								var p=document.getElementById("LeftPanel");
								for(var i=0;i<lsp.length;i++){
									if(o.pos<lsp[i].pos){
										p.insertBefore(o.parent,lsp[i].parent);
										lsp.splice(i,0,o);
										return(o);
									}
								}
								p.appendChild(o.parent);
								lsp.push(o);
								return(o);
			}
			return(null);
		};
	this.toggleDIV=function(m,flag,quick) {
		var o=wins[m];
		if(o) {
			if(flag) {
				if(!quick) {
					var a=new TAnimation();
					var p=$DIV({
						css:'ModulAniFrame',
						left:getAbsX(o.tbd),
						top:getAbsY(o.tbd),
						width:o.tbd.offsetWidth,
						height:o.tbd.offsetHeight
					});
					document.body.appendChild(p);
					o.tbd.style.visibility='hidden';
					o.tit.style.visibility='hidden';
					o.tit.style.display='block';
					a.set([
						[p,'left',getAbsX(o.tit)],
						[p,'top',getAbsY(o.tit)],
						[p,'width',o.tit.offsetWidth],						
						[p,'height',o.tit.offsetHeight],						
					]);
					a.animate(10,25,function() {
							o.tbd.style.display='none';
							o.div.style.display='block';
							o.tit.style.visibility='visible';
							document.body.removeChild(p);
						});
				} else {				
					o.tbd.style.display='none';
					o.div.style.display='block';
					o.tit.style.display='block';
				}
				
			} else {
				if(!quick) {
					var a=new TAnimation();
					var p=$DIV({
						css:'ModulAniFrame',
						left:getAbsX(o.tit),
						top:getAbsY(o.tit),
						width:o.tit.offsetWidth,
						height:o.tit.offsetHeight
					});
					document.body.appendChild(p);
					o.tbd.style.visibility='hidden';
				}
				o.tbd.style.display='block';
				if(a)a.set([
						[p,'left',getAbsX(o.tbd)],
						[p,'top',getAbsY(o.tbd)],
						[p,'width',o.tbd.offsetWidth],						
						[p,'height',o.tbd.offsetHeight],						
					]);
				
				o.div.style.display='none';
				o.tit.style.display='none';
				
				if(a)a.animate(10,25,function() {
						o.tbd.style.visibility='visible';
						document.body.removeChild(p);
					});
			}
		}
	}
	
	this.init=function() {
		// initialize system	
		if((session)&&(session!='')){
			this.State=new TState(session);
		}
		
		for (i=0;i<_modules.length;i++) {
			var obj=_modules[i];
			obj.init(me.time,me.region,me.resolution);
			var hdr = document.getElementById(obj.id);
			if((hdr)&&(hdr.previousSibling))hdr.previousSibling.appendChild($DIV({clear:'both',fontSize:0,height:0,width:0}));

			if (obj.ResponseExcl) init.Excl=true;
			if (obj.ResponseNoise) init.Noise=true;
			if (obj.ResponseHR) init.HR=true;
			if (obj.ResponseST) init.ST=true;
			if (obj.ResponseStrip) init.Strip=true;
			if (obj.ResponsePatEv) init.PEvt=true;
			if (obj.ResponseEvt) init.Evt=true;
			if (obj.ResponseRecord) init.Record=true;
		}
		// request list of inhibitions if required
		reloadResp();

		// adjust window (scroll, so that "Main" fits into window
///*@IE@*/		var i=(main.offsetLeft+main.offsetWidth+26)-document.body.offsetWidth;
		var i=(main.offsetLeft+main.offsetWidth+26)-window.innerWidth;
		if(i>0)window.scrollTo(i,0);
	};
	this.addCtrl = function(callerid,ctrl) {
		var obj = document.getElementById(callerid);		
		if(obj)obj.previousSibling.appendChild($IMG(ctrl.src,{onclick:ctrl.onclick}));
	};
	
	// _setTime
	// called by modules to set ECG cursor position (in seconds from beginning of
	// recording)
	// ==========================================================================
	this.setTime=_setTime;
	function _setTime(callerid,t,obj) {
		me.Request('/tom24x/hr','sid='+T24SID+'&a=get&xval='+t,Core,'ResponseHR');
		me.time=t;
		if(checkRegion())me.setRgn('*',me.region);
		for (var i=0; i<_modules.length; i++) if ((_modules[i].id!=callerid) && (_modules[i].isActive) && _modules[i].setTime) _modules[i].setTime(t,obj);
		if((tbx)&&(tbx.setTime))tbx.setTime(t,obj);
	}
	function checkRegion() {
		// check if selected time is out of bounds and adjust region if nescecary
		var vis=720*me.resolution;
		if((me.time<me.region)||(me.time>(me.region+vis))) {
			// place cursor in middle of viewport
			me.region=me.time-(vis/2);
			if((me.region+vis)>me.reclen)me.region=me.reclen-vis;
			if(me.region<0)me.region=0;
			return true;
		} else return false;
	}
	
	// _setRgn
	// called by modules to set the start of the viewing region (in seconds from 
	// beginning of recording)
	// ==========================================================================
	this.setRgn=function(callerid,t) {
			me.region=t;
			for (var i=0; i<_modules.length; i++) if ((_modules[i].id!=callerid) && (_modules[i].isActive) && _modules[i].setRgn) _modules[i].setRgn(t);
		};
	
	// _setRes
	// called by modules to set resolution of (non-ECG) views (in seconds per
	// pixel)
	// ==========================================================================
	this.setRes=function(callerid,r) {
			var rgn2=me.time-(me.time-me.region)*r/me.resolution;
			me.resolution=r;
			for (var i=0; i<_modules.length; i++) if ((_modules[i].id!=callerid) && (_modules[i].isActive) && _modules[i].setRes) _modules[i].setRes(r);
			if((tbx)&&(tbx.setRes))tbx.setRes(r);
			me.setRgn(0,(rgn2>=0)?rgn2:0);
		};
		
	// setDay
	// called by modules to select day filter
	// ==========================================================================
	this.setDay=function(callerid,d) {
			me.day=d;
			for (var i=0; i<_modules.length; i++) if ((_modules[i].id!=callerid) && (_modules[i].isActive) && _modules[i].setDay) _modules[i].setDay(d);
			if((tbx)&&(tbx.setDay))tbx.setDay(d);
		};
		
	// _reload
	// send a reload command to all registered modules
	// ==========================================================================
	this.reload=_reload;
	function _reload(callerid,r) {
		for (var i=0; i<_modules.length; i++) if ((_modules[i].id!=callerid) && (_modules[i].isActive) && (_modules[i].reload)) _modules[i].reload(''+r);
		if(!r)reloadResp();
	}
	
	// tool box
	// defines tool box content
	// ==========================================================================
	this.toolBox=function(tools) {
			if(!tbx)tbx=new TToolBox();
			tbx.init(tools);
			// initialize
			if(tbx.setTime)tbx.setTime(time);
			tbx.setVisible(true);
		};
	
	// key Mapping
	// defines the keys supported for the actual page and assigns them to modules
	// ==========================================================================
	this.keyMap=function(keys) {
		var q,f,o,p,h='';
		var k=keys.split(';');
		for (var i=0; i<k.length; i++) {
			q=k[i].split('.');
			if ((_massoc[q[1]]) && (_modules[_massoc[q[1]][0]].cmd)) {
				f=parseInt(q[2],10);
				o=_modules[_massoc[q[1]][0]];
				p=q[0].substr(1);
				hlp.addKey(new String(q[0]),o.help[f]);
				switch (q[0].substr(0,1)) {
					case '+' :	// cursor all directions
											_keyMap['37'+p]=o.cmd.bind(o,f);
											_keyMap['38'+p]=o.cmd.bind(o,f);
											_keyMap['39'+p]=o.cmd.bind(o,f);
											_keyMap['40'+p]=o.cmd.bind(o,f);
											break;
					case '-' :	// cursor left/right
											_keyMap['37'+p]=o.cmd.bind(o,f);
											_keyMap['39'+p]=o.cmd.bind(o,f);
											break;
					case '|' :	// cursor up/down
											_keyMap['38'+p]=o.cmd.bind(o,f);
											_keyMap['40'+p]=o.cmd.bind(o,f);
											break;
					case '%' :	// page up/down
											_keyMap['33'+p]=o.cmd.bind(o,f);
											_keyMap['34'+p]=o.cmd.bind(o,f);
											break;
					case '~' : 	// pos1 / end
											_keyMap['35'+p]=o.cmd.bind(o,f);
											_keyMap['36'+p]=o.cmd.bind(o,f);
											break;
					default :		// singe key expression
											_keyMap[q[0]]=o.cmd.bind(o,f);
				}
			}
		}
	};
	// _setKeyEnabled (flag)
	// Switches key event processing on or off
	// ==========================================================================	
	this.setKeyEnabled=function(flag) {
		_keyEnabled=flag;
	};
	// captureKey(key,func)
	// relates temporarily a key to a function
	// ==========================================================================	
	this.captureKey=function(key,func) {
		_tmpMap[key]=func;
	};
	this.uncaptureKey=function(key) {
		_tmpMap[key]=null;
	};
	// _key_handler
	// processes key events from FF
	// ==========================================================================

	// direction defines: <0=left or up >0=right or down -/+1=left/right -/+2=up/down -/+3=pg up/down
	var _dir=[-3,3,4,-4,-1,-2,1,2];
	// register key event handler	
	document.onkeydown=function(e) {
		if (_keyEnabled) {
			var ch=0;
  		e = e || window.event;
		  if (e.which) ch = e.which; else if (e.keyCode) ch = e.keyCode;
		  // check for SHIFT,CTRL,ALT => not handeled separately
		  if ((ch>=16) && (ch<=20)) return true;
		  var alt=''+ch+((e.ctrlKey)?'c':'')+((e.shiftKey)?'s':'')+((e.altKey)?'a':'');
		  // if movement key -> translate direction
		  if ((ch>=33) && (ch<=40)) ch=_dir[ch-33];
		  if (_tmpMap[alt]) return(!(_tmpMap[alt](ch)));
		  else if (_keyMap[alt]) return(!(_keyMap[alt](ch)));
		}
		return true;
	};
	window.onhelp=function() {
	    // prevents opening the IE browser help
  	  return false;
		};

	this.toggleActive=_toggleActive;
	function _toggleActive(modul) {
		var mod=_massoc[modul];
		if (mod[1]) {
			mod[1].setActive(!mod[1].isActive);
			var img = document.getElementById('flipImg_'+modul);
			if (img) img.src = (mod[1].isActive) ? _imgFlippedDown : _imgFlippedUp;
		}
	}
	this.isActive=function(modul) {
		var mod=_massoc[modul];
		if (mod[1]) {
			return (mod[1].isActive);
		}
		return false;
	};
	
	// fromRecorder
	// checks if a recording has been made by a specific recorder
	// ===========================================================================
	this.fromRecorder=function(r) {
		if(recsup=='')return false;
		if (!isArray(r)) r=[r];
		for (var i=0; i<r.length; i++)if (recsup.indexOf(r[i])>-1) return true;
		return false;
	};

	// hasPermit
	// checks if user has specific permission
	// ===========================================================================
	this.hasPermit=function(u) {
			return (urc.indexOf(u)>0);
		};

	function decodeUR(u) {
		if(!u)return '';
		var _permStr=new String('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ');
		var _urights=[
				'SEEALL','SEECUS','SEEOWN','BACKUP','DELETE',
	      'OPTION','ANALYS','EDITOR','HOLTER','UPLOAD',
	      'DNLOAD','VIEWEC','ARRADJ','PROREP','REPORT',
	      'ALLREP','REPULD','REPDEO','REPDEL','EVENTS',
	      'ACCADM','USRADM','BILADM','STATIS','SUPERU',
	      'CUSADM','RECADM','RECRUN','TECSUP','TAGSHG',
	      'TAGSHU','TAGSCU','EVTEDT','SRQALL','SRQCUS',
	      'SRQEDI','CLOREC','ACCUSR','AUTENR','PATEDT',
	      'SHAREP','******','******','******','******'
			];
		var us='';
		for(var i=0;i<u.length;i++) {
			var n=_permStr.indexOf(u.charAt(i));
			var p=1;
			for(var j=0;j<6;j++){
				if((n&p)>0)us+='|'+_urights[i*5+j];
				p*=2;
			}
		}
		return us;
	}
	
}
// ================================================================= Core END



// _arrName(atyp) {
// converts an internal name of an arrhythmia into a user readable
function getEventName(atyp) {
	switch (String(atyp).toUpperCase()) {
		case '0':
		case 'PAUSE': return 'Pause';
		case '1':
		case 'TACHY': return 'Tachycardia';
		case '2':
		case 'BRADY': return 'Bradycardia';
		case '3':
		case 'TRIGEMINY': return 'Trigeminy';
		case '4':
		case 'BIGEMINY': return 'Bigeminy';
		case '6':
		case 'COUPLET': return 'Couplet';		
		case 'MONOCOUPLET': return 'MonoCouplet';
		case '5':
		case 'POLYCOUPLET': return 'PolyCouplet';
		case '7':
		case 'VTACHY': return 'VT';
		case '8':
		case 'NSVTACHY': return 'NSVT';
		case '9':
		case 'SVECT': return 'PAC/PJC';
		case '10':
		case 'VECT': return 'PVC';
		case '11':
		case 'SALVO': return 'Salvo';
		case '12':
		case 'IVRHYTH': return 'IVR';
		case '13':
		case 'PSVT': return 'PSVT';
		case '14':
		case 'IRREGULAR': return 'Irregular';
		case '15':
		case 'UNSPECTACHY' : return 'Tachycardia*';
		case '16':
		case 'AVI': return 'AV Block I';
		case '17':
		case 'AVII1': return 'AV II Type 1';
		case '18':
		case 'AVII2': return 'AV II Type 2';
		case '19':
		case 'AVII3': return 'AV II Type 3';
		case '20':
		case 'AVIII': return 'AV Block III';
		case '21':
		case 'AFIB': return 'Atrial Fibrillation';
		case '22':
		case 'AFLUT': return 'Atrial Flutter';
		case 'APNOE': return 'Apnea';
		case 'APNOEC': return 'Central Apnea';
		case 'APNOEO': return 'Obstetric Apnea';
		case '98': return 'AF(end)';
		case 'AFONSET':
		case '99': return 'AF(start)';
		case 'AF':
		case '100': return 'AF';
		case 'EVTECG':
		case '101': return 'Patient Event';
		case 'MANUAL':
		case '200': return 'ECG Request';
		case 'PEVENT':
		case '201': return 'Patient Event';
		default: return '['+atyp+']';
	}
}

// Auxilliary functions

function zeroPad(i,digits) {
	var s=String(i);
	while (s.length<digits) s='0'+s;
	return (s);
}
