/*
	TableSort revisited v4.9 by frequency-decoder.com

	Released under a creative commons Attribution-ShareAlike 2.5 license (http://creativecommons.org/licenses/by-sa/2.5/)

	Please credit frequency decoder in any derivative work - thanks

	You are free:

	* to copy, distribute, display, and perform the work
	* to make derivative works
	* to make commercial use of the work

	Under the following conditions:

		by Attribution.
		--------------
		You must attribute the work in the manner specified by the author or licensor.

		sa
		--
		Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under a license identical to this one.

	* For any reuse or distribution, you must make clear to others the license terms of this work.
	* Any of these conditions can be waived if you get permission from the copyright holder.
*/

(function() {
fdTableSort = {
	regExp_Currency:	/^[Â£$â‚¬Â¥Â¤]/,
	regExp_Number:	  /^(\-)?[0-9]+(\.[0-9]*)?$/,
	pos:		    -1,
	uniqueHash:	     1,
	thNode:		 null,
	tableId:		null,
	tableCache:	     {},
	tmpCache:	       {},
	sortActiveClass:	"sort-active",
	/*@cc_on
	/*@if (@_win32)
	colspan:		"colSpan",
	rowspan:		"rowSpan",
	@else @*/
	colspan:		"colspan",
	rowspan:		"rowspan",
	/*@end
	@*/
	addEvent: function(obj, type, fn, tmp) {
		tmp || (tmp = true);
		if( obj.attachEvent ) {
			obj["e"+type+fn] = fn;
			obj[type+fn] = function(){obj["e"+type+fn]( window.event );};
			obj.attachEvent( "on"+type, obj[type+fn] );
		} else {
			obj.addEventListener( type, fn, true );
		};
	},
	removeEvent: function(obj, type, fn, tmp) {
		tmp || (tmp = true);
		try {
			if( obj.detachEvent ) {
				obj.detachEvent( "on"+type, obj[type+fn] );
				obj[type+fn] = null;
			} else {
				obj.removeEventListener( type, fn, true );
			};
		} catch(err) {};
	},
	stopEvent: function(e) {
		e = e || window.event;

		if(e.stopPropagation) {
			e.stopPropagation();
			e.preventDefault();
		};
		/*@cc_on@*/
		/*@if(@_win32)
		e.cancelBubble = true;
		e.returnValue  = false;
		/*@end@*/
		return false;
	},
	parseClassName: function(head, tbl) {
		var colMatch = tbl.className.match(new RegExp(head + "((-[\\d]+([r]){0,1})+)"));
		return colMatch && colMatch.length ? colMatch[0].replace(head, "").split("-") : [];
	},
	disableSelection: function(element) {
		element.onselectstart = function() {
			return false;
		};
		element.unselectable = "on";
		element.style.MozUserSelect = "none";
	},
	removeTableCache: function(tableId) {
		if(!(tableId in fdTableSort.tableCache)) return;

		fdTableSort.tableCache[tableId] = null;
		delete fdTableSort.tableCache[tableId];

		var tbl = document.getElementById(tableId);
		if(!tbl) return;
		var ths = tbl.getElementsByTagName("th");
		var a;
		for(var i = 0, th; th = ths[i]; i++) {
			a = th.getElementsByTagName("a");
			if(a.length) a[0].onkeydown = a[0].onclick = null;
			th.onclick = th.onselectstart = th = a = null;
		};
	},
	removeTmpCache: function(tableId) {
		if(!(tableId in fdTableSort.tmpCache)) return;
		var headers = fdTableSort.tmpCache[tableId].headers;
		var a;
		for(var i = 0, row; row = headers[i]; i++) {
			for(var j = 0, th; th = row[j]; j++) {
				a = th.getElementsByTagName("a");
				if(a.length) a[0].onkeydown = a[0].onclick = null;
				th.onclick = th.onselectstart = th = a = null;
			};
		};
		fdTableSort.tmpCache[tableId] = null;
		delete fdTableSort.tmpCache[tableId];
	},
	initEvt: function(e) {
		fdTableSort.init(false);
	},
	init: function(tableId) {
		if (!document.getElementsByTagName || !document.createElement || !document.getElementById) return;

		var tables = tableId && document.getElementById(tableId) ? [document.getElementById(tableId)] : document.getElementsByTagName("table");
		var c, ii, len, colMatch, showOnly, match, showArrow, columnNumSortObj, obj, workArr, headers, thtext, aclone, multi, colCnt, cel, allRowArr, rowArr, sortableTable, celCount, colspan, rowspan, rowLength;

		var a	  = document.createElement("a");
		a.href	 = "#";
		a.className    = "fdTableSortTrigger";

		var span       = document.createElement("span");

		for(var k = 0, tbl; tbl = tables[k]; k++) {

			if(tbl.id) {
				fdTableSort.removeTableCache(tbl.id);
				fdTableSort.removeTmpCache(tbl.id);
			};

			allRowArr     = tbl.getElementsByTagName('thead').length ? tbl.getElementsByTagName('thead')[0].getElementsByTagName('tr') : tbl.getElementsByTagName('tr');
			rowArr	= [];
			sortableTable = false;

			for(var i = 0, tr; tr = allRowArr[i]; i++) {
				if(tr.getElementsByTagName('td').length || !tr.getElementsByTagName('th').length) { continue; };
				rowArr[rowArr.length] = tr.getElementsByTagName('th');
				for(var j = 0, th; th = rowArr[rowArr.length - 1][j]; j++) {
					if(th.className.search(/sortable/) != -1) { sortableTable = true; };
				};
			};

			if(!sortableTable) continue;

			if(!tbl.id) { tbl.id = "fd-table-" + fdTableSort.uniqueHash++; };

			showArrow   = tbl.className.search("no-arrow") == -1;
			showOnly    = tbl.className.search("sortable-onload-show") != -1;

			columnNumSortObj = {};
			colMatch	 = fdTableSort.parseClassName(showOnly ? "sortable-onload-show" : "sortable-onload", tbl);
			for(match = 1; match < colMatch.length; match++) {
				columnNumSortObj[parseInt(colMatch[match], 10)] = { "reverse":colMatch[match].search("r") != -1 };
			};

			rowLength = rowArr[0].length;

			for(c = 0;c < rowArr[0].length;c++){
				if(rowArr[0][c].getAttribute(fdTableSort.colspan) && rowArr[0][c].getAttribute(fdTableSort.colspan) > 1){
					rowLength = rowLength + (rowArr[0][c].getAttribute(fdTableSort.colspan) - 1);
				};
			};

			workArr = new Array(rowArr.length);
			for(c = rowArr.length;c--;){ workArr[c]= new Array(rowLength); };

			for(c = 0;c < workArr.length;c++){
				celCount = 0;
				for(i = 0;i < rowLength;i++){
					if(!workArr[c][i]){
						cel = rowArr[c][celCount];
						colspan = (cel.getAttribute(fdTableSort.colspan) > 1) ? cel.getAttribute(fdTableSort.colspan):1;
						rowspan = (cel.getAttribute(fdTableSort.rowspan) > 1) ? cel.getAttribute(fdTableSort.rowspan):1;
						for(var t = 0;((t < colspan)&&((i+t) < rowLength));t++){
							for(var n = 0;((n < rowspan)&&((c+n) < workArr.length));n++) {
								workArr[(c+n)][(i+t)] = cel;
							};
						};
						if(++celCount == rowArr[c].length) break;
					};
				};
			};

			for(c = 0;c < workArr.length;c++) {
				for(i = 0;i < workArr[c].length;i++){

					if(workArr[c][i].className.search("fd-column-") == -1 && workArr[c][i].className.search("sortable") != -1) workArr[c][i].className = workArr[c][i].className + " fd-column-" + i;

					if(workArr[c][i].className.match('sortable')) {
						workArr[c][i].className = workArr[c][i].className.replace(/forwardSort|reverseSort/, "");

						if(i in columnNumSortObj) {
							columnNumSortObj[i]["thNode"] = workArr[c][i];
							columnNumSortObj["active"] = true;
						};

						thtext = fdTableSort.getInnerText(workArr[c][i]);

						for(var cn = workArr[c][i].childNodes.length; cn--;) {
							// Skip image nodes and links created by the filter script.
							if(workArr[c][i].childNodes[cn].nodeType == 1 && (workArr[c][i].childNodes[cn].className == "fdFilterTrigger" || /img/i.test(workArr[c][i].childNodes[cn].nodeName))) {
								continue;
							};
							if(workArr[c][i].childNodes[cn].nodeType == 1 && /^a$/i.test(workArr[c][i].childNodes[cn].nodeName)) {
								workArr[c][i].childNodes[cn].onclick = workArr[c][i].childNodes[cn].onkeydown = null;
							};
							workArr[c][i].removeChild(workArr[c][i].childNodes[cn]);
						};

						aclone = a.cloneNode(true);
						aclone.appendChild(document.createTextNode(thtext));
						aclone.title = "Sort on \u201c" + thtext + "\u201d";
						aclone.onclick = aclone.onkeydown = workArr[c][i].onclick = fdTableSort.initWrapper;
						workArr[c][i].appendChild(aclone);
						if(showArrow) workArr[c][i].appendChild(span.cloneNode(false));
						workArr[c][i].className = workArr[c][i].className.replace(/fd-identical|fd-not-identical/, "");
						fdTableSort.disableSelection(workArr[c][i]);
						aclone = null;
					};
				};
			};

			fdTableSort.tmpCache[tbl.id] = {cols:rowLength, headers:workArr};

			workArr = null;
			multi   = 0;

			if("active" in columnNumSortObj) {
				fdTableSort.tableId = tbl.id;
				fdTableSort.prepareTableData(document.getElementById(fdTableSort.tableId));

				delete columnNumSortObj["active"];

				for(col in columnNumSortObj) {
					obj = columnNumSortObj[col];
					if(!("thNode" in obj)) { continue; };
					fdTableSort.multi = true;

					len = obj.reverse ? 2 : 1;

					for(ii = 0; ii < len; ii++) {
						fdTableSort.thNode = obj.thNode;
						if(!showOnly) {
							fdTableSort.initSort(false, true);
						} else {
							fdTableSort.addThNode();
						};
					};

					if(showOnly) {
						fdTableSort.removeClass(obj.thNode, "(forwardSort|reverseSort)");
						fdTableSort.addClass(obj.thNode, obj.reverse ? "reverseSort" : "forwardSort");
						if(showArrow) {
							span = fdTableSort.thNode.getElementsByTagName('span')[0];
							if(span.firstChild) { span.removeChild(span.firstChild); };
							span.appendChild(document.createTextNode(len == 1 ? " \u2193" : " \u2191"));
						};
					};
				};
				if(showOnly && (fdTableSort.tableCache[tbl.id].colStyle || fdTableSort.tableCache[tbl.id].rowStyle)) {
					fdTableSort.redraw(tbl.id, false);
				};
			} else if(tbl.className.search(/onload-zebra/) != -1) {
				fdTableSort.tableId = tbl.id;
				fdTableSort.prepareTableData(tbl);
				if(fdTableSort.tableCache[tbl.id].rowStyle) { fdTableSort.redraw(tbl.id, false); };
			};
		};

		fdTableSort.thNode = aclone = a = span = columnNumSortObj = thNode = tbl = allRowArr = rowArr = null;
	},
	initWrapper: function(e) {
		e = e || window.event;
		var kc = e.type == "keydown" ? e.keyCode != null ? e.keyCode : e.charCode : -1;
		if(fdTableSort.thNode == null && (e.type == "click" || kc == 13)) {
			var targ = this;
			while(targ.tagName.toLowerCase() != "th") { targ = targ.parentNode; };
			fdTableSort.thNode = targ;
			while(targ.tagName.toLowerCase() != "table") { targ = targ.parentNode; };
			fdTableSort.tableId = targ.id;
			fdTableSort.multi = e.shiftKey;
			fdTableSort.addSortActiveClass();
			setTimeout(fdTableSort.initSort,5,false);
			return fdTableSort.stopEvent(e);
		};
		return kc != -1 ? true : fdTableSort.stopEvent(e);
	},
	jsWrapper: function(tableid, colNums) {
		if(!(tableid in fdTableSort.tmpCache)) { return false; };
		if(!(tableid in fdTableSort.tableCache)) { fdTableSort.prepareTableData(document.getElementById(tableid)); };
		if(!(colNums instanceof Array)) { colNums = [colNums]; };

		fdTableSort.tableId = tableid;
		var len = colNums.length, colNum;

		if(fdTableSort.tableCache[tableid].thList.length == colNums.length) {
			var identical = true;
			var th;
			for(var i = 0; i < len; i++) {
				colNum = colNums[i];
				th = fdTableSort.tmpCache[tableid].headers[0][colNum];
				if(th != fdTableSort.tableCache[tableid].thList[i]) {
					identical = false;
					break;
				};
			};
			if(identical) {
				fdTableSort.thNode = th;
				fdTableSort.initSort(true);
				return;
			};
		};

		fdTableSort.addSortActiveClass();

		for(var i = 0; i < len; i++) {
			fdTableSort.multi = i;
			colNum = colNums[i];
			fdTableSort.thNode = fdTableSort.tmpCache[tableid].headers[0][colNum];
			fdTableSort.initSort(true);
		};
	},
	addSortActiveClass: function() {
		if(fdTableSort.thNode == null) { return; };
		fdTableSort.addClass(fdTableSort.thNode, fdTableSort.sortActiveClass);
		fdTableSort.addClass(document.getElementsByTagName('body')[0], fdTableSort.sortActiveClass);
	},
	removeSortActiveClass: function() {
		if(fdTableSort.thNode == null) return;
		fdTableSort.removeClass(fdTableSort.thNode, fdTableSort.sortActiveClass);
		fdTableSort.removeClass(document.getElementsByTagName('body')[0], fdTableSort.sortActiveClass);
	},
	doCallback: function(init) {
		if(!fdTableSort.tableId || !(fdTableSort.tableId in fdTableSort.tableCache)) { return; };
		fdTableSort.callback(fdTableSort.tableId, init ? fdTableSort.tableCache[fdTableSort.tableId].initiatedCallback : fdTableSort.tableCache[fdTableSort.tableId].completeCallback);
	},
	addClass: function(e,c) {
		if (typeof e == 'undefined') return;
		if(new RegExp("(^|\\s)" + c + "(\\s|$)").test(e.className)) { return; };
		e.className += ( e.className ? " " : "" ) + c;
	},
	/*@cc_on
	/*@if (@_win32)
	removeClass: function(e,c) {
		if (typeof e == 'undefined') return;
		e.className = !c ? "" : e.className.replace(new RegExp("(^|\\s)" + c + "(\\s|$)"), " ").replace(/^\s*((?:[\S\s]*\S)?)\s*$/, '$1');
	},
	@else @*/
	removeClass: function(e,c) {
		if (typeof e == 'undefined') return;
		e.className = !c ? "" : e.className.replace(new RegExp("(^|\\s)" + c + "(\\s|$)"), " ").replace(/^\s\s*/, '').replace(/\s\s*$/, '');
	},
	/*@end
	@*/
	callback: function(tblId, cb) {
		var func;
		if(cb.indexOf(".") != -1) {
			var split = cb.split(".");
			func = window;
			for(var i = 0, f; f = split[i]; i++) {
				if(f in func) {
					func = func[f];
				} else {
					func = "";
					break;
				};
			};
		} else if(cb + tblId in window) {
			func = window[cb + tblId];
		} else if(cb in window) {
			func = window[cb];
		};
		if(typeof func == "function") { func(tblId, fdTableSort.tableCache[tblId].thList); };
		func = null;
	},
	prepareTableData: function(table) {
		var data = [];

		var start = table.getElementsByTagName('tbody');
		start = start.length ? start[0] : table;

		var trs = start.rows;
		var ths = table.getElementsByTagName('th');

		var numberOfRows = trs.length;
		var numberOfCols = fdTableSort.tmpCache[table.id].cols;

		var data = [];
		var identical = new Array(numberOfCols);
		var identVal  = new Array(numberOfCols);

		for(var tmp = 0; tmp < numberOfCols; tmp++) identical[tmp] = true;

		var tr, td, th, txt, tds, col, row;

		var re = new RegExp(/fd-column-([0-9]+)/);
		var rowCnt = 0;

		var sortableColumnNumbers = [];

		for(var tmp = 0, th; th = ths[tmp]; tmp++) {
			if(th.className.search(re) == -1) continue;
			sortableColumnNumbers[sortableColumnNumbers.length] = th;
		};

		for(row = 0; row < numberOfRows; row++) {

			tr	      = trs[row];
			if(tr.parentNode != start || tr.getElementsByTagName("th").length || (tr.parentNode && tr.parentNode.tagName.toLowerCase().search(/thead|tfoot/) != -1)) continue;
			data[rowCnt]    = [];
			tds	     = tr.cells;

			for(var tmp = 0, th; th = sortableColumnNumbers[tmp]; tmp++) {
				col = th.className.match(re)[1];

				td  = tds[col];
				txt = fdTableSort.getInnerText(td) + " ";
				txt = txt.replace(/^\s+/,'').replace(/\s+$/,'');

				if(th.className.search(/sortable-date/) != -1) {
					txt = fdTableSort.dateFormat(txt, th.className.search(/sortable-date-dmy/) != -1);
				} else if(th.className.search(/sortable-numeric|sortable-currency/) != -1) {
					txt = parseFloat(txt.replace(/[^0-9\.\-]/g,''));
					if(isNaN(txt)) txt = "";
				} else if(th.className.search(/sortable-text/) != -1) {
					txt = txt.toLowerCase();
				} else if (th.className.search(/sortable-keep/) != -1) {
					txt = rowCnt;
				} else if(th.className.search(/sortable-([a-zA-Z\_]+)/) != -1) {
					if((th.className.match(/sortable-([a-zA-Z\_]+)/)[1] + "PrepareData") in window) {
						txt = window[th.className.match(/sortable-([a-zA-Z\_]+)/)[1] + "PrepareData"](td, txt);
					};
				} else if(txt != "") {
					fdTableSort.removeClass(th, "sortable");
					if(fdTableSort.dateFormat(txt) != 0) {
						fdTableSort.addClass(th, "sortable-date");
						txt = fdTableSort.dateFormat(txt);
					} else if(txt.search(fdTableSort.regExp_Number) != -1 || txt.search(fdTableSort.regExp_Currency) != -1) {
						fdTableSort.addClass(th, "sortable-numeric");
						txt = parseFloat(txt.replace(/[^0-9\.\-]/g,''));
						if(isNaN(txt)) txt = "";
					} else {
						fdTableSort.addClass(th, "sortable-text");
						txt = txt.toLowerCase();
					};
				};

				if(rowCnt > 0 && identical[col] && identVal[col] != txt) { identical[col] = false; };

				identVal[col]     = txt;
				data[rowCnt][col] = txt;
			};
			data[rowCnt][numberOfCols] = tr;
			rowCnt++;
		};

		var colStyle = table.className.search(/colstyle-([\S]+)/) != -1 ? table.className.match(/colstyle-([\S]+)/)[1] : false;
		var rowStyle = table.className.search(/rowstyle-([\S]+)/) != -1 ? table.className.match(/rowstyle-([\S]+)/)[1] : false;
		var iCBack   = table.className.search(/sortinitiatedcallback-([\S-]+)/) == -1 ? "sortInitiatedCallback" : table.className.match(/sortinitiatedcallback-([\S]+)/)[1];
		var cCBack   = table.className.search(/sortcompletecallback-([\S-]+)/) == -1 ? "sortCompleteCallback" : table.className.match(/sortcompletecallback-([\S]+)/)[1];
		iCBack = iCBack.replace("-", ".");
		cCBack = cCBack.replace("-", ".");
		fdTableSort.tableCache[table.id] = { hook:start, initiatedCallback:iCBack, completeCallback:cCBack, thList:[], colOrder:{}, data:data, identical:identical, colStyle:colStyle, rowStyle:rowStyle, noArrow:table.className.search(/no-arrow/) != -1 };
		sortableColumnNumbers = data = tr = td = th = trs = identical = identVal = null;
	},
	onUnload: function() {
		for(tbl in fdTableSort.tableCache) { fdTableSort.removeTableCache(tbl); };
		for(tbl in fdTableSort.tmpCache) { fdTableSort.removeTmpCache(tbl); };
		fdTableSort.removeEvent(window, "load", fdTableSort.initEvt);
		fdTableSort.removeEvent(window, "unload", fdTableSort.onUnload);
		fdTableSort.tmpCache = fdTableSort.tableCache = null;
	},
	addThNode: function() {
		var dataObj = fdTableSort.tableCache[fdTableSort.tableId];
		var pos     = fdTableSort.thNode.className.match(/fd-column-([0-9]+)/)[1];
		var alt     = false;

		if(!fdTableSort.multi) {
			if(dataObj.colStyle) {
				var len = dataObj.thList.length;
				for(var i = 0; i < len; i++) {
					dataObj.colOrder[dataObj.thList[i].className.match(/fd-column-([0-9]+)/)[1]] = false;
				};
			};
			if(dataObj.thList.length && dataObj.thList[0] == fdTableSort.thNode) alt = true;
			dataObj.thList = [];
		};

		var found = false;
		var l = dataObj.thList.length;

		for(var i = 0, n; n = dataObj.thList[i]; i++) {
			if(n == fdTableSort.thNode) {
				found = true;
				break;
			};
		};

		if(!found) {
			dataObj.thList.push(fdTableSort.thNode);
			if(dataObj.colStyle) { dataObj.colOrder[pos] = true; };
		};

		var ths = document.getElementById(fdTableSort.tableId).getElementsByTagName("th");
		for(var i = 0, th; th = ths[i]; i++) {
			found = false;
			for(var z = 0, n; n = dataObj.thList[z]; z++) {
				if(n == th) {
					found = true;
					break;
				};
			};
			if(!found) {
				fdTableSort.removeClass(th, "(forwardSort|reverseSort)");
				if(!dataObj.noArrow) {
					span = th.getElementsByTagName('span');
					if(span.length) {
						span = span[0];
						while(span.firstChild) span.removeChild(span.firstChild);
					};
				};
			};
		};

		if(dataObj.thList.length > 1) {
			classToAdd = fdTableSort.thNode.className.search(/forwardSort/) != -1 ? "reverseSort" : "forwardSort";
			fdTableSort.removeClass(fdTableSort.thNode, "(forwardSort|reverseSort)");
			fdTableSort.addClass(fdTableSort.thNode, classToAdd);
			dataObj.pos = -1
		} else if(alt) { dataObj.pos = fdTableSort.thNode };
	},
	initSort: function(noCallback, ident) {
		var thNode      = fdTableSort.thNode;
		var tableElem   = document.getElementById(fdTableSort.tableId);

		if(!(fdTableSort.tableId in fdTableSort.tableCache)) { fdTableSort.prepareTableData(document.getElementById(fdTableSort.tableId)); };

		fdTableSort.addThNode();

		if(!noCallback) { fdTableSort.doCallback(true); };

		fdTableSort.pos = thNode.className.match(/fd-column-([0-9]+)/)[1];
		var dataObj     = fdTableSort.tableCache[tableElem.id];
		var lastPos     = dataObj.pos && dataObj.pos.className ? dataObj.pos.className.match(/fd-column-([0-9]+)/)[1] : -1;
		var len1	= dataObj.data.length;
		var len2	= dataObj.data.length > 0 ? dataObj.data[0].length - 1 : 0;
		var identical   = dataObj.identical[fdTableSort.pos];
		var classToAdd  = "forwardSort";

		if(dataObj.thList.length > 1) {
			var js  = "var sortWrapper = function(a,b) {\n";
			var l   = dataObj.thList.length;
			var cnt = 0;
			var e,d,th,p,f;

			for(var i=0; i < l; i++) {
				th = dataObj.thList[i];
				p  = th.className.match(/fd-column-([0-9]+)/)[1];
				if(dataObj.identical[p]) { continue; };
				cnt++;

				if(th.className.match(/sortable-(numeric|currency|date|keep)/)) {
					f = "fdTableSort.sortNumeric";
				} else if(th.className.match('sortable-text')) {
					f = "fdTableSort.sortText";
				} else if(th.className.search(/sortable-([a-zA-Z\_]+)/) != -1 && th.className.match(/sortable-([a-zA-Z\_]+)/)[1] in window) {
					f = "window['" + th.className.match(/sortable-([a-zA-Z\_]+)/)[1] + "']";
				} else  f = "fdTableSort.sortText";

				e = "e" + i;
				d = th.className.search('forwardSort') != -1 ? "a,b" : "b,a";
				js += "fdTableSort.pos   = " + p + ";\n";
				js += "var " + e + " = "+f+"(" + d +");\n";
				js += "if(" + e + ") return " + e + ";\n";
				js += "else { \n";
			};

			js += "return 0;\n";

			for(var i=0; i < cnt; i++) {
				js += "};\n";
			};

			if(cnt) js += "return 0;\n";
			js += "};\n";

			eval(js);
			dataObj.data.sort(sortWrapper);
			identical = false;
		} else if((lastPos == fdTableSort.pos && !identical) || (thNode.className.search(/sortable-keep/) != -1 && lastPos == -1)) {
			dataObj.data.reverse();
			classToAdd = thNode.className.search(/reverseSort/) != -1 ? "forwardSort" : "reverseSort";
			if(thNode.className.search(/sortable-keep/) != -1 && lastPos == -1) fdTableSort.tableCache[tableElem.id].pos = thNode;
		} else {
			fdTableSort.tableCache[tableElem.id].pos = thNode;
			classToAdd = thNode.className.search(/forwardSort/) != -1 ? "reverseSort" : "forwardSort";
			if(!identical) {
				if(thNode.className.match(/sortable-(numeric|currency|date|keep)/)) {
					dataObj.data.sort(fdTableSort.sortNumeric);
				} else if(thNode.className.match('sortable-text')) {
					dataObj.data.sort(fdTableSort.sortText);
				} else if(thNode.className.search(/sortable-([a-zA-Z\_]+)/) != -1 && thNode.className.match(/sortable-([a-zA-Z\_]+)/)[1] in window) {
					dataObj.data.sort(window[thNode.className.match(/sortable-([a-zA-Z\_]+)/)[1]]);
				};

				if(thNode.className.search(/(^|\s)favour-reverse($|\s)/) != -1) {
					classToAdd = classToAdd == "forwardSort" ? "reverseSort" : "forwardSort";
					dataObj.data.reverse();
				};
			};
		};
		if(ident) { identical = false; };
		if(dataObj.thList.length == 1) {
			fdTableSort.removeClass(thNode, "(forwardSort|reverseSort)");
			fdTableSort.addClass(thNode, classToAdd);
		};
		if(!dataObj.noArrow) {
			var span = fdTableSort.thNode.getElementsByTagName('span')[0];
			if(span.firstChild) span.removeChild(span.firstChild);
			span.appendChild(document.createTextNode(fdTableSort.thNode.className.search(/forwardSort/) != -1 ? " \u2193" : " \u2191"));
		};
		if(!dataObj.rowStyle && !dataObj.colStyle && identical) {
			fdTableSort.removeSortActiveClass();
			if(!noCallback) { fdTableSort.doCallback(false); };
			fdTableSort.thNode = null;
			return;
		};
		if("tablePaginater" in window && "tableInfo" in tablePaginater && fdTableSort.tableId in tablePaginater.tableInfo) {
			tablePaginater.redraw(fdTableSort.tableId, identical);
		} else {
			fdTableSort.redraw(fdTableSort.tableId, identical);
		};
		fdTableSort.removeSortActiveClass();
		if(!noCallback) { fdTableSort.doCallback(false); };
		fdTableSort.thNode = null;
	},
	redraw: function(tableid, identical) {
		if(!tableid || !(tableid in fdTableSort.tableCache)) { return; };
		var dataObj     = fdTableSort.tableCache[tableid];
		var data	= dataObj.data;
		var len1	= data.length;
		var len2	= len1 ? data[0].length - 1 : 0;
		var hook	= dataObj.hook;
		var colStyle    = dataObj.colStyle;
		var rowStyle    = dataObj.rowStyle;
		var colOrder    = dataObj.colOrder;
		var highLight   = 0;
		var reg	 = /(^|\s)invisibleRow(\s|$)/;
		var tr, tds;

		for(var i = 0; i < len1; i++) {
			tr = data[i][len2];
			if(colStyle) {
				tds = tr.cells;
				for(thPos in colOrder) {
					if(!colOrder[thPos]) fdTableSort.removeClass(tds[thPos], colStyle);
					else fdTableSort.addClass(tds[thPos], colStyle);
				};
			};
			if(!identical) {
				if(rowStyle && tr.className.search(reg) == -1) {
					if(highLight++ & 1) fdTableSort.addClass(tr, rowStyle);
					else fdTableSort.removeClass(tr, rowStyle);
				};

				// Netscape 8.1.2 requires the removeChild call or it freaks out, so add the line if you want to support this browser
				// hook.removeChild(tr);
				hook.appendChild(tr);
			};
		};
		tr = tds = hook = null;
	},
	getInnerText: function(el) {
		if (typeof el == "string" || typeof el == "undefined") return el;
		if(el.innerText) return el.innerText;
		var txt = '', i;
		for(i = el.firstChild; i; i = i.nextSibling) {
			if(i.nodeType == 3)	    txt += i.nodeValue;
			else if(i.nodeType == 1)       txt += fdTableSort.getInnerText(i);
		};
		return txt;
	},
	dateFormat: function(dateIn, favourDMY) {
		var dateTest = [
			{ regExp:/^(0?[1-9]|1[012])([- \/.])(0?[1-9]|[12][0-9]|3[01])([- \/.])((\d\d)?\d\d)$/, d:3, m:1, y:5 },  // mdy
			{ regExp:/^(0?[1-9]|[12][0-9]|3[01])([- \/.])(0?[1-9]|1[012])([- \/.])((\d\d)?\d\d)$/, d:1, m:3, y:5 },  // dmy
			{ regExp:/^(\d\d\d\d)([- \/.])(0?[1-9]|1[012])([- \/.])(0?[1-9]|[12][0-9]|3[01])$/, d:5, m:3, y:1 }      // ymd
			];
		var start, cnt = 0, numFormats = dateTest.length;
		while(cnt < numFormats) {
			start = (cnt + (favourDMY ? numFormats + 1 : numFormats)) % numFormats;
			if(dateIn.match(dateTest[start].regExp)) {
				res = dateIn.match(dateTest[start].regExp);
				y = res[dateTest[start].y];
				m = res[dateTest[start].m];
				d = res[dateTest[start].d];
				if(m.length == 1) m = "0" + String(m);
				if(d.length == 1) d = "0" + String(d);
				if(y.length != 4) y = (parseInt(y) < 50) ? "20" + String(y) : "19" + String(y);

				return y+String(m)+d;
			};
			cnt++;
		};
		return 0;
	},
	sortNumeric:function(a,b) {
		var aa = a[fdTableSort.pos];
		var bb = b[fdTableSort.pos];
		if(aa == bb) return 0;
		if(aa === "" && !isNaN(bb)) return -1;
		if(bb === "" && !isNaN(aa)) return 1;
		return aa - bb;
	},
	sortText:function(a,b) {
		var aa = a[fdTableSort.pos];
		var bb = b[fdTableSort.pos];
		if(aa == bb) return 0;
		if(aa < bb)  return -1;
		return 1;
	}
};
})();
fdTableSort.addEvent(window, "load",   fdTableSort.initEvt);
fdTableSort.addEvent(window, "unload", fdTableSort.onUnload);

