/**
 * core script for vxJS framework
 * 
 * @author Gregor Kofler, info@gregorkofler.at
 * @version 1.0.3 2009-06-22
 * 
 * @todo purgeEvents() for removing listeners before removing DOM nodes
 * @todo cw us style
 * @todo generalize selection functions
 * @todo complete element functionality
 * @todo optimize serve/custom event handling
 */ 
 /*global Coord, Color */

var vxJS = vxJS || {};

(function() {

	/* private functions & properties */
	var global = this,
		doc = this.document;

	var isHostMethod = function(o, m) {
		var t = typeof o[m];
		return !!((o[m] && /^object|function$/.test(t)) || t === "unknown");
	};
	
	var beget = function(o) {
		function F() {}
		F.prototype = o;
		return new F();
	};
	
	var isEmpty = function(o) {
		var p;

		if(!o || typeof o !== "object") {
			return true;
		}
		for(p in o) {
			if(o.hasOwnProperty(p)) {
				return false;
			}
		}
		return true;
	};
	
	var merge = function(o, add) {
		var p;
		if(!o || !add || typeof o !== "object" || typeof o !== "object") {
			return;
		}
		for(p in add) {
			o[p] = add[p];
		}
		return o;
	};

	/**
	 * wrapper functionality for both DOM elements and widgets,
	 * allowing fx, drag and drop, etc.
	 * 
	 *  type: "dropBox", "boundingBox", ...
	 *  widget: 
	 *  element:
	 *  fx: [..]
	 *  listener: [..]
	 *  draggable:
	 *  
	 *  
	 */
	vxJS.element = function() {
		var registry = [];

		var getElement = function(e) {
			var i, r, ndx = e.nodeType && e.nodeType === 1 ? "element" : "widget";

			for(i = registry.length; i--; ) {
				r = registry[i];
				if(r[ndx] && r[ndx] === e) {
					return r;
				}
			}
		};

		/**
		 * register element; element can either be dom element
		 * or widget that exposes _getElem() method or element property
		 */
		var register = function(e) {
			var o;
			if(!(o = getElement(e))) {
				o = new vxJS.E();

				if(typeof e._getElem === "function") {
					o.widget = e;
					o.element = e._getElem();
				}
				else if(e.element && e.element.nodeType === 1) {
					o.widget = e;
					o.element = e.element;
				}
				else if(e.nodeType && e.nodeType === 1) {
					o.element = e;
				}
				registry.push(o);
			}
			return o;
		};

		return {
			register: register,
			registry: registry
		};
	}();
	
	vxJS.E = function() {};

	/**
	 * add effect element
	 * @param {String} effect name
	 * @param {Object} effect parameters
	 * @param {Boolean} queue effect
	 * 
	 * (chained) unqueued effects are executed immediately
	 */
	vxJS.E.prototype = {
		fx: function(effect, param, queued) {
			if(vxJS.fx && vxJS.fx[effect]) {
				vxJS.fx.add(this, effect, param, queued);
			}
			return this;
		}
	};

	
	vxJS.widget = {};

	vxJS.event = function() {
		var	model = function() {
			if(
				isHostMethod(document, 'createEvent') &&
				isHostMethod(document, 'addEventListener') &&
				isHostMethod(document,'removeEventListener')) {
				return "W3C";
			}
			else if(
				isHostMethod(document, 'fireEvent') &&
				isHostMethod(document, 'attachEvent') &&
				isHostMethod(document, 'detachEvent')) {
				return "MS";
			}
			return "PRE";
		}(),
		registry = [], regNdx = 0;

		return {
			/**
			 * trigger event
			 * @param {Object} DOM element
			 * @param {String} type of event
			 */
			fireEvent: function(elem, evt) {
				var e;
				switch (model) {
					case "W3C":
						e = document.createEvent("Event");
						e.initEvent(evt, true, true);
						elem.dispatchEvent(e);
						return;
					case "MS":
						elem.fireEvent("on" + evt, document.createEventObject());
						return;
					default:
						elem["on"+evt]();
				}
			},

			/**
			 * add listener
			 * @param {Object} DOM Object to which event gets attached
			 * @param {String} type of event
			 * @param {Function} callback
			 */
			addListener: function(obj, type, fn) {
				var n = "__ID__" + (regNdx++), f, i, found;

				// try to distinguish between dom elements and js objects (i.e. widgets)
				if(obj.ownerDocument && obj.ownerDocument === doc || obj === doc) {
					switch (model) {
						case "W3C":
							obj.addEventListener(type, f = function(e) { fn.call(e.target, e); }, false);
							break;
						case "MS":
							obj.attachEvent("on"+type, f = function() { fn.call(window.event.srcElement, window.event); });
							break;
						default:
							if(typeof obj["on"+type] === "function") {
								for (i = registry.length; i--; ) {
									if (registry[i].object === obj && registry[i].type === type) {
										found = true;
										break;
									}
								}
								if(!found) {
									registry.push({ id: 0, object: obj, type: type, fn: obj["on"+type]});
								}
							}
	
							obj["on"+type] = function(e) {
								var t = e.target || e.srcElement || obj;
	
								for(i = 0; i < registry.length; ++i) {
									if(registry[i].object === obj && registry[i].type === e.type) {
										registry[i].fn.call(t, e);
									}
								}
							};
					}
				}
				else {
					f = function(eParams) { fn.call(obj, eParams); };
				}
				registry.push({ id: n, object: obj, type: type, fn: f, callback: fn });
				return n;
			},

			/**
			 * remove listener
			 * @param {String} registry ID or {Function} callback
			 */
			removeListener: function(id) {
				var i, r, ndx = typeof id === "function" ? "callback" : "id";

				for(i = registry.length; i--;) {
					if(registry[i][ndx] === id) {
						r = registry[i];
						registry.splice(i, 1);
						break;
					}
				}
				if(!r) {
					return;
				}

				switch (model) {
					case "W3C":
						r.object.removeEventListener(r.type, r.fn, false); break;
					case "MS":
						r.object.detachEvent("on"+r.type, r.fn);
						delete r.fn;
				}
			},

			/**
			 * allow custom event listeners for widgets
			 * @param {String} event type
			 * @param {object} widget object
			 * @param {object} optional parameters
			 */
			serve: function(obj, type, e) {
//				console.log(arguments);
				var i, l = registry.length, r;
				for(i = 0; i < l; ++i) {
					r = registry[i];
					if(r.object === obj && r.type === type) {
						r.fn(e);
					}
				}
			},

			getAbsMousePos: function(e) {
				var x = e.clientX, y = e.clientY, body = document.documentElement || document.body;
	
				if(typeof window.pageXOffset != "undefined") {
					return new Coord(x+window.pageXOffset, y+window.pageYOffset);
				}
				if(body && typeof body.scrollLeft != "undefined") {
					return new Coord(x+body.scrollLeft, y+body.scrollTop);
				}
				return new Coord(x, y);
			},
			
			cancelBubbling: function(e) {
				e.cancelBubble = true;
				if (e.stopPropagation) {
					e.stopPropagation();
				}
			},
			
			preventDefault: function(e) {
				if (e.preventDefault) {
					e.preventDefault();
				}
				e.returnValue = false;
			}
		};
	}();

	vxJS.dom = {
		setOpacity: function(elem, opac) {
			var s = elem.style, o = opac > 1 ? 1 : (opac < 0 ? 0 : opac);

			if(typeof s.opacity  === "string") {
				s.opacity = ""+o;
			}
			else if(typeof s.filter  === "string") {
				s.filter = "alpha(opacity="+(o * 100)+")";
			}
		},
		
		getOpacity: function(elem) {
			var s = elem.style, o;

			if(typeof s.opacity  === "string") {
				return !s.opacity ? 1 : +s.opacity;
			}
			if(typeof s.filter  === "string") {
				o = s.filter.match(/opacity=(\d+)/);
				return o && o[1] ? o[1]/100 : 1;
			}
			return 1;
		},

		getStyle: function(elem, styleProp) {
			if(window.getComputedStyle) {
				return window.getComputedStyle( elem, "")[styleProp]; 
			}
			if( elem.currentStyle) {
				return elem.currentStyle[styleProp];
			}
			return false;
		},
		
		appendChildren: function(elem, c) {
			var i;
		
			if (c) {
				if (/^string|number$/.test(typeof c)) {
					elem.appendChild(document.createTextNode(c));
				}
				else if (typeof c.length !== "undefined" && typeof c.nodeName === "undefined") {
					for (i = 0; i < c.length; i++) {
						if (/^string|number$/.test(typeof c)) {
							elem.appendChild(document.createTextNode(c[i]));
						} else {
							elem.appendChild(c[i]);
						}
					}
				} else {
					elem.appendChild(c);
				}
			}
			return elem;
		},

		deleteChildNodes: function(n) {
			while(n.hasChildNodes()) {
				n.removeChild(n.lastChild);
			}
		},

		cleanDOM: function() {
			var r = /\S/;

			return function(n) {
				var i;

				if(n.nodeType === 8 || (n.nodeType === 3 && !r.test(n.data))) {
					n.parentNode.removeChild(n);
					return;
				}
				if(n.childNodes) {
					for(i = n.childNodes.length; i--;) {
						arguments.callee(n.childNodes[i]);
					}
				}
			};
		}(),

		walk: function (n, f) {
			f(n);
			n = n.firstChild;
			while (n) {
				this.walk(n, f);
				n = n.nextSibling;
			}
		},

		getFirstElementByName: function(n, name) {
			var i, found;

			if(n.name === name) {
				return n;
			}
			n = n.firstChild;
			while(n) {
				if((found = this.getFirstElementByName(n, name))) {
					return found;
				}
				n = n.nextSibling;
			}
		},

		/**
		 * @param {String} class name
		 * @param {Object} parent node (optional)
		 * @param {String} tag, restricts selection to this node name (optional) 
		 */
		getElementsByClassName: function() {
			if(isHostMethod(document, "getElementsByClassName")) {
				return function(cName, parent, tag) {
					var nl = parent ? parent.getElementsByClassName(cName) : document.getElementsByClassName(cName), i = 0, elem, list = [];
					if(!tag) {
						while((elem = nl[i++])) {
							list.push(elem);
						}
					}
					else {
						tag = tag.toLowerCase();
						while((elem = nl[i++])) {
							if(elem.nodeName.toLowerCase() === tag) {
								list.push(elem);
							}
						}
					}
					return list;
				};
			}

			if(isHostMethod(document, "evaluate")) {
				return function(cName, parent, tag) {
					var	list = [], elem, nl, i = 0;

					nl = document.evaluate(
						".//" + (tag || "*").toLowerCase() + "[contains(concat(' ', @class, ' '), ' " + cName + " ')]",
						parent || document,
						function(prefix) { return prefix === "html" ? "http://www.w3.org/1999/xhtml" : null; },
						XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
						null);

					while((elem = nl.snapshotItem(i++))) {
						list.push(elem);
					}
					return list;
				};
			}

			return function(cName, parent, tag) {
				var i = 0, elem, list = [], rex = new RegExp("(^|\\s)" + cName + "(\\s|$)");
				var ce	= parent ? parent.getElementsByTagName("*") : document.getElementsByTagName("*");

				if(!tag) {
					while ((elem = ce[i++])) {
						if(rex.test(elem.className)) {
							list.push(elem);
						}
					}
				}
				else {
					tag = tag.toLowerCase();
					while ((elem = ce[i++])) {
						if(rex.test(elem.className) && elem.nodeName.toLowerCase() === tag) {
							list.push(elem);
						}
					}
				}
				return list;
			};
		}(),

		getParentElement: function(elem, tag) {
			var t, m, r;
			if(!tag) { return elem.parentNode || null; }

			if (/^[a-z0-9]+$/i.test(tag)) {
				t = tag.toUpperCase();
				while ((elem = elem.parentNode)) {
					if (elem.nodeName === t) {
						return elem;
					}
				}
			}
			else {
				m = tag.match(/^([a-z0-9]+)([\.#])(\w+)$/i);
				if(!m || m.length < 3) {
					return null;
				}
				t = m[1].toUpperCase();
				r = m[2] === "." ? new RegExp("\\b"+m[3]+"\\b") : null;
				while ((elem = elem.parentNode)) {
					if (elem.nodeName === t && (!r && elem.id === m[3] || r.test(elem.className))) {
						return elem;
					}
				}
			}
			return null;
		},

		getElementOffset: function(elem, container) {
			var pos, p, oP, doc = elem.ownerDocument;

			if(!container) {
				container = doc;
			}
			if(elem === container) {
				return new Coord(0, 0);
			}

			p = elem.parentNode;
			oP = elem.offsetParent;
			pos = new Coord(elem.offsetLeft, elem.offsetTop);

			while(p && p !== container) {
				if(p !== doc.body && p !== doc.documentElement) {
					pos.x -= elem.scrollLeft;
					pos.y -= elem.scrollTop;
				}
				if(p === oP) {
					if(p !== doc.body) {
						pos.x += oP.offsetLeft;
						pos.y += oP.offsetTop;
						oP = p.offsetParent;
					}
				}
				p = p.parentNode;
			}
			return pos;
		},
	
		getElementSize: function(elem) {
			return new Coord(elem.offsetWidth, elem.offsetHeight);
		},
	
		setElementSize: function(elem, pos) {
			elem.style.width	= pos.x+"px";
			elem.style.height	= pos.y+"px";
		},

		getElementPosition: function(elem) {
			return new Coord(parseInt(elem.style.left, 10), parseInt(elem.style.top, 10));
		},
	
		setElementPosition: function(elem, pos) {
			elem.style.left	= pos.x+"px";
			elem.style.top	= pos.y+"px";
		},

		nextNeighbor: function(n) {
			var nN = n.nodeName;
			while((n = n.nextSibling)) {
				if(n.nodeName === nN) {
					return n;
				}
			}
		},

		prevNeighbor: function(n) {
			var nN = n.nodeName;
			while((n = n.previousSibling)) {
				if(n.nodeName === nN) {
					return n;
				}
			}
		},

		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 t = "", i, j, c = n.childNodes;
			
			for (i = 0; i < c.length; i++){
				switch (c[i].nodeType){
					case 1:
						n = c[i].nodeName;
						if (n === "INPUT" && c[i].type === "text") {
							t += c[i].value;
						}
						else if (n === "SELECT" && c[i].type === "select-one" && c[i].options && c[i].selectedIndex >= 0 && c[i].options[c[i].selectedIndex]) {
							t += this.concatText(c[i].options[c[i].selectedIndex]);
							break;
						}
						else if (n === "SELECT" && c[i].type === "select-multiple" && c[i].options) {
							for (j = c[i].options.length; j--;) {
								t += c[i].options[j].selected ? this.concatText(c[i].options[j]) : "";
							}
						}
						else if(n === "IMG") {
							t += c[i].title ? c[i].title : (c[i].alt ? c[i].alt : c[i].src);
						}
						else {
							t += this.concatText(c[i]);
						}	
						break;
					case 3:
	          			t += c[i].nodeValue;
				}
			}
			return t;
		},

		parse: function(elem) {
			var insertTree = function(n, p) {
				var i, l, e, pr = n.properties;
		
				if(n.node) {
					e = n.node.create();	
				}
				else if(n.text){
					p.appendChild(document.createTextNode(n.text));
					return;
				}
				else if(n.html){
					p.innerHTML += n.html;
					return;
				}
				else {
					return;
				}

				if (typeof pr === "object") {
					for (i in pr) {
						if (pr.hasOwnProperty(i)) {
							if(i === "text") {
								e.appendChild(document.createTextNode(pr[i]));
							}
							else {
								e[i] = pr[i];
							}
						}
					}
				}
				if(n.childnodes && (l = n.childnodes.length)) {
					for(i = 0; i < l; i++) {
						insertTree(n.childnodes[i], e);
					}
				}
				p.appendChild(e);
			};
			
			var d = document.createDocumentFragment(), i;
			if(elem.constructor === Array) {
				for(i = 0; i < elem.length; i++) {
					insertTree(elem[i], d);
				}
			}
			else {
				insertTree(elem, d);
			}
			return d;
		}
	};

	vxJS.selection = {
		set: function(elem, s, len) {
			var r;
			
			s = s || 0;
			len = len || elem.value.length - s;

			if (typeof elem.setSelectionRange === "function") {
				elem.setSelectionRange(s, s+len);
			}
			else if (/object|function/.test(typeof elem.createTextRange)) {
				r = elem.createTextRange();
				r.moveStart("character", s);
				r.moveEnd("character", s + len - elem.value.length);
				r.select();
			}
			elem.focus();
		},

		get: function() {
		},
		
		replace: function() {
		},
		
		setCaretPosition: function(elem, pos) {
			if (pos === "end") {
				this.set(elem, elem.value.length);
			}
			else {
				this.set(elem, 0, 0);
			}
		}

/*		insertAtSelection: function(elem, txt) {
			var s, e;
			if ((s = document.selection)) {
				s.createRange();
				s.text = txt;
				s.select();
			}
			else if(typeof elem.selectionStart !== "undefined") {
	    		s = elem.selectionStart;
				e = elem.selectionEnd;
	    		elem.value = elem.value.slice(0, s) + txt + elem.value.slice(e);
	    		elem.selectionStart = elem.selectionEnd = s + txt.length;
			}
			else {
				elem.value = txt;
			}
		}
*/	};

	vxJS.isHostMethod = isHostMethod;
	vxJS.beget = beget;
	vxJS.isEmpty = isEmpty;
	vxJS.merge = merge;
})();

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)));
	}
};

