

function setSelectionRange(input, start, end) {
	if (typeof(input.setSelectionRange) != "undefined") {
		input.setSelectionRange(start, end);
	} else {
		// assumed IE
		var range = input.createTextRange();
		range.collapse(true);
		range.moveStart("character", start);
		range.moveEnd("character", end - start);
		range.select();
	}
};

function getSelectionStart(input) {
	if (typeof(input.selectionStart) != "undefined")
		return input.selectionStart;
	var range = document.selection.createRange();
	var isCollapsed = range.compareEndPoints("StartToEnd", range) == 0;
	if (!isCollapsed)
		range.collapse(true);
	var b = range.getBookmark();
	return b.charCodeAt(2) - 2;
};

function getSelectionEnd(input) {
	if (typeof(input.selectionEnd) != "undefined")
		return input.selectionEnd;
	var range = document.selection.createRange();
	var isCollapsed = range.compareEndPoints("StartToEnd", range) == 0;
	if (!isCollapsed)
		range.collapse(false);
	var b = range.getBookmark();
	return b.charCodeAt(2) - 2;
};



if (typeof(Nethrom) == "undefined") Nethrom = {};

if (typeof(Nethrom) != "object")
	throw new Error("Nethrom is not an object. Nethrom.Validator has not been created.")


Nethrom.Validator = {
	
	// holder for configurable options and everything the object has except for functions
	options: {
		radix: (Projapi.Globals.LOCALE_NUM_FORMAT.toLowerCase() == "european")? "," : ".",
		thousands: (Projapi.Globals.LOCALE_NUM_FORMAT.toLowerCase() == "european")? "." : ",",
		maxYear: 2050,
		daysInMonth: [31, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]	// index starts at 1; for invalid month (0), the max is 31 days
	},	// Nethrom.Validator.options
	
	isEmpty: function(s) {
		return s.length == 0;
	},	// Nethrom.Validator.isEmpty
	
	
	
	/*
	**  Numb3rs
	*/
	
	isNumber: function(x) {
		if (typeof(x) == "string" && x.match(this.options.radix).length>1)	// check that there is no more than 1 radix point
			return false;
		return (typeof(x) == "number" || (typeof(x) == "string" && /^[+-]?(([\.\,](?=\d))?\d)+$/.test(x)));
	},	// Nethrom.Validator.isNumber

	areNumbers: function() {
		var i;
		for (i = 0; i < arguments.length; i++)
			if (!this.isNumber(arguments[i]))
				return false;
		return true;
	},	// Nethrom.Validator.areNumbers

	isInteger: function(x) {
		return String(x).replace(/[0-9]+/, '') == '';
	},	// Nethrom.Validator.isInteger

	isNumberInRange: function(x, min, max) {
		return this.isNumber(x) && parseFloat(x) >= parseFloat(min) && parseFloat(x) <= parseFloat(max);
	},	// Nethrom.Validator.isNumberInRange
	
	// data properties: str, format, fromEnd, noRadixCheck, newChar
	parseProgressNumber: function(data) {
		//"->,>>>,>>9.99"
		var subFormat = function(format, index) {
			var i = -1, t, pos, r = /[>0-9]/;
			pos = -1;
			while (i++ < index)
				if (t = format.substring(pos+1).match(r))
					pos += 1 + t.index;
			return format.substring((t)?pos:0);
		}
		
		var r = "", i, j = 0, t, match = false, str = data.str, format = data.format;
		
		if (str.length + format.length == 0) return "";		// quick way out
		data.fromEnd = !!data.fromEnd; data.noRadixCheck = !!data.noRadixCheck;
		
		if (str.length == format.length+1 && (typeof data.newChar == "number") && data.newChar > -1 && data.newChar < str.length-2 &&
			(str.charAt(data.newChar) == str.charAt(data.newChar+1) || str.substr(data.newChar-1, 2).match(/^\d\d$/)))
			str = str.substr(0, data.newChar) + str.substr(data.newChar+1);
		if (format.indexOf("-") == 0) {
			if ((t = str.indexOf("-")) >= 0 && (t < (i = str.search(/\d/)) || i < 0)) {
				r += "-";
				data.newChar--;	// keep position up to date
			}
			format = format.replace("-", "");
		}
		if (!data.noRadixCheck && data.newChar > -1) {
			// if one too many digits were entered before the radix, handle it like this... replace
			i = format.lastIndexOf(this.options.radix);
			if (i == -1) i = format.length;
			j = str.lastIndexOf(this.options.radix);
			if ((j > i && data.newChar <= j) || (j == -1 && str.length > i)) {
				i1 = 0;
				if (j == -1) j = str.length;
				while (data.newChar+i1 < j && str.charAt(data.newChar+i1).match(/\D/))
					i1++;
				if (data.newChar+i1 >= j)
					i1 = -1;	// drop the inserted digit, we are at the radix
				str = str.substr(0, data.newChar+i1) + str.substr(data.newChar+i1+1);
				if (i1 > -1)
					data.replaced = i1+1;
			}
		}
		
		i = format.indexOf("9");
		if (i != -1) {
			var i1 = format.lastIndexOf("9");
			t = eval("/" + format.substring(i, i1+1).replace(/9/g, "\\d").replace(/>/g, "\\d").replace(/\./g, "\\.") + "/g");
			j = -1;
			while (match = t.exec(str))
				j = match;
			if (j && (typeof j.index != "undefined")) {
				return r + this.parseProgressNumber({str: str.substring(0, j.index), format: format.substring(0, i), fromEnd: true, noRadixCheck: true}) +
					j[0] + this.parseProgressNumber({str: str.substring(j.index+i1-i+1), format: format.substring(i1+1), fromEnd: false, noRadixCheck: true});
			}
		}
		
		if (!data.noRadixCheck) {
			if ((i = str.indexOf(this.options.radix)) != -1 && (j = format.indexOf(this.options.radix)) != -1) {
				return r + this.parseProgressNumber({str: str.substring(0, i), format: format.substring(0, j), fromEnd: true, noRadixCheck: true}) +
					this.options.radix + this.parseProgressNumber({str: str.substring(i+1), format: format.substring(j+1), fromEnd: false, noRadixCheck: true});
			}
		}
		
		str = str.replace(/[^0-9]/g, "");
		
		// do we have to dynamically partition the string?
		var trueFormat = format.replace(/[^>0-9]/g, "");
		
		if (trueFormat.indexOf(">") < (i = trueFormat.indexOf("9")) && (j = trueFormat.lastIndexOf("9")) > str.length) {
			if (j-i+1 > str.length)
				format = subFormat(format, i);
			else
				format = subFormat(format, j+1-str.length);
		}
		
		if (data.fromEnd)
			if (trueFormat.length > str.length)
				format = subFormat(format, trueFormat.length-str.length);
			else if (str.length > trueFormat.length)
				str = str.substr(-trueFormat.length);
		
		j = 0; match = false;
		for (i = 0; i < format.length; i++)
			try {
				t = format.charAt(i);
				if ("->9".indexOf(t) > -1 && j >= str.length)
					throw $break;
				switch (t) {
					//case '-': if (str[j] == '-') { r += '-'; j++; }; break;
					case '>':
						if (str.charAt(j) != '0' || match) {
							r += str.charAt(j);
							match = true;
						} else
							match = false;
						j++;
						break;
					case '9': r += str.charAt(j); j++; match = true; break;
					default:
						if (i == 0 || format.charAt(i-1) != ">" || match)
							r += format.charAt(i);
						break;
				}
			} catch(e) {
				if (e == $break) break;
							else throw e;
			}
		return r;
	},	// Nethrom.Validator.parseProgressNumber
	
	
	
	/*
	**  Dates
	*/
	
	isValidDate: function(x, format) {
		var formatParts = format.split(/[^dmy]/);
		var delimiters = new RegExp("[" + format.replace(/[dmy]/g, '').split().join('') + "]", "g");
		var parts = x.split(delimiters);
		// extract date components
		var day, month, year, i;
		for (i = 0; i < 3; i++)
			switch (formatParts[i].charAt(0)) {
				case 'd': day   = parts[i]; break;
				case 'm': month = parts[i]; break;
				case 'y': year  = parts[i]; if (year.length > formatParts[i].length) return false; break;
				default: assert(false); break;
			}
		day   = parseInt(day);
		month = parseInt(month);
		year  = parseInt(year);
		// check
		if (! /yyy/.test(format)) (year<=this.options.maxDateYear-2000) ? year+=2000 : (year+=1900);
		var date = new Date(year, month-1, day);
		return day==date.getDate() && month==date.getMonth()+1 && year==date.getYear();
	},	// Nethrom.Validator.isValidDate

	parseDate: function(x, format, info) {
		// lz adds leading zeroes until the requested width
		var lz = function(s, len) {
			while (s.length < len)
				s = '0' + s;
			return s;
		}
		var newChar = -1;
		if (typeof info != "undefined" && typeof info.newChar == "number")
			newChar = info.newChar;
		var formatParts = format.split(/[^dmy]/);
		var delimiters = format.replace(/[dmy]/g, '').split("");
		var delimRX = new RegExp("[" + delimiters.join('') + "]", "g");
		var parts = x.split(delimRX);
		var i, chr, now = new Date(), dateObj = {d:now.getDate(), m:now.getMonth()+1, y:now.getFullYear(), mind: 1, minm: 1};
														// (for above) mind = min day, minm = min month
		delete now;
		// fill in blanks :)
		for (i = 0; i < parts.length && i < formatParts.length; i++)
			if (!parts[i]) {
				chr = formatParts[i].charAt(0);
				if (dateObj[chr]) parts[i] = String(dateObj[chr]);
							 else parts[i] = "0";
			}
		while (parts.length < formatParts.length) {
			chr = formatParts[parts.length].charAt(0);
			if (dateObj[chr]) parts.push(String(dateObj[chr]));
						 else parts.push("0");
		}
		// extract date components
		for (i = 0; i < 3; i++) {
			chr = formatParts[i].charAt(0);
			dateObj["p" + chr] = formatParts[i].length;							// how many characters the least? (for leading zero)
			if (dateObj["p" + chr] == 1)
				dateObj[chr] = parts[i];										// get the value (string), the mask says any number of chars
			else
				if (newChar < parts[i].length && newChar >= dateObj["p" + chr])
					dateObj[chr] = parts[i].slice(newChar-dateObj["p" + chr]+1, newChar+1);	// always keep the new character
				else
					dateObj[chr] = parts[i].slice(0, dateObj["p" + chr]);			// get the value (string), only the number of characters the mask wants
				newChar -= parts[i].length;
				newChar--; // account for delimiter
			if (isNaN(dateObj["i"+chr] = dateObj[chr]-0)) dateObj["i"+chr] = 0;	// get the value (number)
		}
		if (dateObj.y && dateObj.y.length > format.replace(/[^y]/g, '').length) dateObj.y = dateObj.y.slice(0, format.replace(/[^y]/g, '').length);
		// limit
        while (String(dateObj.iy).length>String(this.options.maxYear).length) dateObj.iy = Math.floor(dateObj.iy/10);
        if (dateObj.iy>this.options.maxYear) dateObj.iy=this.options.maxYear;
        while (dateObj.im > 12) dateObj.im = Math.floor(dateObj.im/10);         // Math.floor is required since there is no 'int' in JS, just 'number'
        while (dateObj.id > this.options.daysInMonth[dateObj.im]) dateObj.id = Math.floor(dateObj.id/10);
        // leap year?
        if (dateObj.id == 29 && dateObj.im == 2 && dateObj.iy != 0 && dateObj.iy%4 == 0)
			dateObj.id = Math.floor(dateObj.id/10);
		// reconstruct string
		var str = "";
		for (i = 0; i < 3; i++) {
			chr = formatParts[i].charAt(0);
			if (typeof(dateObj["min"+chr]) != "undefined" && dateObj["i"+chr] < dateObj["min"+chr])
				dateObj["i"+chr] = dateObj["min"+chr];
			if (dateObj[chr].replace(/^0+/, '') != String(dateObj["i"+chr])) dateObj[chr] = "" + dateObj["i"+chr];
			str += lz(dateObj[chr], dateObj["p"+chr]);
			if (i < 2) str += delimiters[i];
		}
		return str;
	}	// Nethrom.Validator.parseDate
}

