/**
 * Core script for vxJS framework
 * 
 * @author Gregor Kofler, info@gregorkofler.at
 * @version 0.3.0 2008-05-09
 */
var __global = __global || this;

if(!vxJS) {
	var vxJS = {

		widget: {
		},

		event: {
			model: null,
			registry: [],
			regNdx: 0,
	
			init: function() {
				if(!this.model) {
					if(document.createEvent && document.addEventListener && document.removeEventListener) { this.model = "W3C"; }
					else if(document.fireEvent && document.attachEvent && document.detachEvent) { this.model = "MS"; }
					else { this.model = "PRE"; }
				}
				return this.model;
			},
		
			/*
			 * Trigger Event
			 * @param {string} type of event
			 * @param {object} associated DOM-Object
			 */
			fireEvent: function(evt, elem) {
				var t = this.model || this.init(), e;
				switch (t) {
					case "W3C":
						e = document.createEvent("Events");
						e.initEvent(evt, true, true);
						elem.dispatchEvent(e); break;
					case "MS":
						e = document.createEventObject();
						elem.fireEvent("on" + evt, e); break;
					default:
						elem["on"+evt]();
				}
			},
					
			/**
			 * Add Listener
			 * @param {object} DOM Object to which event gets attached
			 * @param {string} type of event
			 * @param {function} called function upon event
			 */
			addListener: function(obj, type, fn) {
				var n = "__ID__" + (this.regNdx++);
				this.registry.push({ id: n, object: obj, type: type, fn: fn});

				switch (this.model || this.init()) {
					case "W3C":	obj.addEventListener(type, fn, false); break;
					case "MS":	obj.attachEvent("on"+type, obj["e"+type+fn] = function() { fn(window.event); } ); break;
					default:	this.buildListener(obj, type);
				}
				return n;
			},
		
			/**
			 * Remove Listener
			 * @param {string} registry ID
			 */
			removeListener: function(id) {
				var i, r;
				for(i = this.registry.length; i--;) {
					if(this.registry[i].id == id) {
						r = this.registry[i];
						delete this.registry[i];
						break;
					}
				}
				if(!r) { return; }

				switch (this.model || this.init()) {
					case "W3C":
						r.object.removeEventListener(r.type, r.fn, false); break;
					case "MS":
						r.object.detachEvent("on"+r.type, r.object["e"+r.type+r.fn]);
						delete r.object["e"+r.type+r.fn]; break;
					default:
						this.buildListener(r.object, r.type);
				}
			},
			
			/**
			 * build listener for "old" handling via on{event}
			 * @param {object} object
			 * @param {string} type
			 */
			buildListener: function (obj, type) {
//				var r, cb = null;
//				for(var i = this.registry.length; i--;) {
//					r = this.registry[i];
//					if(r.object == obj && r.type == type) {
//						cb = !cb ? r.fn.call(obj) : function(){ r.fn.call(obj); cb.call(obj); };
//					}
//				}
//				obj["on"+type] = cb;
//				if(cb) {
//					alert(cb.toSource());
//				}
			},

			getAbsMousePos: function(e) {
				var x = e.clientX, y = e.clientY;
	
				if(typeof window.pageXOffset != "undefined") {
					return new Coord(x+window.pageXOffset, y+window.pageYOffset);
				}
				if(document.documentElement && typeof document.documentElement.scrollLeft != "undefined") {
					return new Coord(x+document.documentElement.scrollLeft, y+document.documentElement.scrollTop);
				}
				if(document.body && typeof document.body.scrollLeft != "undefined") {
					return new Coord(x+document.body.scrollLeft, y+document.body.scrollTop);
				}
				return new Coord(x, y);
			}
		},

		dom: {
			showElement: function(elem) { elem.style.display = "block"; },

			hideElement: function(elem) { elem.style.display = "none"; },

			isHidden: function(elem) { return elem.style.display == "none"; },

			setOpacity: function(elem, opac) {
				var s = elem.style;

				if(typeof s.filter  == 'string') {
					s.filter = s.filter.indexOf("alpha(opacity=") == -1 ?
					"alpha(opacity="+opac+")" :
					s.filter.replace(/alpha\(opacity=[0-9.-]+\)/, "alpha(opacity="+opac+")");
				} else {
					s.opacity = opac/100;
				}
			},

			getStyle: function(elem, cssRule) {
				if(window.getComputedStyle) {
					return window.getComputedStyle( elem, "").getPropertyValue(cssRule.replace(/[A-Z]/g, function(match) { return "-" + match.toLowerCase(); })); 
				}
				if( elem.currentStyle) {
					return elem.currentStyle[cssRule];
				}
				return false;
			},
			
			getNetScrollHeight: function(elem) {
					return (elem.scrollHeight
					- (parseInt(vxJS.dom.getStyle(elem, "paddingTop"), 10) || 0)
					- (parseInt(vxJS.dom.getStyle(elem, "paddingBottom"), 10) || 0)
					- (parseInt(vxJS.dom.getStyle(elem, "borderTopWidth"), 10) || 0)
					- (parseInt(vxJS.dom.getStyle(elem, "borderBottomWidth"), 10) || 0));
			},
				
			appendChildren: function(tag, c) {
				var i;
			
				if (c) {
					if (typeof c == "string" || typeof c == "number") {
						tag.appendChild(document.createTextNode(c));
					}
					else if (typeof c.length != "undefined" && typeof c.nodeName == "undefined") {
						for (i = 0; i < c.length; i++) {
							if (typeof c[i] == "string" || typeof c[i] == "number") {
								tag.appendChild(document.createTextNode(c[i]));
							} else {
								tag.appendChild(c[i]);
							}
						}
					} else {
						tag.appendChild(c);
					}
				}
				return tag;
			},

			deleteChildNodes: function(n) {
				for (var i = n.childNodes.length; i--;) { n.removeChild(n.childNodes[i]); }
			},
		
			getElementsByClassName: function(clsName, parentElem) {
				var i, elem, f = [];
				var childElems	= arguments.length < 2 ? document.getElementsByTagName("*") : parentElem.getElementsByTagName("*");
		
				for (i = 0;( elem = childElems[i]); i++) {
					if(elem.className.match(new RegExp("(^|\\s)" + clsName + "(\\s|$)"))) { f.push(elem); }
				}
			    return f;
			},
			
			getParentElement: function(elem, tag) {
				if(!tag) { return elem.parent || null; }
				var p;
				while(p = elem.parent) {
					if(p.nodeName == tag.toUpperCase()) { return p; }
				}
				return null;
			},
		
			getElementOffset: function(elem) {
				var pos = new Coord(elem.offsetLeft, elem.offsetTop);
				while(elem = elem.offsetParent) {
					pos.x += elem.offsetLeft; pos.y += elem.offsetTop;
				}
				return pos;
			},
		
			getElementPosition: function(elem) {
				return new Coord(elem.style.left, elem.style.top);
			},
		
			getElementSize: function(elem) {
				return new Coord(elem.offsetWidth, elem.offsetHeight);
			},
		
			setElementPosition: function(elem, pos) {
				elem.style.left	= ""+pos.x+"px";
				elem.style.top	= ""+pos.y+"px";
			},

			nextNeighbor: function(n) {
				var nN = n.nodeName;
				n = n.nextSibling;
				while(n != null) {
					if(n.nodeName == nN) { return n; }
					n = n.nextSibling;
				}
				return null;
			},
		
			prevNeighbor: function(n) {
				var nN = n.nodeName;
				n = n.previousSibling;
				while(n != null) {
					if(n.nodeName == nN) { return n; }
					n = n.previousSibling;
				}
				return null;
			},

			moveBefore: function(n1, n2) {
				var p = n1.parentNode;
				p.removeChild(n1);
				p.insertBefore(n1, n2);
			},
		
			moveAfter: function(n1, n2) {
				var p = n1.parentNode;
				p.removeChild(n1);
				p.insertBefore(n1, n2 ? n2.nextSibling : null);
			},
			
			concatText: function(n) {
				var text = "", i, c;
		
				for (i = 0; i < n.childNodes.length; i++){
					c = node.childNodes[i];
					switch (c.nodeType){
						case 1:
		          			text += this.concatText(c); break;
						case 3:
		          			text += c.nodeValue;
					}
				}
				return text;
			},
	
			deleteTableRow: function(n) {
				while(n.parentNode) {
					if(n.nodeName == "TR") { n.parentNode.removeChild(n); return; }
					n = n.parentNode;
				}
			}
		}
	};
}