/* CUSTOM SORTS */
/* IMAGE */
var sortImage = fdTableSort.sortText;
function sortImagePrepareData(td, innerText) {
	if (!td) return '';
	var img = td.getElementsByTagName('img');
	return img.length ? img[0].src: "";
}





/*
	paginate table object v1.6 by frequency-decoder.com

	Released under a creative commons Attribution-ShareAlike 2.5 license (http://creativecommons.org/licenses/by-sa/2.5/)

	Please credit frequency decoder in any derivative work - thanks

	You are free:

	* to copy, distribute, display, and perform the work
	* to make derivative works
	* to make commercial use of the work

	Under the following conditions:

		by Attribution.
		--------------
		You must attribute the work in the manner specified by the author or licensor.

		sa
		--
		Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under a license identical to this one.

	* For any reuse or distribution, you must make clear to others the license terms of this work.
	* Any of these conditions can be waived if you get permission from the copyright holder.
*/
tablePaginater = {
	tableInfo: {},
	uniqueID:0,
	/*

	Localise the button titles here...

	%p is replaced with the appropriate page number
	%t is replaced with the total number of pages

	*/
	text: ["First Page","Previous Page (Page %p)","Next Page (Page %p)","Last Page (Page %t)","Page %p of %t"],

	addEvent: function(obj, type, fn, tmp) {
		tmp || (tmp = true);
		if( obj.attachEvent ) {
			obj["e"+type+fn] = fn;
			obj[type+fn] = function(){obj["e"+type+fn]( window.event );};
			obj.attachEvent( "on"+type, obj[type+fn] );
		} else {
			obj.addEventListener( type, fn, true );
		};
	},

	addClass: function(e,c) {
		if (typeof e == 'undefined') return;
		if(new RegExp("(^|\\s)" + c + "(\\s|$)").test(e.className)) return;
		e.className += ( e.className ? " " : "" ) + c;
	},

	/*@cc_on
	/*@if (@_win32)
	removeClass: function(e,c) {
		if (typeof e == 'undefined') return;
		e.className = !c ? "" : e.className.replace(new RegExp("(^|\\s)" + c + "(\\s|$)"), " ").replace(/^\s*((?:[\S\s]*\S)?)\s*$/, '$1');
	},
	@else @*/
	removeClass: function(e,c) {
		if (typeof e == 'undefined') return;
		e.className = !c ? "" : e.className.replace(new RegExp("(^|\\s)" + c + "(\\s|$)"), " ").replace(/^\s\s*/, '').replace(/\s\s*$/, '');
	},
	/*@end
	@*/

	init: function(tableId) {
		var tables = tableId && typeof(tableId) == "string" ? [document.getElementById(tableId)] : document.getElementsByTagName('table');
		var hook, maxPages, visibleRows, numPages, cp, cb, rowList;

		for(var t = 0, tbl; tbl = tables[t]; t++) {
			if(tbl.className.search(/paginate-([0-9]+)/) == -1) { continue; };

			if(!tbl.id) { tbl.id = "fdUniqueTableId_" + tablePaginater.uniqueID++; };

			maxPages = tbl.className.search(/max-pages-([0-9]+)/) == -1 ? null : Number(tbl.className.match(/max-pages-([0-9]+)/)[1]);
			if(maxPages % 2 == 0 && maxPages > 1) { maxPages--; };

			hook = tbl.getElementsByTagName('tbody');
			hook = (hook.length) ? hook[0] : tbl;

			visibleRows = tablePaginater.calculateVisibleRows(hook);

			if(maxPages > (visibleRows / Number(tbl.className.match(/paginate-([0-9]+)/)[1]))) {
				maxPages = null;
			};

			numPages = Math.ceil(visibleRows / Number(tbl.className.match(/paginate-([0-9]+)/)[1]));

			if(numPages < 2 && !(tbl.id in tablePaginater.tableInfo)) {
				continue;
			};

			cp = (tbl.id in tablePaginater.tableInfo) ? Math.min(tablePaginater.tableInfo[tbl.id].currentPage, numPages) : 1;

			cb = tbl.className.search(/paginationcallback-([\S-]+)/) == -1 ? "" : tbl.className.match(/paginationcallback-([\S]+)/)[1];

			// Replace "-" with "." to enable Object.method callbacks
			cb = cb.replace("-", ".");

			tablePaginater.tableInfo[tbl.id] = {
				rowsPerPage:Number(tbl.className.match(/paginate-([0-9]+)/)[1]),
				currentPage:cp,
				totalRows:hook.getElementsByTagName('tr').length,
				hook:hook,
				maxPages:maxPages,
				numPages:numPages,
				rowStyle:tbl.className.search(/rowstyle-([\S]+)/) != -1 ? tbl.className.match(/rowstyle-([\S]+)/)[1] : false,
				callback:cb || "paginationCallback"
			};

			tablePaginater.showPage(tbl.id);
			hook = null;
		};
	},
	calculateVisibleRows: function(hook) {
		var trs = hook.rows;
		var cnt = 0;
		var reg = /(^|\s)invisibleRow(\s|$)/;

		for(var i = 0, tr; tr = trs[i]; i++) {
			if(tr.parentNode != hook || tr.getElementsByTagName("th").length || (tr.parentNode && tr.parentNode.tagName.toLowerCase().search(/thead|tfoot/) != -1)) continue;

			if(tr.className.search(reg) == -1) {
				cnt++;
			};
		};
		return cnt;
	},
	createButton: function(details, ul, pseudo) {
		var li   = document.createElement("li");
		var but  = document.createElement(pseudo ? "div" : "a");
		var span = document.createElement("span");

		if(!pseudo) { but.href = "#"; };
		if(!pseudo) { but.title = details.title; };

		but.className = details.className;

		ul.appendChild(li);
		li.appendChild(but);
		but.appendChild(span);
		span.appendChild(document.createTextNode(details.text));

		if(!pseudo) { li.onclick = but.onclick = tablePaginater.buttonClick; };
		if(!pseudo && details.id) { but.id = details.id; };

		li = but = span = null;
	},
	removePagination: function(tableId) {
		var wrapT = document.getElementById(tableId + "-fdtablePaginaterWrapTop");
		var wrapB = document.getElementById(tableId + "-fdtablePaginaterWrapBottom");
		if(wrapT) { wrapT.parentNode.removeChild(wrapT); };
		if(wrapB) { wrapB.parentNode.removeChild(wrapB); };
	},
	buildPagination: function(tblId) {
		if(!(tblId in tablePaginater.tableInfo)) { return; };

		tablePaginater.removePagination(tblId);

		var details = tablePaginater.tableInfo[tblId];

		if(details.numPages < 2) return;

		function resolveText(txt, curr) {
			curr = curr || details.currentPage;
			return txt.replace("%p", curr).replace("%t", details.numPages);
		};

		if(details.maxPages) {
			findex = Math.max(0, Math.floor(Number(details.currentPage - 1) - (Number(details.maxPages - 1) / 2)));
			lindex = findex + Number(details.maxPages);
			if(lindex > details.numPages) {
				lindex = details.numPages;
				findex = Math.max(0, details.numPages - Number(details.maxPages));
			};
		} else {
			findex = 0;
			lindex = details.numPages;
		};


		//var wrapT = document.createElement("div");
		//wrapT.className = "fdtablePaginaterWrap";
		//wrapT.id = tblId + "-fdtablePaginaterWrapTop";

		var wrapB = document.createElement("div");
		wrapB.className = "fdtablePaginaterWrap";
		wrapB.id = tblId + "-fdtablePaginaterWrapBottom";

		// Create list scaffold
		//var ulT = document.createElement("ul");
		//ulT.id  = tblId + "-tablePaginater";
		//ulT.className = "fdtablePaginater";

		var ulB = document.createElement("ul");
		ulB.id  = tblId + "-tablePaginaterClone";
		ulB.className = "fdtablePaginater";

		// Add to the wrapper DIVs
		//wrapT.appendChild(ulT);
		wrapB.appendChild(ulB);

		// FIRST (only created if maxPages set)
		if(details.maxPages) {
			//tablePaginater.createButton({title:tablePaginater.text[0], className:"first-page", text:"\u00ab"}, ulT, !findex);
			tablePaginater.createButton({title:tablePaginater.text[0], className:"first-page", text:"\u00ab"}, ulB, !findex);
		};

		// PREVIOUS (only created if there are more than two pages)
		if(details.numPages > 2) {
			//tablePaginater.createButton({title:resolveText(tablePaginater.text[1], details.currentPage-1), className:"previous-page", text:"\u2039", id:tblId+"-previousPage"}, ulT, details.currentPage == 1);
			tablePaginater.createButton({title:resolveText(tablePaginater.text[1], details.currentPage-1), className:"previous-page", text:"\u2039", id:tblId+"-previousPageC"}, ulB, details.currentPage == 1);
		};

		// NUMBERED
		for(var i = findex; i < lindex; i++) {
			//tablePaginater.createButton({title:resolveText(tablePaginater.text[4], i+1), className:i != (details.currentPage-1) ? "page-"+(i+1) : "currentPage page-"+(i+1), text:(i+1), id:i == (details.currentPage-1) ? tblId + "-currentPage" : ""}, ulT);
			tablePaginater.createButton({title:resolveText(tablePaginater.text[4], i+1), className:i != (details.currentPage-1) ? "page-"+(i+1) : "currentPage page-"+(i+1), text:(i+1), id:i == (details.currentPage-1) ? tblId + "-currentPageC" : ""}, ulB);
		};

		// NEXT (only created if there are more than two pages)
		if(details.numPages > 2) {
			//tablePaginater.createButton({title:resolveText(tablePaginater.text[2], details.currentPage + 1), className:"next-page", text:"\u203a", id:tblId+"-nextPage"}, ulT, details.currentPage == details.numPages);
			tablePaginater.createButton({title:resolveText(tablePaginater.text[2], details.currentPage + 1), className:"next-page", text:"\u203a", id:tblId+"-nextPageC"}, ulB, details.currentPage == details.numPages);
		};

		// LAST (only created if maxPages set)
		if(details.maxPages) {
			//tablePaginater.createButton({title:resolveText(tablePaginater.text[3], details.numPages), className:"last-page", text:"\u00bb"}, ulT, lindex == details.numPages);
			tablePaginater.createButton({title:resolveText(tablePaginater.text[3], details.numPages), className:"last-page", text:"\u00bb"}, ulB, lindex == details.numPages);
		};

		// DOM inject wrapper DIVs (FireFox Bug: this has to be done here if you use display:table)
		if(document.getElementById(tblId+"-paginationListWrapTop")) {
			//document.getElementById(tblId+"-paginationListWrapTop").appendChild(wrapT);
		} else {
			//document.getElementById(tblId).parentNode.insertBefore(wrapT, document.getElementById(tblId));
		};

		if(document.getElementById(tblId+"-paginationListWrapBottom")) {
			document.getElementById(tblId+"-paginationListWrapBottom").appendChild(wrapB);
		} else {
			document.getElementById(tblId).parentNode.insertBefore(wrapB, document.getElementById(tblId).nextSibling);
		};
	},
	// The tableSort script uses this function to redraw.
	redraw: function(tableid, identical) {
		if(!tableid || !(tableid in fdTableSort.tableCache) || !(tableid in tablePaginater.tableInfo)) { return; };

		var dataObj     = fdTableSort.tableCache[tableid];
		var data	= dataObj.data;
		var len1	= data.length;
		var len2	= len1 ? data[0].length - 1 : 0;
		var hook	= dataObj.hook;
		var colStyle    = dataObj.colStyle;
		var rowStyle    = dataObj.rowStyle;
		var colOrder    = dataObj.colOrder;

		var page	= tablePaginater.tableInfo[tableid].currentPage - 1;
		var d1	  = tablePaginater.tableInfo[tableid].rowsPerPage * page;
		var d2	  = Math.min(tablePaginater.tableInfo[tableid].totalRows, d1 + tablePaginater.tableInfo[tableid].rowsPerPage);

		var cnt	 = 0;
		var rs	  = 0;
		var reg	 = /(^|\s)invisibleRow(\s|$)/;

		var tr, tds, cell, pos;

		for(var i = 0; i < len1; i++) {
			tr = data[i][len2];

			if(colStyle) {
				tds = tr.cells;
				for(thPos in colOrder) {
					if(!colOrder[thPos]) tablePaginater.removeClass(tds[thPos], colStyle);
					else tablePaginater.addClass(tds[thPos], colStyle);
				};
			};

			if(tr.className.search(reg) != -1) {
				continue;
			};

			if(!identical) {
				cnt++;

				if(cnt > d1 && cnt <= d2) {
					if(rowStyle) {
						if(rs++ & 1) tablePaginater.addClass(tr, rowStyle);
						else tablePaginater.removeClass(tr, rowStyle);
					};
					tr.style.display = "";
				} else {
					tr.style.display = "none";
				};

				// Netscape 8.1.2 requires the removeChild call or it freaks out, so add the line if you want to support this browser
				// hook.removeChild(tr);
				hook.appendChild(tr);
			};
		};

		tr = tds = hook = null;
	},
	showPage: function(tblId, pageNum) {
		if(!(tblId in tablePaginater.tableInfo)) { return; };

		var page = !pageNum ? tablePaginater.tableInfo[tblId].currentPage - 1 : pageNum - 1;

		var d1  = tablePaginater.tableInfo[tblId].rowsPerPage * page;
		var d2  = Math.min(tablePaginater.tableInfo[tblId].totalRows, d1 + tablePaginater.tableInfo[tblId].rowsPerPage);
		var trs = tablePaginater.tableInfo[tblId].hook.rows;
		var cnt = 0;
		var rc  = 0;
		var len = trs.length;
		var rs  = tablePaginater.tableInfo[tblId].rowStyle;
		var reg = /(^|\s)invisibleRow(\s|$)/;

		for(var i = 0; i < len; i++) {
			if(trs[i].getElementsByTagName("th").length || (trs[i].parentNode && trs[i].parentNode.tagName.toLowerCase().search(/thead|tfoot/) != -1)) continue;

			if(trs[i].className.search(reg) != -1) {
				continue;
			};

			cnt++;

			if(cnt > d1 && cnt <= d2) {
				if(rs) {
					if(rc++ & 1) {
						tablePaginater.addClass(trs[i], rs);
					} else {
						tablePaginater.removeClass(trs[i], rs);
					}
				};
				trs[i].style.display = "";
			} else {
				trs[i].style.display = "none";
			};
		};

		tablePaginater.buildPagination(tblId);
		tablePaginater.callback(tblId);
	},
	callback: function(tblId) {
		var func;
		if(tablePaginater.tableInfo[tblId].callback.indexOf(".") != -1) {
			var split = tablePaginater.tableInfo[tblId].callback.split(".");
			func = window;
			for(var i = 0, f; f = split[i]; i++) {
				if(f in func) {
					func = func[f];
				} else {
					func = "";
					break;
				};
			};
		} else if(tablePaginater.tableInfo[tblId].callback in window) {
			func = window[tablePaginater.tableInfo[tblId].callback];
		};

		if(typeof func == "function") {
			func(tblId);
		};

		func = null;
	},
	buttonClick: function(e) {
		e = e || window.event;

		var a = this.tagName.toLowerCase() == "a" ? this : this.getElementsByTagName("a")[0];

		if(a.className.search("currentPage") != -1) return false;

		var ul = this;
		while(ul.tagName.toLowerCase() != "ul") ul = ul.parentNode;

		var tblId = ul.id.replace("-tablePaginaterClone","").replace("-tablePaginater", "");

		tablePaginater.tableInfo[tblId].lastPage = tablePaginater.tableInfo[tblId].currentPage;

		var showPrevNext = 0;

		if(a.className.search("previous-page") != -1) {
			tablePaginater.tableInfo[tblId].currentPage = tablePaginater.tableInfo[tblId].currentPage > 1 ? tablePaginater.tableInfo[tblId].currentPage - 1 : tablePaginater.tableInfo[tblId].numPages;
			showPrevNext = 1;
		} else if(a.className.search("next-page") != -1) {
			tablePaginater.tableInfo[tblId].currentPage = tablePaginater.tableInfo[tblId].currentPage < tablePaginater.tableInfo[tblId].numPages ? tablePaginater.tableInfo[tblId].currentPage + 1 : 1;
			showPrevNext = 2;
		} else if(a.className.search("first-page") != -1) {
			tablePaginater.tableInfo[tblId].currentPage = 1;
		} else if(a.className.search("last-page") != -1) {
			tablePaginater.tableInfo[tblId].currentPage = tablePaginater.tableInfo[tblId].numPages;
		} else {
			tablePaginater.tableInfo[tblId].currentPage = parseInt(a.className.match(/page-([0-9]+)/)[1]) || 1;
		};

		tablePaginater.showPage(tblId);

		// Focus on the appropriate button (previous, next or the current page)
		// I'm hoping screen readers are savvy enough to indicate the focus event to the user
		if(showPrevNext == 1) {
			var elem = document.getElementById(ul.id.search("-tablePaginaterClone") != -1 ? tblId + "-previousPageC" : tblId + "-previousPage");
		} else if(showPrevNext == 2) {
			var elem = document.getElementById(ul.id.search("-tablePaginaterClone") != -1 ? tblId + "-nextPageC" : tblId + "-nextPage");
		} else {
			var elem = document.getElementById(ul.id.search("-tablePaginaterClone") != -1 ? tblId + "-currentPageC" : tblId + "-currentPage");
		};

		if(elem && elem.tagName.toLowerCase() == "a") { elem.focus(); };

		if(e.stopPropagation) {
			e.stopPropagation();
			e.preventDefault();
		};

		/*@cc_on
		@if(@_win32)
		e.cancelBubble = true;
		e.returnValue  = false;
		@end
		@*/
		return false;
	},
	onUnLoad: function(e) {
		var tbl, lis, pagination, uls;
		for(tblId in tablePaginater.tableInfo) {
			uls = [tblId + "-tablePaginater", tblId + "-tablePaginaterClone"];
			for(var z = 0; z < 2; z++) {
				pagination = document.getElementById(uls[z]);
				if(!pagination) { continue; };
				lis = pagination.getElementsByTagName("li");
				for(var i = 0, li; li = lis[i]; i++) {
					li.onclick = null;
					if(li.getElementsByTagName("a").length) { li.getElementsByTagName("a")[0].onclick = null; };
				};
			};
		};
	}
};

tablePaginater.addEvent(window, "load",   tablePaginater.init);
tablePaginater.addEvent(window, "unload", tablePaginater.onUnLoad);





/*
        DatePicker v4.3 by frequency-decoder.com

        Released under a creative commons Attribution-ShareAlike 2.5 license (http://creativecommons.org/licenses/by-sa/2.5/)

        Please credit frequency-decoder in any derivative work - thanks.

        You are free:

        * to copy, distribute, display, and perform the work
        * to make derivative works
        * to make commercial use of the work

        Under the following conditions:

                by Attribution.
                --------------
                You must attribute the work in the manner specified by the author or licensor.

                sa
                --
                Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under a license identical to this one.

        * For any reuse or distribution, you must make clear to others the license terms of this work.
        * Any of these conditions can be waived if you get permission from the copyright holder.
*/