function _MaskAPI(){
	this.version = "0.4a";
	this.instances = 0;
	this.objects = {};
}
MaskAPI = new _MaskAPI();

function Mask(field, m, t, df){
	this.type = (typeof t == "string") ? t : "string";

	//replace 99-99-9999 progress format with dd-mm-yyyy universal format
	if (typeof df != "undefined"){
		var s = m.split(/[9]+/);
		if (s[0].length == 0) s.splice(0, 1);
		
		var pp = m.split(s[0]);
	    
		pp[df.indexOf("d")] = pp[df.indexOf("d")].replace(/9/g,"d");	
		pp[df.indexOf("m")] = pp[df.indexOf("m")].replace(/9/g,"m");	
		pp[df.indexOf("y")] = pp[df.indexOf("y")].replace(/9/g,"y");	

		m = pp[0] + s[0] + pp[1] + s[0] + pp[2];
	}	// ...............

	//translate progress format like x(20) into the required format 
	if (m.indexOf("(") != -1){
		var char = m.substring(0,1).toUpperCase();
		var len = m.substring(m.indexOf("(") + 1,m.indexOf(")"));
		m = "";
		var format = "";

		switch(char){
			case "X": format = "*"; break;
			case "N": format = "*"; break;
			case "A": format = "x"; break;
			case "9": format = "#"; break;
			default	: format = "*";	
		}
		
		for (var i = 0; i < len; i++){
			m = m + format;
		}
	}

	this.mask = m;
	if (Projapi.Globals.LOCALE_NUM_FORMAT == 'european')
		this.mask = this.mask.replace('.', '<radix>').replace(/,/g, '.').replace('<radix>', ',');
	this.error = [];
	this.errorCodes = [];
	this.value = "";
	this.strippedValue = "";
	this.allowPartial = false;
	this.id = MaskAPI.instances++;
	this.ref = "MaskAPI.objects['" + this.id + "']";
	MaskAPI.objects[this.id] = this;
	this.attach($(field));
	//this.attach(document.getElementById(field));
	
	var oThis = this;
	($(field).notifyChange = function() {
		if (! $(field).value) return;
		if (oThis.type == "decimal")
			$(field).value = oThis.value = Nethrom.Validator.parseProgressNumber({str: $(field).value, format: oThis.mask});
		else if (oThis.type == "date" && oThis.value != "")
			$(field).value = oThis.value = Nethrom.Validator.parseDate($(field).value, oThis.mask);
	})();
	Event.observe($(field), "blur", $(field).notifyChange, false);
}

