/**
 * @namespace sda
 */


var sda={};

sda.node=function(id){
	if(id instanceof Array){
		var r=[];
		for(var i=0;i<id.length;i++){
			r.push(document.getElementById(id[i]));
		}
		return r;
	}else{
		return document.getElementById(id);
	}
};

sda.nval=function(el){
	return parseInt(el.value);
};

sda.rand=function(max){
	return Math.floor(Math.random()*max);
};

sda.checkFormat=function(){
	for(var i=0;i<arguments.length;i+=2){
		if(typeof(arguments[i+1]) !="string" ||
			typeof(arguments[i]) != arguments[i+1]
		) return false;
	}
	return true;
};

/**
 * Embed a function in a fixed scope wrapper.
 * @param {Function} func The function to be put in a fixed scope wrapper.
 * @param {Object} scope The scope that the function should be fixed at.
 * @return {Function}
 */
sda.hardScope=function(func,scope){
	if(!sda.checkFormat(
		func,"function"
	)) return false;
	
	if(sda.checkFormat(
		scope,"object"
	)) return function(){
		var args=arguments;
		args[args.length]=this;
		return func.apply(scope,args);
	}; else return func;
}

sda.makeDeepCopy=function(obj){
	if((typeof(obj)!="object") || (!(obj instanceof Array) && obj.constructor!=Object) || obj==null)
		return obj;
	
	var out;
	
	if(obj instanceof Array){
		out=[];
		for(var i=0;i<obj.length;i++){
			if((typeof(obj[i])=="object") && obj[i]!=null)
				out.push(sda.makeDeepCopy(obj[i]));
			else
				out.push(obj[i]);
		}
	}else{
		out={};
		for(var i in obj){
			if( i!="constructor" && i!="prototype" && typeof(obj[i])=="object" && obj[i]){
				out[i]=sda.makeDeepCopy(obj[i]);
			}else
				out[i]=obj[i];
		}
	}

	return out;
}

sda.addEvent=function(el,type,handler,scope){
		if(!sda.checkFormat(
			el,"object",
			type,"string",
			handler,"function"
		)) return false;
		
		var encHandler=null;
		
		if(sda.checkFormat(scope,"object"))
			encHandler=sda.hardScope(handler,scope);
		else
			encHandler=handler;
			
		if(el.addEventListener) el.addEventListener(type,encHandler,false);
		else if(el.attachEvent) el.attachEvent("on"+type,encHandler);
		return encHandler;
	}

	sda.removeEvent=function(el,type,handler){
		if(!sda.checkFormat(
			el,"object",
			type,"string",
			handler,"function"
		)) return false;
			
		if(el.removeEventListener) el.removeEventListener(type,handler,false);
		else if(el.detachEvent) el.detachEvent("on"+type,handler);
	}

/*
	sda.encMethods
		�berschreibt alle Methoden eines Objekts mit Scope Wrappern
*/
sda.encMethods=function(obj,scope,follow){
	if(!sda.checkFormat(
		obj,"object"
	)) return false;
	
	
	if(typeof(scope)=="undefined") scope=obj;
	for(var i in obj){
		if(typeof(obj[i])=="function"){
			obj[i]=sda.hardScope(obj[i],scope);
		}else if(typeof(obj[i])=="object"){
			if(follow) sda.encMethods(obj[i],scope);
		}
	}
	
	return obj;
}

/*
	(Klasse) sda.httpRequest
		sendet eine HTTP Anfrage und liefert das Ergebnis an die Handlerfunktion
*/
sda.httpRequest=function(url,targetfnc,servexml){
//				netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
	sda.encMethods(this);
	
	this.targetfunc=targetfnc;
	this.servexml=servexml;
	this.http_request = new XMLHttpRequest();
	this.http_request.overrideMimeType((this.servexml?'text/xml':'text/plain'));

	this.http_request.onload = this.responseHandler;
	this.http_request.open("GET", url);
	this.http_request.send(null);
}

sda.httpRequest.prototype.responseHandler=function(response){
//				netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
	if ((response.target.readyState == 4) && (response.target.status == 200 || (response.target.status == 0)))
		this.targetfunc((this.servexml?response.target:response.target.responseText));
}
			
/*
	sda.walkTree
		Folgt einer Objektstruktur entsprechend des Properties die im Array angegeben wurden
*/
sda.walkTree=function(obj,propTreeArray){
	for(var i=0;i<propTreeArray.length;i++){
		if(typeof(obj[propTreeArray[i]])=="undefined") return undefined;
		obj=obj[propTreeArray[i]];
	}
	return obj;
}
			