Color = function(c) {
	var r, g, b;
	if(!c) {
		this.r = 0;
		this.g = 0;
		this.b = 0;
		return;
	}
	if(/rgb\s*\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*\)/.test(c)) {
		c = c.slice(4, -1).split(",");
		r = +c[0];
		g = +c[1];
		b = +c[2];
	}
	else if(c.slice(0, 1) === "#") {
		if(c.length === 4) {
			r = parseInt(c.slice(1, 2)+c.slice(1, 2), 16);
			g = parseInt(c.slice(2, 3)+c.slice(2, 3), 16);
			b = parseInt(c.slice(3)+c.slice(3), 16);
		}
		else {
			r = parseInt(c.slice(1, 3), 16);
			g = parseInt(c.slice(3, 5), 16);
			b = parseInt(c.slice(5), 16);
		}
	}
	this.r = isNaN(r) || r > 255 ? 255: r;
	this.g = isNaN(g) || g > 255 ? 255: g;
	this.b = isNaN(b) || b > 255 ? 255: b;
};

Color.prototype = {
		toHex: function() {
			return "#"+("00"+Math.round(this.r).toString(16)).slice(-2)+("00"+Math.round(this.g).toString(16)).slice(-2)+("00"+Math.round(this.b).toString(16)).slice(-2);
		},
		toRGB: function() {
			return "rgb("+Math.round(this.r)+","+Math.round(this.g)+","+Math.round(this.b)+")";
		}
};