// attach mask handlers to events
Mask.prototype.attach = function (o){
	$addEvent(o, "onkeydown", "return " + this.ref + ".onKeyDown(event || window.event, this);", true);
	$addEvent(o, "onkeypress", "return " + this.ref + ".onKeyPress(event || window.event, this);", true);
	//$addEvent(o, "onkeyup", "return " + this.ref + ".getKeyPress(event, this);", true);
	//$addEvent(o, "onblur", "this.value = " + this.ref + ".format(this.value);", true);
}

Mask.prototype.onKeyDown = function(event, object) {
	// handle events that should execute object action (probably open a list to pick an item)
	if (event.keyCode == Event.KEY_DOWN)
		if (object.nextSibling && object.nextSibling.onclick) {
			if (typeof(object.nextSibling.onclick) == "function")
				object.nextSibling.onclick();
			else
				eval(object.nextSibling.onclick);	// assume string
			object.blur();
			Event.stop(event);
			return false;
		}
	return true;
}

Mask.prototype.onKeyPress = function (event, object) {
	// Gecko calls onkeypress for non-printable characters, although it shouldn't. Allow control codes to pass
	if (typeof(event.charCode) != "undefined" && event.charCode == 0)
		return true;
	// get the key
	var kC = event.keyCode || event.charCode;
	var chr = String.fromCharCode(kC);
	var posStart = getSelectionStart(object);
	var posEnd = getSelectionEnd(object);
	// filter it
	switch (this.type) {
		case "string":
			if (object.value.length >= this.mask.length && !event.ctrlKey && !event.altKey)
				return false;
			break;
		case "decimal":
			if (/[-0-9]/.test(chr)) {
				if (typeof(object.selectionStart) == "undefined" && /MSIE 7\.\d/.test(navigator.userAgent)) {
				} else {
					posStart++; posEnd++;
				}
				var newValue = object.value.substr(0, posStart-1) + chr + object.value.substr(posEnd-1);
				var data = {str: newValue, format: this.mask, newChar: posStart};
				object.value = this.value = Nethrom.Validator.parseProgressNumber(data);
				if (data.replaced)					// move cursor because we replaced the next char instead of inserting
					posStart += data.replaced;		// maybe we skipped a delimiter
				if (newValue.indexOf(object.value) != 0)				// if we didn't truncate, then...
					posStart += object.value.length - newValue.length;	// ...offset new/removed characters
				if (object.value.length > newValue.length && object.value.charAt(posStart-1).match(/\D/))
					posStart--;	// don't auto-skip new delimiters
				if (object.value.charAt(object.value.length-1).match(/\D/) && object.value.charAt(object.value.length-1) == this.mask.charAt(object.value.length-1))
					posStart++;	// unless we can no longer add digits
				setSelectionRange(object, posStart, posStart);	
			} else if (chr == "?") {
				object.value = this.value = "";
				// this.value = null;
			} else if (!event.ctrlKey && !event.altKey && (chr == " " || kC >= 40)) {	// opera sends all the keys, filter out arrow, home/end/pgup/pgdn/etc.
				if (typeof(object.selectionStart) == "undefined" && /MSIE 7\.\d/.test(navigator.userAgent)) {
				} else {
					posStart++; posEnd++;
				}
				try {
					if (object.value.length >= posStart && object.value.charAt(posStart-1).match(/\D/))
						setSelectionRange(object, posStart, posStart);
				} catch(e) { // throws exception if empty
				}
			} else if (event.ctrlKey && !event.altKey && chr == "v") {
				// PASTE
				var old = object.value;
				var oThis = this;
				setTimeout(function() {
					var len = object.value.length - old.length + posEnd - posStart;
					var paste = object.value.substr(posStart, len);
					var data = {str: object.value, format: oThis.mask};
					object.value = this.value = Nethrom.Validator.parseProgressNumber(data);
				}, 10);
				break;
			} else
				break;		// out of switch
			return false;	// we did the char insertions ourselves, thank you
			break;
		case "date":
			if (chr == "?") {
				object.value = this.value = "";
				return false;
			}
			if (typeof(object.selectionStart) == "undefined" && /MSIE 7\.\d/.test(navigator.userAgent)) {
					// IE 7
					if (posStart < 2) {
						posStart++; posEnd++;
					}
			} else {
				posStart++; posEnd++;
			}
			if (! /[0-9]/.test(chr)) {
				if (object.value.length >= posStart && ! /[0-9]/.test(object.value.charAt(posStart))) { // is there a separator which we should skip?
					posStart++;
					setSelectionRange(object, posStart, posStart);
				}
				return false;
			}
			if (posEnd == posStart)
				posEnd++;	// disable for insert-mode
			newValue = object.value.substr(0, posStart-1) + chr + object.value.substring(posStart-1, posEnd-1).replace(/[0-9]/g, '') +
				object.value.substr(posEnd-1);
			object.value = this.value = Nethrom.Validator.parseDate(newValue, this.mask.replace(/yyyy/g, 'y'), {newChar: posStart-1});
			if (object.value.charAt(posStart-1) == "0" && chr != "0")	// did we pad a zero?
				posStart++;
			// 'else' because... well... never both
			else if (object.value.length >= posStart && ! /[0-9]/.test(object.value.charAt(posStart))) // is there a separator next which we should skip?
				posStart++;
			setSelectionRange(object, posStart, posStart);
			return false;	// we did the char insertions ourselves, thank you
			break;
		default: break;
	}
	setSelectionRange(object, posStart, posEnd);
	return true;
}