/*
	sda.bubbleSort
		Sortiert ein Array
*/
sda.bubbleSort=function(arr,propTreeArray){
	var ch;
	do{
		ch=false;
		for(var i=0;(i+1)<arr.length;i++){
			var ev0=sda.walkTree(arr[i],propTreeArray);
			var ev1=sda.walkTree(arr[i+1],propTreeArray);
			
			if(typeof(ev0)=="undefined" || typeof(ev1)=="undefined") return undefined;
			if(ev0 > ev1){
				var t=arr[i];
				arr[i]=arr[i+1];
				arr[i+1]=t;
				ch=true;
			}
		}
	}while(ch);
	
	return arr;
}

/*
	sda.shuffle
		Mischt ein Array
*/
sda.shuffle=function(arr,cnt){
	if(typeof(cnt)=="undefined") cnt=10;
	
	for(var i=0;i<(arr.length*cnt);i++){
		var f1=Math.floor(Math.random()*arr.length);
		var f2=Math.floor(Math.random()*arr.length);
		
		var t=arr[f1];
		arr[f1]=arr[f2];
		arr[f2]=t;
	}
	
	return arr;
}
/*
	sda.getElementsByClass
		Findet alle Elemente mit angegebenem Typ und Klasse
*/
sda.getElementsByClass=function(cls,el){
	if(typeof(el)!="undefined"){
		var r=[];
		var ns=document.getElementsByTagName(el);
		for(var i=0;i<ns.length;i++){
			if(ns[i].className==cls) r.push(ns[i]);
		}
		return (r);
	}
}
			
/*
	sda.serialize
		Serialisiert Objekte
*/
sda.serialize=function(obj,lvl){
	if(!lvl)lvl=0;
	var s="";
	for(var i=0;i<lvl;i++) s+="\t";
	var out="";
	if(obj instanceof Array){
		out+=s+"[\n";
		for(var i=0;i<obj.length;i++) out+=sda.serialize(obj[i],lvl+1)+",\n";
		out+=s+"]\n";
	}else if(typeof(obj)=="string"){
		out+=s+'"';
		out+=obj.replace("\n","\\n").replace("\r","\\r").replace("\"","\\\"");
		out+='"';
	}else if(typeof(obj)=="number"){
		out+=s+obj;
	}else if(typeof(obj)=="undefined"){
		out+=s+"undefined";
	}else if(typeof(obj)=="null"){
		out+=s+"null";
	}else if(typeof(obj)=="boolean"){
		out+=s+(obj?true:false);
	}else if(typeof(obj)=="function"){
		out+=s+("null");
	}else if(typeof(obj)=="object"){
		out+=s+"{\n";
		for(var i in obj) out+="\""+i+"\": "+sda.serialize(obj[i],lvl+1)+",\n";
		out+=s+"}\n";
	}
	return out;
}

/*
	sda.topLeft
		Liefert die obere linke Ecke eines Elements
		Sind x und y angegeben wird die obere linke Ecke stattdessen an diese Position vershcoben
*/
sda.topLeft=function(node,x,y){
	if(typeof(x)=="number" && typeof(y)=="number"){
		var offset=null;
		if(node.offsetParent)
			offset=sda.topLeft(node.offsetParent);
		else
			offset={x:0,y:0};
		
		sda.pAbs(node,x-offset.x,y-offset.y);
	}else{
		var x=0;
		var y=0;
		do{
			x+=node.offsetLeft-node.scrollLeft;
			y+=node.offsetTop-node.scrollTop;
		}while(node=node.offsetParent);
		return({x:x,y:y});
	}
}

/*
	sda.bottomRight
		Liefert die untere rechte Ecke eines Elements
		Sind x und y angegeben wird die untere rechte Ecke stattdessen an diese Position vershcoben
*/
sda.bottomRight=function(node,x,y){
	if(typeof(x)=="number" && typeof(y)=="number"){
		var offset=null;
		if(node.offsetParent)
			offset=sda.topLeft(node.offsetParent);
		else
			offset={x:node.offsetWidth,y:node.offsetHeight};
		
		sda.pAbs(node,x-offset.x,y-offset.y);
	}else{
		var x=node.offsetWidth;
		var y=node.offsetHeight;
		do{
			x+=node.offsetLeft;
			y+=node.offsetTop;
		}while(node=node.offsetParent);
		return({x:x,y:y});
	}
};