/**
 * native object and prototype augmentation
 * 
 * Number sign	= Math.sgn(number)
 * String formatted Number = Number.format(Number decimals, String dec_point, String thousands_sep) 
 * 
 * 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 | Date d	= String.toDateTime(locale, return as date object)
 * String str	= String.toUcFirst()
 * 
 * Number days	= Date.getDaysOfMonth()
 */

Math.sgn = function(n){
	n = typeof n !== "number"? parseFloat(n) : n;
	if(isNaN(n)) {
		return NaN;
	}
	return n < 0 ? -1 : 1;
};

Number.prototype.format = function(dec, decPoint, thdSep) {
	var f, p, t = "";

	decPoint = decPoint || ".";
	thdSep = thdSep || ",";
	dec = typeof dec === "number" ? Math.round(dec) : 0;

	f = this.toFixed(dec).toString();
	p = f.split(".");

	if(thdSep) {
		while(p[0].length > 3) {
			t = thdSep + p[0].slice(-3)+t;
			p[0] = p[0].slice(0, -3);
		}
		p[0] = p[0]+t;
	}
	return (this.sgn() < 0 ? "-" : "") + p[0] + (p[1] ? (decPoint + p[1]) : "");
};

Array.prototype.arrayPosition = function(needle) {
	for (var i = this.length; i--;) { if(this[i] === needle) { return i; }}
	return false;
};