Mask.prototype.getKeyPress = function (e, o, _u){
	this.allowPartial = true;
	var xe = new qEvent(e);
	if( (xe.keyCode > 47) || (_u == true) || (xe.keyCode == 8 || xe.keyCode == 46) ){
		var v = o.value, d;
		if( xe.keyCode == 8 || xe.keyCode == 46 ) d = true;
		else d = false

		if (this.type == "decimal") this.value = Nethrom.Validator.parseProgressNumber({str: v, format: this.mask});//this.setNumber(v, d);
		else if (this.type == "date") this.value = Nethrom.Validator.parseDate(v, this.mask);//this.setDateKeyPress(v, d);
		else this.value = this.setGeneric(v, d);

		var posStart = getSelectionStart(o);
		var posEnd = getSelectionEnd(o);
		o.value = this.value;
		setSelectionRange(o, posStart, posEnd);
	}
	/* */

	this.allowPartial = false;
	return true;
}

Mask.prototype.format = function (s){
	if( this.type == "decimal" ) this.value = this.setNumber(s);
	else if( this.type == "date" ) this.value = this.setDate(s);
	else this.value = this.setGeneric(s);
	return this.value;
}

Mask.prototype.throwError = function (c, e, v){
	this.error[this.error.length] = e;
	this.errorCodes[this.errorCodes.length] = c;
	
	if( typeof v == "string" ) return v;
	return true;
}