Coord = function(x, y) {
	this.x = !x ? 0 : parseInt(x,10);
	this.y = !y ? 0 : parseInt(y,10);
}

Coord.prototype = {
	add: function(that) {
		return new Coord(this.x+(!+that.x ? 0 : parseInt(that.x,10)), this.y+(!that.y ? 0 : parseInt(that.y,10)));
	},
	sub: function(that) {
		return new Coord(this.x-(!+that.x ? 0 : parseInt(that.x,10)), this.y-(!that.y ? 0 : parseInt(that.y,10)));
	}
}		

/**
 * Prototype extensions
 * 
 * Object.begetObject() = prototypal inheritance by Douglas Crockford
 *
 * int pos		= Array.arrayPosition(needle)
 * bool result	= Array.inArray(needle)
 * array arr	= Array.copy()
 * array arr	= Array.fill(value, count)
 * array arr	= Array.map(function)
 * 
 * string str	= String.ltrim()
 * string str	= String.rtrim()
 * string str	= String.trim()
 * string str	= String.lpad()
 * string str	= String.rpad()
 * string str	= String.encode()
 * string str	= String.decode()
 * string str | date d	= String.toDateTime(locale, return as date object)
 * 
 * int days		= Date.getDaysOfMonth()
 */
if (typeof Object.beget !== 'function') {
	Object.beget = function (o) {
		function F() {}
		F.prototype = o;
		return new F();
    };
}