Array.prototype.copy = function () { return this.concat(); };

Array.prototype.fill = function(val, cnt) {
	for(; --cnt >= 0;) {
		this.push(val);
	}
	return this;
};

Array.prototype.inArray = function(needle) {
	for (var i = this.length; i--;) { if(this[i] === needle) { return true; }}
	return false;
};

Array.prototype.map = function(f) {
	var i, l = this.length, r = [];
	for (i = 0; i < l; i++) { r.push(f(this[i])); }
	return r;
};

Array.prototype.swap = function(a, b) {
	if(typeof b === "undefined") { b = ++a; }
	if(a < 0 || a >= this.length || b < 0 || b >= this.length) { return; }
	var c = this[a];
	this[a] = this[b];
	this[b] = c;
};

String.prototype.ltrim = function() { return this.replace(/^\s+/, ""); };

String.prototype.rtrim = function() { return this.replace(/\s+$/, ""); };

if(!String.prototype.trim) {
	String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ""); };
}

String.prototype.lpad = function(len, fchar) {
	var i = len-this.length, pad = "", f = fchar || " ";
	while(--i >= 0) { pad += f; }
	return pad+this;
};

String.prototype.rpad = function(len, fchar) {
	var i = len-this.length, pad = "", f = fchar || " ";
	while(--i >= 0) { pad += f; }
	return this+pad;
};

