/*
	Javascript Infrastructure!
*/

// Debug!
function $debug(x){if(console)console.log(x)}

// dmp: convert object to printable string for debug purposes
function $dmp(a){if((typeof(a)=="object")||(typeof(a)=="array")){var r="{";for(var x in a)r+= x+": "+$dmp(a[x])+", ";return r+"}"}return a}


// List of extra functions for specific types of DOM nodes:
var NodesData = {
// Select box
select:{
Clear:function(){try{var z=this.options[0]}catch(e){return null};while (this.options.length>0)this.options[0]=null;return z},
Add:function(v,t){o=$N.New("OPTION");o.text=t;o.value=v;this.options.add(o)},
Get:function(v){var o=this.options;var i=this.selectedIndex;if(v){for(var x=0;x < o.length;x++)if(o[x].value==v)return o[x];return null}else return i>-1?o[i]:null},
GetV:function(){return(x=this.Get())?x.value:null},
GetT:function(v){return(x=this.Get(v))?x.text:null},
Len:function(){return this.options.length},
Select:function(v){var o=this.options;for(var i=0;i<o.length;i++)if(o[i].value==v)return o[i].selected=true;this.selectedIndex=-1},
Set:function(v,t){var o=this.options;for(var i=0;i<o.length;i++)if(o[i].value==v)return o[i].text=t;return null},
Del:function(v){var o=this.options;for(var i=0;i<o.length;i++)if(o[i].value==v) this.remove(i)},
Populate:function(a,d){this.Clear();for(var i in a)this.Add(i,a[i]);this.Select(d)},
ExtractV:function(){var ret={};for(var i=0;i<this.options.length;i++)ret[i]=this.options[i].value;return ret},
Extract:function(){var ret={};for(var i=0;i<this.options.length;i++)ret[this.options[i].value]=this.options[i].text;return ret},
Splice:function(p,c){var o=this.options;var t=o.length;for(var x=p;x<(t-c);x++)o[x]=o[x+c];while(c>0){this.remove(o.length-1);c--}},
Swap:function(a,b){var o=this.options,av=o[a].value,at=o[a].text;o[a].value=o[b].value;o[a].text=o[b].text;o[b].value=av;o[b].text=at},
Edit:function(v,t){var o=this.options;for(var i=0;i<o.length;i++)if(o[i].value==v) o[i].text = t}
}}

// Core: Access a node and assigns all extra behaviours to a given node.
function $(j,debug){
// Get the proper node
var i=j;if(typeof(j)=="string")i=document.getElementById(j)
if(i==null){if(debug)alert("The object '"+j+"' is null!");return null}
if(i.storage==true)return i
i.storage={}// store extra data here


i.Pos= // Get/Set relative position
function(p,u){
	var x=this.Css("position");
	if(p&&(x!="static")){
		if(!u)u="px";
		if ((x=="relative")||(x=="absolute"))
			p=this.Abs2Rel(p);
		this.Css('left',p.left+u);
		this.Css('top',p.top+u)
	}
	return this.Rel2Abs({left:this.offsetLeft,top:this.offsetTop})
}
i.Rel2Abs= // Convert relative position to absolute page (body) position
function(p){var o=this.offsetParent;while(o){p.left+=o.offsetLeft;p.top+=o.offsetTop;o=o.offsetParent};return p}
i.Abs2Rel= // Convert page (body) position to relative to parent
function(p){var o=this.offsetParent;while(o){p.left-=o.offsetLeft;p.top-=o.offsetTop;o=o.offsetParent};return p}
i.Size= // Get/Set size
function(s,u){if(s){if(!u)u="px";this.Css('width',s.width+u);this.Css('height',s.height+u)}return{width:parseInt(this.offsetWidth),height:parseInt(this.offsetHeight)}}
i.Rect= // Get/Set rect (left,top,right,bottom)
function(l,t,r,b){if(l&&t&&r&&b){this.Size({width:r-l,height:b-t});this.Pos({left:l,top:t})}var p=this.Pos(),s=this.Size();return{left:p.left,top:p.top,right:p.left+s.width,bottom:p.top+s.height,width:s.width,height:s.height}}
i.Css= // Get/Set CSS properties
function(p,v){if(v)this.style[p]=v;return this.style[p]}
i.Show= // Get/Set CSS display property
function(s){if(s==null)s="block";else if(s==false)s="none";else if(s==true)s="block";this.Css("display",s)}
i.ShowToggle= // Switch Get/Set no matter what it was
function(t){this.Show((this.Css("display")=="none")?t:false)}
i.Shown= // Is visible?
function(){return this.Css("display")!="none"}

i.Store= // Store data in node
function(l,x){this.storage[l] = x}
i.Retrieve= // Retrieve stored data
function(l){return this.storage[l]?this.storage[l]:null}
i.clearNodes= // Remove all childs
function(){while(this.childNodes[0])this.removeChild(this.childNodes[0])}

// Append any DOM node specific method
var tag = i.tagName.toLowerCase();if(NodesData[tag]!=null)for(var m in NodesData[tag])i[m]=NodesData[tag][m]
return i
}