var datePickerController = (function datePickerController() {
        var languageInfo        = navigator.language ? navigator.language.toLowerCase().replace(/-[a-z]+$/, "") : navigator.userLanguage ? navigator.userLanguage.toLowerCase().replace(/-[a-z]+$/, "") : "en",
            datePickers         = {},
            uniqueId            = 0,
            weeksInYearCache    = {},
            localeImport        = false,
            nbsp                = String.fromCharCode(160),
            nodrag              = false,
            buttonTabIndex      = true,
            returnLocaleDate    = false,
            splitAppend         = ["-dd","-mm"],
            cellFormat          = "d-sp-F-sp-Y",
            titleFormat         = "F-sp-d-cc-sp-Y",
            formatParts         = ["placeholder", "sp-F-sp-Y"],
            formatMasks         = ["Y-sl-m-sl-d","m-sl-d-sl-Y","d-sl-m-sl-Y","Y-ds-m-ds-d","m-ds-d-ds-Y","d-ds-m-ds-Y"];

        void function() {
                var scriptFiles = document.getElementsByTagName('head')[0].getElementsByTagName('script'),
                    scriptInner = scriptFiles[scriptFiles.length - 1].innerHTML.replace(/[\n\r\s\t]+/g, " ").replace(/^\s+/, "").replace(/\s+$/, ""),
                    json        = parseJSON(scriptInner);

                if(typeof json === "object" && !("err" in json)) {
                        affectJSON(json);
                };

                if(typeof(fdLocale) != "object") {
                        var loc    = scriptFiles[scriptFiles.length - 1].src.substr(0, scriptFiles[scriptFiles.length - 1].src.lastIndexOf("/")) + "/lang/" + useLocale + ".js",
                            script = document.createElement('script');

                        script.type = "text/javascript";
                        script.src  = loc;
                        script.setAttribute("charset", "utf-8");
                        /*@cc_on
                        /*@if(@_win32)
                        var bases = document.getElementsByTagName('base');
                        if (bases.length && bases[0].childNodes.length) {
                                bases[0].appendChild(script);
                        } else {
                                document.getElementsByTagName('head')[0].appendChild(script);
                        };
                        bases = null;
                        @else @*/
                        document.getElementsByTagName('head')[0].appendChild(script);
                        /*@end
                        @*/

                        script = null;
                } else {
                        returnLocaleDate = true;
                };
        }();

        function affectJSON(json) {
                if(typeof json !== "object") { return; };
                for(key in json) {
                        value = json[key];
                        switch(key.toLowerCase()) {
                                case "lang":
                                        if(value.search(/^[a-z]{2}$/i) != -1) {
                                                languageInfo = value;
                                                returnLocaleDate = true;
                                        };
                                        break;
                                case "split":
                                        if(typeof value === 'object') {
                                                if(value.length && value.length == 2) {
                                                        splitAppend = value;
                                                };
                                        };
                                        break;
                                case "formats":
                                        if(typeof value === 'object') {
                                                if(value.length) {
                                                        var tmpMasks = [];
                                                        for(var m = 0, msk; msk = value[m]; m++) {
                                                                if(msk.match(/((sp|dt|sl|ds|cc)|([d|D|l|j|N|w|S|W|M|F|m|n|t|Y|o|y|O|p]))(-((sp|dt|sl|ds|cc)|([d|D|l|j|N|w|S|W|M|F|m|n|t|Y|o|y|O|p])))+/)) {
                                                                        tmpMasks.push(msk);
                                                                };
                                                        };
                                                        if(tmpMasks.length) { formatMasks = tmpMasks; };
                                                };
                                        };
                                        break;
                                case "nodrag":
                                        nodrag = !!value;
                                        break;
                                case "buttontabindex":
                                        buttonTabIndex = !!value;
                                        break;
                                case "cellformat":
                                        if(typeof value == "string" && value.match(/^((sp|dt|sl|ds|cc)|([d|D|l|j|N|w|S|W|M|F|m|n|t|Y|o|y|O|p]))(-((sp|dt|sl|ds|cc)|([d|D|l|j|N|w|S|W|M|F|m|n|t|Y|o|y|O|p])))+$/)) {
                                                parseCellFormat(value);
                                        };
                                        break;
                                case "titleformat":
                                        if(value.match(/((sp|dt|sl|ds|cc)|([d|D|l|j|N|w|S|W|M|F|m|n|t|Y|o|y|O|p]))(-((sp|dt|sl|ds|cc)|([d|D|l|j|N|w|S|W|M|F|m|n|t|Y|o|y|O|p])))+/)) {
                                                titleFormat = value;
                                        };
                        };
                };
        };

        function parseCellFormat(value) {
                // I'm sure this could be done with a regExp and a split in one line... seriously...
                var parts       = value.split("-"),
                    fullParts   = [],
                    tmpParts    = [],
                    part;

                for(var pt = 0; pt < parts.length; pt++) {
                        part = parts[pt];
                        if(part == "j" || part == "d") {
                                if(tmpParts.length) {
                                        fullParts.push(tmpParts.join("-"));
                                        tmpParts = [];
                                };
                                fullParts.push("placeholder");
                        } else {
                                tmpParts.push(part);
                        };
                };

                if(tmpParts.length) {
                        fullParts.push(tmpParts.join("-"));
                };

                if(!fullParts.length || fullParts.length > 3) {
                        formatParts = window.opera ? ["placeholder"] : ["placeholder", "sp-F-sp-Y"];
                        cellFormat = "j-sp-F-sp-Y";
                        return;
                };

                // Don't use hidden text for opera due to focus outline problems
                formatParts = window.opera ? ["placeholder"] : fullParts;
                cellFormat  = window.opera ? "j-sp-F-sp-Y" : value;
        };

        function pad(value, length) {
                length = length || 2;
                return "0000".substr(0,length - Math.min(String(value).length, length)) + value;
        };

        function addEvent(obj, type, fn) {
                try {
                if( obj.attachEvent ) {
                        obj["e"+type+fn] = fn;
                        obj[type+fn] = function(){obj["e"+type+fn]( window.event );};
                        obj.attachEvent( "on"+type, obj[type+fn] );
                } else {
                        obj.addEventListener( type, fn, true );
                };
                } catch(err) {
                        alert(obj + " " + type + " " + fn)
                }
        };

        function removeEvent(obj, type, fn) {
                try {
                        if( obj.detachEvent ) {
                                obj.detachEvent( "on"+type, obj[type+fn] );
                                obj[type+fn] = null;
                        } else {
                                obj.removeEventListener( type, fn, true );
                        };
                } catch(err) {};
        };

        function stopEvent(e) {
                e = e || document.parentWindow.event;
                if(e.stopPropagation) {
                        e.stopPropagation();
                        e.preventDefault();
                };
                /*@cc_on
                @if(@_win32)
                e.cancelBubble = true;
                e.returnValue = false;
                @end
                @*/
                return false;
        };

        function parseJSON(str) {
                // Check we have a String
                if(typeof str !== 'string' || str == "") { return {}; };
                try {
                        // Does the Douglas Crockford JSON parser exist in the global scope?
                        if("JSON" in window && "parse" in window.JSON && typeof window.JSON.parse == "function") {
                                return window.JSON.parse(str);
                        // Genious code taken from: http://kentbrewster.com/badges/
                        } else if(/lang|split|formats|nodrag/.test(str.toLowerCase())) {
                                var f = Function(['var document,top,self,window,parent,Number,Date,Object,Function,',
                                        'Array,String,Math,RegExp,Image,ActiveXObject;',
                                        'return (' , str.replace(/<\!--.+-->/gim,'').replace(/\bfunction\b/g,'function­') , ');'].join(''));
                                return f();
                        };
                } catch (e) { };
                return {"err":"Trouble parsing JSON object"};
        };

        function setARIARole(element, role) {
                if(element && element.tagName) {
                        element.setAttribute("role", role);
                };
        };

        function setARIAProperty(element, property, value) {
		if(element && element.tagName) {
                        element.setAttribute("aria-" + property, value);
                };
	};

        // The datePicker object itself
        function datePicker(options) {
                this.dateSet             = null;
                this.timerSet            = false;
                this.visible             = false;
                this.fadeTimer           = null;
                this.timer               = null;
                this.yearInc             = 0;
                this.monthInc            = 0;
                this.dayInc              = 0;
                this.mx                  = 0;
                this.my                  = 0;
                this.x                   = 0;
                this.y                   = 0;
                this.date                = new Date();
                this.defaults            = {};
                this.created             = false;
                this.disabled            = false;
                this.id                  = options.id;
                this.opacity             = 0;
                this.firstDayOfWeek      = localeImport.firstDayOfWeek;
                this.buttonWrapper       = "buttonWrapper" in options ? options.buttonWrapper : false;
                this.staticPos           = "staticPos" in options ? !!options.staticPos : false;
                this.disabledDays        = "disabledDays" in options && options.disabledDays.length ? options.disabledDays : [0,0,0,0,0,0,0];
                this.disabledDates       = "disabledDates" in options ? options.disabledDates : {};
                this.enabledDates        = "enabledDates" in options ? options.enabledDates : {};
                this.showWeeks           = "showWeeks" in options ? !!options.showWeeks : false;
                this.low                 = options.low || "";
                this.high                = options.high || "";
                this.dragDisabled        = nodrag ? true : ("dragDisabled" in options ? !!options.dragDisabled : false);
                this.positioned          = "positioned" in options ? options.positioned : false;
                this.hideInput           = this.staticPos ? false : "hideInput" in options ? !!options.hideInput : false;
                this.splitDate           = "splitDate" in options ? !!options.splitDate : false;
                this.format              = options.format || "d-sl-m-sl-Y";
                this.statusFormat        = options.statusFormat || "";
                this.highlightDays       = options.highlightDays && options.highlightDays.length ? options.highlightDays : [0,0,0,0,0,1,1];
                this.noFadeEffect        = "noFadeEffect" in options ? !!options.noFadeEffect : false;
                this.opacityTo           = this.noFadeEffect || this.staticPos ? 99 : 90;
                this.callbacks           = {};
                this.fillGrid            = !!options.fillGrid;
                this.noToday             = !!options.noToday;
                this.labelledBy          = "labelledBy" in options ? options.labelledBy : "";
                this.constrainSelection  = this.fillGrid && !!options.constrainSelection;
                this.finalOpacity        = !this.staticPos && "finalOpacity" in options ? +options.finalOpacity : 90;
                this.dynDisabledDates    = {};
                this.inUpdate            = false;
                this.noFocus             = true;
                this.kbEventsAdded       = false;
                this.fullCreate          = false;
                this.selectedTD          = null;
                this.cursorTD            = null;
                this.addSpans            = this.staticPos;
                this.spansAdded          = false;

                /*@cc_on
                @if(@_win32)
                this.interval            = new Date();
                this.iePopUp             = null;
                this.isIE7               = false;
                @end
                @*/

                /*@cc_on
                @if(@_jscript_version <= 5.7)
                this.isIE7               = document.documentElement && typeof document.documentElement.style.maxHeight != "undefined";
                @end
                @*/

                for(var thing in options.callbacks) {
                        this.callbacks[thing] = options.callbacks[thing];
                };

                // Adjust time to stop daylight savings madness on windows
                this.date.setHours(12);

                this.changeHandler = function() {
                        o.setDateFromInput();
                        if(o.created) { o.updateTable(); };
                };
                this.getScrollOffsets = function() {
                        if(typeof(window.pageYOffset) == 'number') {
                                //Netscape compliant
                                return [window.pageXOffset, window.pageYOffset];
                        } else if(document.body && (document.body.scrollLeft || document.body.scrollTop)) {
                                //DOM compliant
                                return [document.body.scrollLeft, document.body.scrollTop];
                        } else if(document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop)) {
                                //IE6 standards compliant mode
                                return [document.documentElement.scrollLeft, document.documentElement.scrollTop];
                        };
                        return [0,0];
                };
                this.reposition = function() {
                        if(!o.created || !o.getElem() || o.staticPos) { return; };

                        o.div.style.visibility = "hidden";
                        o.div.style.left = o.div.style.top = "0px";
                        o.div.style.display = "block";

                        var osh         = o.div.offsetHeight,
                            osw         = o.div.offsetWidth,
                            elem        = document.getElementById('fd-but-' + o.id),
                            pos         = o.truePosition(elem),
                            trueBody    = (document.compatMode && document.compatMode!="BackCompat") ? document.documentElement : document.body,
                            sOffsets    = o.getScrollOffsets(),
                            scrollTop   = sOffsets[1],
                            scrollLeft  = sOffsets[0];

                        o.div.style.visibility = "visible";

                        o.div.style.left  = Number(parseInt(trueBody.clientWidth+scrollLeft) < parseInt(osw+pos[0]) ? Math.abs(parseInt((trueBody.clientWidth+scrollLeft) - osw)) : pos[0]) + "px";
                        o.div.style.top   = Number(parseInt(trueBody.clientHeight+scrollTop) < parseInt(osh+pos[1]+elem.offsetHeight+2) ? Math.abs(parseInt(pos[1] - (osh + 2))) : Math.abs(parseInt(pos[1] + elem.offsetHeight + 2))) + "px";

                        /*@cc_on
                        @if(@_jscript_version <= 5.7)
                        if(o.isIE7) return;
                        o.iePopUp.style.top    = o.div.style.top;
                        o.iePopUp.style.left   = o.div.style.left;
                        o.iePopUp.style.width  = osw + "px";
                        o.iePopUp.style.height = (osh - 2) + "px";
                        @end
                        @*/
                };
                this.removeOldFocus = function() {
                        var td = document.getElementById(o.id + "-date-picker-hover");
                        if(td) {
                                try {
                                        td.setAttribute(!/*@cc_on!@*/false ? "tabIndex" : "tabindex", "-1");
                                        td.tabIndex = -1;
                                        td.className = td.className.replace(/date-picker-hover/, "");
                                        td.id = "";
                                } catch(err) {};
                        };
                };
                this.setNewFocus = function() {
                        var td = document.getElementById(o.id + "-date-picker-hover");
                        if(td) {
                                try {
                                        td.setAttribute(!/*@cc_on!@*/false ? "tabIndex" : "tabindex", "0");
                                        td.tabIndex = 0;
                                        td.className = td.className.replace(/date-picker-hover/, "") + " date-picker-hover";
                                        if(!this.noFocus) {
                                                setTimeout(function() { try { td.focus(); } catch(err) {}; }, 0);
                                        };
                                } catch(err) {};
                        };
                };
                this.updateTable = function(noCallback) {
                        if(o.inUpdate) return;

                        o.inUpdate = true;
                        o.removeOldFocus();

                        if(o.timerSet) {
                                o.date.setDate(Math.min(o.date.getDate()+o.dayInc, daysInMonth(o.date.getMonth()+o.monthInc,o.date.getFullYear()+o.yearInc)) );
                                o.date.setMonth(o.date.getMonth() + o.monthInc);
                                o.date.setFullYear(o.date.getFullYear() + o.yearInc);
                        };

                        o.outOfRange();
                        if(!o.noToday) { o.disableTodayButton(); };
                        o.showHideButtons(o.date);

                        var cd = o.date.getDate(),
                            cm = o.date.getMonth(),
                            cy = o.date.getFullYear(),
                            cursorDate = (String(cy) + pad(cm+1) + pad(cd)),
                            tmpDate    = new Date(cy, cm, 1);

                        tmpDate.setHours(5);

                        var dt, cName, td, i, currentDate, cellAdded, col, currentStub, abbr, bespokeRenderClass, spnC,
                        weekDayC            = ( tmpDate.getDay() + 6 ) % 7,
                        firstColIndex       = (((weekDayC - o.firstDayOfWeek) + 7 ) % 7) - 1,
                        dpm                 = daysInMonth(cm, cy),
                        today               = new Date(),
                        dateSetD            = (o.dateSet != null) ? o.dateSet.getFullYear() + pad(o.dateSet.getMonth()+1) + pad(o.dateSet.getDate()) : false,
                        stub                = String(tmpDate.getFullYear()) + pad(tmpDate.getMonth()+1),
                        cellAdded           = [4,4,4,4,4,4],
                        lm                  = new Date(cy, cm-1, 1),
                        nm                  = new Date(cy, cm+1, 1),
                        daySub              = daysInMonth(lm.getMonth(), lm.getFullYear()),
                        stubN               = String(nm.getFullYear()) + pad(nm.getMonth()+1),
                        stubP               = String(lm.getFullYear()) + pad(lm.getMonth()+1),
                        weekDayN            = (nm.getDay() + 6) % 7,
                        weekDayP            = (lm.getDay() + 6) % 7,
                        today               = today.getFullYear() + pad(today.getMonth()+1) + pad(today.getDate()),
                        spn                 = document.createElement('span');

                        o.firstDateShown    = !o.constrainSelection && o.fillGrid && (0 - firstColIndex < 1) ? String(stubP) + (daySub + (0 - firstColIndex)) : stub + "01";
                        o.lastDateShown     = !o.constrainSelection && o.fillGrid ? stubN + pad(41 - firstColIndex - dpm) : stub + String(dpm);
                        o.currentYYYYMM     = stub;

                        bespokeRenderClass  = o.callback("redraw", {id:o.id, dd:pad(cd), mm:pad(cm+1), yyyy:cy, firstDateDisplayed:o.firstDateShown, lastDateDisplayed:o.lastDateShown}) || {};
                        o.dynDisabledDates  = o.getDisabledDates(cy, cm + 1);

                        spn.className       = "fd-screen-reader";

                        if(this.selectedTD != null) {
                                setARIAProperty(this.selectedTD, "selected", false);
                                this.selectedTD = null;
                        };

                        for(var curr = 0; curr < 42; curr++) {
                                row  = Math.floor(curr / 7);
                                td   = o.tds[curr];
                                spnC = spn.cloneNode(false);

                                while(td.firstChild) td.removeChild(td.firstChild);

                                if((curr > firstColIndex && curr <= (firstColIndex + dpm)) || o.fillGrid) {
                                        currentStub     = stub;
                                        weekDay         = weekDayC;
                                        dt              = curr - firstColIndex;
                                        cName           = [];
                                        selectable      = true;

                                        if(dt < 1) {
                                                dt              = daySub + dt;
                                                currentStub     = stubP;
                                                weekDay         = weekDayP;
                                                selectable      = !o.constrainSelection;
                                                cName.push("month-out");
                                        } else if(dt > dpm) {
                                                dt -= dpm;
                                                currentStub     = stubN;
                                                weekDay         = weekDayN;
                                                selectable      = !o.constrainSelection;
                                                cName.push("month-out");
                                        };

                                        weekDay = ( weekDay + dt + 6 ) % 7;
                                        cName.push("day-" + localeDefaults.dayAbbrs[weekDay].toLowerCase());

                                        currentDate = currentStub + String(dt < 10 ? "0" : "") + dt;

                                        if(o.low && +currentDate < +o.low || o.high && +currentDate > +o.high) {
                                                td.className = "out-of-range";
                                                td.title = "";
                                                td.appendChild(document.createTextNode(dt));
                                                if(o.showWeeks) { cellAdded[row] = Math.min(cellAdded[row], 2); };
                                        } else {
                                                if(selectable) {
                                                        td.title = titleFormat ? printFormattedDate(new Date(+String(currentStub).substr(0,4), +String(currentStub).substr(4, 2) - 1, +dt), titleFormat, true) : "";
                                                        cName.push("cd-" + currentDate + " yyyymm-" + currentStub + " mmdd-" + currentStub.substr(4,2) + pad(dt));
                                                } else {
                                                        td.title = titleFormat ? getTitleTranslation(13) + " " + printFormattedDate(new Date(+String(currentStub).substr(0,4), +String(currentStub).substr(4, 2) - 1, +dt), titleFormat, true) : "";
                                                        cName.push("yyyymm-" + currentStub + " mmdd-" + currentStub.substr(4,2) + pad(dt) + " not-selectable");
                                                };

                                                if(currentDate == today) { cName.push("date-picker-today"); };

                                                if(dateSetD == currentDate) {
                                                        cName.push("date-picker-selected-date");
                                                        setARIAProperty(td, "selected", "true");
                                                        this.selectedTD = td;
                                                };

                                                if(o.disabledDays[weekDay] || currentDate in o.dynDisabledDates) { cName.push("day-disabled"); if(titleFormat && selectable) { td.title = getTitleTranslation(13) + " " + td.title; }; }

                                                if(currentDate in bespokeRenderClass) { cName.push(bespokeRenderClass[currentDate]); }

                                                if(o.highlightDays[weekDay]) { cName.push("date-picker-highlight"); };

                                                if(cursorDate == currentDate) {
                                                        td.id = o.id + "-date-picker-hover";
                                                };

                                                td.className = cName.join(" ");

                                                if(o.kbEventsAdded || o.addSpans) {
                                                        for(var pt = 0, part; part = formatParts[pt]; pt++) {
                                                                if(part == "placeholder") {
                                                                        td.appendChild(document.createTextNode(dt));
                                                                } else {
                                                                        spnC = spn.cloneNode(spn);
                                                                        spnC.appendChild(document.createTextNode(printFormattedDate(new Date(+String(currentStub).substr(0,4), +String(currentStub).substr(4, 2) - 1, +dt), part, true)));
                                                                        td.appendChild(spnC);
                                                                };
                                                        };

                                                } else {
                                                        td.appendChild(document.createTextNode(dt));
                                                };

                                                if(o.showWeeks) {
                                                        cellAdded[row] = Math.min(cName[0] == "month-out" ? 3 : 1, cellAdded[row]);
                                                };
                                        };
                                } else {
                                        td.className = "date-picker-unused";
                                        td.appendChild(document.createTextNode(nbsp));
                                        td.title = "";
                                };

                                if(o.showWeeks && curr - (row * 7) == 6) {
                                        while(o.wkThs[row].firstChild) o.wkThs[row].removeChild(o.wkThs[row].firstChild);
                                        o.wkThs[row].appendChild(document.createTextNode(cellAdded[row] == 4 && !o.fillGrid ? nbsp : getWeekNumber(cy, cm, curr - firstColIndex - 6)));
                                        o.wkThs[row].className = "date-picker-week-header" + (["",""," out-of-range"," month-out",""][cellAdded[row]]);
                                };
                        };

                        o.spansAdded = o.kbEventsAdded || o.addSpans;
                        var span = o.titleBar.getElementsByTagName("span");
                        while(span[0].firstChild) span[0].removeChild(span[0].firstChild);
                        while(span[1].firstChild) span[1].removeChild(span[1].firstChild);
                        span[0].appendChild(document.createTextNode(getMonthTranslation(cm, false) + nbsp));
                        span[1].appendChild(document.createTextNode(cy));

                        if(o.timerSet) {
                                o.timerInc = 50 + Math.round(((o.timerInc - 50) / 1.8));
                                o.timer = window.setTimeout(o.updateTable, o.timerInc);
                        };

                        o.inUpdate = false;
                        o.setNewFocus();
                };

                this.show = function(autoFocus) {
                        if(this.staticPos) { return; };

                        var elem = this.getElem();
                        if(!elem || (elem && elem.disabled)) { return; };

                        this.noFocus = true;
                        if(!document.getElementById('fd-' + this.id)) {
                                this.created    = false;
                                this.fullCreate = false;
                                this.create();
                                this.fullCreate = true;
                        } else {
                                this.setDateFromInput();
                                this.reposition();
                        };

                        this.noFocus = !!!autoFocus;

                        if(this.noFocus) {
                                addEvent(document, "mousedown", this.onmousedown);
                        };

                        this.updateTable();

                        this.opacityTo = this.finalOpacity;
                        this.div.style.display = "block";

                        /*@cc_on
                        @if(@_jscript_version <= 5.7)

                        if(!o.isIE7) {
                                this.iePopUp.style.width = this.div.offsetWidth + "px";
                                this.iePopUp.style.height = this.div.offsetHeight + "px";
                                this.iePopUp.style.display = "block";
                        };

                        @end
                        @*/

                        this.setNewFocus();
                        this.fade();
                        var butt = document.getElementById('fd-but-' + this.id);
                        if(butt) { butt.className = butt.className.replace("dp-button-active", "") + " dp-button-active"; };
                };
                this.hide = function() {
                        if(!this.visible || !this.created || !document.getElementById('fd-' + this.id)) return;

                        this.stopTimer();
                        this.removeFocusEvents();

                        if(this.staticPos) { return; };

                        var butt = document.getElementById('fd-but-' + this.id);
                        if(butt) butt.className = butt.className.replace("dp-button-active", "");

                        removeEvent(document, "mousedown", this.onmousedown);

                        /*@cc_on
                        @if(@_jscript_version <= 5.7)
                        if(!this.isIE7) { this.iePopUp.style.display = "none"; };
                        @end
                        @*/

                        this.opacityTo = 0;
                        this.fade();

                        // Programmatically remove the focus style on the calendar
                        this.div.className = this.div.className.replace("datepicker-focus", "");

                        // Update status bar
                        if(this.statusBar) { this.updateStatus(getTitleTranslation(9)); };
                };
                this.destroy = function() {

                        if(document.getElementById("fd-but-" + this.id)) {
                                document.getElementById("fd-but-" + this.id).parentNode.removeChild(document.getElementById("fd-but-" + this.id));
                        };

                        if(!this.created) { return; };

                        // Cleanup for Internet Explorer
                        removeEvent(this.table, "mousedown", o.onmousedown);
                        removeEvent(this.table, "mouseover", o.onmouseover);
                        removeEvent(this.table, "mouseout", o.onmouseout);
                        removeEvent(document, "mousedown", o.onmousedown);
                        removeEvent(document, "mouseup",   o.clearTimer);
                        o.removeFocusEvents();
                        clearTimeout(o.fadeTimer);
                        clearTimeout(o.timer);

                        /*@cc_on
                        @if(@_jscript_version <= 5.7)
                        if(!o.staticPos && !o.isIE7) {
                                try {
                                        o.iePopUp.parentNode.removeChild(o.iePopUp);
                                        o.iePopUp = null;
                                } catch(err) {};
                        };
                        @end
                        @*/

                        if(this.div && this.div.parentNode) {
                                this.div.parentNode.removeChild(this.div);
                        };

                        o = null;
                };
                this.resizeInlineDiv = function()  {
                        o.div.style.width = o.table.offsetWidth + "px";
                        o.div.style.height = o.table.offsetHeight + "px";
                };
                this.create = function() {
                        if(this.created) { return; };

                        this.noFocus = true;

                        function createTH(details) {
                                var th = document.createElement('th');
                                if(details.thClassName) th.className = details.thClassName;
                                if(details.colspan) {
                                        /*@cc_on
                                        /*@if (@_win32)
                                        th.setAttribute('colSpan',details.colspan);
                                        @else @*/
                                        th.setAttribute('colspan',details.colspan);
                                        /*@end
                                        @*/
                                };
                                /*@cc_on
                                /*@if (@_win32)
                                th.unselectable = "on";
                                /*@end@*/
                                return th;
                        };
                        function createThAndButton(tr, obj) {
                                for(var i = 0, details; details = obj[i]; i++) {
                                        var th = createTH(details);
                                        tr.appendChild(th);
                                        var but = document.createElement('span');
                                        but.className = details.className;
                                        but.id = o.id + details.id;
                                        but.appendChild(document.createTextNode(details.text || o.nbsp));
                                        but.title = details.title || "";
                                        /*@cc_on
                                        /*@if(@_win32)
                                        th.unselectable = but.unselectable = "on";
                                        /*@end@*/
                                        th.appendChild(but);
                                };
                        };

                        this.div                     = document.createElement('div');
                        this.div.id                  = "fd-" + this.id;
                        this.div.className           = "datePicker";

                        // Attempt to hide the div from screen readers during content creation
                        this.div.style.visibility = "hidden";
                        this.div.style.display = "none";

                        // Set the ARIA describedby property if the required block available
                        if(document.getElementById("fd-datepicker-aria-describedby")) {
                                setARIAProperty(this.div, "describedby", "fd-datepicker-aria-describedby");
                        };

                        // Set the ARIA labelled property if the required label available
                        if(this.labelledBy) {
                                setARIAProperty(this.div, "labelledby", this.labelledBy.id);
                        };

                        var tr, row, col, tableHead, tableBody, tableFoot;

                        this.table             = document.createElement('table');
                        this.table.className   = "datePickerTable";
                        this.table.onmouseover = this.onmouseover;
                        this.table.onmouseout  = this.onmouseout;
                        this.table.onclick     = this.onclick;

                        if(this.staticPos) {
                                this.table.onmousedown  = this.onmousedown;
                        };

                        this.div.appendChild(this.table);

                        var dragEnabledCN = !this.dragDisabled ? " drag-enabled" : "";

                        if(!this.staticPos) {
                                this.div.style.visibility = "hidden";
                                this.div.className += dragEnabledCN;
                                document.getElementsByTagName('body')[0].appendChild(this.div);

                                /*@cc_on
                                @if(@_jscript_version <= 5.7)

                                if(!this.isIE7) {
                                        this.iePopUp = document.createElement('iframe');
                                        this.iePopUp.src = "javascript:'<html></html>';";
                                        this.iePopUp.setAttribute('className','iehack');
                                        // Remove iFrame from tabIndex
			                this.iePopUp.setAttribute("tabIndex", -1);
                                        // Hide it from ARIA aware technologies
			                setARIARole(this.iePopUp, "presentation");
                                        setARIAProperty(this.iePopUp, "hidden", "true");
                                        this.iePopUp.scrolling = "no";
                                        this.iePopUp.frameBorder = "0";
                                        this.iePopUp.name = this.iePopUp.id = this.id + "-iePopUpHack";
                                        document.body.appendChild(this.iePopUp);
                                };

                                @end
                                @*/

                                // Aria "hidden" property for non active popup datepickers
                                setARIAProperty(this.div, "hidden", "true");
                        } else {
                                elem = this.positioned ? document.getElementById(this.positioned) : this.getElem();
                                if(!elem) {
                                        this.div = null;
                                        throw this.positioned ? "Could not locate a datePickers associated parent element with an id:" + this.positioned : "Could not locate a datePickers associated input with an id:" + this.id;
                                };

                                this.div.className += " static-datepicker";

                                if(this.positioned) {
                                        elem.appendChild(this.div);
                                } else {
                                        elem.parentNode.insertBefore(this.div, elem.nextSibling);
                                };

                                if(this.hideInput) {
                                        var elemList = [elem];
                                        if(this.splitDate) {
                                                elemList[elemList.length] = document.getElementById(this.id + splitAppend[1]);
                                                elemList[elemList.length] = document.getElementById(this.id + splitAppend[0]);
                                        };
                                        for(var i = 0; i < elemList.length; i++) {
                                                if(elemList[i].tagName) elemList[i].className += " fd-hidden-input";
                                        };
                                };

                                setTimeout(this.resizeInlineDiv, 300);
                        };

                        // ARIA Grid role
                        setARIARole(this.div, "grid");

                        if(this.statusFormat) {
                                tableFoot = document.createElement('tfoot');
                                this.table.appendChild(tableFoot);
                                tr = document.createElement('tr');
                                tr.className = "date-picker-tfoot";
                                tableFoot.appendChild(tr);
                                this.statusBar = createTH({thClassName:"date-picker-statusbar" + dragEnabledCN, colspan:this.showWeeks ? 8 : 7});
                                tr.appendChild(this.statusBar);
                                this.updateStatus();
                        };

                        tableHead = document.createElement('thead');
                        this.table.appendChild(tableHead);

                        tr  = document.createElement('tr');
                        setARIARole(tr, "presentation");

                        tableHead.appendChild(tr);

                        // Title Bar
                        this.titleBar = createTH({thClassName:"date-picker-title" + dragEnabledCN, colspan:this.showWeeks ? 8 : 7});

                        tr.appendChild(this.titleBar);
                        tr = null;

                        var span = document.createElement('span');
                        span.appendChild(document.createTextNode(nbsp));
                        span.className = "month-display" + dragEnabledCN;
                        this.titleBar.appendChild(span);

                        span = document.createElement('span');
                        span.appendChild(document.createTextNode(nbsp));
                        span.className = "year-display" + dragEnabledCN;
                        this.titleBar.appendChild(span);

                        span = null;

                        tr  = document.createElement('tr');
                        setARIARole(tr, "presentation");
                        tableHead.appendChild(tr);

                        createThAndButton(tr, [
                        {className:"prev-but prev-year",  id:"-prev-year-but", text:"\u00AB", title:getTitleTranslation(2) },
                        {className:"prev-but prev-month", id:"-prev-month-but", text:"\u2039", title:getTitleTranslation(0) },
                        {colspan:this.showWeeks ? 4 : 3, className:"today-but", id:"-today-but", text:getTitleTranslation(4)},
                        {className:"next-but next-month", id:"-next-month-but", text:"\u203A", title:getTitleTranslation(1)},
                        {className:"next-but next-year",  id:"-next-year-but", text:"\u00BB", title:getTitleTranslation(3) }
                        ]);

                        tableBody = document.createElement('tbody');
                        this.table.appendChild(tableBody);

                        var colspanTotal = this.showWeeks ? 8 : 7,
                            colOffset    = this.showWeeks ? 0 : -1,
                            but, abbr;

                        for(var rows = 0; rows < 7; rows++) {
                                row = document.createElement('tr');

                                if(rows != 0) {
                                        // ARIA Grid role
                                        setARIARole(row, "row");
                                        tableBody.appendChild(row);
                                } else {
                                        tableHead.appendChild(row);
                                };

                                for(var cols = 0; cols < colspanTotal; cols++) {
                                        if(rows === 0 || (this.showWeeks && cols === 0)) {
                                                col = document.createElement('th');
                                        } else {
                                                col = document.createElement('td');

                                                col.onblur  = this.onblur;
                                                col.onfocus = this.onfocus;
                                                setARIAProperty(col, "describedby", this.id + "-col-" + cols + (this.showWeeks ? " " + this.id + "-row-" + rows : ""));
                                                setARIAProperty(col, "selected", "false");
                                        };

                                        /*@cc_on@*/
                                        /*@if(@_win32)
                                        col.unselectable = "on";
                                        /*@end@*/

                                        row.appendChild(col);
                                        if((this.showWeeks && cols > 0 && rows > 0) || (!this.showWeeks && rows > 0)) {
                                                setARIARole(col, "gridcell");
                                        } else {
                                                if(rows === 0 && cols > colOffset) {
                                                        col.className = "date-picker-day-header";
                                                        col.scope = "col";
                                                        setARIARole(col, "columnheader");
                                                        col.id = this.id + "-col-" + cols;
                                                } else {
                                                        col.className = "date-picker-week-header";
                                                        col.scope = "row";
                                                        setARIARole(col, "rowheader");
                                                        col.id = this.id + "-row-" + rows;
                                                };
                                        };
                                };
                        };

                        col = row = null;

                        this.ths = this.table.getElementsByTagName('thead')[0].getElementsByTagName('tr')[2].getElementsByTagName('th');
                        for (var y = 0; y < colspanTotal; y++) {
                                if(y == 0 && this.showWeeks) {
                                        this.ths[y].appendChild(document.createTextNode(getTitleTranslation(6)));
                                        this.ths[y].title = getTitleTranslation(8);
                                        continue;
                                };

                                if(y > (this.showWeeks ? 0 : -1)) {
                                        but = document.createElement("span");
                                        but.className = "fd-day-header";
                                        /*@cc_on@*/
                                        /*@if(@_win32)
                                        but.unselectable = "on";
                                        /*@end@*/
                                        this.ths[y].appendChild(but);
                                };
                        };

                        but = null;

                        this.trs             = this.table.getElementsByTagName('tbody')[0].getElementsByTagName('tr');
                        this.tds             = this.table.getElementsByTagName('tbody')[0].getElementsByTagName('td');
                        this.butPrevYear     = document.getElementById(this.id + "-prev-year-but");
                        this.butPrevMonth    = document.getElementById(this.id + "-prev-month-but");
                        this.butToday        = document.getElementById(this.id + "-today-but");
                        this.butNextYear     = document.getElementById(this.id + "-next-year-but");
                        this.butNextMonth    = document.getElementById(this.id + "-next-month-but");

                        if(this.noToday) {
                                this.butToday.style.display = "none";
                        };

                        if(this.showWeeks) {
                                this.wkThs = this.table.getElementsByTagName('tbody')[0].getElementsByTagName('th');
                                this.div.className += " weeks-displayed";
                        };

                        tableBody = tableHead = tr = createThAndButton = createTH = null;

                        if(this.low && this.high && (this.high - this.low < 7)) { this.equaliseDates(); };

                        this.setDateFromInput();
                        this.updateTableHeaders();
                        this.created = true;
                        this.callback("create", {id:this.id});
                        this.updateTable();

                        if(this.staticPos) {
                                this.visible = true;
                                this.opacity = this.opacityTo;
                                this.div.style.visibility = "visible";
                                this.div.style.display = "block";
                                this.noFocus = true;
                                this.fade();
                        } else {
                                this.reposition();
                                this.div.style.visibility = "visible";
                                this.fade();
                                this.noFocus = true;
                        };

                        this.addSpans = false;
                };
                this.fade = function() {
                        window.clearTimeout(o.fadeTimer);
                        o.fadeTimer = null;
                        var diff = Math.round(o.opacity + ((o.opacityTo - o.opacity) / 4));
                        o.setOpacity(diff);
                        if(Math.abs(o.opacityTo - diff) > 3 && !o.noFadeEffect) {
                                o.fadeTimer = window.setTimeout(o.fade, 50);
                        } else {
                                o.setOpacity(o.opacityTo);
                                if(o.opacityTo == 0) {
                                        o.div.style.display    = "none";
                                        o.div.style.visibility = "hidden";
                                        setARIAProperty(o.div, "hidden", "true");
                                        o.visible = false;
                                } else {
                                        setARIAProperty(o.div, "hidden", "false");
                                        o.visible = true;
                                };
                        };
                };
                this.trackDrag = function(e) {
                        e = e || window.event;
                        var diffx = (e.pageX?e.pageX:e.clientX?e.clientX:e.x) - o.mx;
                        var diffy = (e.pageY?e.pageY:e.clientY?e.clientY:e.Y) - o.my;
                        o.div.style.left = Math.round(o.x + diffx) > 0 ? Math.round(o.x + diffx) + 'px' : "0px";
                        o.div.style.top  = Math.round(o.y + diffy) > 0 ? Math.round(o.y + diffy) + 'px' : "0px";
                        /*@cc_on
                        @if(@_jscript_version <= 5.7)
                        if(o.staticPos || o.isIE7) return;
                        o.iePopUp.style.top    = o.div.style.top;
                        o.iePopUp.style.left   = o.div.style.left;
                        @end
                        @*/
                };
                this.stopDrag = function(e) {
                        removeEvent(document,'mousemove',o.trackDrag, false);
                        removeEvent(document,'mouseup',o.stopDrag, false);
                        o.div.style.zIndex = 9999;
                };
                this.onmousedown = function(e) {
                        e = e || document.parentWindow.event;
                        var el     = e.target != null ? e.target : e.srcElement,
                            origEl = el,
                            hideDP = true,
                            reg    = new RegExp("^fd-(but-)?" + o.id + "$");

                        o.mouseDownElem = null;

                        // Are we within the wrapper div or the button
                        while(el) {
                                if(el.id && el.id.length && el.id.search(reg) != -1) {
                                        hideDP = false;
                                        break;
                                };
                                try { el = el.parentNode; } catch(err) { break; };
                        };

                        // If not, then ...
                        if(hideDP) {
                                hideAll();
                                return true;
                        };

                        if((o.div.className + origEl.className).search('fd-disabled') != -1) { return true; };

                        // We check the mousedown events on the buttons
                        if(origEl.id.search(new RegExp("^" + o.id + "(-prev-year-but|-prev-month-but|-next-month-but|-next-year-but)$")) != -1) {
                                o.mouseDownElem = origEl;

                                addEvent(document, "mouseup", o.clearTimer);
                                addEvent(origEl, "mouseout",  o.clearTimer);

                                var incs = {
                                        "-prev-year-but":[0,-1,0],
                                        "-prev-month-but":[0,0,-1],
                                        "-next-year-but":[0,1,0],
                                        "-next-month-but":[0,0,1]
                                    },
                                    check = origEl.id.replace(o.id, ""),
                                    dateYYYYMM = Number(o.date.getFullYear() + pad(o.date.getMonth()+1));

                                o.timerInc      = (o.currentYYYYMM > dateYYYYMM || o.currentYYYYMM < dateYYYYMM) ? 1600 : 800;
                                o.timerSet      = true;
                                o.dayInc        = incs[check][0];
                                o.yearInc       = incs[check][1];
                                o.monthInc      = incs[check][2];

                                o.addSpans      = false;

                                o.updateTable();

                        } else if(el.className.search("drag-enabled") != -1) {
                                o.mx = e.pageX ? e.pageX : e.clientX ? e.clientX : e.x;
                                o.my = e.pageY ? e.pageY : e.clientY ? e.clientY : e.Y;
                                o.x  = parseInt(o.div.style.left);
                                o.y  = parseInt(o.div.style.top);
                                addEvent(document,'mousemove',o.trackDrag, false);
                                addEvent(document,'mouseup',o.stopDrag, false);
                                o.div.style.zIndex = 10000;
                        };
                        return true;
                };
                this.onclick = function(e) {
                        if(o.opacity != o.opacityTo || o.disabled) return stopEvent(e);

                        e = e || document.parentWindow.event;
                        var el = e.target != null ? e.target : e.srcElement;

                        while(el.parentNode) {
                                // Are we within a valid TD node
                                if(el.tagName && el.tagName.toLowerCase() == "td") {
                                        if(el.className.search(/cd-([0-9]{8})/) == -1 || el.className.search(/date-picker-unused|out-of-range|day-disabled|no-selection|not-selectable/) != -1) return stopEvent(e);
                                        var cellDate = el.className.match(/cd-([0-9]{8})/)[1];
                                        o.date          = new Date(cellDate.substr(0,4),cellDate.substr(4,2)-1,cellDate.substr(6,2));
                                        o.dateSet       = new Date(o.date);
                                        o.noFocus       = true;
                                        o.returnFormattedDate();
                                        o.hide();
                                        o.stopTimer();
                                        break;
                                } else if(el.id && el.id == o.id + "-today-but") {
                                        o.date = new Date();
                                        o.updateTable();
                                        o.stopTimer();
                                        break;
                                } else if(el.className.search(/date-picker-day-header/) != -1) {
                                        var cnt = o.showWeeks ? -1 : 0,
                                        elem = el;

                                        while(elem.previousSibling) {
                                                elem = elem.previousSibling;
                                                if(elem.tagName.toLowerCase() == "th") cnt++;
                                        };

                                        o.firstDayOfWeek = (o.firstDayOfWeek + cnt) % 7;
                                        o.updateTableHeaders();
                                        break;
                                };
                                try { el = el.parentNode; } catch(err) { break; };
                        };

                        return stopEvent(e);
                };
                this.onblur = function(e) {
                        e = e || document.parentWindow.event;
                        var el = e.target != null ? e.target : e.srcElement;

                        while(el.parentNode) {
                                if(el.id && el.id == "fd-" + o.id) {
                                        return true;
                                };
                                try { el = el.parentNode; } catch(err) { break; };
                        };

                        // Remove the keyboard events from the document
                        if(o.kbEventsAdded) o.removeFocusEvents();

                        // Programmatically remove the focus style on the calendar
                        o.div.className = o.div.className.replace("datepicker-focus", "");
                        o.noFocus  = true;
                        o.addSpans = false;
                        o.hide();

                        // Update status bar
                        if(o.statusBar) { o.updateStatus(getTitleTranslation(9)); };
                };
                this.onfocus = function(e) {
                        if(o.staticPos) {
                                o.noFocus = false;
                                if(!o.spansAdded) {
                                        o.addSpans = true;
                                        o.updateTable();
                                };
                        };
                        o.addFocusEvents();
                        o.noFocus = false;
                };
                this.onkeydown = function (e) {
                        o.stopTimer();
                        if(!o.visible) return false;

                        if(e == null) e = document.parentWindow.event;
                        var kc = e.keyCode ? e.keyCode : e.charCode;

                        if( kc == 13 ) {
                                // RETURN/ENTER: close & select the date
                                var td = document.getElementById(o.id + "-date-picker-hover");
                                if(!td || td.className.search(/cd-([0-9]{8})/) == -1 || td.className.search(/no-selection|out-of-range|day-disabled/) != -1) {
                                        return stopEvent(e);
                                };
                                o.dateSet = new Date(o.date);
                                o.returnFormattedDate();
                                o.hide();
                                return stopEvent(e);
                        } else if(kc == 27) {
                                // ESC: close, no date selection
                                o.hide();
                                return stopEvent(e);
                        } else if(kc == 32 || kc == 0) {
                                // SPACE: goto today's date
                                o.date = new Date();
                                o.updateTable();
                                return stopEvent(e);
                        };

                        // Internet Explorer fires the keydown event faster than the JavaScript engine can
                        // update the interface. The following attempts to fix this.

                        /*@cc_on
                        @if(@_win32)
                        if(new Date().getTime() - o.interval.getTime() < 50) { return stopEvent(e); };
                        o.interval = new Date();
                        @end
                        @*/

                        if ((kc > 49 && kc < 56) || (kc > 97 && kc < 104)) {
                                if(kc > 96) kc -= (96-48);
                                kc -= 49;
                                o.firstDayOfWeek = (o.firstDayOfWeek + kc) % 7;
                                o.updateTableHeaders();
                                return stopEvent(e);
                        };

                        if ( kc < 33 || kc > 40 ) return true;

                        var d = new Date(o.date), tmp, cursorYYYYMM = o.date.getFullYear() + pad(o.date.getMonth()+1);

                        // HOME: Set date to first day of current month
                        if(kc == 36) {
                                d.setDate(1);
                        // END: Set date to last day of current month
                        } else if(kc == 35) {
                                d.setDate(daysInMonth(d.getMonth(),d.getFullYear()));
                        // PAGE UP & DOWN
                        } else if ( kc == 33 || kc == 34) {
                                var add = (kc == 34) ? 1 : -1;

                                // CTRL + PAGE UP/DOWN: Moves to the same date in the previous/next year
                                if(e.ctrlKey) {
                                        d.setFullYear(d.getFullYear() + add);
                                // PAGE UP/DOWN: Moves to the same date in the previous/next month
                                } else {
                                        if(!((kc == 33 && o.currentYYYYMM > cursorYYYYMM) || (kc == 34 && o.currentYYYYMM < cursorYYYYMM))) {
                                                tmp = new Date(d);
                                                tmp.setDate(2);
                                                tmp.setMonth(d.getMonth() + add);
                                                d.setDate(Math.min(d.getDate(), daysInMonth(tmp.getMonth(),tmp.getFullYear())));
                                                d.setMonth(d.getMonth() + add);
                                        };
                                };
                        // LEFT ARROW
                        } else if ( kc == 37 ) {
                                d = new Date(o.date.getFullYear(), o.date.getMonth(), o.date.getDate() - 1);
                        // RIGHT ARROW
                        } else if ( kc == 39 || kc == 34) {
                                d = new Date(o.date.getFullYear(), o.date.getMonth(), o.date.getDate() + 1 );
                        // UP ARROW
                        } else if ( kc == 38 ) {
                                d = new Date(o.date.getFullYear(), o.date.getMonth(), o.date.getDate() - 7);
                        // DOWN ARROW
                        } else if ( kc == 40 ) {
                                d = new Date(o.date.getFullYear(), o.date.getMonth(), o.date.getDate() + 7);
                        };

                        if(o.outOfRange(d)) { return stopEvent(e); };
                        o.date = d;

                        if(o.statusBar) { o.updateStatus(printFormattedDate(o.date, o.statusFormat, true)); };
                        var t = String(o.date.getFullYear()) + pad(o.date.getMonth()+1) + pad(o.date.getDate());

                        if(e.ctrlKey || (kc == 33 || kc == 34) || t < o.firstDateShown || t > o.lastDateShown) {
                                o.updateTable();
                                /*@cc_on
                                @if(@_win32)
                                o.interval = new Date();
                                @end
                                @*/
                        } else {
                                if(!o.noToday) { o.disableTodayButton(); };
                                o.removeOldFocus();
                                var dt = "cd-" + o.date.getFullYear() + pad(o.date.getMonth()+1) + pad(o.date.getDate());

                                for(var i = 0, td; td = o.tds[i]; i++) {
                                        if(td.className.search(dt) == -1) {
                                                continue;
                                        };
                                        o.showHideButtons(o.date);
                                        td.id = o.id + "-date-picker-hover";
                                        o.setNewFocus();
                                        break;
                                };
                        };

                        return stopEvent(e);
                };
                this.onmouseout = function(e) {
                        e = e || document.parentWindow.event;
                        var p = e.toElement || e.relatedTarget;
                        while (p && p != this) try { p = p.parentNode } catch(e) { p = this; };
                        if (p == this) return false;
                        if(o.currentTR) {
                                o.currentTR.className = "";
                                o.currentTR = null;
                        };
                        if(o.statusBar) { o.updateStatus(printFormattedDate(o.date, o.statusFormat, true)); };
                };
                this.onmouseover = function(e) {
                        e = e || document.parentWindow.event;
                        var el = e.target != null ? e.target : e.srcElement;
                        while(el.nodeType != 1) { el = el.parentNode; };

                        if(!el || ! el.tagName) { return; };

                        var statusText = getTitleTranslation(9);
                        switch (el.tagName.toLowerCase()) {
                                case "td":
                                        if(el.className.search(/date-picker-unused|out-of-range/) != -1) {
                                                statusText = getTitleTranslation(9);
                                        } if(el.className.search(/cd-([0-9]{8})/) != -1) {
                                                o.stopTimer();
                                                var cellDate = el.className.match(/cd-([0-9]{8})/)[1];

                                                o.removeOldFocus();
                                                el.id = o.id+"-date-picker-hover";
                                                o.setNewFocus();

                                                o.date = new Date(+cellDate.substr(0,4),+cellDate.substr(4,2)-1,+cellDate.substr(6,2));
                                                if(!o.noToday) { o.disableTodayButton(); };
                                                statusText = printFormattedDate(o.date, o.statusFormat, true);
                                        };
                                        break;
                                case "th":
                                        if(!o.statusBar) { break; };
                                        if(el.className.search(/drag-enabled/) != -1) {
                                                statusText = getTitleTranslation(10);
                                        } else if(el.className.search(/date-picker-week-header/) != -1) {
                                                var txt = el.firstChild ? el.firstChild.nodeValue : "";
                                                statusText = txt.search(/^(\d+)$/) != -1 ? getTitleTranslation(7, [txt, txt < 3 && o.date.getMonth() == 11 ? getWeeksInYear(o.date.getFullYear()) + 1 : getWeeksInYear(o.date.getFullYear())]) : getTitleTranslation(9);
                                        };
                                        break;
                                case "span":
                                        if(!o.statusBar) { break; };
                                        if(el.className.search(/drag-enabled/) != -1) {
                                                statusText = getTitleTranslation(10);
                                        } else if(el.className.search(/day-([0-6])/) != -1) {
                                                var day = el.className.match(/day-([0-6])/)[1];
                                                statusText = getTitleTranslation(11, [getDayTranslation(day, false)]);
                                        } else if(el.className.search(/prev-year/) != -1) {
                                                statusText = getTitleTranslation(2);
                                        } else if(el.className.search(/prev-month/) != -1) {
                                                statusText = getTitleTranslation(0);
                                        } else if(el.className.search(/next-year/) != -1) {
                                                statusText = getTitleTranslation(3);
                                        } else if(el.className.search(/next-month/) != -1) {
                                                statusText = getTitleTranslation(1);
                                        } else if(el.className.search(/today-but/) != -1 && el.className.search(/disabled/) == -1) {
                                                statusText = getTitleTranslation(12);
                                        };
                                        break;
                                default:
                                        statusText = "";
                        };
                        while(el.parentNode) {
                                el = el.parentNode;
                                if(el.nodeType == 1 && el.tagName.toLowerCase() == "tr") {
                                        if(o.currentTR) {
                                                if(el == o.currentTR) break;
                                                o.currentTR.className = "";
                                        };
                                        el.className = "dp-row-highlight";
                                        o.currentTR = el;
                                        break;
                                };
                        };
                        if(o.statusBar && statusText) { o.updateStatus(statusText); };
                };
                this.clearTimer = function() {
                        o.stopTimer();
                        o.timerInc      = 800;
                        o.yearInc       = 0;
                        o.monthInc      = 0;
                        o.dayInc        = 0;

                        removeEvent(document, "mouseup", o.clearTimer);
                        if(o.mouseDownElem != null) {
                                removeEvent(o.mouseDownElem, "mouseout",  o.clearTimer);
                        };
                        o.mouseDownElem = null;
                };

                var o = this;


                if(this.staticPos) {
                        this.create();
                } else {
                        this.createButton();
                };

                this.setDateFromInput();

                (function() {
                        var elem = o.getElem();
                        if(elem && elem.tagName && elem.tagName.search(/select|input/i) != -1) {
                                addEvent(elem, "change", o.changeHandler);
                                if(this.splitDate) {
                                        addEvent(document.getElementById(o.id + splitAppend[1]), "change", o.changeHandler);
                                        addEvent(document.getElementById(o.id + splitAppend[0]), "change", o.changeHandler);
                                };
                        };

                        if(!elem || elem.disabled == true) {
                               o.disableDatePicker();
                        };
                })();

                // We have fully created the datepicker...
                this.fullCreate = true;
        };
        datePicker.prototype.addButtonEvents = function(but) {
                but.onkeydown = but.onclick = function(e) {
                        e = e || window.event;

                        var inpId     = this.id.replace('fd-but-',''),
                            dpVisible = isVisible(inpId),
                            autoFocus = false;

                        if(e.type == "keydown") {
                                var kc = e.keyCode != null ? e.keyCode : e.charCode;
                                if(kc != 13) return true;
                                if(dpVisible) {
                                        this.className = this.className.replace("dp-button-active", "");
                                        hideAll();
                                        return false;
                                };
                                autoFocus = true;
                        };

                        this.className = this.className.replace("dp-button-active", "");

                        if(!dpVisible) {
                                this.className += " dp-button-active";
                                hideAll(inpId);
                                showDatePicker(inpId, autoFocus);
                        } else {
                                hideAll();
                        };

                        return false;
                };

                if(!buttonTabIndex) {
                        but.setAttribute(!/*@cc_on!@*/false ? "tabIndex" : "tabindex", "-1");
                        but.tabIndex = -1;
                        but.onkeydown = null;
                } else {
                        but.setAttribute(!/*@cc_on!@*/false ? "tabIndex" : "tabindex", "0");
                        but.tabIndex = 0;
                };
        };

        datePicker.prototype.createButton = function() {

                if(this.staticPos || document.getElementById("fd-but-" + this.id)) { return; };

                var inp         = this.getElem(),
                    span        = document.createElement('span'),
                    but         = document.createElement('a');

                but.href        = "#" + this.id;
                but.className   = "date-picker-control";
                but.title       = getTitleTranslation(5);
                but.id          = "fd-but-" + this.id;

                span.appendChild(document.createTextNode(nbsp));
                but.appendChild(span);

                span = document.createElement('span');
                span.className = "fd-screen-reader";
                span.appendChild(document.createTextNode(but.title));
                but.appendChild(span);

                // Set the ARIA role to be "button"
                setARIARole(but, "button");

                // Set a "haspopup" ARIA property - should this not be a list if ID's????
                setARIAProperty(but, "haspopup", true);

                if(this.buttonWrapper && document.getElementById(this.buttonWrapper)) {
                        document.getElementById(this.buttonWrapper).appendChild(but);
                } else if(inp.nextSibling) {
                        inp.parentNode.insertBefore(but, inp.nextSibling);
                } else {
                        inp.parentNode.appendChild(but);
                };

                this.addButtonEvents(but);

                but = null;
        };
        datePicker.prototype.setRangeLow = function(range) {
                this.low = (String(range).search(/^(\d\d\d\d)(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])$/) == -1) ? false : range;
                this.checkSelectedDate();
                if(this.created) { this.updateTable(); };
        };
        datePicker.prototype.setRangeHigh = function(range) {
                this.high = (String(range).search(/^(\d\d\d\d)(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])$/) == -1) ? false : range;
                this.checkSelectedDate();
                if(this.created) { this.updateTable(); };
        };
        datePicker.prototype.setDisabledDays = function(dayArray) {
                this.disabledDays = dayArray;
                this.checkSelectedDate();
                if(this.created) { this.updateTable(); };
        };
        datePicker.prototype.setDisabledDates = function(dateArray) {
                this.disabledDates = {};
                this.addDisabledDates(dateArray);
        };
        datePicker.prototype.addDisabledDates = function(dateArray) {
                var disabledDateObj = {};
                if(typeof dateArray !== "object") dateArray = [dateArray];
                for(var i = dateArray.length; i-- ;) {
                        if(dateArray[i].match(/^(\d\d\d\d|\*\*\*\*)(0[1-9]|1[012]|\*\*)(0[1-9]|[12][0-9]|3[01])$/) != -1) {
                                this.disabledDates[dateArray[i]] = 1;
                        };
                };
                this.checkSelectedDate();
                if(this.created) { this.updateTable(); };
        };
        datePicker.prototype.checkSelectedDate = function() {
                if(!this.dateSet) return;
                if(!this.canDateBeSelected(this.dateSet)) {
                        this.dateSet = null;
                };
        };
        datePicker.prototype.addFocusEvents = function() {
                if(this.kbEventsAdded || this.noFocus) {
                        return;
                };
                this.div.className = this.div.className.replace(/datepicker-focus/, "") + " datepicker-focus";
                addEvent(document, "keypress", this.onkeydown);
                addEvent(document, "mousedown", this.onmousedown);

                /*@cc_on
                @if(@_win32)
                removeEvent(document, "keypress", this.onkeydown);
                addEvent(document, "keydown", this.onkeydown);
                @end
                @*/
                if(window.devicePixelRatio) {
                        removeEvent(document, "keypress", this.onkeydown);
                        addEvent(document, "keydown", this.onkeydown);
                };
                this.noFocus = false;
                this.kbEventsAdded = true;
        };
        datePicker.prototype.removeFocusEvents = function() {
                if(!this.kbEventsAdded) { return; };
                this.div.className = this.div.className.replace(/datepicker-focus/, "");
                removeEvent(document, "keypress",  this.onkeydown);
                removeEvent(document, "keydown",   this.onkeydown);
                removeEvent(document, "mousedown", this.onmousedown);
                this.noFocus       = true;
                this.kbEventsAdded = false;
        };
        datePicker.prototype.stopTimer = function() {
                this.timerSet = false;
                window.clearTimeout(this.timer);
        };
        datePicker.prototype.setOpacity = function(op) {
                this.div.style.opacity = op/100;
                this.div.style.filter = 'alpha(opacity=' + op + ')';
                this.opacity = op;
        };
        datePicker.prototype.getElem = function() {
                return document.getElementById(this.id.replace(/^fd-/, '')) || false;
        };
        datePicker.prototype.getDisabledDates = function(y, m) {
                m = pad(m);

                var obj = {},
                    lower  = this.firstDateShown,
                    upper  = this.lastDateShown,
                    dt1, dt2, rngLower, rngUpper;

                if(!upper || !lower) {
                        lower = this.firstDateShown = y + pad(m) + "01";
                        upper = this.lastDateShown  = y + pad(m) + pad(daysInMonth(m, y));
                };

                for(var dt in this.disabledDates) {
                        dt1 = dt.replace(/^(\*\*\*\*)/, y).replace(/^(\d\d\d\d)(\*\*)/, "$1"+m);
                        dt2 = this.disabledDates[dt];

                        if(dt2 == 1) {
                                if(+lower <= +dt1 && +upper >= +dt1) {
                                        obj[dt1] = 1;
                                };
                                continue;
                        };

                        // Range of disabled dates
                        if(Number(dt1.substr(0,6)) <= +String(this.firstDateShown).substr(0,6) && Number(dt2.substr(0,6)) >= +String(this.lastDateShown).substr(0,6)) {
                                // Same month
                                if(Number(dt1.substr(0,6)) == Number(dt2.substr(0,6))) {
                                        for(var i = dt1; i <= dt2; i++) {
                                                obj[i] = 1;
                                        };
                                        continue;
                                };

                                // Different months but we only want this month
                                rngLower = Number(dt1.substr(0,6)) == +String(this.firstDateShown).substr(0,6) ? dt1 : lower;
                                rngUpper = Number(dt2.substr(0,6)) == +String(this.lastDateShown).substr(0,6) ? dt2 : upper;
                                for(var i = +rngLower; i <= +rngUpper; i++) {
                                        obj[i] = 1;
                                };
                        };
                };

                for(dt in this.enabledDates) {
                        dt1 = dt.replace(/^(\*\*\*\*)/, y).replace(/^(\d\d\d\d)(\*\*)/, "$1"+m);
                        dt2 = this.enabledDates[dt];

                        if(dt2 == 1) {
                                if(dt1 in obj) {
                                        obj[dt1] = null;
                                        delete obj[dt1];
                                };
                                continue;
                        };

                        // Range
                        if(Number(dt1.substr(0,6)) <= +String(this.firstDateShown).substr(0,6) && Number(dt2.substr(0,6)) >= +String(this.lastDateShown).substr(0,6)) {
                                // Same month
                                if(Number(dt1.substr(0,6)) == Number(dt2.substr(0,6))) {
                                        for(var i = dt1; i <= dt2; i++) {
                                                if(i in obj) {
                                                        obj[i] = null;
                                                        delete obj[i];
                                                };
                                        };
                                        continue;
                                };

                                // Different months but we only want this month
                                rngLower = Number(dt1.substr(0,6)) == +String(this.firstDateShown).substr(0,6) ? dt1 : lower;
                                rngUpper = Number(dt2.substr(0,6)) == +String(this.lastDateShown).substr(0,6) ? dt2 : upper;
                                for(var i = +rngLower; i <= +rngUpper; i++) {
                                        if(i in obj) {
                                                obj[i] = null;
                                                delete obj[i];
                                        };
                                };
                        };
                };
                return obj;
        };
        datePicker.prototype.truePosition = function(element) {
                var pos = this.cumulativeOffset(element);
                if(window.opera) { return pos; };
                var iebody      = (document.compatMode && document.compatMode != "BackCompat")? document.documentElement : document.body,
                    dsocleft    = document.all ? iebody.scrollLeft : window.pageXOffset,
                    dsoctop     = document.all ? iebody.scrollTop  : window.pageYOffset,
                    posReal     = this.realOffset(element);
                return [pos[0] - posReal[0] + dsocleft, pos[1] - posReal[1] + dsoctop];
        };
        datePicker.prototype.realOffset = function(element) {
                var t = 0, l = 0;
                do {
                        t += element.scrollTop  || 0;
                        l += element.scrollLeft || 0;
                        element = element.parentNode;
                } while(element);
                return [l, t];
        };
        datePicker.prototype.cumulativeOffset = function(element) {
                var t = 0, l = 0;
                do {
                        t += element.offsetTop  || 0;
                        l += element.offsetLeft || 0;
                        element = element.offsetParent;
                } while(element);
                return [l, t];
        };
        datePicker.prototype.equaliseDates = function() {
                var clearDayFound = false, tmpDate;
                for(var i = this.low; i <= this.high; i++) {
                        tmpDate = String(i);
                        if(!this.disabledDays[new Date(tmpDate.substr(0,4), tmpDate.substr(6,2), tmpDate.substr(4,2)).getDay() - 1]) {
                                clearDayFound = true;
                                break;
                        };
                };
                if(!clearDayFound) { this.disabledDays = [0,0,0,0,0,0,0] };
        };
        datePicker.prototype.outOfRange = function(tmpDate) {
                if(!this.low && !this.high) { return false; };

                var level = false;
                if(!tmpDate) {
                        level   = true;
                        tmpDate = this.date;
                };

                var d  = pad(tmpDate.getDate()),
                    m  = pad(tmpDate.getMonth() + 1),
                    y  = tmpDate.getFullYear(),
                    dt = String(y)+String(m)+String(d);

                if(this.low && +dt < +this.low) {
                        if(!level) { return true; };
                        this.date = new Date(this.low.substr(0,4), this.low.substr(4,2)-1, this.low.substr(6,2), 5, 0, 0);
                        return false;
                };
                if(this.high && +dt > +this.high) {
                        if(!level) { return true; };
                        this.date = new Date(this.high.substr(0,4), this.high.substr(4,2)-1, this.high.substr(6,2), 5, 0, 0);
                };
                return false;
        };
        datePicker.prototype.canDateBeSelected = function(tmpDate) {
                if(!tmpDate) return false;

                var d  = pad(tmpDate.getDate()),
                    m  = pad(tmpDate.getMonth() + 1),
                    y  = tmpDate.getFullYear(),
                    dt = String(y)+String(m)+String(d),
                    dd = this.getDisabledDates(+y, +m),
                    wd = printFormattedDate(tmpDate, "W");

                if((this.low && +dt < +this.low) || (this.high && +dt > +this.high) || (dt in dd) || this.disabledDays[wd]) {
                        return false;
                };

                return true;
        };
        datePicker.prototype.updateStatus = function(msg) {
                while(this.statusBar.firstChild) { this.statusBar.removeChild(this.statusBar.firstChild); };

                if(msg && this.statusFormat.search(/-S|S-/) != -1 && msg.search(/([0-9]{1,2})(st|nd|rd|th)/) != -1) {
                        msg = msg.replace(/([0-9]{1,2})(st|nd|rd|th)/, "$1<sup>$2</sup>").split(/<sup>|<\/sup>/);
                        var dc = document.createDocumentFragment();
                        for(var i = 0, nd; nd = msg[i]; i++) {
                                if(/^(st|nd|rd|th)$/.test(nd)) {
                                        var sup = document.createElement("sup");
                                        sup.appendChild(document.createTextNode(nd));
                                        dc.appendChild(sup);
                                } else {
                                        dc.appendChild(document.createTextNode(nd));
                                };
                        };
                        this.statusBar.appendChild(dc);
                } else {
                        this.statusBar.appendChild(document.createTextNode(msg ? msg : getTitleTranslation(9)));
                };
        };
        datePicker.prototype.setDateFromInput = function() {
                this.dateSet = null;

                var elem = this.getElem(),
                    upd  = false,
                    dt;

                if(!elem || elem.tagName.search(/select|input/i) == -1) return;

                if(!this.splitDate && elem.value.replace(/\s/g, "") !== "") {
                        var dynFormatMasks = formatMasks.concat([this.format]).reverse();
                        for(var i = 0, fmt; fmt = dynFormatMasks[i]; i++) {
                                dt = parseDateString(elem.value, fmt);
                                if(dt) {
                                        upd = true;
                                        break;
                                };
                        };
                } else if(this.splitDate) {
                        var mmN  = document.getElementById(this.id + splitAppend[1]),
                            ddN  = document.getElementById(this.id + splitAppend[0]),
                            tm   = parseInt(mmN.tagName.toLowerCase() == "input"  ? mmN.value  : mmN.options[mmN.selectedIndex || 0].value, 10),
                            td   = parseInt(ddN.tagName.toLowerCase() == "input"  ? ddN.value  : ddN.options[ddN.selectedIndex || 0].value, 10),
                            ty   = parseInt(elem.tagName.toLowerCase() == "input" ? elem.value : elem.options[elem.selectedIndex || 0].value, 10);

                        if(!(/\d\d\d\d/.test(ty)) || !(/^(0?[1-9]|1[012])$/.test(tm)) || !(/^(0?[1-9]|[12][0-9]|3[01])$/.test(td))) {
                                dt = false;
                        } else {
                                if(+td > daysInMonth(+tm - 1, +ty)) {
                                        upd = true;
                                        td  = daysInMonth(+tm - 1, +ty);
                                        dt  = new Date(ty,tm-1,td);
                                } else {
                                        dt = new Date(ty,tm-1,td);
                                };
                        };
                };

                if(!dt || isNaN(dt)) {
                        this.date = new Date();
                        this.date.setHours(5);
                        this.outOfRange();
                        return;
                };

                dt.setHours(5);
                this.date = new Date(dt);
                this.outOfRange();

                if(dt.getTime() == this.date.getTime() && this.canDateBeSelected(this.date)) {
                        this.dateSet = new Date(this.date);
                };

                if(upd) { this.returnFormattedDate(true); };
        };
        datePicker.prototype.setSelectIndex = function(elem, indx) {
                for(var opt = elem.options.length-1; opt >= 0; opt--) {
                        if(elem.options[opt].value == +indx) {
                                elem.selectedIndex = opt;
                                return;
                        };
                };
        };
        datePicker.prototype.returnFormattedDate = function(noFocus) {
                var elem = this.getElem();
                if(!elem || this.dateSet == null) return;

                noFocus = !!noFocus;

                var d                   = pad(this.date.getDate()),
                    m                   = pad(this.date.getMonth() + 1),
                    yyyy                = this.date.getFullYear(),
                    disabledDates       = this.getDisabledDates(+yyyy, +m),
                    weekDay             = (this.date.getDay() + 6) % 7;

                if(!(this.disabledDays[weekDay] || String(yyyy)+m+d in this.disabledDates)) {
                        if(this.splitDate) {
                                var ddE = document.getElementById(this.id+splitAppend[0]),
                                    mmE = document.getElementById(this.id+splitAppend[1]),
                                    tY = elem.value, tM = mmE.value, tD = ddE.value;

                                if(+tY == +yyyy && +tM == +m && +tD == +d) { return; };

                                if(ddE.tagName.toLowerCase() == "input") { ddE.value = d; }
                                else { this.setSelectIndex(ddE, d); };
                                if(mmE.tagName.toLowerCase() == "input") { mmE.value = m; }
                                else { this.setSelectIndex(mmE, m); };
                                if(elem.tagName.toLowerCase() == "input") elem.value = yyyy;
                                else { this.setSelectIndex(elem, yyyy); };
                        } else if(elem.tagName.toLowerCase() == "input") {
                                var oldVal = elem.value,
                                    newVal = printFormattedDate(this.date, this.format, returnLocaleDate);
                                if(oldVal == newVal) { return; };
                                elem.value = newVal;
                        };

                        if(this.staticPos) {
                                this.noFocus = true;
                                this.updateTable();
                                this.noFocus = false;
                        };

                        if(this.fullCreate) {
                                if(elem.type && elem.type != "hidden" && !noFocus) { elem.focus(); };
                                this.callback("dateselect", { "id":this.id, "date":this.dateSet, "dd":d, "mm":m, "yyyy":yyyy });
                        };
                };
        };
        datePicker.prototype.disableDatePicker = function() {
                if(this.disabled) return;

                if(this.staticPos) {
                        this.removeFocusEvents();
                        this.noFocus = true;
                        this.div.className = this.div.className.replace(/dp-disabled/, "") + " dp-disabled";
                        this.table.onmouseover = this.table.onclick = this.table.onmouseout = this.table.onmousedown = null;
                        removeEvent(document, "mousedown", this.onmousedown);
                        removeEvent(document, "mouseup",   this.clearTimer);
                } else {
                        if(this.visible) this.hide();
                        var but = document.getElementById("fd-but-" + this.id);
                        if(but) {
                                but.className = but.className.replace(/dp-disabled/, "") + " dp-disabled";
                                // Set a "disabled" ARIA state
                                setARIAProperty(but, "disabled", true);
                                but.onkeydown = but.onclick = function() { return false; };
                                but.setAttribute(!/*@cc_on!@*/false ? "tabIndex" : "tabindex", "-1");
                                but.tabIndex = -1;
                        };
                };

                clearTimeout(this.timer);

                this.disabled = true;
        };
        datePicker.prototype.enableDatePicker = function() {
                if(!this.disabled) return;

                if(this.staticPos) {
                        this.removeOldFocus();
                        this.noFocus = true;
                        this.addSpans = true;
                        this.updateTable();
                        this.div.className = this.div.className.replace(/dp-disabled/, "");
                        this.disabled = false;
                        this.table.onmouseover = this.onmouseover;
                        this.table.onmouseout  = this.onmouseout;
                        this.table.onclick     = this.onclick;
                        this.table.onmousedown = this.onmousedown;
                } else {
                        var but = document.getElementById("fd-but-" + this.id);
                        if(but) {
                                but.className = but.className.replace(/dp-disabled/, "");
                                // Reset the "disabled" ARIA state
                                setARIAProperty(but, "disabled", false);
                                this.addButtonEvents(but);
                        };
                };

                this.disabled = false;
        };
        datePicker.prototype.disableTodayButton = function() {
                var today = new Date();
                this.butToday.className = this.butToday.className.replace("fd-disabled", "");
                if(this.outOfRange(today) || (this.date.getDate() == today.getDate() && this.date.getMonth() == today.getMonth() && this.date.getFullYear() == today.getFullYear())) {
                        this.butToday.className += " fd-disabled";
                };
        };
        datePicker.prototype.updateTableHeaders = function() {
                var colspanTotal = this.showWeeks ? 8 : 7,
                    colOffset    = this.showWeeks ? 1 : 0,
                    d, but;

                for(var col = colOffset; col < colspanTotal; col++ ) {
                        d = (this.firstDayOfWeek + (col - colOffset)) % 7;
                        this.ths[col].title = getDayTranslation(d, false);

                        if(col > colOffset) {
                                but = this.ths[col].getElementsByTagName("span")[0];
                                while(but.firstChild) { but.removeChild(but.firstChild); };
                                but.appendChild(document.createTextNode(getDayTranslation(d, true)));
                                but.title = this.ths[col].title;
                                but.className = but.className.replace(/day-([0-6])/, "") + " day-" + d;
                                but = null;
                        } else {
                                while(this.ths[col].firstChild) { this.ths[col].removeChild(this.ths[col].firstChild); };
                                this.ths[col].appendChild(document.createTextNode(getDayTranslation(d, true)));
                        };

                        this.ths[col].className = this.ths[col].className.replace(/date-picker-highlight/g, "");
                        if(this.highlightDays[d]) {
                                this.ths[col].className += " date-picker-highlight";
                        };
                };

                if(this.created) { this.updateTable(); }
        };
        datePicker.prototype.callback = function(type, args) {
                if(!type || !(type in this.callbacks)) return false;

                var ret = false;
                for(var func = 0; func < this.callbacks[type].length; func++) {
                        ret = this.callbacks[type][func](args || this.id);
                        if(!ret) return false;
                };
                return ret;
        };
        datePicker.prototype.showHideButtons = function(tmpDate) {
                var tdm = tmpDate.getMonth(),
                    tdy = tmpDate.getFullYear();

                this.butPrevYear.className = this.butPrevYear.className.replace("fd-disabled", "");
                if(this.outOfRange(new Date((tdy - 1), tdm, daysInMonth(+tdm, tdy-1)))) {
                        this.butPrevYear.className += " fd-disabled";
                        if(this.yearInc == -1) this.stopTimer();
                };

                this.butPrevMonth.className = this.butPrevMonth.className.replace("fd-disabled", "");
                if(this.outOfRange(new Date(tdy, (+tdm - 1), daysInMonth(+tdm-1, tdy)))) {
                        this.butPrevMonth.className += " fd-disabled";
                        if(this.monthInc == -1) this.stopTimer();
                };

                this.butNextYear.className = this.butNextYear.className.replace("fd-disabled", "");
                if(this.outOfRange(new Date((tdy + 1), +tdm, 1))) {
                        this.butNextYear.className += " fd-disabled";
                        if(this.yearInc == 1) this.stopTimer();
                };

                this.butNextMonth.className = this.butNextMonth.className.replace("fd-disabled", "");
                if(this.outOfRange(new Date(tdy, +tdm + 1, 1))) {
                        this.butNextMonth.className += " fd-disabled";
                        if(this.monthInc == 1) this.stopTimer();
                };
        };
        var localeDefaults = {
                fullMonths:["January","February","March","April","May","June","July","August","September","October","November","December"],
                monthAbbrs:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],
                fullDays:  ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"],
                dayAbbrs:  ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"],
                titles:    ["Previous month","Next month","Previous year","Next year", "Today", "Show Calendar", "wk", "Week [[%0%]] of [[%1%]]", "Week", "Select a date", "Click \u0026 Drag to move", "Display \u201C[[%0%]]\u201D first", "Go to Today\u2019s date", "Disabled date"],
                firstDayOfWeek:0,
                imported:  false
        };
        var grepRangeLimits = function(sel) {
                var range = [];
                for(var i = 0; i < sel.options.length; i++) {
                        if(sel.options[i].value.search(/^\d\d\d\d$/) == -1) { continue; };
                        if(!range[0] || Number(sel.options[i].value) < range[0]) { range[0] = Number(sel.options[i].value); };
                        if(!range[1] || Number(sel.options[i].value) > range[1]) { range[1] = Number(sel.options[i].value); };
                };
                return range;
        };
        var joinNodeLists = function() {
                if(!arguments.length) { return []; }
                var nodeList = [];
                for (var i = 0; i < arguments.length; i++) {
                        for (var j = 0, item; item = arguments[i][j]; j++) {
                                nodeList[nodeList.length] = item;
                        };
                };
                return nodeList;
        };
        var cleanUp = function() {
                var dp;
                for(dp in datePickers) {
                        if(!document.getElementById(datePickers[dp].id)) {
                                if(datePickers[dp].created) datePickers[dp].destroy();
                                datePickers[dp] = null;
                                delete datePickers[dp];
                        };
                };
        };
        var hideAll = function(exception) {
                var dp;
                for(dp in datePickers) {
                        if(!datePickers[dp].created || (exception && exception == datePickers[dp].id)) continue;
                        datePickers[dp].hide();
                };
        };
        var hideDatePicker = function(inpID) {
                if(inpID in datePickers) {
                        if(!datePickers[inpID].created || datePickers[inpID].staticPos) return;
                        datePickers[inpID].hide();
                };
        };
        var showDatePicker = function(inpID, autoFocus) {
                if(!(inpID in datePickers)) return false;
                datePickers[inpID].show(autoFocus);
                return true;
        };
        var destroy = function() {
                for(dp in datePickers) {
                        datePickers[dp].destroy();
                        datePickers[dp] = null;
                        delete datePickers[dp];
                };
                datePickers = null;
                removeEvent(window, 'load',   datePickerController.create);
                removeEvent(window, 'unload', datePickerController.destroy);
        };
        var destroySingleDatePicker = function(id) {
                if(id && (id in datePickers)) {
                        datePickers[id].destroy();
                        datePickers[id] = null;
                        delete datePickers[id];
                };
        };
        var getTitleTranslation = function(num, replacements) {
                replacements = replacements || [];
                if(localeImport.titles.length > num) {
                         var txt = localeImport.titles[num];
                         if(replacements && replacements.length) {
                                for(var i = 0; i < replacements.length; i++) {
                                        txt = txt.replace("[[%" + i + "%]]", replacements[i]);
                                };
                         };
                         return txt.replace(/[[%(\d)%]]/g,"");
                };
                return "";
        };
        var getDayTranslation = function(day, abbreviation) {
                var titles = localeImport[abbreviation ? "dayAbbrs" : "fullDays"];
                return titles.length && titles.length > day ? titles[day] : "";
        };
        var getMonthTranslation = function(month, abbreviation) {
                var titles = localeImport[abbreviation ? "monthAbbrs" : "fullMonths"];
                return titles.length && titles.length > month ? titles[month] : "";
        };
        var daysInMonth = function(nMonth, nYear) {
                nMonth = (nMonth + 12) % 12;
                return (((0 == (nYear%4)) && ((0 != (nYear%100)) || (0 == (nYear%400)))) && nMonth == 1) ? 29: [31,28,31,30,31,30,31,31,30,31,30,31][nMonth];
        };
        var getWeeksInYear = function(Y) {
                if(Y in weeksInYearCache) {
                        return weeksInYearCache[Y];
                };
                var X1, X2, NW;
                with (X1 = new Date(Y, 0, 4)) {
                        setDate(getDate() - (6 + getDay()) % 7);
                };
                with (X2 = new Date(Y, 11, 28)) {
                        setDate(getDate() + (7 - getDay()) % 7);
                };
                weeksInYearCache[Y] = Math.round((X2 - X1) / 604800000);
                return weeksInYearCache[Y];
        };
        var parseRangeFromString = function(str) {
                if(!str) return "";

                var low = str.search(/^range-low-/) != -1;
                str = str.replace(/range-(low|high)-/, "");

                if(str.search(/^(\d\d\d\d)(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])$/) != -1) { return str; };

                var tmpDate = new Date();

                if(str.search(/^today$/) != -1) { return tmpDate.getFullYear() + pad(tmpDate.getMonth() + 1) + pad(tmpDate.getDate()); };

                var regExp = /^(\d)-(day|week|month|year)$/;

                if(str.search(regExp) != -1) {
                        var parts       = str.match(regExp),
                            acc         = { day:0,week:0,month:0,year:0 };

                        acc[parts[2]]   = low ? -(+parts[1]) : +parts[1];
                        tmpDate.setFullYear(tmpDate.getFullYear() + +acc.year);
                        tmpDate.setMonth(tmpDate.getMonth() + +acc.month);
                        tmpDate.setDate(tmpDate.getDate() + +acc.day + (7 * +acc.week));
                        return !tmpDate || isNaN(tmpDate) ? "" : tmpDate.getFullYear() + pad(tmpDate.getMonth() + 1) + pad(tmpDate.getDate());
                };

                return "";
        };
        var getWeekNumber = function(y,m,d) {
                var d = new Date(y, m, d, 0, 0, 0);
                var DoW = d.getDay();
                d.setDate(d.getDate() - (DoW + 6) % 7 + 3); // Nearest Thu
                var ms = d.valueOf(); // GMT
                d.setMonth(0);
                d.setDate(4); // Thu in Week 1
                return Math.round((ms - d.valueOf()) / (7 * 864e5)) + 1;
        };
        var printFormattedDate = function(date, fmt, useImportedLocale) {
                if(!date || isNaN(date)) { return ""; };

                var parts = fmt.split("-"),
                      str = [],
                        d = date.getDate(),
                        D = date.getDay(),
                        m = date.getMonth(),
                        y = date.getFullYear(),
                    flags = {
                                "sp":" ",
                                "dt":".",
                                "sl":"/",
                                "ds":"-",
                                "cc":",",
                                "d":pad(d),
                                "D":useImportedLocale ? localeImport.dayAbbrs[D == 0 ? 6 : D - 1] : localeDefaults.dayAbbrs[D == 0 ? 6 : D - 1],
                                "l":useImportedLocale ? localeImport.fullDays[D == 0 ? 6 : D - 1] : localeDefaults.fullDays[D == 0 ? 6 : D - 1],
                                "j":d,
                                "N":D == 0 ? 7 : D,
                                "w":D,
                                "W":getWeekNumber(date),
                                "M":useImportedLocale ? localeImport.monthAbbrs[m] : localeDefaults.monthAbbrs[m],
                                "F":useImportedLocale ? localeImport.fullMonths[m] : localeDefaults.fullMonths[m],
                                "m":pad(m + 1),
                                "n":m + 1,
                                "t":daysInMonth(m + 1, y),
                                "Y":y,
                                "o":y,
                                "y":String(y).substr(2,2),
                                "S":["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
                            };

                for(var pt = 0, part; part = parts[pt]; pt++) {
                        str.push(!(part in flags) ? "" : flags[part]);
                };

                return str.join("");
        };
        var parseDateString = function(str, fmt) {
                var d     = false,
                    m     = false,
                    y     = false,
                    now   = new Date(),
                    parts = fmt.replace(/-sp(-sp)+/g, "-sp").split("-"),
                    divds = { "dt":".","sl":"/","ds":"-","cc":"," };

                loopLabel:
                for(var pt = 0, part; part = parts[pt]; pt++) {
                        if(str.length == 0) { return false; };

                        switch(part) {
                                // Dividers
                                case "sp": // Space " "
                                                if(str.charAt(0).search(/\s/) != -1) {
                                                        // Be easy on multiple spaces...
                                                        while(str.charAt(0).search(/\s/) != -1) { str = str.substr(1); };
                                                        break;
                                                } else return "";
                                case "dt":
                                case "sl":
                                case "ds":
                                case "cc":
                                                if(str.charAt(0) == divds[part]) {
                                                        str = str.substr(1);
                                                        break;
                                                } else return "";
                                // DAY
                                case "d": // Day of the month, 2 digits with leading zeros (01 - 31)
                                case "j": // Day of the month without leading zeros (1 - 31)
                                          // Accept both when parsing
                                                if(str.search(/^(3[01]|[12][0-9]|0?[1-9])/) != -1) {
                                                        d = +str.match(/^(3[01]|[12][0-9]|0?[1-9])/)[0];
                                                        str = str.substr(str.match(/^(3[01]|[12][0-9]|0?[1-9])/)[0].length);
                                                        break;
                                                } else return "";

                                case "D": // A textual representation of a day, three letters (Mon - Sun)
                                case "l": // A full textual representation of the day of the week (Monday - Sunday)
                                                l = part == "D" ? localeDefaults.dayAbbrs : localeDefaults.fullDays;
                                                for(var i = 0; i < 7; i++) {
                                                        if(new RegExp("^" + l[i], "i").test(str)) {
                                                                str = str.substr(l[i].length);
                                                                continue loopLabel;
                                                        };
                                                };
                                                return "";
                                /*
                                case "j": // Day of the month without leading zeros (1 - 31)
                                                if(str.search(/^([1-9]|[12][0-9]|3[01])/) != -1) {
                                                        d = +str.match(/^([1-9]|[12][0-9]|3[01])/)[0];
                                                        str = str.substr(str.match(/^(\s?[1-9]|[12][0-9]|3[01])/)[0].length);
                                                        break;
                                                } else return "";
                                */
                                case "N": // ISO-8601 numeric representation of the day of the week (added in PHP 5.1.0) 1 (for Monday) through 7 (for Sunday)
                                case "w": // Numeric representation of the day of the week 0 (for Sunday) through 6 (for Saturday)
                                                if(str.search(part == "N" ? /^([1-7])/ : /^([0-6])/) != -1) {
                                                        str = str.substr(1);
                                                        break;
                                                } else return "";
                                case "S": // English ordinal suffix for the day of the month, 2 characters: st, nd, rd or th
                                                if(str.search(/^(st|nd|rd|th)/i) != -1) {
                                                        str = str.substr(2);
                                                        break;
                                                } else return "";
                                case "z": // The day of the year (starting from 0): 0 - 365
                                                if(str.search(/^([0-9]|[1-9][0-9]|[12][0-9]{2}|3[0-5][0-9]|36[0-5])/) != -1) {
                                                        str = str.substr(str.match(/^([0-9]|[1-9][0-9]|[12][0-9]{2}|3[0-5][0-9]|36[0-5])/)[0].length);
                                                        break;
                                                } else return "";
                                // WEEK
                                case "W": // ISO-8601 week number of year, weeks starting on Monday (added in PHP 4.1.0): 1 - 53
                                                if(str.search(/^([1-9]|[1234[0-9]|5[0-3])/) != -1) {
                                                        str = str.substr(str.match(/^([1-9]|[1234[0-9]|5[0-3])/)[0].length);
                                                        break;
                                                } else return "";
                                // MONTH
                                case "M": // A short textual representation of a month, three letters
                                case "F": // A full textual representation of a month, such as January or March
                                                l = localeDefaults.fullMonths.concat(localeDefaults.monthAbbrs); // : localeDefaults.fullMonths;
                                                for(var i = 0; i < 24; i++) {
                                                        if(str.search(new RegExp("^" + l[i],"i")) != -1) {
                                                                str = str.substr(l[i].length);
                                                                m = ((i + 12) % 12);
                                                                continue loopLabel;
                                                        };
                                                };
                                                return "";
                                case "m": // Numeric representation of a month, with leading zeros
                                case "n": // Numeric representation of a month, without leading zeros
                                                //l = part == "m" ? /^(0[1-9]|1[012])/ : /^([1-9]|1[012])/;
                                                // Accept either when parsing
                                                l = /^(1[012]|0?[1-9])/;
                                                if(str.search(l) != -1) {
                                                        m = +str.match(l)[0] - 1;
                                                        str = str.substr(str.match(l)[0].length);
                                                        break;
                                                } else return "";
                                case "t": // Number of days in the given month: 28 through 31
                                                if(str.search(/2[89]|3[01]/) != -1) {
                                                        str = str.substr(2);
                                                        break;
                                                } else return "";
                                // YEAR
                                case "Y": // A full numeric representation of a year, 4 digits
                                case "o": // ISO-8601 year number. This has the same value as Y
                                                if(str.search(/^(\d{4})/) != -1) {
                                                        y = str.substr(0,4);
                                                        str = str.substr(4);
                                                        break;
                                                } else return "";
                                case "y": // A two digit representation of a year
                                                if(str.search(/^(0[0-9]|[1-9][0-9])/) != -1) {
                                                        y = +str.substr(0,2);
                                                        y = +y < 50 ? '20' + y : '19' + y;
                                                        str = str.substr(2);
                                                        break;
                                                } else return "";
                                default:
                                                return "";
                        };
                };

                d = d === false ? now.getDate() : d;
                m = m === false ? now.getMonth() - 1 : m;
                y = y === false ? now.getFullYear() : y;

                var tmpDate = new Date(y,m,d);
                return isNaN(tmpDate) ? "" : tmpDate;
        };
        var repositionDatePickers = function(e) {
                for(dp in datePickers) {
                        if(!datePickers[dp].created || datePickers[dp].staticPos || (!datePickers[dp].staticPos && !datePickers[dp].dragDisabled)) continue;
                        datePickers[dp].reposition();
                };
        };
        var findLabelForElement = function(element) {
                var label;
                if(element.parentNode && element.parentNode.tagName.toLowerCase() == "label") lebel = element.parentNode;
                else {
                        var labelList = document.getElementsByTagName('label');
                        // loop through label array attempting to match each 'for' attribute to the id of the current element
                        for(var lbl = 0; lbl < labelList.length; lbl++) {
                                // Internet Explorer requires the htmlFor test
                                if((labelList[lbl]['htmlFor'] && labelList[lbl]['htmlFor'] == element.id) || (labelList[lbl].getAttribute('for') == element.id)) {
                                        label = labelList[lbl];
                                        break;
                                };
                        };
                };

                if(label && !label.id) { label.id = element.id + "_label"; };
                return label;
        };
        var addDatePicker = function(options) {
                if(!options.id) { throw "A datePicker requires an associated element with an id attribute"; };
                if(options.id in datePickers) { return; };
                var elem = document.getElementById(options.id);
                if(!elem) throw "Cannot locate a datePicker's associated form element with an id of: " + options.id;
                if(elem.tagName.search(/select|input/i) == -1) {
                        if(!("callbacks" in options) || !("dateselect" in options.callbacks)) {
                                throw "A 'dateselect' callback function is required for datePickers not associated with a form element";
                        };
                        options.staticPos       = true;
                        options.splitDate       = false;
                        options.hideInput       = false;
                        options.noFadeEffect    = true;
                        options.dragDisabled    = true;
                        options.positioned      = false;
                } else if(!options.staticPos) {
                        options.hideInput       = false;
                } else {
                        options.noFadeEffect    = true;
                        options.dragDisabled    = true;
                };

                datePickers[options.id] = new datePicker(options);
        };
        var parseCallbacks = function(cbs) {
                if(cbs == null) { return {}; };
                var func,
                    type,
                    cbObj = {},
                    parts,
                    obj;
                for(var i = 0, fn; fn = cbs[i]; i++) {
                        type = fn.match(/(cb_(dateselect|redraw|create)_)([^\s|$]+)/i)[1].replace(/^cb_/i, "").replace(/_$/, "");
                        fn   = fn.replace(/cb_(dateselect|redraw|create)_/i, "").replace(/-/g, ".");

                        try {
                                if(fn.indexOf(".") != -1) {
                                        parts = fn.split('.');
                                        obj   = window;
                                        for (var x = 0, part; part = obj[parts[x]]; x++) {
                                                if(part instanceof Function) {
                                                        (function() {
                                                                var method = part;
                                                                func = function (data) { method.apply(obj, [data]) };
                                                        })();
                                                } else {
                                                obj = part;
                                                };
                                        };
                                } else {
                                        func = window[fn];
                                };

                                if(!(func instanceof Function)) continue;
                                if(!(type in cbObj)) { cbObj[type] = []; };
                                cbObj[type][cbObj[type].length] = func;
                        } catch (err) {};
                };
                return cbObj;
        };
        // Used by the button to dictate whether to open or close the datePicker
        var isVisible = function(id) {
                return (!id || !(id in datePickers)) ? false : datePickers[id].visible;
        };
        var create = function(inp) {
                if(!(typeof document.createElement != "undefined" && typeof document.documentElement != "undefined" && typeof document.documentElement.offsetWidth == "number")) { return; };

                // Has the locale file loaded?
                if(typeof(fdLocale) == "object" && !localeImport) {
                        localeImport = {
                                titles          : fdLocale.titles,
                                fullMonths      : fdLocale.fullMonths,
                                monthAbbrs      : fdLocale.monthAbbrs,
                                fullDays        : fdLocale.fullDays,
                                dayAbbrs        : fdLocale.dayAbbrs,
                                firstDayOfWeek  : ("firstDayOfWeek" in fdLocale) ? fdLocale.firstDayOfWeek : 0,
                                imported        : true
                        };
                } else if(!localeImport) {
                        localeImport = localeDefaults;
                };

                var formElements = (inp && inp.tagName) ? [inp] : joinNodeLists(document.getElementsByTagName('input'), document.getElementsByTagName('select')),
                    disableDays  = /disable-days-([1-7]){1,6}/g,
                    highlight    = /highlight-days-([1-7]{1,7})/,
                    rangeLow     = /range-low-(((\d\d\d\d)(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01]))|((\d)-(day|week|month|year))|(today))/,
                    rangeHigh    = /range-high-(((\d\d\d\d)(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01]))|((\d)-(day|week|month|year))|(today))/,
                    dateFormat   = /dateformat(-((sp|dt|sl|ds|cc)|([d|D|l|j|N|w|S|W|M|F|m|n|t|Y|o|y|O|p])))+/,
                    statusFormat = /statusformat(-((sp|dt|sl|ds|cc)|([d|D|l|j|N|w|S|W|M|F|m|n|t|Y|o|y|O|p])))+/,
                    disableDates = /disable((-(\d\d\d\d)(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])){2}|(-((\d\d\d\d)|(xxxx))((0[1-9]|1[012])|(xx))(0[1-9]|[12][0-9]|3[01])))/g,
                    enableDates  = /enable((-(\d\d\d\d)(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])){2}|(-((\d\d\d\d)|(xxxx))((0[1-9]|1[012])|(xx))(0[1-9]|[12][0-9]|3[01])))/g,
                    callbacks    = /((cb_(dateselect|redraw|create)_)([^\s|$]+))/ig,
                    positioned   = /display-inline-([^\s|$]+)/i,
                    bPositioned  = /button-([^\s|$]+)/i,
                    range,tmp,j,t,options,dts,parts;

                for(var i = 0, elem; elem = formElements[i]; i++) {
                        if(elem.className && (elem.className.search(dateFormat) != -1 || elem.className.search(/split-date/) != -1) && ((elem.tagName.toLowerCase() == "input" && (elem.type == "text" || elem.type == "hidden")) || elem.tagName.toLowerCase() == "select")) {

                                if(elem.id && elem.id in datePickers) {
                                        if(!datePickers[elem.id].staticPos) { datePickers[elem.id].createButton(); }
                                        else {
                                                if(!document.getElementById("fd-" + elem.id)) {
                                                        datePickers[elem.id].created = false;
                                                        datePickers[elem.id].create();
                                                } else if(inp) {
                                                        // Only do this if called from an ajax update etc
                                                        datePickers[elem.id].setDateFromInput();
                                                        datePickers[elem.id].updateTable();
                                                };
                                        };
                                        continue;
                                };

                                if(!elem.id) { elem.id = "fdDatePickerInput-" + uniqueId++; };

                                options = {
                                        id:elem.id,
                                        low:"",
                                        high:"",
                                        format:"d-sl-m-sl-Y",
                                        statusFormat:"",
                                        highlightDays:[0,0,0,0,0,1,1],
                                        disabledDays:[0,0,0,0,0,0,0],
                                        disabledDates:{},
                                        enabledDates:{},
                                        noFadeEffect:elem.className.search(/no-animation/i) != -1,
                                        staticPos:elem.className.search(/display-inline/i) != -1,
                                        hideInput:elem.className.search(/hide-input/i) != -1,
                                        noToday:elem.className.search(/no-today-button/i) != -1,
                                        showWeeks:elem.className.search(/show-week/i) != -1,
                                        dragDisabled:nodrag ? true : elem.className.search(/disable-drag/i) != -1,
                                        positioned:false,
                                        firstDayOfWeek:localeImport.firstDayOfWeek,
                                        fillGrid:elem.className.search(/fill-grid/i) != -1,
                                        constrainSelection:elem.className.search(/fill-grid-no-select/i) != -1,
                                        callbacks:parseCallbacks(elem.className.match(callbacks)),
                                        buttonWrapper:"",
                                        labelledBy:findLabelForElement(elem)
                                };

                                // Positioning of static dp's
                                if(options.staticPos && elem.className.search(positioned) != -1) {
                                        options.positioned = elem.className.match(positioned)[1];
                                };

                                // Positioning of non-static dp's button
                                if(!options.staticPos && elem.className.search(bPositioned) != -1) {
                                        options.buttonWrapper = elem.className.match(bPositioned)[1];
                                };

                                // Opacity of non-static datePickers
                                if(!options.staticPos) {
                                        options.finalOpacity = elem.className.search(/opacity-([1-9]{1}[0-9]{1})/i) != -1 ? elem.className.match(/opacity-([1-9]{1}[0-9]{1})/i)[1] : 90
                                };

                                // Dates to disable
                                dts = elem.className.match(disableDates);
                                if(dts) {
                                        for(t = 0; t < dts.length; t++) {
                                                parts = dts[t].replace(/xxxx/, "****").replace(/xx/, "**").replace("disable-", "").split("-");
                                                options.disabledDates[parts[0]] = (parts.length && parts.length == 2) ? parts[1] : 1;
                                        };
                                };

                                // Dates to enable
                                dts = elem.className.match(enableDates);
                                if(dts) {
                                        for(t = 0; t < dts.length; t++) {
                                                parts = dts[t].replace(/xxxx/, "****").replace(/xx/, "**").replace("enable-", "").split("-");
                                                options.enabledDates[parts[0]] = (parts.length && parts.length == 2) ? parts[1] : 1;
                                        };
                                };

                                // Split the date into three parts ?
                                options.splitDate = (elem.className.search(/split-date/) != -1 && document.getElementById(elem.id+splitAppend[0]) && document.getElementById(elem.id+splitAppend[1]) && document.getElementById(elem.id+splitAppend[0]).tagName.search(/input|select/i) != -1 && document.getElementById(elem.id+splitAppend[1]).tagName.search(/input|select/i) != -1);

                                // Date format
                                if(!options.splitDate && elem.className.search(dateFormat) != -1) {
                                        options.format = elem.className.match(dateFormat)[0].replace('dateformat-','');
                                };

                                // Status bar date format
                                if(elem.className.search(statusFormat) != -1) {
                                        options.statusFormat = elem.className.match(statusFormat)[0].replace('statusformat-','');
                                };

                                // The days of the week to highlight
                                if(elem.className.search(highlight) != -1) {
                                        tmp = elem.className.match(highlight)[0].replace(/highlight-days-/, '');
                                        options.highlightDays = [0,0,0,0,0,0,0];
                                        for(j = 0; j < tmp.length; j++) {
                                                options.highlightDays[tmp.charAt(j) - 1] = 1;
                                        };
                                };

                                // The days of the week to disable
                                if(elem.className.search(disableDays) != -1) {
                                        tmp = elem.className.match(disableDays)[0].replace(/disable-days-/, '');
                                        options.disabledDays = [0,0,0,0,0,0,0];
                                        for(j = 0; j < tmp.length; j++) {
                                                options.disabledDays[tmp.charAt(j) - 1] = 1;
                                        };
                                };

                                // The lower limit
                                if(elem.className.search(rangeLow) != -1) {
                                        options.low = parseRangeFromString(elem.className.match(rangeLow)[0]);
                                };

                                // The higher limit
                                if(elem.className.search(rangeHigh) != -1) {
                                        options.high = parseRangeFromString(elem.className.match(rangeHigh)[0]);
                                };

                                // Always round lower & higher limits if a selectList involved
                                if(elem.tagName.search(/select/i) != -1) {
                                        range        = grepRangeLimits(elem);
                                        options.low  = options.low  ? range[0] + String(options.low).substr(4,4)  : range[0] + "0101";
                                        options.high = options.high ? range[1] + String(options.high).substr(4,4) : range[1] + "1231";
                                };

                                addDatePicker(options);
                        };
                };
        };

        addEvent(window, 'load',   create);
        addEvent(window, 'unload', destroy);
        addEvent(window, 'resize', repositionDatePickers);

        return {
                addEvent:               function(obj, type, fn) { return addEvent(obj, type, fn); },
                removeEvent:            function(obj, type, fn) { return removeEvent(obj, type, fn); },
                stopEvent:              function(e) { return stopEvent(e); },
                show:                   function(inpID) { return showDatePicker(inpID, false); },
                hide:                   function(inpID) { return hideDatePicker(inpID); },
                create:                 function(inp) { create(inp); },
                destroyDatePicker:      function(inpID) { destroySingleDatePicker(inpID); },
                cleanUp:                function() { cleanUp(); },
                repositionDatePickers:  function() { repositionDatePickers(); },
                printFormattedDate:     function(dt, fmt, useImportedLocale) { return printFormattedDate(dt, fmt, useImportedLocale); },
                setDateFromInput:       function(inpID) { if(!inpID || !(inpID in datePickers) || !datePickers[inpID].created) return false; datePickers[inpID].setDateFromInput(); },
                setRangeLow:            function(inpID, yyyymmdd) { if(!inpID || !(inpID in datePickers)) return false; datePickers[inpID].setRangeLow(yyyymmdd); },
                setRangeHigh:           function(inpID, yyyymmdd) { if(!inpID || !(inpID in datePickers)) return false; datePickers[inpID].setRangeHigh(yyyymmdd); },
                parseDateString:        function(str, format) { return parseDateString(str, format); },
                setGlobalVars:          function(json) { affectJSON(json); },
                dateValidForSelection:  function(inpID, dt) { if(!inpID || !(inpID in datePickers)) return false; datePickers[inpID].canDateBeSelected(dt); },
                addDisabledDates:       function(inpID, dts) { if(!inpID || !(inpID in datePickers)) return false; datePickers[inpID].addDisabledDates(dts); },
                setDisabledDates:       function(inpID, dts) { if(!inpID || !(inpID in datePickers)) return false; datePickers[inpID].setDisabledDates(dts); },
                disable:                function(inpID) { if(!inpID || !(inpID in datePickers)) return false; datePickers[inpID].disableDatePicker();},
                enable:                 function(inpID) { if(!inpID || !(inpID in datePickers)) return false; datePickers[inpID].enableDatePicker();}
        };
})();