sda.getWindowDimensions=function(){
	var base=null;
	var compat=false;
	if(document.compatMode=='BackCompat' || !document.compatMode || !document.documentElement) compat=true;
	
	base=(compat?document.body:document.documentElement);
	
	var r={"x":base.clientWidth,"y":base.clientHeight,"u":base.scrollLeft,"v":base.scrollTop};
	if(window.innerWidth){
		var w=window.innerWidth;
		var h=window.innerHeight;
		if(Math.abs(r.x-w)>20) r.x=w;
		if(Math.abs(r.y-h)>20) r.y=h;
	}
	
	return r;
}

sda.pAbs=function(node,x,y,w,h,xi,yi){
	if(!node) return;
	node.style.position="absolute";
	if(x!=undefined) node.style[(xi?"right":"left")]=x+"px";
	if(y!=undefined) node.style[(yi?"bottom":"top")]=y+"px";
	if(w!=undefined) node.style.width=w+"px";
	if(h!=undefined) node.style.height=h+"px";
}

sda.clone=function(obj){
	if(typeof(obj)!="object") return obj;
	
	var out={};
	
	for(var i in obj)
		if(obj.hasProperty(i))
			out[i]=obj[i];
	
	return out;
}

sda.json2html=function(tree){
	if(typeof(tree)!="object") return document.createTextNode(String(tree));
	
	var node=document.createElement(tree[0]);
	for(var i=1;i<tree.length;i++){
		if(tree[i] instanceof Array) for(var j=0;j<tree[i].length;j++) node.appendChild(arguments.callee(tree[i][j]));
		else for(var j in tree[i]) node.setAttribute(j,String(tree[i][j]));
	}
	
	return node;
};

sda.insertAfter=function(newNode,prevNode){
	if(newNode==prevNode) return;
	var next=prevNode;
	
	do{
		next=next.nextSibling;
	}while(next && next.nodeType!=1);
	if(next) next.parentNode.insertBefore(newNode,next);
	else prevNode.parentNode.appendChild(newNode);
	
	return true;
}

sda.swap=function(obj,p1,p2){
	var t=obj[p1];
	obj[p1]=obj[p2];
	obj[p2]=t;
};

sda.clickInEl=function(evt,el){
	var ol=sda.topLeft(el);
	return !(evt.clientX<ol.x || evt.clientY<ol.y || evt.clientX>ol.x+el.offsetWidth || evt.clientY>ol.y+el.offsetHeight);
}

sda.nonBlockingLoop=function(initFunc,condFunc,postFunc,bodyFunc){
	if(!(this instanceof sda.nonBlockingLoop)) throw (new sda.exception("sda.nonBlockingLoop $constructor called as function",5,1025));
	sda.encMethods(this);
	this.init=initFunc;
	this.condition=condFunc;
	this.post=postFunc;
	this.body=bodyFunc;
	this.funcVariables={};
	
	this.init.call(this.funcVariables);
	if(!this.condition.call(this.funcVariables)) return;
	
	this.runLoop();
}

sda.nonBlockingLoop.prototype.runLoop=function(){
	this.body.call(this.funcVariables);
	this.post.call(this.funcVariables);
	if(this.condition.call(this.funcVariables)) window.setTimeout(this.runLoop,50);
}
			
sda.timetween=function(callback,totaltime, se,mindelay){
	var animdata={};
	animdata.mindelay=(mindelay?mindelay:25);
	animdata.totaltime=totaltime;
	animdata.callback=callback;
	
	animdata.startvals=[];
	animdata.range=[];
	animdata.endvals=[];

	for(var i=0;i<se.length;i++){
		animdata.startvals.push(se[i][0]);
		animdata.range.push(se[i][1]-se[i][0]);
		animdata.endvals.push(se[i][1]);
	}
	
	var loop=null;
	loop=sda.hardScope(function(){
		if(this.time>=this.totaltime) this.callback(this.endvals,true);
		else{
			var vals=[];
			var p=this.time/this.totaltime;
			for(var i=0;i<this.startvals.length;i++)
				vals.push(this.startvals[i]+this.range[i]*p);

			this.callback(vals,false);
			this.time=(new Date()).getTime()-this.start;
			window.setTimeout(loop,this.mindelay);
		}
	},animdata);
	animdata.start=(new Date()).getTime();
	animdata.time=0;
	loop(animdata);
}

sda.UserEvent=function(type,param){
	this.type=type;
	this.param=param;
}