// DOM Nodes management (New,NewText,NewStructure)
var $N = {}
$N.New= // Create a new node
function(e,a){var r=document.createElement(e);r.SetAttr=function(i,v){this.setAttribute(i,v)};for(var i in a)r.SetAttr(i, a[i]);return $(r)}
$N.NewText= //Create new Text node
function(x){return document.createTextNode(x)}
$N.NewStructure= // Create a complex structure of nodes. Format: {n:node,a:attrs,c:child nodes}
function(s){var r=$N.New(s["n"],s["a"]);if(s["c"]!= null)for(var c in s["c"])r.appendChild($N.NewStructure(s["c"][c]));return r}

// Cookie management (Set,Get,Del)
var $C = {}
$C.Set = function(n,v,ed,em,ey ){var s= n+"="+escape(v);if ( ey ){var x=new Date(ey,em,ed);s+="; expires="+x.toGMTString();}document.cookie=s}
$C.Get = function (n){var r = document.cookie.match('(^|;) ?'+n+'=([^;]*)(;|$)');return (r)?(unescape(r[2])):null}
$C.Del = function(n){var d=new Date();  d.setTime(d.getTime()-1);document.cookie=n+="=; expires="+d.toGMTString()}

// Event management (Register,Get,Caller)
var $E = {}
$E.Register= // Register an event. Also register onmouseexit and onmouseenter...
function(o,t,f){

if(o=="document")o=document
if(typeof(o)=="string")o=$(o)

if (!o.EnterExitOK&&((t=="mouseenter")||(t=="mouseexit")))
{
	o.EnterExitOK=true
	var fen = function(e,o){var opn=o.parentNode;if(opn&&opn.fen){opn.CancelExit=true;opn.fen(e)}if(o.MouseEnter==false){if(o.onmouseenter)o.onmouseenter(e);o.MouseEnter=true}o.CancelExit=true;return true}

	o.MouseEnter=o.CancelExit=false

	$E.Register(o, "mouseover", fen )
	$E.Register(o, "mouseout", function(e,o){
									var fex=function(e,o){if(!fex.o.CancelExit&&(fex.o.MouseEnter==true))if(fex.o.onmouseexit){fex.o.onmouseexit(e);fex.o.MouseEnter=false}return true}
									fex.o = o
									o.CancelExit = false
									setTimeout( fex,100 );return true})
}
var e = "on"+t
if(!o.OnMyEvent)o.OnMyEvent=[]
if (o.OnMyEvent[e])o.OnMyEvent[e].push(f)
else
{
	var dh=function(e){
				if(dh.o.OnMyEvent&&dh.o.OnMyEvent[dh.e])
					for(var i=0; i<dh.o.OnMyEvent[dh.e].length;i++)
						if(dh.o.OnMyEvent[dh.e][i](e,dh.o)==false)break;
			}
	dh.e=e
	dh.o=o
	if (!$E.Register)$E.Register={}
	var original_handler=o[e]
	o.OnMyEvent[e]=[]
	if(original_handler!=null)o.OnMyEvent[e].push(original_handler)
	if(typeof(f)!="function")f=new Function(f)
	o.OnMyEvent[e].push(f)
	o[e]=dh
}
}
$E.Get= // Return event (for shitty IS compatibility)
function(e){return e||window.event;}
$E.Caller= // Get caller DOM node
function(e){if(!e)e=$E.Get(e);if(!e)return null;return e.target?e.target:e.srcElement?e.srcElement:null}

// Serialize is used for JS -> PHP data transferring...
function $Serialize(x)
{var t=typeof x,v;
switch(t){
case "undefined":v="N";break;case "boolean":v="b:"+(x?"1":"0");break;
case "number":v=(Math.round(x)==x?"i":"d")+":"+x;break;
case "string":v="s:"+x.length+':"'+x+'"';break;
case "array":
case "object":
var c=0,vs="",ok;
for(k in x){ok=(k.match(/^[0-9]+$/)?parseInt(k):k);vs+=$Serialize(ok)+$Serialize(x[k]);c++}v="a:"+c+":{"+vs+"}";break}
if(t!="object"&&t!="array")v+=";";return v
}