/**
 * FILE BROWSER
 */
var FileBrowser = Class.create({
	options: {},
	data: {},
	initialize: function(element, selectedViewType, folderP) {
		this.element = element;
		this.options = Object.extend({
			viewType: selectedViewType,
			folderPrefix: folderP,
			cFolder: "",
			linkGetFolderContents: "/admin/filebrowser/getFolderContentsAsJSON",
			linkGetFileInfo: "/admin/filebrowser/getFileInfoAsJSON",
			iconsPath: "/skin/images/fileTypes/icon_"
		});
		this.initView();
	},
	initView: function() {
		this.initAndHideAllElements();
		if (this.options.viewType == 'columns') {
			this.CVinit();
		} else {
			this.throwError('viewType '+this.options.viewType+' not recognized!');
		}
	},
	initAndHideAllElements: function() {
		this.columnsDrop = this.element.getElementsBySelector("#columnView")[0].hide();
		this.treeDrop = this.element.getElementsBySelector("#treeView")[0].hide();
	},


	// COLUMNS VIEW
	CVinit: function() {
		this.columnsDrop.show().update('<table cellpadding="0" cellspacing="0" id="CVTable"><tbody><tr id="CVRow"></tr></tbody></table>');
		this.columnsViewRow = this.element.getElementsBySelector("#CVRow")[0];
		this.CVOpenFolder("","");
	},
	CVOpenFolder: function(link, path) {
		if (link) {
			link.up().siblings().each(function(el) { el.removeClassName("active"); });
			link.up().addClassName("active");
		}
		pathArr = path.split("/");
		this.CVRemoveColumns(pathArr.length-1);
		cColumn = this.CVAddNewColumn(path);
		cColumn.fbo = this;
		new Ajax.Request(this.options.linkGetFolderContents, {
			parameters: { folder: path, column: column.id }, evalJS: false,
			onLoading: function(param) { $(param.request.options.parameters.column).addClassName("columnLoading"); },
			onSuccess: function(param) {
				fbo.options.cFolder = param.request.options.parameters.folder;
				cColumn = $(param.request.options.parameters.column);
				cColumn.removeClassName("columnLoading").update(fbo.CVJsonToList(param.responseJSON));
			}
		});
	},
	CVRemoveColumns: function(toLength) {
		cells = this.columnsViewRow.getElementsBySelector('td');
		if (cells && cells.length > toLength) {
			for (i=toLength; i<cells.length; ++i) cells[i].remove();
		}
		for (i=0; i<cells.length; ++i) cells[i].addClassName('unactive');
	},
	CVAddNewColumn: function(path) {
		this.columnsViewRow.insert('<td class="CVcolumn" id="col_'+path+'" valign="top"></td>');
		column = this.columnsViewRow.getElementsBySelector('#col_'+path);
		column.id = "col_"+path;
		return column;
	},
	CVJsonToList: function(json) {
		c = "<ul>";
		for (i=0; i<json.directories.length; ++i) {
			c+="<li class='folder'><a href='#' onclick='fbo.CVOpenFolder(this, \""+this.options.cFolder+"/"+json.directories[i]+"\");this.blur(); return false;'>"+json.directories[i]+"</a></li>";
		}
		for (i=0; i<json.files.length; ++i) {
			c+="<li class='file'><a href='#' onclick='fbo.CVFileInfo(this, \""+this.options.cFolder+"/"+json.files[i].name+"\"); this.blur(); return false;'>"+json.files[i].name+"</a></li>";
		}
		c+= "</ul>";
		return c;
	},
	CVFileInfo: function(link, path) {
		if (link) {
			link.up().siblings().each(function(el) { el.removeClassName("active"); });
			link.up().addClassName("active");
		}
		pathArr = path.split("/");
		this.CVRemoveColumns(pathArr.length-1);
		cColumn = this.CVAddNewColumn(path);
		new Ajax.Request(this.options.linkGetFileInfo, {
			parameters: { file: path, column: column.id }, evalJS: false,
			onLoading: function(param) { $(param.request.options.parameters.column).addClassName("columnLoading"); },
			onSuccess: function(param) {
				cColumn = $(param.request.options.parameters.column);
				cColumn.removeClassName("columnLoading").update(fbo.CVJsonToInfo(param.responseJSON));
			}
		});
	},
	CVJsonToInfo: function( json) {
		link.up().addClassName("active");
		return "test";
	},


	getFolderFromFile: function(file) {
		//
	},
	throwError: function(errMsg) {
		alert("FileBrowser error: "+errMsg);
		return false;
	}
});