Array.prototype.arrayPosition = function(needle) {
	for (var i = this.length; i--;) { if(this[i] === needle) { return i; }}
	return false;
};

Array.prototype.inArray = function(needle) {
	for (var i = this.length; i--;) { if(this[i] === needle) { return true; }}
	return false;
};

Array.prototype.copy = function () { return ([].concat(this)); };

Array.prototype.fill = function(val, cnt) { for(var i = 0; i < cnt; i++) { this.push(val); }};

Array.prototype.map = function(f) {
	var i, r = [];
	for (i = 0; i < this.length; i++) { r.push(f(this[i]))}
	return r;
};

String.prototype.ltrim = function() { return (this.replace(/^\s+/, "")); };
String.prototype.rtrim = function() { return (this.replace(/\s+$/, "")); };
String.prototype.trim = function() { return (this.replace(/^\s+\s+$/, "")); };

String.prototype.lpad = function(len, fchar) {
	var i, pad = "", fchar = fchar || " ";
	for(i = 0; i < len-this.length; i++) { pad += fchar; }
	return pad+this;
};
String.prototype.rpad = function(len, fchar) {
	var i, pad = "", fchar = fchar || " ";
	for(i = 0; i < len-this.length; i++) { pad += fchar; }
	return this+pad;
};

String.prototype.encode = function() { return escape(this.replace(/\s/g, "+")); };
String.prototype.decode = function() { return unescape(this.replace(/\+/g, " ")); };

String.prototype.toDateTime = function(locale, asObj) {
	var del, erg, d, t, m, j;

	locale = locale || "date_de";

	switch (locale) {
		case "date_us":

		case "date_de":
			del = this.match(/\d{1,2}([-\/.])\d{1,2}\1\d{0,4}/);

			if(!del &&  this.search(/^([0-9]{4}|[0-9]{6}|[0-9]{8})$/) === 0) {
				erg = [this.substr(0,2), this.substr(2,2), this.substr(4)];
			}
			else if(del && del.length === 2) {
				erg = this.split(del[1]);
				if(erg.length !== 3){ return false; }
			}
			else					{ return false; }

			d	= [	"00".substring(0, 2-erg[0].length)+erg[0],
					"00".substring(0, 2-erg[1].length)+erg[1],
					(""+new Date().getFullYear()).substring(0, 4-erg[2].length)+erg[2]];

			if(locale == "date_us") {
				t = +d[1];
				m = +d[0];
			}
			else {
				t = +d[0];
				m = +d[1];
			}
			j = +d[2];

			if(m < 1 || m > 12)										{ return false; }
			if(t < 1 || t > new Date(j, m-1, 1).getDaysOfMonth())	{ return false; }

			if(asObj) { return new Date(j, m-1, t); }
			del = locale == "date_de" ? "." : "/";
			return d[0]+del+d[1]+del+d[2];
		
		case "date_iso":
			del = this.match(/\d{2}(\d{2})?([-\/.])\d{1,2}\2\d{1,2}/);

			if(!del && this.search(/^([0-9]{6}|[0-9]{8})$/) == 0) {
				erg = [this.substr(0,this.length-4), this.substr(this.length-4,2), this.substr(this.length-2)];
			}
			else if(del && del.length == 3) {
				erg = this.split(del[2]);
				if(erg.length != 3)			{ return false; }
			}
			else							{ return false; }

			d	= [
				(""+new Date().getFullYear()).substring(0, 4-erg[0].length)+erg[0],
				"00".substring(0, 2-erg[1].length)+erg[1],
				"00".substring(0, 2-erg[2].length)+erg[2]
			];

			j = +d[0];
			m = +d[1];
			t = +d[2];

			if(m < 1 || m > 12)										{ return false; }
			if(t < 1 || t > new Date(j, m-1, 1).getDaysOfMonth())	{ return false; }

			if(asObj) { return new Date(j, m-1, t); }
			return d[0]+"-"+d[1]+"-"+d[2];

		case "time_hm":
			del = this.match(/\d{1,2}([-:.])\d{1,2}/);

			if(!del && this.search(/^[0-9]{4}$/) == 0) {
				erg = [this.substr(0,2), this.substr(2)];
			}
			else if(del && del.length === 2) {			
				erg = this.split(del[1]);
				if(erg.length !== 2){ return false; }
			}
			else 					{ return false; }
			
			if(+erg[0] > 23 || +erg[1] > 59)
									{ return false; }
			return "00".substring(0, 2-erg[0].length)+erg[0]+":"+"00".substring(0, 2-erg[1].length)+erg[1];

		case "time_hms":
			del = this.match(/\d{1,2}([-:.])\d{1,2}\1\d{1,2}/);
			if(!del &&  this.search(/^([0-9]{4}|[0-9]{6})$/) == 0) {
				erg = [this.substr(0,2), this.substr(2,2), this.substr(4)];
			}
			else if(del && del.length === 2) {			
				erg = this.split(del[1]);
				if(erg.length !== 3){ return false; }
			}
			else 					{ return false; }

			h = +erg[0];
			m = +erg[1];
			s = +erg[2];

			if(+erg[0] > 23 || +erg[1] > 59 || +erg[2] > 59)
									{ return false; }
			return "00".substring(0, 2-erg[0].length)+erg[0]+":"+"00".substring(0, 2-erg[1].length)+erg[1]+":"+"00".substring(0, 2-erg[2].length)+erg[2];
	}
};