sda.addUserEventListener=function(el,type,target,scope){
	if(!el) throw("addUserEventListener on non-existent element");
	var exists=false;
	if(!el.__$tw$__userEventListeners) el.__$tw$__userEventListeners={};
	
	if(scope) target=sda.hardScope(target,scope);
	
	if(!el.__$tw$__userEventListeners[type]) el.__$tw$__userEventListeners[type]=target;
	else if(typeof(el.__$tw$__userEventListeners[type])=="function"){
		if(el.__$tw$__userEventListeners[type]!=target)
			el.__$tw$__userEventListeners[type]=[el.__$tw$__userEventListeners[type],target];
		else
			exists=true;
	}else if(el.__$tw$__userEventListeners[type] instanceof Array){
		for(var i=0;i<el.__$tw$__userEventListeners[type].length;i++)
			if(el.__$tw$__userEventListeners[type][i]==target) exists=true;
		
		if(!exists)el.__$tw$__userEventListeners[type].push(target);
	}
	
	if(!exists)
		return [el,type,target];
	else
		return null;
}

sda.removeUserEventListener=function(el_type_target_Array){
	if(!(el_type_target_Array instanceof Array)) return false;
	
	el=el_type_target_Array[0];
	type=el_type_target_Array[1];
	target=el_type_target_Array[2];
	
	if(!el) return false;
	if(typeof(type)!="string") return false;
	if(typeof(target)!="function") return false;
	if(!el.__$tw$__userEventListeners) return false;
	if(!el.__$tw$__userEventListeners[type]) return false;
	
	if(typeof(el.__$tw$__userEventListeners[type])=="function"){
		if(el.__$tw$__userEventListeners[type]==target) el.__$tw$__userEventListeners[type]=null;
	}else if(el.__$tw$__userEventListeners[type] instanceof Array){
		for(var i=0;i<el.__$tw$__userEventListeners[type].length;i++)
			if(el.__$tw$__userEventListeners[type][i]==target){
				el.__$tw$__userEventListeners[type].splice(i,1);
				return;
			}
	}
}

sda.dispatchUserEventToSingleElement=function(el,evt){
	if(!el.__$tw$__userEventListeners || !el.__$tw$__userEventListeners[evt.type]) return;
	
	if(typeof(el.__$tw$__userEventListeners[evt.type])=="function")
		el.__$tw$__userEventListeners[evt.type].call(el,evt);
	else if((el.__$tw$__userEventListeners[evt.type]) instanceof Array)
		for(var i=0;i<el.__$tw$__userEventListeners[evt.type].length;i++)
			el.__$tw$__userEventListeners[evt.type][i].call(el,evt);
}

sda.dispatchUserEvent=function(el,evt){
	do{
		if(el!=window && el!=document) sda.dispatchUserEventToSingleElement(el,evt);
	}while(el=el.parentNode);
	
	sda.dispatchUserEventToSingleElement(document,evt);
	sda.dispatchUserEventToSingleElement(window,evt);
}

sda.propagateUserEvent=function(el,evt){
	var baseEl=el;

	do{
		sda.dispatchUserEventToSingleElement(el,evt);
		if(el.firstChild) el=el.firstChild;
		else if(el.nextSibling) el=el.nextSibling;
		else{
			while(!el.nextSibling && el!=baseEl){ el=el.parentNode;}
			if(el!=baseEl) el=el.nextSibling;
		}
	}while(el!=baseEl)
	
}

sda.makeOverload=function(){
	var innerHandlers=[];
	for(var i=0;i<arguments.length;i++){
		if(!(arguments[i] instanceof Array))
			throw("Incorrect overload definition: a parameter is not an Array");
		for(var j=0;j<arguments[i].length;j++)
			if(typeof(arguments[i][j])!="function" && typeof(arguments[i][j])!="string" && typeof(arguments[i][j])!="object")
				throw("Incorrect overload definition: a parameter element is not string or function");
		
		if(typeof(arguments[i][arguments[i].length-1])!="function")
			throw("Incorrect overload definition: last parameter element is not a function");
		
		innerHandlers.push(arguments[i]);
			
	}
	
	innerHandlers=innerHandlers.sort(function(a,b){return b.length-a.length;});

	var handler=function(){
		
		var args=[];
		for(var i=0;i<arguments.length;i++)
			args.push(arguments[i]);
		
		var match=false;
		
		for(var i=0;i<innerHandlers.length && !match;i++){
			match=true;
			for(var j=0; j<(innerHandlers[i].length-1) && match; j++){
				if(typeof(innerHandlers[i][j])=="string" && typeof(arguments[j])!=innerHandlers[i][j])
					match=false;
				if((typeof(innerHandlers[i][j])=="function" || typeof(innerHandlers[i][j])=="object") && !(arguments[j] instanceof innerHandlers[i][j]))
					match=false;
			}
			if(match){
				return innerHandlers[i][innerHandlers[i].length-1].apply(this,args);
			}
		}
		
		throw("No match for overloaded function call");
	}

	return handler;
}