/*

SIFORM

Copyright (c) 2008 Dominik Znidar <dominik.znidar@gmail.com>

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

*/

var SF_mup_exPos = [];

var SiForm = Class.create({
	validations: [],
	errorTips: {},
	tips: {},
	forms: [],
	showErrorOnButtons: false,

	/**
	 * Initilizes siform
	 *
	 * @param Element
	 * @param Object siform's options
	 * @return void
	 */
	initialize: function() {
		this.forms = [];
		this.validations = [];
		this.tips = {};
		this.errorTips = {};
	},

	createForm: function(element, options) {
		this.element = $(element);
		this.options = Object.extend({
			elements: [],
			columnsSpacing: 6,
			showErrorOnButtons: false,
			noSubmit: false,
			additionalElementPadding: 4,
			afterCreate: null
		}, options || {});
		if (this.element) this.element.update('').setStyle({ width: this.options.width+'px' });
		this.buildElements(this.options.elements, this.element);

		// create tooltip
		tt = Builder.node('div', { className: 'sf-tooltip', id: 'sf-tooltip', style: 'display:none;' });
		tt.first = true;
		$$('body')[0].insert(tt);
		ht = Builder.node('div', { className: 'sf-helpTip', id: 'sf-helpTip', style: 'display:none;' });
		ht.first = true;
		$$('body')[0].insert(ht);
		
		if (this.options.onComplete) this.options.onComplete();

		try {
			Event.observe(this.forms[this.forms.length - 1].formOptions.id, 'submit', SiForm.Tools.formValidator.bindAsEventListener(this, this.forms[this.forms.length-1], this));
		} catch (e) {
		}
	},

	/**
	 * Generates elements
	 *
	 * @param Array elements
	 * @param DomElement wrapper
	 * @return void
	 */
	buildElements: function(elements, wrapper, doWrapWith, additOptions) {
		additOptions = additOptions || {};
		for (var i=0, len=elements.length; i<len; ++i) {
			var elOpts = Object.extend(Object.clone(additOptions)||{},elements[i]);
			if (elOpts.prevElementOptions) {
				elOpts.elementOptions = Object.extend(elOpts.prevElementOptions, elOpts.elementOptions || {});
			}
			if (!elOpts.type || !SiForm.Elements[elOpts.type]) continue;
			elMethod = SiForm.Elements[elOpts.type].bind(this);
			if (!elMethod) continue;

			var el = elMethod(elOpts);
			if (!el) continue;
			el.sfOptions = elOpts;

			if (elOpts.validations) {
				this.validations.push(elOpts);
			}

			labelCallback = SiForm.Elements.label.bind(this);
			el = labelCallback(el, elOpts);
			if (elOpts.type != 'hidden') {
				el = this.wrapElement(el, doWrapWith);
			}
			wrapper.appendChild(el);
		}
	},

	/**
	 * Adds a wrapping div to element(s)
	 *
	 * @param DomElement|Array
	 * @param string className
	 * @return DomElement
	 */
	wrapElement: function(el, className) {
		if (!className) return el;
		return Builder.node('div', { className: 'sf-' + className }, [
			el,
			Builder.node('div', { className: 'sf-cf' })
		]);
	}

});