// Execute some code... this prevents the use of eval()
function $exec(x,p){
if(typeof(x)!="function")
	x=new Function(x);
var ret = x(p)
return ret
}


// Some screen/viewport related utilities (GetMouse,DocSize,ViewSize,ViewPos,ViewRect)
var $S = {}
$S.GetMouse= // Get mouse screen coordinates
function(e){e=$E.Get(e);return (e.pageX||e.pageY)?{left:e.pageX,top:e.pageY}:({ left:e.clientX+document.body.scrollLeft-document.body.clientLeft,top:e.clientY+document.body.scrollTop-document.body.clientTop})}
$S.DocSize= // Get size of the body
function(){var D = document,h=Math.max(Math.max(D.body.scrollHeight,D.documentElement.scrollHeight),Math.max(D.body.offsetHeight,D.documentElement.offsetHeight),Math.max(D.body.clientHeight,D.documentElement.clientHeight)),w=Math.max(Math.max(D.body.scrollWidth, D.documentElement.scrollWidth),Math.max(D.body.offsetWidth, D.documentElement.offsetWidth),Math.max(D.body.clientWidth, D.documentElement.clientWidth));return {width:w,height:h}}
$S.ViewSize= // Get size of the viewport (browser inner window)
function(){
var w,h;
 if (typeof window.innerWidth != 'undefined')
 {w=window.innerWidth;h=window.innerHeight}
 else if (typeof document.documentElement != 'undefined'
     && typeof document.documentElement.clientWidth !=
     'undefined' && document.documentElement.clientWidth != 0)
 {w=document.documentElement.clientWidth;h=document.documentElement.clientHeight}
 else
 {w=document.getElementsByTagName('body')[0].clientWidth;h=document.getElementsByTagName('body')[0].clientHeight}
	return{width:w,height:h}}
$S.ViewPos= // Get position of the scrolled view port
function(){
	if (document.documentElement){
		var t1=document.documentElement.scrollTop
		var l1=document.documentElement.scrollLeft
	}
	else{var t1=0,l1=0}
	if (document.body){
		var t2=document.body.scrollTop
		var l2=document.body.scrollLeft
	}
	else{var t2=0,l2=0}
	return {scrollLeft:Math.max(l1,l2),scrollTop:Math.max(t1,t2)}
}
$S.ViewRect= // Get viewport as a rect
function(){var s=$S.ViewSize(),p=$S.ViewPos()
return{left:p.scrollLeft,top:p.scrollTop,right:p.scrollLeft+s.width,bottom:p.scrollTop+s.height,width:p.scrollLeft+s.width,height:p.scrollTop+s.height}}

$S.ObjRect=
function(t){
	var div = $N.New("div",{style:'position:absolute;visibility:hidden;height:auto;width:auto;'})
	var olp=t.parentNode
	div.appendChild(t)
	document.body.appendChild(div)
	var r = $(div).Rect();
	olp.appendChild(t)
	delete div
	return r
}








// Advanced array class (IS THIS REALY USEFUL TO ANYBODY?)
function $A(a)
{
	this.Push = this.Set = this.Add = function(v,k){if(k==null)k=this.count;this.data[k]=v;this.count++}
	this.LastKey = function(){for(var x in this.data);return x}
	this.Last = function(){return this.data[this.LastKey]}
		
	this.Pop = function(k){
			if ( k == null )
				k = this.LastKey()
			if ( this.data[k] )
			{
				var tmp = this.data[k]
				this.Del(k)
				this.count--
				return tmp
			}
		}

	this.Del = function(k){
			if ( this.data[k] != null )
			{
				delete a[k]
				this.count--
			}
		}

	this.ToObject = function(){return this.data}

	this.ToArray = function(){var r=[];for(var i in this.data)r.push(this.data[i]);return r}

	this.Count = function(){var t=0;for(var i in this.a)t++;return t}
		
	this.Splice = function(p,c){
			for(var x=p;x<(this.count-c);x++)this.data[x]=this.data[x+c]
			while((c--)>0)this.Pop()
		}
		
	this.Iterate = function(f){for(var i in this.data)f(o[i])}

	alert( typeof(a) )
	
	if ( a == null )
		this.data = {}
	else if ( typeof(a) == "array" )
	{
		this.data = {}
		for(var i in a)
			if(typeof(a[i])!="function")
				this.data[i]=a[i]
	}
	else if ( typeof(a) == "object" )
		this.data = a
	else
		this.data = {0:a}
	this.count = this.Count()
}