Date.prototype.getDaysOfMonth = function() {
	var d, m = this.getMonth(), y = this.getFullYear();
	d = m === 3 || m === 5 || m === 8 || m === 10 ? 30 : 31;
	if(m === 1) { d -= y%4 === 0 && y%100 !== 0 || y%400 === 0 ? 2 : 3; }
	return d;
};

/**
 * added DOM functionality
 */

String.prototype.tags = "acronym|address|applet|area|a|base|basefont|big|blockquote|body|br|b|caption|center|cite|code|dd|dfn|dir|div|dl|dt|em|font|form|h1|h2|h3|h4|h5|h6|head|hr|html|img|input|i|kbd|link|li|map|menu|meta|ol|option|param|pre|p|q|samp|script|select|small|strike|strong|style|sub|sup|table|td|textarea|th|title|tr|tt|ul|u|var".split("|");

/**
 * creates DOM element, adds previously set attributes and adds optional child node(s) or text node
 * e.g. "div".createElement("p".createElement("Ich bin ein Absatz"));
 */
String.prototype.createElement = function(children) {
	var i, a, e = document.createElement(this);

	if(this.attr) {
		for(i = this.attr.length; i--;) {
			if(this.attr[i].name == "name") {
				try	{ e = document.createElement("<"+this+" name="+this.attr[i].value+">"); } catch(e) { }
				break;
			}
		}

		for(i = this.attr.length; i--;) {
			a = this.attr[i];
			if(a.name == "class") { e.className = a.value; }
			else { e.setAttribute(a.name, a.value); }
		}
	}
	return vxJS.dom.appendChildren(e, children);
}

/**
 * sets one or more attributes
 * e.g.: "img".setAttr("src","test.jpg");
 * e.g.: "input".setAttr([["class", "format"], ["type", "submit"]]);
 */
String.prototype.setAttr = function(n, v) {
	var i;
	if (!this.attr) { this.attr = []; }

	if (arguments.length > 1) {
		this.attr.push({name: n, value: v});
	}
	else if (arguments.length == 1 && typeof n == "object") {
		for (i = 0; i < n.length; i++) {
			this.attr.push({name : n[i][0], value : n[i][1]});
		}
	}
	return this;
}

/**
 * creates DOM elements for each element in array
 * e.g. ["td","td","td"].createElement();
 */
Array.prototype.createElement = function(children) {
	var i, e = [];
	for (var i = 0; i < this.length; i++) { e[i] = this[i].createElement(children); }
	return e;
}

/**
 * sets one or more attributes for each element in array
 * e.g. ["td","td"].setAttr("style","color:blue");
 */
Array.prototype.setAttr = function(n, v) {
	for (i = this.length; i--;){ this[i] = this[i].setAttr(n, v); }
	return this;
}

/**
 * encloses array elements with given tags
 * if argument is an array every element is enclosed in all tags given in array
 * e.g. ["id","name","address"].domWrapWithTag("th");
 *      ["id","name","address"].domWrapWithTag(["a","b","div".setAttr("id","1")]);
 */
Array.prototype.domWrapWithTag = function(tag) {
	var i, j;
	if (typeof tag == "string") {
		for (i = 0; i < this.length; i++) {
			if ("".tags.inArray(this[i]))	{ this[i] = tag.createElement(this[i].createElement()); }
			else							{ this[i] = tag.createElement(this[i]); }
		}
	}
	else {
		for (i = 0; i < this.length; i++) {
			for (j = 0; j < tag.length; j++) { this[i] = tag[j].createElement(this[i]); }
		}
	}
	return this;
}