SiForm.Elements = {

	html: function(options) {
		options = Object.extend({
			value: "",
			customClass: '',
			customStyle: '',
			id: ''
		}, options || {});

		var div = Builder.node('div', SiForm.Tools.setClassAndStyle(options, { id: options.id }, 'sf-html'));
		$(div).update(options.value);

		return div;
	},

	form: function(options) {
		options = Object.extend({
			action: '#',
			method: 'post',
			update: '',
			elementOptions: {},
			elements: [],
			buttons: [],
			id: 'sf-form-'+Math.round(Math.random() * 100000),
			ajax: false,
			ajaxOptions: {},
			update: '',
			hasUpload: false,
			enctype: 'application/x-www-form-urlencoded'
		}, options || {});
		if (options.hasUpload) options.enctype = 'multipart/form-data';

		formTagOpts = SiForm.Tools.setClassAndStyle(options, {
			action: options.action,
			method: options.method,
			id: options.id,
			enctype: options.enctype
		}, 'sf-form');

		var el = Builder.node('form', formTagOpts);
		el.formOptions = options;
		el.siformObj = this;

		// build elements
		passedOptions = Object.extend({ prevElementOptions: Object.clone(options.elementOptions) }, options.elementOptions || {});
		this.buildElements(options.elements, el, 'form-element', passedOptions);

		// build buttons
		if (options.buttons && !this.options.noSubmit) {
			clbck = SiForm.Elements.buttonsBar.bind(this);
			el.appendChild(clbck(Object.extend(options.buttons, options.elementOptions)));
		}

		this.forms.push(el);

		return el;

	},

	label: function(el, options) {
		options = Object.extend({
			label: false,
			labelWidth: 100,
			customLabelStyle: '',
			customLabelClass: '',
			helpText: '',
			toolbox: ''
		}, options || {});
		if (!options.label) return el;

		if (el && typeof el=="object" && options.type != 'hidden') {
			forEl = $(el).identify();
			if (options.label==' :') options.label=true;
			var labelSpan = Builder.node('span', options.label);
			if (options.label !== true && options.validations && ((Object.isString(options.validations) && options.validations=='required') || (Object.isArray(options.validations) && options.validations.indexOf('required') > -1))) {
				$(labelSpan).insert({ top: Builder.node('span', { className: "star" }, '* ')});
			}
			var label = Builder.node('label', {
				className: 'sf-label'+(options.customLabelClass ? ' '+options.customLabelClass : ''),
				style: 'width: '+options.labelWidth+'px;'+(options.customLabelStyle ? ' '+options.customLabelStyle : ''),
				htmlFor: forEl
			}, [ labelSpan ]);
			if (options.type == 'dateselect') {
				nel = el.childNodes[3]; // find hidden for dateselect
				if (nel) {
					nel.label = label;
					nel.sfOptions = options;
				}
			}
			el.label = label;

			toWrap = [label, el];

			if (options.helpText != "") {
				helpText = Builder.node('div', { className: 'sf-help-text' }, options.helpText);
				//$(helpText).insert({ top: "<iframe style='position: absolute; top: 0; left: 0; height: 100%; width:100%; border: 0;'></iframe>" });
				//$(helpText).insert({ bottom: "<div class='sf-help-text-inside'>"+options.helpText+"</div>" });
				helpEl = Builder.node('div', { className: 'sf-help' }, [
					Builder.node('div', { className: 'sf-help-icon' }),
					helpText
				]);
				$(helpEl).observe('mouseover', SiForm.Tools.showHelp);
					/*$(this).setStyle({ 'z-index': 9999 });
					$(this).addClassName('sf-help-over');
					if($('dummyFrame') == null) {
						dummyFrame = Builder.node('iFrame',
						{'src': '', 'id': 'dummyFrame',
						'scrolling': 'no', 'style': 'background-color:' +
						' transparent; border: 0px;filter:progid:' +
						'DXImageTransform.Microsoft.Alpha(style=0,opacity=0);' +
						'position:absolute; top: 16px; right:0px; z-index: 999;' +
						'width: 100%;height:100%;'});
						el = $(this).select('.sf-help-text')[0];
						el.setStyle({ 'z-index': 10000 });
						dummyFrame.setStyle({ width: $(el).getWidth()+'px', height: $(el).getHeight()+'px' });
						$(this).insert({ top: dummyFrame });
					}
				//})*/
				$(helpEl).observe('mouseout', SiForm.Tools.hideHelp);
				toWrap.push(helpEl);
			}

			return this.wrapElement(toWrap, 'labeled-element');
		} else {
			return el;
		}

	},

	buttonsBar: function(options) {
		options = Object.extend({
			label: true,
			elements: []
		}, options || {});
		if (!Object.isArray(options.elements)) options.elements = [];
		if (options.elements.length == 0) options.elements = [{ type: 'button', buttonType: 'submit', title: 'Save' }];

		var bar = Builder.node('div', { className: 'sf-buttons-bar' });
		var buttons = Builder.node('div', { className: 'sf-buttons' });

		this.buildElements(options.elements, buttons);

		labelCallback = SiForm.Elements.label.bind(this);
		bar.appendChild(labelCallback(buttons, options));

		return bar;

	},

	button: function(options) {
		options = Object.extend({
			type: 'submit',
			title: 'submit',
			url: '',
			usageType: ''
		}, options || {});

		var btn = Builder.node('input', SiForm.Tools.setClassAndStyle(options, {
			type: 'submit',
			value: options.title
		}, 'sf-button'));
		btn.sfObj = this;

		//alert(options.buttonType);
		if (options.buttonType=='reset' || options.buttonType=='url' || options.buttonType=='action') {
			$(btn).observe('click', function(ev) { if (!SiForm.Tools.buttonCallback(ev)) { ev.stop(); } });
		}

		return btn;

	},

	textfield: function(options) {
		// set default options
		options = Object.extend({
			name: '',
			value: '',
			isPassword: false,
			customClass: '',
			width: 150,
			readonly: false
		}, options || {});

		// set properties
		if (options.readonly) options.customClass = (options.customClass ? options.customClass.toString()+" " : "")+"sf-readonly-field";
		properties = SiForm.Tools.setClassAndStyle(options, {
			type: options.isPassword ? "password" : "text",
			name: options.name,
			value: options.value,
			id: 'f_'+options.name
		}, 'sf-textfield');
		if (options.readonly) properties.readonly = "1";

		// build element
		return Builder.node('input', properties);
	},

	textarea: function(options) {
		// set default options
		options = Object.extend({
			name: "",
			value: "",
			rows: 3,
			customStyle: '',
			customClass: '',
			width: 150
		}, options || {});

		var properties = SiForm.Tools.setClassAndStyle(options, {
			name: options.name,
			id: "f_"+options.name,
			rows: options.rows
		}, 'sf-textarea');

		return Builder.node("textarea", properties, options.value);
	},
	
	toolbox: function(options) {
		options = Object.extend({
			name: '',
			value: '',
			displayValue: '',
			customStyle: '',
			customClass: '',
			url: ''
		}, options || {});

		els = [];
		optClone = Object.clone(options);
		tfOptions = Object.extend(optClone, { name: optClone.name+'_dv', value: optClone.displayValue, readonly: true });
		//console.log(tfOptions);
		els.push(SiForm.Elements.textfield(tfOptions));
		els.push(SiForm.Elements.hidden(options));

		tbLinkId = 'f_toolbox_'+options.name;
		toolboxIcon = Builder.node('a', { id: tbLinkId, href: '#' }, [ Builder.node('img', { src: '/p.gif' }) ]);
		toolboxIcon.sfOptions = options;
		$(toolboxIcon).observe('click', SiForm.Tools.openToolbox);
		toolboxDiv = Builder.node('div', { className: 'sf-toolbox-icon' }, [ toolboxIcon ]);

		els.push(toolboxDiv);

		return Builder.node('div', { className: 'sf-toolbox' }, els);
	},

	select: function(options) {
		//return Builder.node('div', 'select not implemented yet');
		// set default options
		options = Object.extend({
			name: "",
			value: "",
			values: [],
			valueField: "id",
			titleField: "title",
			multiple: false,
			size: 3,
			customStyle: '',
			customClass: '',
			width: 150,
			showEmpty: ''
		}, options || {});

		// build element
		node = Builder.node("select", SiForm.Tools.setClassAndStyle(
			options,
			Object.extend(options.multiple ? { multiple: "1", size: options.size } : {}, {
				name: options.name,
				id: "f_"+options.name
			}),
		'sf-select'));

		if (options.showEmpty != '') {
			node.appendChild(Builder.node("option", { value: '' }, options.showEmpty));
		}

		var els = [];
		var valuesType = Object.isArray(options.values[0]) ? "array" : (typeof(options.values[0])=="object" ? "json" : "simple");
		for (var i=0, len=options.values.length; i<len; ++i) {
			if (valuesType == "object") {
				values = $H(options.values[i]);
				value = values.get(valueField);
				title = values.get(titleField);
			} else if (valuesType == "array") {
				value = options.values[i][0];
				title = options.values[i][1];
			} else {
				value = options.values[i];
				title = options.values[i];
			}
			node.appendChild(Builder.node("option", { value: value }, title));
		}

		for (var i=0, len=node.options.length; i<len; i++) {
			if (node.options[i].value == options.value) node.options[i].selected = true;
		}

		SiForm.Tools.addCallbacks(node, options);

		return node;

	},

	radio : function(options) {
		// set default options
		options = Object.extend({
			name: "",
			value: "",
			values: [],
			newline: true
		}, options || {});

		// build element
		var el = Builder.node("div", SiForm.Tools.setClassAndStyle(
			options,
			{ id: 'f_'+options.name },
			'sf-radios'+(!options.newline ? " sf-subelements-no-newline" : '')
		)), els = [];

		if (!options.values.length) return "no values";
		for (var i=0, len=options.values.length; i<len; ++i) {
			if (Object.isArray(options.values[i])) {
				value = options.values[i][0];
				title = options.values[i][1];
			} else {
				value = title = options.values[i];
			}
			el.appendChild(this.wrapElement([
				Builder.node("input", Object.extend(value==options.value ? { checked: "1" } : {}, { type: "radio", name: options.name, value: value, id: "f_"+options.name+"_"+value.underscore(), className: "sf-radio"})),
				Builder.node("label", { htmlFor: "f_"+options.name+"_"+value.underscore(), className: "sf-sub-label", style: options.customSubelementLabelStyle+""}, title)
			], 'sub-element'));

		}

		return el;
	},

	checkbox : function(options) {
		// set default options
		options = Object.extend({
			name: "",
			value: "",
			values: [],
			newline: true,
			readonly: false
		}, options || {});

		// build element
		var el = Builder.node("div", SiForm.Tools.setClassAndStyle(
			options,
			{ id: 'f_'+options.name },
			'sf-checkboxes'+(!options.newline ? " sf-subelements-no-newline" : '')
		));

		if (!options.values.length) return "no values";
		if (!Object.isArray(options.value)) options.value = [options.value];
		for (var i=0, len=options.values.length; i<len; ++i) {
			if (Object.isArray(options.values[i])) {
				value = options.values[i][0];
				title = options.values[i][1];
			} else {
				value = title = options.values[i];
			}
			el.appendChild(this.wrapElement([
				Builder.node("input", Object.extend(options.readonly ? { disabled:"1" } : {}, Object.extend(options.value.indexOf(value)>-1 ? { checked: "1" } : {}, { type: "checkbox", name: options.name, value: value, id: "f_"+options.name+"_"+value.underscore(), className: "sf-checkbox"}))),
				Builder.node("label", { htmlFor: "f_"+options.name+"_"+value.underscore(), className:"sf-sub-label"}, title)
			], 'sub-element'));
		}

		return el;
	},

	dateselect: function(options) {
		//return Builder.node('div', 'dateselect not implemented yet');
		var d = new Date();
		options = Object.extend({
			name: "",
			value: d.getFullYear()+'-'+"0".concat(d.getMonth()+1).slice(-2)+"-"+"0".concat(d.getDate()).slice(-2),
			monthDisplay: "string",
			startYear: d.getFullYear()-10,
			endYear: d.getFullYear()+10,
			includeTime: 0,
			useDatepicker: false
		}, options || {});

		if (options.useDatepicker) {

			var ds = SiForm.Elements.textfield({
				name: options.name,
				value: options.value,
				customClass: ((typeof options.customClass!="undefined") ? options.customClass+" " : "")+"dateformat-Y-ds-m-ds-d",
				customStyle: options.customStyle+""
			});

		} else {
			dtArr = options.value.split(' ');
			valArr = dtArr[0].split("-");
			if (dtArr[1]) timeVal = dtArr[1];
			else timeVal = '00:00';

			valueContainer = SiForm.Elements.hidden({ name: options.name, value: options.value, customClass: options.customClass });
			valueCallback = function() {
				vcName = this.valueContainer.name;
				dsElement = $('ds_'+vcName);
				dp = dsElement.dateparts;
				value = dp.year.options[dp.year.selectedIndex].value + "-" +
				        "0".concat(dp.month.options[dp.month.selectedIndex].value).slice(-2) + "-" +
				        "0".concat(dp.day.options[dp.day.selectedIndex].value).slice(-2);
				if (dp.timei) value += " "+dp.timei.value;
				this.valueContainer.value = value;
			};

			//var ds = Builder.node("div", { className: "sf-dateselect", id: 'ds_'+options.name });
			var ds = Builder.node("div", SiForm.Tools.setClassAndStyle(
				options,
				{ id: 'ds_'+options.name },
				'sf-dateselect'
			));
			ds.dateparts = { day: false, month: false, year: false, timei: false };

			var days = SiForm.Elements.select({ name: options.name+"_day", values: $A($R(1,31)), value: parseFloat(valArr[2]), width: false });
			days.valueContainer = valueContainer;
			days.onchange = valueCallback;
			ds.appendChild(days);
			ds.dateparts.day = days;

			var months = SiForm.Elements.select({ name: options.name+"_month", values: SiForm.Locale.months, value: parseFloat(valArr[1]), width: false });
			months.valueContainer = valueContainer;
			months.onchange = valueCallback;
			ds.appendChild(months);
			ds.dateparts.month = months;

			var years = SiForm.Elements.select({ name: options.name+"_year", values: $A($R(parseFloat(options.startYear),parseFloat(options.endYear))), value: valArr[0], width: false });
			years.valueContainer = valueContainer;
			years.onchange = valueCallback;
			ds.appendChild(years);
			ds.dateparts.year = years;

			if (options.includeTime == 1) {
				var timeI = SiForm.Elements.textfield({ name: options.name+"_time", value: timeVal||"00:00", width: 60, customStyle: 'margin-left: 5px;' });
				timeI.valueContainer = valueContainer;
				$(timeI).observe('change', valueCallback);
				ds.appendChild(timeI);
				ds.dateparts.timei = timeI;
			}

			ds.appendChild(valueContainer);
		}

		return ds;
	},

	group: function(options) {
		options = Object.extend({
			title: '',
			collapsible: false,
			collapsed: false,
			customClass: '',
			customStyle: '',
			width: 272,
			elementOptions: {},
			elements: []
		}, options || {});

		var fs = Builder.node("fieldset", {
			className: 'sf-group' + (options.customClass ? " "+options.customClass : "")
		});
		var legend = Builder.node('legend', {
			className: 'sf-group-title' + (options.customTitleClass ? " "+options.customTitleClass : "")
		}, [Builder.node('span', options.title)]);
		var fsContent = Builder.node('div', { className: 'sf-group-content' });

		if (options.collapsible) {
			$(fs).addClassName('sf-group-collapsible');
			if (options.collapsed) $(fs).addClassName('sf-group-collapsed');
			$(legend).observe('click', function(event) {
				var el = event.element();
				el.up('fieldset').toggleClassName('sf-group-collapsed');
			});
		}

		fs.appendChild(legend);
		passedOptions = Object.extend({ prevElementOptions: Object.clone(options.elementOptions), groupNode: fs }, options.elementOptions || {});
		this.buildElements(options.elements, fsContent, 'group-element', passedOptions);
		fs.appendChild(fsContent);

		return fs;

	},

	tabs: function(options) {
		options = Object.extend({
			elementOptions: [],
			tabs: [],
			id: 'sf-tabs-'+Math.round(Math.random() * 10000)
		}, options || {});
		if (options.tabs.length == 0) return false;

		var tabsel = Builder.node('div', { className: 'sf-tabs-container', id: options.id }),
		    tabs = [],
		    tabsco = Builder.node('div', { className: 'sf-tabs-contents' });
		tabsel.tabtitles = [];
		tabsel.tabcontents = [];

		tabsel.setTabs = function(selInd) {
			this.tabtitles.invoke('removeClassName', 'sf-tab-active');
			this.tabcontents.invoke('hide');
			this.tabtitles[selInd].addClassName('sf-tab-active');
			this.tabcontents[selInd].show();
		}

		// create tabs
		for (var i=0,len=options.tabs.length; i<len; i++) {
			var tab = Builder.node('div', { className: 'sf-tab' }, [ Builder.node('span', options.tabs[i].title) ]);
			tab.tabsContainer = tabsel;
			$(tab).observe('click', SiForm.Tools.tabsCallback);
			if (i==0) tab.addClassName('sf-tab-active');
			tabsel.tabtitles.push(tab);
			tabs.push(tab);
		}

		// create tab contents
		for (var i=0,len=options.tabs.length; i<len; i++) {
			var tabcontent = Builder.node('div', { className: 'sf-tab-content' });
			passedOptions = Object.extend({ prevElementOptions: Object.clone(options.elementOptions), tabTitle: tabsel.tabtitles[i] }, options.elementOptions || {});
			this.buildElements(options.tabs[i].elements, tabcontent, 'tab-element', Object.extend(passedOptions));
			tabsel.tabcontents.push(tabcontent);
			tabsco.appendChild(tabcontent);
		}

		tabsel.setTabs(0);

		tabsel.appendChild(this.wrapElement(tabs, 'tabs'));
		tabsel.appendChild(tabsco);
		return tabsel;

	},

	columns: function(options) {
		options = Object.extend({
			elementOptions: {},
			elements: [],
			customClass: '',
			customStyle: '',
			width: 150,
			name: 'sf-columns-'+Math.round(Math.random() * 10000),
			id: 'sf-tabs-'+Math.round(Math.random() * 10000)
		}, options || {});

		var el = Builder.node('div', {
			className: 'sf-columns' + (options.customClassName ? ' '+options.customClassName : ''),
			style: "width: "+ (parseFloat(options.width) + (this.options.additionalElementPadding*2))+ "px;" + (options.customStyle ? " "+options.customStyle : "")
		}), elLen = options.columns || options.elements.length, elPad = this.options.additionalElementPadding, coSpa = this.options.columnsSpacing;
		cId = el.identify();

		passedOptions = Object.extend({ prevElementOptions: Object.clone(options.elementOptions) }, options.elementOptions || {});
		paCount = (elLen - 1) * 2;
		spCount = elLen - 1;
		colWidth = Math.floor((options.width - paCount * elPad - spCount * coSpa) / elLen);
		for (var i=0,len = options.elements.length; i<len; i++) {
			po = Object.clone(passedOptions);
			elCols = options.elements[i].columns || 1;
			if (elCols==1) {
				po.width = colWidth;
			} else {
				po.width = colWidth * elCols + (elCols-1)*coSpa + (elCols-1) * 2 * elPad;
			}
			if (options.elements[i].type == 'html' || options.elements[i].type == 'radio' || options.elements[i].type == 'checkbox' || options.elements[i].type == 'dateselect') po.width += 8;
			if (i < len-1) po.customStyle = 'margin-right: '+coSpa+'px;'
			po.labelFromId = cId;
			this.buildElements([options.elements[i]], el, 'column-element', po);
		}

		return el;

	},

	upload: function(options) {
		options = Object.extend({
			name: ''
		}, options || {});

		return Builder.node('input', SiForm.Tools.setClassAndStyle(
			options,
			{ type: 'file', name: options.name, id: 'f_'+options.name },
			'sf-file'
		));
	},

	hidden: function(options) {
		options = Object.extend({
			value: '',
			name: '',
			customClass: ''
		}, options || {});
		return Builder.node('input', { type: 'hidden', value: options.value, name: options.name, id: 'f_'+options.name, className: options.customClass+"" });
	},
	
	multipleUpload: function(options) {
		options = Object.extend({
			uploadAction: '', // url for upload
			deleteAction: '', // url for delete
			defaultAction: '', // url for setDefault
			value: '', // existing images
			thumbnailPath: '/getThumbnail/{?}', // path to thumbnail, {?} will be replaced by image name
			name: '', // input name,
			uploadName: 'picture', // input file name
			limit: 0 // limit
		}, options || {});

		imagesArr = options.value ? options.value.split(',') : [];
		options.uploadAction += '/field/'+options.name;

		SF_mup_exPos[options.name] = -1;
		var exImages = Builder.node('div', { className: 'mup_exImages' }, [' ']);
		for (var ipos=0; ipos<imagesArr.length; ipos++) {
			exImage = SiForm.Tools.mup_exImageDiv(imagesArr[ipos], ipos, ipos==0, options.thumbnailPath, options.deleteAction);
			exImages.appendChild(exImage);
			SF_mup_exPos[options.name] = ipos;
		}

		var uploadIframe = Builder.node('iframe', { src: options.uploadAction, className: 'mup_iframe', border: "0", frameBorder: "0" });
		var hidden = SiForm.Elements.hidden({ name: options.name, value: options.value });
		var div = Builder.node('div', { id: 'mupControl_'+options.name, className: 'mupControl' }, [
			hidden,
		]);
		div.appendChild(exImages);
		div.appendChild(uploadIframe);
		div.thumbnailPath = options.thumbnailPath;
		div.deleteAction = options.deleteAction;
		div.defaultAction = options.defaultAction;
		div.umName = options.name;

		return div;

	}
};