String.prototype.toFloat = function(decSep) {
	var d, f, i;

	d = this.split(decSep || ".");

	if(d.length > 2) {
		return NaN;
	}
	if(d[1] && !(f = parseInt(d[1], 10))) {
		return NaN;
	}
	if(!(i = d[0].replace(/[^0-9]/g, ""))) {
		return NaN;
	}
	return parseFloat(i+"."+f);
};

String.prototype.toDateTime = function(locale, asObj) {
	var del, erg, d, t, m, j, hr, min, sec, s = this.trim();

	locale = locale || "date_de";

	switch (locale) {
		case "date_us":

		case "date_de":
			del = s.match(/^\d{1,2}([\/.\-])\d{1,2}\1\d{0,4}$/);

			if(!del &&  /^([0-9]{4}|[0-9]{6}|[0-9]{8})$/.test(s)) {
				erg = [s.slice(0,2), s.slice(2,4), s.slice(4)];
			}
			else if(del && del.length === 2) {
				erg = s.split(del[1]);
				if(erg.length !== 3){ return false; }
			}
			else					{ return false; }

			d	= [	("00"+erg[0]).slice(-2),
					("00"+erg[1]).slice(-2),
					(""+new Date().getFullYear()).slice(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 = s.match(/^\d{2}(\d{2})?([\/.\-])\d{1,2}\2\d{1,2}$/);

			if(!del && /^([0-9]{6}|[0-9]{8})$/.test(s)) {
				erg = [s.slice(0,2), s.slice(2,4), s.slice(4)];
			}
			else if(del && del.length === 3) {
				erg = s.split(del[2]);
				if(erg.length !== 3){ return false; }
			}
			else					{ return false; }

			d	= [
				(""+new Date().getFullYear()).slice(0, 4-erg[0].length)+erg[0],
				("00"+erg[1]).slice(-2),
				("00"+erg[2]).slice(-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 = s.match(/^\d{1,2}([:.\-])\d{1,2}$/);

			if(!del && /^[0-9]{4}$/.test(s)) {
				erg = [s.slice(0,2), s.slice(2)];
			}
			else if(del && del.length === 2) {			
				erg = s.split(del[1]);
				if(erg.length !== 2){ return false; }
			}
			else 					{ return false; }
			
			if(+erg[0] > 23 || +erg[1] > 59)
									{ return false; }
			return ("00"+erg[0]).slice(-2)+":"+("00"+erg[1]).slice(-2);

		case "time_hms":
			del = s.match(/^\d{1,2}([:.\-])\d{1,2}\1\d{1,2}$/);
			if(!del &&  /^([0-9]{4}|[0-9]{6})$/.test(s)) {
				erg = [s.slice(0,2), s.slice(2,4), s.slice(4)];
			}
			else if(del && del.length === 2) {			
				erg = s.split(del[1]);
				if(erg.length !== 3){ return false; }
			}
			else 					{ return false; }

			hr = +erg[0];
			min = +erg[1];
			sec = +erg[2];

			if(hr > 23 || min > 59 || sec > 59) { return false; }
			return ("00"+hr).slice(-2)+":"+("00"+min).slice(-2)+":"+("00"+sec).slice(-2);
	}
};

String.prototype.toUcFirst = function() {
	return this.slice(0, 1).toUpperCase()+this.slice(1);
};

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;
};

Date.prototype.format = function(format) {
	var	d = ""+this.getDate(),
		m = (this.getMonth()+1),
		y = this.getFullYear(),
		w = this.getCW(),
		z = ""+(w === 1 && m === 12 ? y+1 : w >= 52 && m === 1 ? y-1 : y);

	var c = {
		"%d": d,
		"%D": d.lpad(2, "0"),
		"%m": ""+m,
		"%M": (""+m).lpad(2, "0"),
		"%y": (""+y).slice(-2),
		"%Y": ""+y,
		"%w": ""+w,
		"%W": (""+w).lpad(2, "0"),
		"%z": z,
		"%Z": z.lpad(2, "0")
	};
	return format.replace(/%[dDmMyYwWzZ]{1}/g, function(m) { return c[m]; });	
};

Date.prototype.getAbsoluteDays = function() {
	return Math.floor(0.1 + this.getTime()/864e5);
};

Date.prototype.getCW = function(usStyle) {
	if (!usStyle) {
		var wt = this.getDay() || 7;
		var t = this.getAbsoluteDays();
		var y = new Date(new Date((t + 4 - wt) * 864e5).getFullYear(), 0, -10);
		return Math.floor((t - wt - y.getAbsoluteDays()) / 7);
	}

	var startDays = new Date(this.getFullYear(), 0, 1).getAbsoluteDays();

	if(new Date(this.getFullYear(), 0, 1).getDay() === 0) {
		return Math.floor((this.getAbsoluteDays() - startDays)/7) + 1;
	}
};

/**
 * creates DOM element, adds previously set attributes and adds optional child node(s) or text node
 * e.g. "div".create("p".create("Ich bin ein Absatz"));
 */
String.prototype.create = 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(err) { }
				break;
			}
		}

		for(i = this.attr.length; i--;) {
			a = this.attr[i];
			if(a.name === "class") { e.className = a.value; }
			else if(e[a.name]) { e[a.name] = a.value; }
			else { e.setAttribute(a.name, a.value); }
		}
	}
	return vxJS.dom.appendChildren(e, children);
};