Mask.prototype.setGeneric = function (_v, _d){
	var v = _v, m = this.mask;
	var r = "x#*", rt = [], nv = "", t, x, a = [], j=0, rx = {"x": "A-Za-z", "#": "0-9",  "*": "A-Za-z0-9"};

	// strip out invalid characters
	v = v.replace(new RegExp("[^" + rx["*"] + "]", "gi"), "");
	
	if( (_d == true) && (v.length == this.strippedValue.length) ) {
		v = v.substring(0, v.length-1);
	}
	
	this.strippedValue = v;
	var b=[];
	for( var i=0; i < m.length; i++ ){
		// grab the current character
		x = m.charAt(i);
		// check to see if current character is a mask, escape commands are not a mask character
		t = (r.indexOf(x) > -1);
		// if the current character is an escape command, then grab the next character
		if( x == "!" ) x = m.charAt(i++);
		// build a regex to test against
		if( (t && !this.allowPartial) || (t && this.allowPartial && (rt.length < v.length)) ) {
			rt[rt.length] = "[" + rx[x] + "]";
		}
		// build mask definition table
		a[a.length] = { "chr": x, "mask": t };
	}
	
	var hasOneValidChar = false;
	
	// if the regex fails, return an error
	if( !this.allowPartial && !(new RegExp(rt.join(""))).test(v) ) {
		return this.throwError(1, "The value \"" + _v + "\" must be in the format " + this.mask + ".", _v);
	}
	// loop through the mask definition, and build the formatted string
	else if( (this.allowPartial && (v.length > 0)) || !this.allowPartial ){
		for( i=0; i < a.length; i++ ){
			if( a[i].mask ){
				while( v.length > 0 && !(new RegExp(rt[j])).test(v.charAt(j)) ) {
					v = (v.length == 1) ? "" : v.substring(1);
				}
				if( v.length > 0 ){
					nv += v.charAt(j);
					hasOneValidChar = true;
				}
				j++;
			} else {
				nv += a[i].chr;
			}
			if( this.allowPartial && (j > v.length) ) break;
		}
	}

	if( this.allowPartial && !hasOneValidChar ) nv = "";
	if( this.allowPartial ){
		if( nv.length < a.length ) this.nextValidChar = rx[a[nv.length].chr];
		else this.nextValidChar = null;
	}

	return nv;
}