if (useLocale && useLocale == 'si') {

	SiForm.Locale = {
			months: [
				[1,"Januar"], [2,"Februar"], [3,'Marec'], [4,'April'],
				[5,'Maj'], [6, 'Junij'], [7,'Julij'], [8,'Avgust'],
				[9,'September'], [10,'Oktober'], [11,'November'], [12,'December']
			],
			days: $w("ponedeljek torek sreda četrtek petek sobota nedelja"),
			validation_required: "To polje je obvezno.",
			validation_email: "Vnesite veljaven e-naslov.",
			validation_numeric: "Vnesite število. Uporabite piko za decimalna mesta: NNN.NN",
			validation_sameAs: "Vrednosti se ne ujemata.",
			validation_minLength: "Vnesite vsaj {?} znakov.",
			validation_maxLength: "Ne morete vnesti več kot {?} znakov.",
			validation_minValue: "Vnestite vrednost {?} ali več.",
			validation_maxValue: "Ne morete vnesti vrednosti večje od {?}.",
			validation_minDate: "Vnestite datum večji od {?}.",
			validation_maxDate: "Ne morete vnesti datuma večjega od {?}.",
			validation_minSelected: "Izberite vsaj {?} elementov.",
			validation_maxSelected: "Ne morete izbrati več kot {?} elementov.",
			validation_fileTypes: "Naložite lahko le datoteke z naslednjimi končnicami: {?}.",
			validation_hasErrors: "V obrazcu so prisotne napake!",

			get: function(str) { return this[str]; },
			getParsed: function(str, params) { return SiForm.Tools.sprintf(this[str], params); }
		};

} else {

	SiForm.Locale = {
		months: [
			[1,"January"], [2,"February"], [3,'March'], [4,'April'],
			[5,'May'], [6, 'June'], [7,'July'], [8,'August'],
			[9,'September'], [10,'October'], [11,'November'], [12,'December']
		],
		days: $w("monday tuesday wednesday thursday friday saturday sunday"),
		validation_required: "This field is required.",
		validation_email: "This is not a valid e-mail address.",
		validation_numeric: "This must be a numeric value.",
		validation_sameAs: "Values do not match.",
		validation_minLength: "Enter at least {?} characters.",
		validation_maxLength: "You can not enter more than {?} characters.",
		validation_minValue: "Enter a value {?} or higher.",
		validation_maxValue: "You can not enter a value higher than {?}.",
		validation_minDate: "Enter a date higher than {?}.",
		validation_maxDate: "You can not enter a date higher than {?}.",
		validation_minSelected: "Select at least {?} item(s).",
		validation_maxSelected: "You can not select more than {?} item(s).",
		validation_fileTypes: "You can only upload files with the following extension(s): {?}.",
		validation_hasErrors: "Errors occured in form!",

		get: function(str) { return this[str]; },
		getParsed: function(str, params) { return SiForm.Tools.sprintf(this[str], params); }
	};

}