/**
 * sets one or more properties of a yet to create DOM element
 * e.g.: "img".setProp("src","test.jpg");
 * e.g.: "input".setProp[["class", "format"], ["type", "submit"]]);
 * returning a new string object is required by Google Chrome
 */
String.prototype.setProp = function(n, v) {
	var i, s = new String(this);
	if (!s.attr) { s.attr = []; }

	if (arguments.length > 1) {
		s.attr.push({name: n, value: v});
	}
	else if (arguments.length === 1 && n.constructor === Array) {
		for (i = n.length; i--;) {
			s.attr.push({name : n[i][0], value : n[i][1]});
		}
	}
	return s;
};

/**
 * creates DOM elements for each element in array
 * e.g. ["td","td","td"].create();
 */
Array.prototype.create = function(children) {
	var i, e = [];
	for (i = 0; i < this.length; i++) { e[i] = this[i].create(children); }
	return e;
};

/**
 * sets one or more attributes for each element in array
 * e.g. ["td","td"].setProp("style","color:blue");
 */
Array.prototype.setProp = function(n, v) {
	for (var i = this.length; i--;){ this[i] = this[i].setProp(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;
	var 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|tbody|td|textarea|th|title|tr|tt|ul|u|var".split("|");
	if (typeof tag === "string") {
		for (i = 0; i < this.length; i++) {
			if (tags.inArray(this[i]))	{ this[i] = tag.create(this[i].create()); }
			else						{ this[i] = tag.create(this[i]); }
		}
	}
	else {
		for (i = 0; i < this.length; i++) {
			for (j = 0; j < tag.length; j++) { this[i] = tag[j].create(this[i]); }
		}
	}
	return this;
};

/*
var i, o, cnt = 20, sum1 = 0, sum2 = 0, bm, x, y, z;

for(o = cnt; o--;) {
bm = new Date().getTime();
y = "FOO";
x = "foo";
for(i = 50000; i--;) {
	if(x.toUpperCase() === y) {
		
	}
}
sum1 += new Date().getTime()-bm;

bm = new Date().getTime();

y = /foo/i;
x = "foo";
for(i = 50000; i--;) {
//	if(y.test(x)) {
	if(y.test(x)) {
		
	}
}
sum2 += new Date().getTime()-bm;
}
console.log(sum1/cnt, sum2/cnt);
*/