Mask.prototype.setNumber = function(_v, _d){
	//change progress number format with the universal format accepted here
	m = this.mask.replace(/>/g,"#").replace(/9/g,"0");
	
	var v = String(_v).replace(/[^\d.-]*/gi, "");
	// make sure there's only one decimal point
	v = v.replace(/\./, "d").replace(/\./g, "").replace(/d/, ".");

	// check to see if an invalid mask operation has been entered
	if( !/^[\$]?((\$?[\+-]?([0#]{1,3},)?[0#]*(\.[0#]*)?)|([\+-]?\([\+-]?([0#]{1,3},)?[0#]*(\.[0#]*)?\)))$/.test(m) )
		return this.throwError(1, "An invalid mask was specified for the \nMask constructor.", _v);

	if( (_d == true) && (v.length == this.strippedValue.length) ) v = v.substring(0, v.length-1);

	if( this.allowPartial && (v.replace(/[^0-9]/, "").length == 0) ) return v;
	this.strippedValue = v;

	if( v.length == 0 ) v = NaN;
	var vn = Number(v);
	if( isNaN(vn) ) return this.throwError(2, "The value entered was not a number.", _v);

	// if no mask, stop processing
	if( m.length == 0 ) return v;

	// get the value before the decimal point
	var vi = String(Math.abs((v.indexOf(".") > -1 ) ? v.split(".")[0] : v));
	// get the value after the decimal point
	var vd = (v.indexOf(".") > -1) ? v.split(".")[1] : "";
	var _vd = vd;

	var isNegative = (vn != 0 && Math.abs(vn)*-1 == vn);

	// check for masking operations
	var show = {
		"$" : /^[\$]/.test(m),
		"(": (isNegative && (m.indexOf("(") > -1)),
		"+" : ( (m.indexOf("+") != -1) && !isNegative )
	}
	show["-"] = (isNegative && (!show["("] || (m.indexOf("-") != -1)));


	// replace all non-place holders from the mask
	m = m.replace(/[^#0.,]*/gi, "");

	/*
		make sure there are the correct number of decimal places
	*/
	// get number of digits after decimal point in mask
	var dm = (m.indexOf(".") > -1 ) ? m.split(".")[1] : "";
	if( dm.length == 0 ){
		vi = String(Math.round(Number(vi)));
		vd = "";
	} else {
		// find the last zero, which indicates the minimum number
		// of decimal places to show
		var md = dm.lastIndexOf("0")+1;
		// if the number of decimal places is greater than the mask, then round off
		if( vd.length > dm.length ) vd = String(Math.round(Number(vd.substring(0, dm.length + 1))/10));
		// otherwise, pad the string w/the required zeros
		else while( vd.length < md ) vd += "0";
	}

	/*
		pad the int with any necessary zeros
	*/
	// get number of digits before decimal point in mask
	var im = (m.indexOf(".") > -1 ) ? m.split(".")[0] : m;
	im = im.replace(/[^0#]+/gi, "");
	// find the first zero, which indicates the minimum length
	// that the value must be padded w/zeros
	var mv = im.indexOf("0")+1;
	// if there is a zero found, make sure it's padded
	if( mv > 0 ){
		mv = im.length - mv + 1;
		while( vi.length < mv ) vi = "0" + vi;
	}


	/*
		check to see if we need commas in the thousands place holder
	*/
	if( /[#0]+,[#0]{3}/.test(m) ){
		// add the commas as the place holder
		var x = [], i=0, n=Number(vi);
		while( n > 999 ){
			x[i] = "00" + String(n%1000);
			x[i] = x[i].substring(x[i].length - 3);
			n = Math.floor(n/1000);
			i++;
		}
		x[i] = String(n%1000);
		vi = x.reverse().join(",");
	}

	/*	combine the new value together  */
	if( (vd.length > 0 && !this.allowPartial) || ((dm.length > 0) && this.allowPartial && (v.indexOf(".") > -1) && (_vd.length >= vd.length)) ){
		v = vi + "." + vd;
	} else if( (dm.length > 0) && this.allowPartial && (v.indexOf(".") > -1) && (_vd.length < vd.length) ){
		v = vi + "." + _vd;
	} else {
		v = vi;
	}

	if( show["$"] ) v = this.mask.replace(/(^[\$])(.+)/gi, "$") + v;
	if( show["+"] ) v = "+" + v;
	if( show["-"] ) v = "-" + v;
	if( show["("] ) v = "(" + v + ")";
	
	return v;
}

//functia primeste ca parametru input-ul de la data (ex. 12-14-2003)
//functie formateaza acest string in functie de un mask (ex. mm-dd-yyyy)
// de exemplu daca input-ul este 1-1-03 si mask-ul este mm-dd-yyyy functia v-a returna 01-01-2003
Mask.prototype.setDate = function (_v){
	var v = _v, m = this.mask;
	var a, e, mm, dd, yy, x, s;

	// split mask into array, to see position of each day, month & year
	a = m.split(/[^mdy]+/);
	// split mask into array, to get delimiters
	s = m.split(/[mdy]+/);
	// convert the string into an array in which digits are together
	e = v.split(/[^0-9]/);
	
	if( s[0].length == 0 ) s.splice(0, 1);

	for( var i=0; i < a.length; i++ ){
		x = a[i].charAt(0).toLowerCase();
		if( x == "m" ) 		mm = parseInt(e[i], 10)-1;
		else if( x == "d" ) dd = parseInt(e[i], 10);
		else if( x == "y" ) yy = parseInt(e[i], 10);
	}

	// if year is abbreviated, guess at the year
	if( String(yy).length < 3 ){
		yy = 2000 + yy;
		if( (new Date()).getFullYear()+20 < yy ) yy = yy - 100;
	}

	// create date object
	var d = new Date(yy, mm, dd);

	if( d.getDate() != dd ) return this.throwError(1, "An invalid day was entered.", _v);
	else if( d.getMonth() != mm ) return this.throwError(2, "An invalid month was entered.", _v);

	var nv = "";

	for( i=0; i < a.length; i++ ){
		x = a[i].charAt(0).toLowerCase();
		if( x == "m" ){
			mm++;
			if( a[i].length == 2 ){
				mm = "0" + mm;
				mm = mm.substring(mm.length-2);
			}
			nv += mm;
		} else if( x == "d" ){
			if( a[i].length == 2 ){
				dd = "0" + dd;
				dd = dd.substring(dd.length-2);
			}
			nv += dd;
		} else if( x == "y" ){
			if( a[i].length == 2 ) nv += d.getYear();
			else nv += d.getFullYear();
		}

		if( i < a.length-1 ) nv += s[i];
	}

	return nv;
}

//functia primeste ca si parametrii un string si o valoare booleana _d care este true daca s-a apasat tasta delete sau backspace
//functia verifica la fiecare keypress daca se respecta formatul impus de mask ...
Mask.prototype.setDateKeyPress = function (_v, _d){
	var v = _v, m = this.mask, k = v.charAt(v.length-1);
	var a, e, c, ml, vl, mm = "", dd = "", yy = "", x, p, z;

	// split mask into array, to get delimiters
	s = m.split(/[mdy]+/);
	// mozilla wants to add an empty array element which needs removed
	if( s[0].length == 0 ) s.splice(0,1);
	
	//remove non-numeric characters
	var v = String(_v).replace(/[^\d/.-]*/gi, "");
	if( v.length == 0 ) return "";	
	
	//process 'delete' and 'backspace' keys
	if( _d == true ){ 
		while( (/[^0-9]/gi).test(v.charAt(v.length-1)) ) {
			v = v.substring(0, v.length-1);
		}
		if( (/[^0-9]/gi).test(this.strippedValue.charAt(this.strippedValue.length-1)) ) {
			v = v.substring(0, v.length-1);
		}
		//if( v.length == 0 ) return "";
		return v;
	}

	// split mask into array, to see position of each day, month & year
	a = m.split(/[^mdy]/);
	// convert the string into an array in which digits are together
	e = v.split(/[^0-9]/);
	// position in mask
	p = (e.length > 0) ? e.length-1 : 0;


	//if user entered separator then leave it and continue
	var len = v.length;
	if (v.charAt(len-1) == s[0]){
		if ((len > 1 && v.charAt(len-2) == s[0]) || e.length > 3)
			return v.substring(0, len - 1);
		else
			return v;
	}


	// determine what mask value the user is currently entering
	c = a[p].charAt(0);
	// determine the length of the current mask value
	ml = a[p].length;

	
	for( var i=0; i < e.length; i++ ){
		x = a[i].charAt(0).toLowerCase();
		if( x == "m" ) {
			//mm = parseInt(e[i], 10)-1;
			//mm = parseInt(e[i], 10);
			//if (isNaN(mm)) mm = "";
			mm = e[i];
		} else if( x == "d" ) {
			//dd = parseInt(e[i], 10);
			//if (isNaN(dd)) dd = "";
			dd = e[i];
		} else if( x == "y" ) {
			//yy = parseInt(e[i], 10);
			//if (isNaN(yy)) yy = "";
			yy = e[i];
		}
	}
	
	var nv = "";
	var j=0;

	for( i=0; i < e.length; i++ ){
		x = a[i].charAt(0).toLowerCase();
	
		if( x == "m" ){
			z = ((/[^0-9]/).test(k) && c == "m");
			//mm++;
			
			if( ((e[i].length == 2 && mm < 10) || (a[i].length == 2 && c != "m")  || (z && a[i].length == 2))  && _d == false){ //|| (mm > 1 && c == "m")
				//mm = "0" + mm;
			}

			vl = String(mm).length;
			if (vl > 2){
				mm = (String(mm)).substr(0,2);
				vl = 2;
			}
			
			ml = 2;
			nv += mm;
		} else if( x == "d" ){
			z = ((/[^0-9]/).test(k) && c == "d");
			if( ((e[i].length == 2 && dd < 10) || (a[i].length == 2 && c != "d")  || (z && a[i].length == 2)) && _d == false){ //|| (dd > 3 && c == "d")
				//dd = "0" + dd;
			}
			
			vl = String(dd).length;
			if (vl > 2) {
				dd = (String(dd)).substr(0,2);
				vl = 2;
			}
			
			ml = 2;
			nv += dd;
		} else if( x == "y" ){
			z = ((/[^0-9]/).test(k) && c == "y");
/*
			if( c == "y" ) yy = String(yy);
			else {
				if( a[i].length == 2 ) yy = d.getYear();
				else yy = d.getFullYear();
			}
*/
			
			if( (e[i].length == 2 && yy < 10) || (a[i].length == 2 && c != "y") || (z && a[i].length == 2) ){
				//yy = "0" + yy;
				//yy = yy.substring(yy.length-2);
			}

			vl = String(yy).length;
			ml = a[i].length;

			if (vl > ml){
				yy = (String(yy)).substr(0,ml);
			}
			nv += yy;
		}

		if( ((ml == vl || z) && (x == c) && (i < s.length)) || (i < s.length && x != c ) ) nv += s[i];
	}

	if( nv.length > m.length ) nv = nv.substring(0, m.length);

	this.strippedValue = (nv == "NaN") ? "" : nv;

	return this.strippedValue;
}

function qEvent(e){
	// routine for NS, Opera, etc DOM browsers
	//if( window.Event ){
		// si atunci la mine in IE de ce window.Event e definit, iar 'e' nu? BPx
	if (e) {
		var isKeyPress = (e.type.substring(0,3) == "key");

		this.keyCode = (isKeyPress) ? parseInt(e.which, 10) : 0;
		this.button = (!isKeyPress) ? parseInt(e.which, 10) : 0;
		this.srcElement = e.target;
		this.type = e.type;
		this.x = e.pageX;
		this.y = e.pageY;
		this.screenX = e.screenX;
		this.screenY = e.screenY;
		if( document.layers ){
			this.altKey = ((e.modifiers & Event.ALT_MASK) > 0);
			this.ctrlKey = ((e.modifiers & Event.CONTROL_MASK) > 0);
			this.shiftKey = ((e.modifiers & Event.SHIFT_MASK) > 0);
			this.keyCode = this.translateKeyCode(this.keyCode);
		} else {
			this.altKey = e.altKey;
			this.ctrlKey = e.ctrlKey;
			this.shiftKey = e.shiftKey;
		}
	// routine for Internet Explorer DOM browsers
	} else {
		e = window.event;
		this.keyCode = parseInt(e.keyCode, 10);
		this.button = e.button;
		this.srcElement = e.srcElement;
		this.type = e.type;
		if( document.all ){
			this.x = e.clientX + document.body.scrollLeft;
			this.y = e.clientY + document.body.scrollTop;
		} else {
			this.x = e.clientX;
			this.y = e.clientY;
		}
		this.screenX = e.screenX;
		this.screenY = e.screenY;
		this.altKey = e.altKey;
		this.ctrlKey = e.ctrlKey;
		this.shiftKey = e.shiftKey;
	}
	if( this.button == 0 ){
		this.setKeyPressed(this.keyCode);
		this.keyChar = String.fromCharCode(this.keyCode);
	}
}

// this method will try to remap the keycodes so the keycode value
// returned will be consistent. this doesn't work for all cases,
// since some browsers don't always return a unique value for a
// key press.
qEvent.prototype.translateKeyCode = function (i){
	var l = {};
	// remap NS4 keycodes to IE/W3C keycodes
	if( !!document.layers ){
		if( this.keyCode > 96 && this.keyCode < 123 ) return this.keyCode - 32;
		l = {
			96:192,126:192,33:49,64:50,35:51,36:52,37:53,94:54,38:55,42:56,40:57,41:48,92:220,124:220,125:221,
			93:221,91:219,123:219,39:222,34:222,47:191,63:191,46:190,62:190,44:188,60:188,45:189,95:189,43:187,
			61:187,59:186,58:186,
			"null": null
		}
	}
	return (!!l[i]) ? l[i] : i;
}

// try to determine the actual value of the key pressed
qEvent.prototype.setKP = function (i, s){
	this.keyPressedCode = i;
	this.keyNonChar = (typeof s == "string");
	this.keyPressed = (this.keyNonChar) ? s : String.fromCharCode(i);
	this.isNumeric = (parseInt(this.keyPressed, 10) == this.keyPressed);
	this.isAlpha = ((this.keyCode > 64 && this.keyCode < 91) && !this.altKey && !this.ctrlKey);
	return true;
}

// try to determine the actual value of the key pressed
qEvent.prototype.setKeyPressed = function (i){
	var b = this.shiftKey;
	if( !b && (i > 64 && i < 91) ) return this.setKP(i + 32);
	if( i > 95 && i < 106 ) return this.setKP(i - 48);
	
	switch( i ){
		case 49: case 51: case 52: case 53: if( b ) i = i - 16; break;
		case 50: if( b ) i = 64; break;
		case 54: if( b ) i = 94; break;
		case 55: if( b ) i = 38; break;
		case 56: if( b ) i = 42; break;
		case 57: if( b ) i = 40; break;
		case 48: if( b ) i = 41; break;
		case 192: if( b ) i = 126; else i = 96; break;
		case 189: if( b ) i = 95; else i = 45; break;
		case 187: if( b ) i = 43; else i = 61; break;
		case 220: if( b ) i = 124; else i = 92; break;
		case 221: if( b ) i = 125; else i = 93; break;
		case 219: if( b ) i = 123; else i = 91; break;
		case 222: if( b ) i = 34; else i = 39; break;
		case 186: if( b ) i = 58; else i = 59; break;
		case 191: if( b ) i = 63; else i = 47; break;
		case 190: if( b ) i = 62; else i = 46; break;
		case 188: if( b ) i = 60; else i = 44; break;

		case 106: case 57379: i = 42; break;
		case 107: case 57380: i = 43; break;
		case 109: case 57381: i = 45; break;
		case 110: i = 46; break;
		case 111: case 57378: i = 47; break;

		case 8: return this.setKP(i, "[backspace]");
		case 9: return this.setKP(i, "[tab]");
		case 13: return this.setKP(i, "[enter]");
		case 16: case 57389: return this.setKP(i, "[shift]");
		case 17: case 57390: return this.setKP(i, "[ctrl]");
		case 18: case 57388: return this.setKP(i, "[alt]");
		case 19: case 57402: return this.setKP(i, "[break]");
		case 20: return this.setKP(i, "[capslock]");
		case 32: return this.setKP(i, "[space]");
		case 91: return this.setKP(i, "[windows]");
		case 93: return this.setKP(i, "[properties]");

		case 33: case 57371: return this.setKP(i*-1, "[pgup]");
		case 34: case 57372: return this.setKP(i*-1, "[pgdown]");
		case 35: case 57370: return this.setKP(i*-1, "[end]");
		case 36: case 57369: return this.setKP(i*-1, "[home]");
		case 37: case 57375: return this.setKP(i*-1, "[left]");
		case 38: case 57373: return this.setKP(i*-1, "[up]");
		case 39: case 57376: return this.setKP(i*-1, "[right]");
		case 40: case 57374: return this.setKP(i*-1, "[down]");
		case 45: case 57382: return this.setKP(i*-1, "[insert]");
		case 46: case 57383: return this.setKP(i*-1, "[delete]");
		case 144: case 57400: return this.setKP(i*-1, "[numlock]");
	}
	
	if( i > 111 && i < 124 ) return this.setKP(i*-1, "[f" + (i-111) + "]");

	return this.setKP(i);
}

// define the addEvent(oElement, sEvent, sCmd, bAppend) function
function $addEvent(o, _e, c, _b){
	if (o[_e + "_h"]) {
		var fct = new Function("event", c);
		o[_e + "_h"].push(fct);
		return;
	}
	var e = _e.toLowerCase(), b = (typeof _b == "boolean") ? _b : true, x = (o[e]) ? o[e].toString() : "";
	// strip out the body of the function
	x = x.substring(x.indexOf("{")+1, x.lastIndexOf("}"));
	x = ((b) ? (x + c) : (c + x)) + "\n";
	return o[e] = (!!window.Event) ? new Function("event", x) : new Function(x);
}