SiForm.Validations = {
	required: {
		pattern: /(.+)/,
		message: SiForm.Locale.validation_required
	},
	numeric: {
		callback: function(sfObj, values, valParams, valCommand) {
			var field1 = values[valCommand['name']].toString();
			return !isNaN(field1);
		},
		message: SiForm.Locale.validation_numeric
	},
	sameAs: {
		callback: function(sfObj, values, valParams, valCommand) {
			var field1 = $("f_"+valParams[0]).value, field2 = $("f_"+valCommand['name']).value;
			return field1 == field2;
		},
		message: SiForm.Locale.validation_sameAs
	},
	minLength: {
		minCharacters: 0,
		callback: function(sfObj, values, valParams, valCommand) {
			this.minCharacters = valParams[0];
			return (values[valCommand['name']].toString() || "").length >= valParams[0];
		},
		message: function() {
			return SiForm.Locale.getParsed("validation_minLength", [this.minCharacters]);
		}
	},
	maxLength: {
		maxCharacters: 0,
		callback: function(sfObj, values, valParams, valCommand) {
			this.maxCharacters = valParams[0];
			return (values[valCommand['name']].toString() || "").length <= valParams[0];
		},
		message: function() {
			return SiForm.Locale.getParsed("validation_maxLength", [this.maxCharacters]);
		}
	},
	minValue: {
		minValue: 0,
		callback: function(sfObj, values, valParams, valCommand) {
			this.minValue = valParams[0];
			return parseFloat(values[valCommand['name']]) >= this.minValue;
		},
		message: function() {
			return SiForm.Locale.getParsed("validation_minValue", [this.minValue]);
		}
	},
	maxValue: {
		maxValue: 0,
		callback: function(sfObj, values, valParams, valCommand) {
			this.maxValue = valParams[0];
			return parseFloat(values[valCommand['name']]) <= this.maxValue;
		},
		message: function() {
			return SiForm.Locale.getParsed("validation_maxValue", [this.maxValue]);
		}
	},
	minDate: {
		minValue: 0,
		callback: function(sfObj, values, valParams, valCommand) {
			if (!valParams[0] || !valParams[1] || !valParams[2]) return true;
			this.minValue = valParams.join("-");
			return values[valCommand['name']] >= this.minValue;
		},
		message: function() {
			return SiForm.Locale.getParsed("validation_minDate", [this.minValue]);
		}
	},
	maxDate: {
		maxValue: 0,
		callback: function(sfObj, values, valParams, valCommand) {
			if (!valParams[0] || !valParams[1] || !valParams[2]) return true;
			this.maxValue = valParams.join("-");
			return values[valCommand['name']] <= this.maxValue;
		},
		message: function() {
			return SiForm.Locale.getParsed("validation_maxDate", [this.maxValue]);
		}
	},
	fileTypes: {
		allowedTypes: [],
		callback: function(sfObj, values, valParams, valCommand) {
			this.allowedTypes = valParams[0].toString().split("|");
			var upFile = $('f_'+valCommand['name']).value;
			if (!upFile) return true;
			var upFileExt = upFile.toString().split('.').pop().toLowerCase();
			for (var f=0; f<this.allowedTypes.length; ++f) {
				if (this.allowedTypes[f].toString().toLowerCase()==upFileExt) {
					return true;
				}
			}
			return false;
		},
		message: function() {
			return SiForm.Locale.getParsed("validation_fileTypes", [this.allowedTypes.join(', ')]);
		}
	},
	minSelected: {
		minSelected: 0,
		callback: function(sfObj, values, valParams, valCommand) {
			this.minSelected = valParams[0];
			return (values[valCommand['name']] || []).length >= this.minSelected;
		},
		message: function() {
			return SiForm.Locale.getParsed("validation_minSelected", [this.minSelected]);
		}
	},
	maxSelected: {
		maxSelected: 0,
		callback: function(sfObj, values, valParams, valCommand) {
			this.maxSelected = valParams[0];
			return (values[valCommand['name']] || []).length <= this.maxSelected;
		},
		message: function() {
			return SiForm.Locale.getParsed("validation_maxSelected", [this.maxSelected]);
		}
	},
	email: {
		callback: function(sfObj, values, valParams, valCommand) {
			pattern = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
			if (values[valCommand['name']]=='') return true;
			var re = new RegExp(pattern);
			return re.test(values[valCommand['name']] || '');
		},
		message: SiForm.Locale.validation_email
	}
}

SiForm.Tools = {
	setClassAndStyle: function(elOpts, bOpts, classNames, styles) {
		elOpts = Object.extend({ customClass: [], customStyle: [], width: '' }, elOpts || {});
		if (Object.isString(elOpts.customClass)) elOpts.customClass = [elOpts.customClass];
		if (Object.isString(elOpts.customStyle)) elOpts.customStyle = [elOpts.customStyle];
		if (classNames) elOpts.customClass.push(classNames);
		if (styles) elOpts.customStyle.push(styles);
		if (elOpts.width && elOpts.type=='toolbox') elOpts.width -= 21;
		if (elOpts.width) elOpts.customStyle.push('width: '+elOpts.width+'px;');

		values = Object.extend(bOpts, {
			className: elOpts.customClass.join(' '),
			style: elOpts.customStyle.join(' ')
		});
		
		if (elOpts.autocomplete === false) values.autocomplete = 'off';
		
		return values;
	},
	addCallbacks: function(el, elOpts) {
		if (elOpts.onchange) $(el).observe('change', elOpts.onchange);
		if (elOpts.onclick) $(el).observe('click', elOpts.onclick);
		if (elOpts.onfocus) $(el).observe('focus', elOpts.onfocus);
		if (elOpts.onblur) $(el).observe('blur', elOpts.onblur);
	},
	buttonCallback: function(event) {
		btn = event.element();
		bt = btn.sfOptions.buttonType || "";
		if (bt == 'reset' && btn.form) {
			event.stop();
			btn.form.reset();
			return false;
		} else if (bt == 'url' && btn.sfOptions.url) {
			event.stop();
			location.href = btn.sfOptions.url;
			return false;
		} else if (bt == 'action' && btn.sfOptions.action) {
			event.stop();
			try { eval(btn.sfOptions.action); } catch (e) {}
			return false;
		}
		if (SiForm.Tools.formValidator(false, btn.form, btn.sfObj)) btn.form.submit();
		return false;
	},
	tabsCallback: function(event) {
		var el = event.element();
		if (el.tagName.toLowerCase() == 'span') el = el.up('div');
		el.tabsContainer.setTabs(el.previousSiblings().length);
	},
	formValidator: function(event, formO, sf, skipValidations) {
		if (!sf) sf=this;
		if (sf.options.noSubmit) return false;
		if (formO.submited) return false;
		var values = formO.serialize(true), fEls = formO.getElements(), foundErrors = false;
		if (!skipValidations) {
			for (var i=0, len=sf.validations.length; i<len; i++) {
				var opts = sf.validations[i], elValidations = opts.validations;
				if (!Object.isArray(elValidations)) elValidations = [elValidations];

				for (var j=0, lenj=elValidations.length; j<lenj; j++) {

					valArr = elValidations[j].split("-");
					validation = valArr.shift();
					valParams = valArr;
					if (vOpts = SiForm.Validations[validation]) {
						// validate
						if (vOpts.callback) {
							passed = vOpts.callback(sf, values, valParams, opts);
						} else {
							valueToTest = values[opts.name];
							el = $('f_'+opts['name']);
							if (el && el.type=='file') valueToTest = el.value;
							re = new RegExp(vOpts.pattern);
							passed = re.test(valueToTest || "");
						}

						if (!passed) {
							foundErrors = true;
							msg = (Object.isFunction(vOpts.message)) ? vOpts.message() : vOpts.message;
							if (el = $('f_'+opts.name)) el.addError(elValidations[j], msg);
						} else {
							if (el = $('f_'+opts.name)) el.removeError(elValidations[j]);
						}
					}
				}
			}
		}

		/*if (!foundErrors && sf.options.showErrorOnButtons) {
			bb = $(sf.element).select(".sf-buttons-bar .sf-label");
			if (bb[0]) {
				$(bb[0]).removeClassName('sf-val-error').update('');
			}
		}*/
		if (foundErrors) {
			if (event) event.stop();
			if (sf.options.showErrorOnButtons) {
				bb = $(sf.element).select(".sf-buttons-bar .sf-label");
				if (bb[0]) {
					$(bb[0]).addClassName('sf-val-error').update('<span>'+SiForm.Locale.validation_hasErrors+"</span>");
				}
			}
			return false;
		} else if (formO.formOptions.ajax) {
			if (!event && !skipValidations) return false;
			opts = formO.formOptions;
			opts.ajaxOptions.parameters = values;
			opts.ajaxOptions.method = opts.method || 'post';

			if (opts.update) {
				new Ajax.Updater(opts.update, opts.action, opts.ajaxOptions);
			} else {
				new Ajax.Request(opts.action, opts.ajaxOptions);
			}

			if (event) event.stop();
			return false;
		}
		if (!formO.submited) formO.submited = true;
		return true;
	},

	showTooltip: function(e) {
		var el = e.element();
		tagName = el.tagName.toLowerCase();
		if (['label','input','textarea'].include(tagName) && el.hasErrors()) {
			el = e.element();
			el.showTooltip(e);
		} else if (tagName == 'span' && el.up().hasErrors() && !el.up().hasClassName('sf-tab')) {
			el.up().showTooltip(e);
		}
		e.stop();
		return false;
	},

	hideTooltip: function(e) {
		$('sf-tooltip').hide();
		e.stop();
	},
	
	openToolbox: function(e) {
		try {
		var el = Event.element(e);
		if (el.tagName != 'a') el = el.up('a');
		myLightWindow.activateWindow({
			href: el.sfOptions.url,
			title: 'Popup',
			type: 'page',
			requestParameters: { field: el.sfOptions.name, toolbox: 1 },
			requestMethod: 'post',
			requestEvalScripts: true,
			loadingAnimation: true,
			dimensions: { page: { height: 400, width: 700 } }
		});
		} catch (e) {  }
		Event.stop(e);
	},
	
	toolboxSetValue: function(value, displayValue, field) {
		//console.log(value, displayValue, field);
		var fld = $('f_'+field), fldDv = $('f_'+field+'_dv');
		//console.log(fld, fldDv);
		if (fld && fldDv) {
			fld.value = value;
			fldDv.value = displayValue;
		}
		myLightWindow.deactivate();
	},
	
	mup_addNewImage: function(image, field) {
		div = $('mupControl_'+field);
		exImages = $(div).select('.mup_exImages')[0];
		if (!exImages) exImages = div.childNodes[1];
		SF_mup_exPos[field]++;
		exImage = SiForm.Tools.mup_exImageDiv(image, SF_mup_exPos, SF_mup_exPos==0, div.thumbnailPath, div.deleteAction);
		exImages.appendChild(exImage);
		// set hidden field
		hf = $('f_'+div.umName);
		hf.value += (hf.value ? ',' : '') + image;
	},

	mup_exImageDiv: function(image, pos, isDefault, thumbnailPath, deleteAction) {
		imageId = 'exImage-'+pos;
		thumbsrc = SiForm.Tools.sprintf(thumbnailPath, [ image ]);
		aDelete = Builder.node('a', { href: '#' }, [ '[ Odstrani ]' ]);
		aDelete.pos = pos;
		aDelete.image = image;
		$(aDelete).observe('click', SiForm.Tools.mup_removeImage);
		idiv = Builder.node('div', { className: 'mup_exImage', id: imageId }, [ 
			Builder.node('img', { src: thumbsrc }),
			Builder.node('div', { className: 'mup_tools' }, [ aDelete ])
		]);

		return idiv;
	},

	mup_removeImage: function(ev, field) {
		if (confirm('Res želite odstraniti to sliko?')) {
			el = ev.element('a');
			div = el.up('.mupControl');
			hf = $('f_'+div.umName);
			hf.value = hf.value.split(',').without(el.image).join(',');
			$('exImage-'+el.pos).remove();
		}
		Event.stop(ev);
	},

	showHelp: function(e) {
		var el = e.element();
		if (el && el.nextSiblings() && el.nextSiblings()[0]) {
			ht.update('<div class="sf-helpTip-text">'+el.nextSiblings()[0].innerHTML.replace(/&lt;/g, "<").replace(/&gt;/g, ">")+'</div>').show();
			dummyFrame = Builder.node('iFrame',
			{'src': '', 'id': 'dummyFrame',
			'scrolling': 'no', 'style': 'background-color:' +
			' transparent; border: 0px;filter:progid:' +
			'DXImageTransform.Microsoft.Alpha(style=0,opacity=0);' +
			'position:absolute; top: 0px; right:0px; z-index: 999;' +
			'width: '+ht.getWidth()+'px;height:'+ht.getHeight()+'px;'});
			$(ht).insert({ top: dummyFrame });
			try {
			ht.clonePosition($(el), { setWidth: false, setHeight: false, offsetTop: 18, offsetLeft: -300 });
			} catch (e) {
			}
		}
	},

	hideHelp: function() {
		ht.hide();
	},

	sprintf: function(str, params) {
		if (str.search("{?}") < 0) return str;
		str = str.split("{?}");
		out = [];
		for (i=0,len=str.length; i<len; ++i) {
			out.push(str[i]);
			if (typeof params[i] != "undefined") out.push(params[i]);
		}
		return out.join("");
	}


};

SiForm.ExtendElement = {
	addError: function(element, error, message) {
		element = $(element);
		if (error.indexOf('||')==-1) error += "||" + element.identify();
		hadErrors = element.hasErrors();
		if (!element.errors) element.errors = {};
		if (!element.errors[error]) element.errors[error] = message;
		/*if (!hadErrors) {*/
			element.addClassName('sf-val-error');
			if (element.sfOptions) {
				label = $(element.label);
				if (label && !element.sfOptions.labelFromId) label.addError(error, message);
				else if (element.sfOptions.labelFromId) {
					if (pel = $(element.sfOptions.labelFromId)) pel.label.addError(error, message);
				}
				if (gn = element.sfOptions.groupNode) $(gn).addError(error,message);
				if (tab = element.sfOptions.tabTitle) tab.addError(error,message);
			}
			if (!element.bindedShowTooltip) {
				element.bindedShowTooltip  = SiForm.Tools.showTooltip.bind(element);
				element.observe('mouseover', element.bindedShowTooltip);
				element.observe('mouseout', SiForm.Tools.hideTooltip);
			}
		//}
	},
	removeError: function(element, error) {
		element = $(element);
		if (error.indexOf('||')==-1) error += "||" + element.identify();
		if (!element.errors) return;
		if (!element.hasErrors()) return;
		if (element.errors[error]) delete element.errors[error];
		if (!element.hasErrors()) {
			element.removeClassName('sf-val-error');
			if (element.sfOptions) {
				if (label = $(element.label)) label.removeError(error);
				else if (element.sfOptions.labelFromId) {
					if (pel = $(element.sfOptions.labelFromId)) pel.label.removeError(error);
				}
				if (gn = element.sfOptions.groupNode) gn.removeError(error);
				if (tab = element.sfOptions.tabTitle) tab.removeError(error);
			}
			element.stopObserving('mouseover', element.bindedShowTooltip);
			element.stopObserving('mouseout', SiForm.Tools.hideTooltip);
			try {
				delete element.bindedShowTooltip;
			} catch (e) {
				//
			}
		}
	},
	hasErrors: function(element) {
		element = $(element);
		if (!element.errors) return false;
		return Object.keys(element.errors).join("") != "";
	},
	showTooltip: function(element, event) {
		element = $(element);
		if (typeof element.style != 'object') return;
		if (element.hasErrors()) {
			var tt = $('sf-tooltip').show();
			try {
				tt.clonePosition(element, { setWidth: false, setHeight: false, offsetTop: element.getHeight() + 3 });
			} catch(e) {
				//
			}
			tt.update(Object.values(element.errors).join("<br/>"));
			//}
		}
	}
}

Element.addMethods(SiForm.ExtendElement);



/* FORM BUILDER */


function formBuilder_resubmitElementForm(ev) {
	var sfobj = this.form.siformObj, formEl = this.form;
	formEl.formOptions.action = formEl.resubmitAction.value;
	formEl.formOptions.ajaxOptions.evalScripts = true;
	//formEl.formOptions.update = 'fbEl';
	SiForm.Tools.formValidator(false, formEl, sfobj, true);
	//this.form.action = this.form.resubmitAction.value;
}