sda.defineClass=sda.makeOverload(
	["object","function",function(prototype,inherit){
		if(inherit==Object)
			inherit=undefined;
			
		if(typeof(inherit)!="undefined" && (typeof(inherit)!="function" || inherit.isInheritable!=true))
			throw("sda.defineClass can only inherit from sda.defineClass created constructror.");
			
		var classCtor=function(){
			for(var i in this){
				
				var typed=(/^\$_(.*?)_(.*)$/).exec(i);
				if (typeof(this[i]) != 'function' && typed) {
					var f = function(v){
						if(arguments.length==0) return arguments.callee.propertyValue;
						
						if (typeof(v) != arguments.callee.propertyType) 
							throw ('IncorrectType Property Exception');
						else 
							arguments.callee.propertyValue = v;
					}
					f.isConstructorHandled=true;
					f.propertyName=typed[2];
					f.propertyType=typed[1];
					f.propertyValue=this[i];
					
					this.__defineSetter__(typed[2],f);
					this.__defineGetter__(typed[2],f);
					
					delete this[i];
				}
				
				if(typeof(this[i])=="function" && !this[i].isConstructorHandled)
					this[i]=sda.hardScope(this[i],this);

				if( i!="constructor" &&  i!="prototype" ){
					if( this[i] && typeof(this[i])=="object")
						this[i]=sda.makeDeepCopy(this[i])
					else
						this[i]=this[i];
				}

			}
			
			if(this["$ctor"])
				this.constructor=this["$ctor"].innerFunction;
			else
				this.constructor=undefined;
				
			if(this["$ctor"]){
				this["$ctor"].apply(this,arguments);
			}
		};
		
		classCtor.methods={};
		classCtor.isInheritable=true;
		
		var parentCtor=function(){};
		
		if(inherit)
			parentCtor.prototype=inherit.prototype;
			
		var parent=new parentCtor();
		classCtor.prototype=parent;
		for(var i in parent)
			if(typeof(parent[i])=="function") classCtor.methods[i]=parent[i];
		
		for(var i in prototype){
			if(typeof(prototype[i])=="function"){
				prototype[i].ownerClass=classCtor;
				prototype[i].__$parentMethod=parent[i];
				classCtor.methods[i]=prototype[i];
			}
			
			if(i!="constructor" && i!="name" && i!="$inherit" && i!="prototype")
				if(prototype[i] && typeof(prototype[i])=="object")
					classCtor.prototype[i]=sda.makeDeepCopy(prototype[i]);
				else
					classCtor.prototype[i]=prototype[i];
				

		}
			
		return classCtor;
	}],
	
	
	["object",function(prototype){
		var parent=Object;
		if(typeof(prototype.$inherit)=="function"){
			parent=prototype.$inherit;
			delete prototype.$inherit;
		}
		
		return sda.defineClass(prototype,parent);
	}],
	["string","object",function(name,prototype){
		return window[name]=sda.defineClass(prototype);
	}],
	["string","object","function",function(name,prototype,inherit){
		return window[name]=sda.defineClass(prototype,inherit);
	}]
);

sda.EvtListener=sda.defineClass({
	type:"",
	el:null,
	handler:null,
	internalhandler:null,
	
	$ctor:function(elarg,typearg,handlerarg){with(this){
		if(elarg instanceof Array) el=elarg; else el=[elarg];
		if(typearg instanceof Array) type=typearg; else type=[typearg];
		if(handlerarg instanceof Array) handler=handlerarg; else handler=[handlerarg];
		internalhandler=[];
		
		for(var i=0;i<el.length;i++){
			if(typeof(el[i])=="string") el[i]=document.getElementById(el[i]);
			for(var j=0;j<type.length;j++)
				for(var k=0;k<handler.length;k++)
					internalhandler.push(sda.addEvent(el[i],type[j],handler[k]));
		}
	}},
	
	cancel:function(){with(this){
		if(!internalhandler || !internalhandler.length) return;

		for(var i=0;i<internalhandler.length;i++){
			sda.removeEvent(el[i],type[i],internalhandler[i]);
		}
	}}

});

sda.getClientRect=function(el){
	var r={top:el.offsetTop,left:el.offsetLeft};
	while(el.offsetParent){
		el=el.offsetParent;
		r.top+=el.offsetTop;
		r.left+=el.offsetLeft;
	}
	
	return r;
}
