/**
 * core script for vxJS framework
 * 
 * @author Gregor Kofler, info@gregorkofler.at
 * @version 1.2.3 2010-03-23
 * 
 * @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 */

if(!vxJS) {
	var vxJS = {};
}

(function() {
	/* private functions & properties */
	var Rex = {
		isHM: /^(object|function)$/i,
		isNumberOrString: /^(string|number)$/
	};

	var global = win = this,
		doc = this.document,
		html = doc.documentElement;

	var isHostMethod = function(o, m) {
		var t = typeof o[m];
		return !!((o[m] && Rex.isHM.test(t)) || t === "unknown");
	};

	function F() {}
	var beget = function(o) {
		F.prototype = o;
		return new F();
	};

	var isEmpty = function(o) {
		var p;

		if(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(typeof o !== "object" || typeof add !== "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.
	 */
	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, type) {
				var e;
				switch (model) {
					case "W3C":
						e = document.createEvent("Event");
						e.initEvent(type, true, true);
						elem.dispatchEvent(e);
						return;
					case "MS":
						elem.fireEvent("on" + type, document.createEventObject());
						return;
					default:
						elem["on"+type]();
				}
			},

			/**
			 * add listener
			 * @param {Object} DOM or widget 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 || obj === win) {
					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(global.event.srcElement, global.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) {
				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 global.pageXOffset != "undefined") {
					return new Coord(x+global.pageXOffset, y+global.pageYOffset);
				}
				if(body && typeof body.scrollLeft != "undefined") {
					return new Coord(x+body.scrollLeft, y+body.scrollTop);
				}
				return new Coord(x, y);
			},
			
			cancelBubbling: function(e) {
				if(isHostMethod(e, "stopPropagation")) {
					e.stopPropagation();
				}
				e.cancelBubble = true;
			},
			
			preventDefault: function(e) {
				if(isHostMethod(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(elem.filters && typeof elem.filters.Alpha.opacity  !== "undefined") {
				return elem.filters.Alpha.opacity/100;
			}

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

			return 1;
		},

		getStyle: function(elem, styleProp) {
			if(global.getComputedStyle) {
				return global.getComputedStyle( elem, "")[styleProp]; 
			}
			if( elem.currentStyle) {
				return elem.currentStyle[styleProp];
			}
			return false;
		},
		
		appendChildren: function(elem, c) {
			var i;
		
			if (Rex.isNumberOrString.test(typeof c)) {
				elem.appendChild(document.createTextNode(c));
			}
			else if(c) {
				if(typeof c.length !== "undefined" && typeof c.nodeName === "undefined") {
					for (i = 0; i < c.length; i++) {
						if (Rex.isNumberOrString.test(typeof c[i])) {
							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;
			}
		},

		/**
		 * @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;
				}
			}
		},

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

		/**
		 * getViewportSize() liftet from David Mark's "My Library"
		 * http://www.cinsoft.net/mylib.html 
		 */
		getViewportSize: function(win) {
			var getRoot, scrollChecks, vpReady, f;

			if(f) {
				return f(win);
			}

			getRoot = typeof doc.compatMode == "string" ?
				function(w) {
					var doc = w.document, html = doc.documentElement;
					return (html && doc.compatMode.toLowerCase().indexOf("css") != -1) ? html : doc.body;
				} :
				function(w) {
					var doc = w.document, html = doc.documentElement;
					return (!html || html.clientWidth === 0) ? doc.body : html;
				};

			if(html) {
				// Test to resolve ambiguity between HTML and body
				scrollChecks = (function() {
					var oldBorder, body = doc.body, result = { compatMode: doc.compatMode },
						clH = html.clientHeight, bodyClH = body.clientHeight,
						div = doc.createElement("div");

					div.style.height = "100px";
					body.appendChild(div);
					result.body = !clH || clH != html.clientHeight;
					result.html = bodyClH != body.clientHeight;
					body.removeChild(div);
					if (result.body || result.html && (result.body != result.html)) {
						// found single scroller--check if borders should be included
						if (typeof body.clientTop == "number" && body.clientTop) {
							oldBorder = body.style.borderWidth;
							body.style.borderWidth = "0px";
							result.includeBordersInBody = body.clientHeight != bodyClH;
							body.style.borderWidth = oldBorder;
						}
						return result;
					}
				})();
			}

			if(typeof doc.clientWidth == "number") {

				f = function(w) {
					var doc = (w || window).document;
					return new Coord(doc.clientWidth, doc.clientHeight);
				};

			}

			else if(html && typeof html.clientWidth == 'number') {
				if(typeof window.innerHeight == 'number') {

					f = function(w) {
						if (!w) {
							w = window;
						}

						var	root = getRoot(w), doc = w.document,
							clH, clW;

						if (scrollChecks) {
							root = scrollChecks.body ? doc.body : doc.documentElement;
						}

						clH = root.clientHeight;
						clW = root.clientWidth;

						if (scrollChecks && scrollChecks.body && scrollChecks.includeBordersInBody) {

							// Add body borders
							clH += doc.body.clientTop * 2;
							clW += doc.body.clientLeft * 2;
						}

						return new Coord(clW, clH);
					};

				}
				else {

					// Unassisted clientWidth/clientHeight (IE always takes this fork)
					// MS invented these properties
					f = function(w) {
						if (!w) {
							w = window;
						}
						var root = getRoot(w);
						return new Coord(root.clientWidth, root.clientHeight);
					};

				}
			}

			else if(typeof window.innerWidth == "number") {

				// Last resort, return innerWidth/Height
				// NOTE: May include space occupied by scroll bars

				f = function(w) {
					if (!w) {
						w = window;
					}
					return new Coord(w.innerWidth, w.innerHeight);
				};
			}
			
			return f(win);
		}
	};

	vxJS.selection = {
		getSelection: (function() {
			if(isHostMethod(global, "getSelection")) {
				return function() { return global.getSelection(); };
			}
			else if(isHostMethod(doc, "selection")) {
				return doc.selection;
			}
			else {
				return null;
			}})(),

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

			if (isHostMethod(elem, "setSelectionRange")) {
				elem.setSelectionRange(s, s+len);
			}
			else if (isHostMethod(elem, "createTextRange")) {
				r = elem.createTextRange();
				r.moveStart("character", s);
				r.moveEnd("character", s + len - elem.value.length);
				r.select();
			}
			elem.focus();
		},

		get: function(elem) {
			var r, s;

			if(typeof elem.selectionStart !== "undefined") {
				return elem.value.substring(elem.selectionStart, elem.selectionEnd);
			}
			else {
				s = this.getSelection();
				if(s && isHostMethod(s, "createRange")) {
					r = s.createRange();
					if(r.parentElement == elem) {
						return r.text;
					}
				}
				else {
					return '';
				}
			}
		},

		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, a;

	if(!c) {
		this.r = 0;
		this.g = 0;
		this.b = 0;
		this.a = 1;
		return;
	}
	if(/rgb\s*\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*(?:,\s(?:1|0)(?:\.\d+)?\s*)?\)/.test(c)) {
		c = c.slice(c.indexOf("(")+1, -1).split(",");
		r = +c[0];
		g = +c[1];
		b = +c[2];
		a = +c[3];
	}
	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, 7), 16);
			a = parseInt(c.slice(7), 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;
	this.a = isNaN(a) || a > 1 ? 1: a;
};

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
 * 
 * Function.bind(newContext)
 * 
 * Number sign	= Math.sgn(number)
 * 
 * String formatted Number = Number.toFormattedString(Number decimals, String dec_point, String thousands_sep)
 * (locale-free alternative for Number.toLocaleString()) 
 * 
 * bool result	= Array.inArray(needle)
 * array arr	= Array.copy()
 * array arr	= Array.fill(value, count)
 * 
 * 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()
 */

if(typeof Function.bind !== "function") {
	Function.prototype.bind = function(that) {

		var f = this, args;

		if(arguments.length > 1) {

			args = Array.prototype.slice.call(arguments, 1);

			return function() {
				return f.apply(that, !arguments.length ? args : Array.prototype.concat.apply(args, arguments));
			};
		}
		else {
			return function() {
				return arguments.length === 1 ? f.apply(that, arguments) : f.call(that);
			};
		}
	};
}

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

Number.prototype.toFormattedString = 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 (Math.sgn(this) < 0 ? "-" : "") + p[0] + (p[1] ? (decPoint + p[1]) : "");
};

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

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

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(a.name === "type") {
				e.setAttribute(a.name, a.value);
			}
			else {
				e[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".setProp("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;
};