/*
	jquery.constrain plugin
	* You may distribute this code under the same license as jQuery (BSD or GPL)
	
	$.constrain usage:
		//limit field to having maximum of one 'p' and four '\'		
		$("#myfield").constrain({
					limit: { "p":1 , "\\":4 }
				});
				
		//prohibit field from having alphabet chars
		$("#myfield").constrain({
					prohibit:	{ regex: "[a-zA-Z]" }
				});		
		//prohibit field from having vowels
		$("#myfield").constrain({
					prohibit:	{ chars: "aeiouAEIOU" }
				});				
		
		//allow field to only have alphabet chars
		$("#myfield").constrain({
					allow:	{ regex: "[a-zA-Z]" }
				});						
	/*
	$.numeric usage:
	This is adapted from number-functions.js at http://www.xaprb.com/blog/2007/07/15/javascript-number-formatting-library-v13-released/
	
	$(".double").numeric({format:"0.0"});	
	$(".double-keyup").numeric({format:"0.000",onblur:false});				
	$(".integer").numeric();
		
	Rev:$090508$	
*/		
		
		
(function($) {
	$.fn.constrain = function(opt) { 
		opt = $.extend(true,{}, {						
			limit: 		{}, //key/value pairs ie {"p":4,"\\":4}
			prohibit:	{chars:"",regex:false},
			allow:		{chars:"",regex:false},
			size:       0
		},opt);
		
		function isProhibitedByLimit(input,e) {
			var prohibited=false;
			$.each(opt.limit,function(token,idx) {				
				var max = this;												
				if(token.charCodeAt(0)==e.which) {
					prohibited = max < 0 ? false : max < $(input).val().split(token).length;
					return false;//break 'each' iterator
				}
			});
			return prohibited;			
		};
		
		//has prohibit or allow been configured by user?
		function isConfigured(item) { 
			return item.chars.length > 0 || (item.regex && item.regex.length > 0);
		};
		
		//does the prohibit or allow collection find a match given the key?
		function match(item,input,e) {
			if( item.regex) {		
				var re = new RegExp(item.regex);
				if(re.test($(input).val() + String.fromCharCode(e.which))){
					return true;
				}
			}

			var arr = item.chars.split("");
			for(var i in arr) {
				var token = arr[i];
				if(token.charCodeAt(0)==e.which) {
					return true;
				}
			}								
			
			return false;
		};
		
		function isProhibited(input,e) {			
			if( e.which == 0 || e.which == 8 || e.which == 27 ) {//always permit space,tab, or escape
				return false;
			}

            if ( opt.size && $(input).val().length == opt.size ) return true;
			
			var prohibit = isConfigured(opt.prohibit) ? match(opt.prohibit,input,e) : false;			
			var allow = isConfigured(opt.allow) ? match(opt.allow,input,e) : true;			
			var limited = isProhibitedByLimit(input,e);									
			return prohibit || !allow || limited;					
		};	
			
		return this.each(function() {
			$(this).bind('keypress change', function(e) { 
				if(isProhibited(this,e)){
					e.preventDefault();
				}
			});
						
		});
	};
	//todo use number formatring script from http://www.xaprb.com/blog/2007/07/15/javascript-number-formatting-library-v13-released/
	$.fn.numeric = function(opt) {
		opt = $.extend(true,{}, {						
			onblur		:	true,
			format		:	""	//"0,0.0" for thousand sep (52,456,34.49) or "0.0" for no thousands sep			
		},opt);
		
		var parts = opt.format.split(".");
		var precision = parts.length > 1 ? parts[1].length : false;		
		return this.each(function() {
			var allowRe = "\\d";
			
			if(opt.format.indexOf(".")>-1){
				allowRe+="\\.";
			}
			if(opt.format.indexOf(",")>-1) {
				allowRe+=",";
			}
			var constraintOptions = {
				allow	:	{ regex: "[" + allowRe + "]" },
				limit	:	{".":1} 
			};
			
			$(this).constrain(constraintOptions);
			
			if( precision ) {
				//on the field's blur event, correct the value to the configured precision, rounding if necessary
				//so if precision.num is set to '1', then 14.2563 would be changed to 14.3 on the blur event
				$(this).blur( function(e) {			
					var n = parseFloat($(this).val());
					if(	!isNaN(n) ) {
						var val = $(this).val();
						$(this).val($.formatNumber(val,opt.format));
					}						
				});
				if(!opt.onblur) {
					//on the field's keyup event, correct the value to the configured precision right away
					//this is a bit jarring as the number they just entered is removed if it exceeds the precision value
					
					var prec = new RegExp("\\d+\\.*\\d{0," + precision + "}") ;
					$(this).keyup( function(e) {					
						//since keyup e.which is considering numberkeys differently 
						if(	( e.which <48 && e.which >57 ) || //number keys
								( e.which < 96 && e.which > 105 )) { //number keypad 
								return; 
						}								
						var val = $(this).val();						
						$(this).val(val.match(prec));	//we can't invoke format fn here 						
					});
				}
			}		
								
		});
	};
	
})(jQuery);
				
/* formatNumber function for extension directly on jQuery namespace */				
(function($) {
	$.numericFormat = $.numericFormat || {}; $.numericFormat.formats = $.numericFormat.formats || new Array();
	
	$.extend({
		formatNumber : function(num,format) {

			//hide our internals so the createNewFormat function can recurse on it without requiring the user to ignore the 'context' arg
			function _numberFormat(num,format,context) {		
				
				function createNewFormat(format,formatName) {			
						
					var code = "var " + formatName + " = function(num){\n";			
					
					//todo: internationalization concerns will need to be met here by sanitizing the correct thousands separator out
					code += "num = num.replace(/,/,'');";
					
					// Decide whether the function is a terminal or a pos/neg/zero function
					var formats = format.split(";");
					switch (formats.length) {
						case 1:
							code += createTerminalFormat(format);
							break;
						case 2:
							code += "return (num < 0) ? _numberFormat(num,\""
								+ escape(formats[1])							
								+ "\", 1) : _numberFormat(num,\""
								+ escape(formats[0])
								+ "\", 2);";
							break;
						case 3:
							code += "return (num < 0) ? _numberFormat(num,\""
								+ escape(formats[1])
								+ "\", 1) : ((num == 0) ? _numberFormat(num,\""
								+ escape(formats[2])
								+ "\", 2) : _numberFormat(num,\""
								+ escape(formats[0])
								+ "\", 3));";
							break;
						default:
							code += "throw 'Too many semicolons in format string';";
							break;
					}
					
					return code + "};";
					
				};
				
				function createTerminalFormat(format) {				
				    // If there is no work to do, just return the literal value
				    if (format.length > 0 && format.search(/[0#?]/) == -1) {
				        return "return '" + escape(format) + "';\n";
				    }
				    // Negative values are always displayed without a minus sign when section separators are used.
					
				    var code = "var val = (context == null) ? new Number(num) : Math.abs(num);\n";
				    var thousands = false;
				    var lodp = format;
				    var rodp = "";
				    var ldigits = 0;
				    var rdigits = 0;
				    var scidigits = 0;
				    var scishowsign = false;
				    var sciletter = "";
				    // Look for (and remove) scientific notation instructions, which can be anywhere
				    m = format.match(/\..*(e)([+-]?)(0+)/i);
				    if (m) {
				        sciletter = m[1];
				        scishowsign = (m[2] == "+");
				        scidigits = m[3].length;
				        format = format.replace(/(e)([+-]?)(0+)/i, "");
				    }
				    // Split around the decimal point
				    var m = format.match(/^([^.]*)\.(.*)$/);
				    if (m) {
				        lodp = m[1].replace(/\./g, "");
				        rodp = m[2].replace(/\./g, "");
				    }
				    // Look for %
				    if (format.indexOf('%') >= 0) {
				        code += "val *= 100;\n";
				    }
				    // Look for comma-scaling to the left of the decimal point
				    m = lodp.match(/(,+)(?:$|[^0#?,])/);
				    if (m) {
				        code += "val /= " + Math.pow(1000, m[1].length) + "\n;";
				    }
				    // Look for comma-separators
				    if (lodp.search(/[0#?],[0#?]/) >= 0) {
				        thousands = true;
				    }
				    // Nuke any extraneous commas
				    if ((m) || thousands) {
				        lodp = lodp.replace(/,/g, "");
				    }
				    // Figure out how many digits to the l/r of the decimal place
				    m = lodp.match(/0[0#?]*/);
				    if (m) {
				        ldigits = m[0].length;
				    }
				    m = rodp.match(/[0#?]*/);
				    if (m) {
				        rdigits = m[0].length;
				    }
					
				    // Scientific notation takes precedence over rounding etc
				    if (scidigits > 0) {
				        code += "var sci = toScientific(num,val,"
				            + ldigits + ", " + rdigits + ", " + scidigits + ", " + scishowsign + ");\n"
				            + "var arr = [sci.l, sci.r];\n";
				    }
				    else {
				        // If there is no decimal point, round to nearest integer, AWAY from zero
				        if (format.indexOf('.') < 0) {
				            code += "val = (val > 0) ? Math.ceil(val) : Math.floor(val);\n";
				        }
				        // Numbers are rounded to the correct number of digits to the right of the decimal
				        //code += "var arr = val.round(" + rdigits + ").toFixed(" + rdigits + ").split('.');\n";
				        code += "var arr = round(val," + rdigits + ").toFixed(" + rdigits + ").split('.');\n";
				        // There are at least "ldigits" digits to the left of the decimal, so add zeros if needed.
				        code += "arr[0] = (val < 0 ? '-' : '') + leftPad((val < 0 ? arr[0].substring(1) : arr[0]), "
				            + ldigits + ", '0');\n";
				    }
				    // Add thousands separators
				    if (thousands) {
				        code += "arr[0] = addSeparators(arr[0]);\n";
				    }
					
				    // Insert the digits into the formatting string.  On the LHS, extra digits are copied
				    // into the result.  On the RHS, rounding has chopped them off.
				    code += "arr[0] = reverse(injectIntoFormat(reverse(arr[0]), '" + escape(reverse(lodp)) + "', true));\n";
				    if (rdigits > 0) {
				        code += "arr[1] = injectIntoFormat(arr[1], '" + escape(rodp) + "', false);\n";
				    }
				    if (scidigits > 0) {
				        code += "arr[1] = arr[1].replace(/(\\d{" + rdigits + "})/, '$1" + sciletter + "' + sci.s);\n";
				    }
					
				    return code + "return arr.join('.');\n";
				};
				
				function toScientific(num,val, ldigits, rdigits, scidigits, showsign) {
				    var result = {l:"", r:"", s:""};
				    var ex = "";
				    // Make ldigits + rdigits significant figures
				    var before = Math.abs(val).toFixed(ldigits + rdigits + 1).trim('0');
				    // Move the decimal point to the right of all digits we want to keep,
				    // and round the resulting value off
				    var after = Math.round(num,new Number(before.replace(".", "").replace(
				        new RegExp("(\\d{" + (ldigits + rdigits) + "})(.*)"), "$1.$2"))).toFixed(0);
				    // Place the decimal point in the new string
				    if (after.length >= ldigits) {
				        after = after.substring(0, ldigits) + "." + after.substring(ldigits);
				    }
				    else {
				        after += '.';
				    }
				    // Find how much the decimal point moved.  This is #places to LODP in the original
				    // number, minus the #places in the new number.  There are no left-padded zeroes in
				    // the new number, so the calculation for it is simpler than for the old number.
				    result.s = (before.indexOf(".") - before.search(/[1-9]/)) - after.indexOf(".");
				    // The exponent is off by 1 when it gets moved to the left.
				    if (result.s < 0) {
				        result.s++;
				    }
				    // Split the value around the decimal point and pad the parts appropriately.
				    result.l = (val < 0 ? '-' : '') + leftPad(after.substring(0, after.indexOf(".")), ldigits, "0");
				    result.r = after.substring(after.indexOf(".") + 1);
				    if (result.s < 0) {
				        ex = "-";
				    }
				    else if (showsign) {
				        ex = "+";
				    }
				    result.s = ex + leftPad(Math.abs(result.s).toFixed(0), scidigits, "0");
				    return result;
				};
				

				function reverse(str) {
				    var res = "";
				    for (var i = str.length; i > 0; --i) {
				        res += str.charAt(i - 1);
				    }
				    return res;
				};	
				function escape(string) {
				    return string.replace(/('|\\)/g, "\\$1");
				};
				function leftPad(val, size, ch) {
				    var result = new String(val);
				    if (ch == null) {
				        ch = " ";
				    }
				    while (result.length < size) {
				        result = ch + result;
				    }
				    return result;
				};
					
				function round(num,decimals) {
					if (decimals > 0) {
						var m = num.toFixed(decimals + 1).match(
							new RegExp("(-?\\d*)\.(\\d{" + decimals + "})(\\d)\\d*$"));
						if (m && m.length) {
							return new Number(m[1] + "." + leftPad(Math.round(m[2] + "." + m[3]), decimals, "0"));
						}
					}
					return num;
				};
				
				function addSeparators(val) {
					//return val.reverse().replace(/(\d{3})/g, "$1,").reverse().replace(/^(-)?,/, "$1");
					var s = reverse(val).replace(/(\d{3})/g, "$1,");			
				    return reverse(s).replace(/^(-)?,/, "$1");
				};
				
				function injectIntoFormat(val, format, stuffExtras) {			
				    var i = 0;
				    var j = 0;
				    var result = "";
				    var revneg = val.charAt(val.length - 1) == '-';
				    if ( revneg ) {
				       val = val.substring(0, val.length - 1);
				    }
				    while (i < format.length && j < val.length && format.substring(i).search(/[0#?]/) >= 0) {
				        if (format.charAt(i).match(/[0#?]/)) {
				            // It's a formatting character; copy the corresponding character
				            // in the value to the result
				            if (val.charAt(j) != '-') {
				                result += val.charAt(j);
				            }
				            else {
				                result += "0";
				            }
				            j++;
				        }
				        else {
				            result += format.charAt(i);
				        }
				        ++i;
				    }
				    if ( revneg && j == val.length ) {
				        result += '-';
				    }
				    if (j < val.length) {
				        if (stuffExtras) {
				            result += val.substring(j);
				        }
				        if ( revneg ) {
				             result += '-';
				        }
				    }
				    if (i < format.length) {
				        result += format.substring(i);
				    }			
				    return result.replace(/#/g, "").replace(/\?/g, " ");
				};
				
				//add our dynamic function			
				var formatName = "numFormat" + $.numericFormat.formats.length++;
				eval(createNewFormat(format,formatName));
				//return our new function named by our formatName
				return eval(formatName);
			};		
			
			if(!$.numericFormat.formats[format]) {										
					$.numericFormat.formats[format] = _numberFormat(num,format);
			};						
			return $.numericFormat.formats[format](num);
		}
	});
	
})(jQuery);

// ===================================================================
// Author: Matt Kruse <matt@mattkruse.com>
// WWW: http://www.mattkruse.com/
//
// NOTICE: You may use this code for any purpose, commercial or
// private, without any further permission from the author. You may
// remove this notice from your final code if you wish, however it is
// appreciated by the author if at least my web site address is kept.
//
// You may *NOT* re-distribute this code in any way except through its
// use. That means, you can include it in your product, or your web
// site, or any other form where the code is actually being used. You
// may not put the plain javascript up on your site for download or
// include it in your javascript libraries for download. 
// If you wish to share this code with others, please just point them
// to the URL instead.
// Please DO NOT link directly to my .js files from your site. Copy
// the files to your server and use them there. Thank you.
// ===================================================================

// HISTORY
// ------------------------------------------------------------------
// May 17, 2003: Fixed bug in parseDate() for dates <1970
// March 11, 2003: Added parseDate() function
// March 11, 2003: Added "NNN" formatting option. Doesn't match up
//                 perfectly with SimpleDateFormat formats, but 
//                 backwards-compatability was required.

// ------------------------------------------------------------------
// These functions use the same 'format' strings as the 
// java.text.SimpleDateFormat class, with minor exceptions.
// The format string consists of the following abbreviations:
// 
// Field        | Full Form          | Short Form
// -------------+--------------------+-----------------------
// Year         | yyyy (4 digits)    | yy (2 digits), y (2 or 4 digits)
// Month        | MMM (name or abbr.)| MM (2 digits), M (1 or 2 digits)
//              | NNN (abbr.)        |
// Day of Month | dd (2 digits)      | d (1 or 2 digits)
// Day of Week  | EE (name)          | E (abbr)
// Hour (1-12)  | hh (2 digits)      | h (1 or 2 digits)
// Hour (0-23)  | HH (2 digits)      | H (1 or 2 digits)
// Hour (0-11)  | KK (2 digits)      | K (1 or 2 digits)
// Hour (1-24)  | kk (2 digits)      | k (1 or 2 digits)
// Minute       | mm (2 digits)      | m (1 or 2 digits)
// Second       | ss (2 digits)      | s (1 or 2 digits)
// AM/PM        | a                  |
//
// NOTE THE DIFFERENCE BETWEEN MM and mm! Month=MM, not mm!
// Examples:
//  "MMM d, y" matches: January 01, 2000
//                      Dec 1, 1900
//                      Nov 20, 00
//  "M/d/yy"   matches: 01/20/00
//                      9/2/00
//  "MMM dd, yyyy hh:mm:ssa" matches: "January 01, 2000 12:30:45AM"
// ------------------------------------------------------------------

var MONTH_NAMES=new Array('January','February','March','April','May','June','July','August','September','October','November','December','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
var DAY_NAMES=new Array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sun','Mon','Tue','Wed','Thu','Fri','Sat');
function LZ(x) {return(x<0||x>9?"":"0")+x}

// ------------------------------------------------------------------
// isDate ( date_string, format_string )
// Returns true if date string matches format of format string and
// is a valid date. Else returns false.
// It is recommended that you trim whitespace around the value before
// passing it to this function, as whitespace is NOT ignored!
// ------------------------------------------------------------------
function isDate(val,format) {
	var date=getDateFromFormat(val,format);
	if (date==0) { return false; }
	return true;
	}

// -------------------------------------------------------------------
// compareDates(date1,date1format,date2,date2format)
//   Compare two date strings to see which is greater.
//   Returns:
//   1 if date1 is greater than date2
//   0 if date2 is greater than date1 of if they are the same
//  -1 if either of the dates is in an invalid format
// -------------------------------------------------------------------
function compareDates(date1,dateformat1,date2,dateformat2) {
	var d1=getDateFromFormat(date1,dateformat1);
	var d2=getDateFromFormat(date2,dateformat2);
	if (d1==0 || d2==0) {
		return -1;
		}
	else if (d1 > d2) {
		return 1;
		}
	return 0;
	}

// ------------------------------------------------------------------
// formatDate (date_object, format)
// Returns a date in the output format specified.
// The format string uses the same abbreviations as in getDateFromFormat()
// ------------------------------------------------------------------
function formatDate(date,format) {
	format=format+"";
	var result="";
	var i_format=0;
	var c="";
	var token="";
	var y=date.getYear()+"";
	var M=date.getMonth()+1;
	var d=date.getDate();
	var E=date.getDay();
	var H=date.getHours();
	var m=date.getMinutes();
	var s=date.getSeconds();
	var yyyy,yy,MMM,MM,dd,hh,h,mm,ss,ampm,HH,H,KK,K,kk,k;
	// Convert real date parts into formatted versions
	var value=new Object();
	if (y.length < 4) {y=""+(y-0+1900);}
	value["y"]=""+y;
	value["yyyy"]=y;
	value["yy"]=y.substring(2,4);
	value["M"]=M;
	value["MM"]=LZ(M);
	value["MMM"]=MONTH_NAMES[M-1];
	value["NNN"]=MONTH_NAMES[M+11];
	value["d"]=d;
	value["dd"]=LZ(d);
	value["E"]=DAY_NAMES[E+7];
	value["EE"]=DAY_NAMES[E];
	value["H"]=H;
	value["HH"]=LZ(H);
	if (H==0){value["h"]=12;}
	else if (H>12){value["h"]=H-12;}
	else {value["h"]=H;}
	value["hh"]=LZ(value["h"]);
	if (H>11){value["K"]=H-12;} else {value["K"]=H;}
	value["k"]=H+1;
	value["KK"]=LZ(value["K"]);
	value["kk"]=LZ(value["k"]);
	if (H > 11) { value["a"]="PM"; }
	else { value["a"]="AM"; }
	value["m"]=m;
	value["mm"]=LZ(m);
	value["s"]=s;
	value["ss"]=LZ(s);
	while (i_format < format.length) {
		c=format.charAt(i_format);
		token="";
		while ((format.charAt(i_format)==c) && (i_format < format.length)) {
			token += format.charAt(i_format++);
			}
		if (value[token] != null) { result=result + value[token]; }
		else { result=result + token; }
		}
	return result;
	}
	
// ------------------------------------------------------------------
// Utility functions for parsing in getDateFromFormat()
// ------------------------------------------------------------------
function _isInteger(val) {
	var digits="1234567890";
	for (var i=0; i < val.length; i++) {
		if (digits.indexOf(val.charAt(i))==-1) { return false; }
		}
	return true;
	}
function _getInt(str,i,minlength,maxlength) {
	for (var x=maxlength; x>=minlength; x--) {
		var token=str.substring(i,i+x);
		if (token.length < minlength) { return null; }
		if (_isInteger(token)) { return token; }
		}
	return null;
	}
	
// ------------------------------------------------------------------
// getDateFromFormat( date_string , format_string )
//
// This function takes a date string and a format string. It matches
// If the date string matches the format string, it returns the 
// getTime() of the date. If it does not match, it returns 0.
// ------------------------------------------------------------------
function getDateFromFormat(val,format) {
	val=val+"";
	format=format+"";
	var i_val=0;
	var i_format=0;
	var c="";
	var token="";
	var token2="";
	var x,y;
	var now=new Date();
	var year=now.getYear();
	var month=now.getMonth()+1;
	var date=1;
	var hh=now.getHours();
	var mm=now.getMinutes();
	var ss=now.getSeconds();
	var ampm="";
	
	while (i_format < format.length) {
		// Get next token from format string
		c=format.charAt(i_format);
		token="";
		while ((format.charAt(i_format)==c) && (i_format < format.length)) {
			token += format.charAt(i_format++);
			}
		// Extract contents of value based on format token
		if (token=="yyyy" || token=="yy" || token=="y") {
			if (token=="yyyy") { x=4;y=4; }
			if (token=="yy")   { x=2;y=2; }
			if (token=="y")    { x=2;y=4; }
			year=_getInt(val,i_val,x,y);
			if (year==null) { return 0; }
			i_val += year.length;
			if (year.length==2) {
				if (year > 70) { year=1900+(year-0); }
				else { year=2000+(year-0); }
				}
			}
		else if (token=="MMM"||token=="NNN"){
			month=0;
			for (var i=0; i<MONTH_NAMES.length; i++) {
				var month_name=MONTH_NAMES[i];
				if (val.substring(i_val,i_val+month_name.length).toLowerCase()==month_name.toLowerCase()) {
					if (token=="MMM"||(token=="NNN"&&i>11)) {
						month=i+1;
						if (month>12) { month -= 12; }
						i_val += month_name.length;
						break;
						}
					}
				}
			if ((month < 1)||(month>12)){return 0;}
			}
		else if (token=="EE"||token=="E"){
			for (var i=0; i<DAY_NAMES.length; i++) {
				var day_name=DAY_NAMES[i];
				if (val.substring(i_val,i_val+day_name.length).toLowerCase()==day_name.toLowerCase()) {
					i_val += day_name.length;
					break;
					}
				}
			}
		else if (token=="MM"||token=="M") {
			month=_getInt(val,i_val,token.length,2);
			if(month==null||(month<1)||(month>12)){return 0;}
			i_val+=month.length;}
		else if (token=="dd"||token=="d") {
			date=_getInt(val,i_val,token.length,2);
			if(date==null||(date<1)||(date>31)){return 0;}
			i_val+=date.length;}
		else if (token=="hh"||token=="h") {
			hh=_getInt(val,i_val,token.length,2);
			if(hh==null||(hh<1)||(hh>12)){return 0;}
			i_val+=hh.length;}
		else if (token=="HH"||token=="H") {
			hh=_getInt(val,i_val,token.length,2);
			if(hh==null||(hh<0)||(hh>23)){return 0;}
			i_val+=hh.length;}
		else if (token=="KK"||token=="K") {
			hh=_getInt(val,i_val,token.length,2);
			if(hh==null||(hh<0)||(hh>11)){return 0;}
			i_val+=hh.length;}
		else if (token=="kk"||token=="k") {
			hh=_getInt(val,i_val,token.length,2);
			if(hh==null||(hh<1)||(hh>24)){return 0;}
			i_val+=hh.length;hh--;}
		else if (token=="mm"||token=="m") {
			mm=_getInt(val,i_val,token.length,2);
			if(mm==null||(mm<0)||(mm>59)){return 0;}
			i_val+=mm.length;}
		else if (token=="ss"||token=="s") {
			ss=_getInt(val,i_val,token.length,2);
			if(ss==null||(ss<0)||(ss>59)){return 0;}
			i_val+=ss.length;}
		else if (token=="a") {
			if (val.substring(i_val,i_val+2).toLowerCase()=="am") {ampm="AM";}
			else if (val.substring(i_val,i_val+2).toLowerCase()=="pm") {ampm="PM";}
			else {return 0;}
			i_val+=2;}
		else {
			if (val.substring(i_val,i_val+token.length)!=token) {return 0;}
			else {i_val+=token.length;}
			}
		}
	// If there are any trailing characters left in the value, it doesn't match
	if (i_val != val.length) { return 0; }
	// Is date valid for month?
	if (month==2) {
		// Check for leap year
		if ( ( (year%4==0)&&(year%100 != 0) ) || (year%400==0) ) { // leap year
			if (date > 29){ return 0; }
			}
		else { if (date > 28) { return 0; } }
		}
	if ((month==4)||(month==6)||(month==9)||(month==11)) {
		if (date > 30) { return 0; }
		}
	// Correct hours value
	if (hh<12 && ampm=="PM") { hh=hh-0+12; }
	else if (hh>11 && ampm=="AM") { hh-=12; }
	var newdate=new Date(year,month-1,date,hh,mm,ss);
	return newdate.getTime();
	}

// ------------------------------------------------------------------
// parseDate( date_string [, prefer_euro_format] )
//
// This function takes a date string and tries to match it to a
// number of possible date formats to get the value. It will try to
// match against the following international formats, in this order:
// y-M-d   MMM d, y   MMM d,y   y-MMM-d   d-MMM-y  MMM d
// M/d/y   M-d-y      M.d.y     MMM-d     M/d      M-d
// d/M/y   d-M-y      d.M.y     d-MMM     d/M      d-M
// A second argument may be passed to instruct the method to search
// for formats like d/M/y (european format) before M/d/y (American).
// Returns a Date object or null if no patterns match.
// ------------------------------------------------------------------
function parseDate(val) {
	var preferEuro=(arguments.length==2)?arguments[1]:false;
	generalFormats=new Array('y-M-d','MMM d, y','MMM d,y','y-MMM-d','d-MMM-y','MMM d');
	monthFirst=new Array('M/d/y','M-d-y','M.d.y','MMM-d','M/d','M-d');
	dateFirst =new Array('d/M/y','d-M-y','d.M.y','d-MMM','d/M','d-M');
	var checkList=new Array('generalFormats',preferEuro?'dateFirst':'monthFirst',preferEuro?'monthFirst':'dateFirst');
	var d=null;
	for (var i=0; i<checkList.length; i++) {
		var l=window[checkList[i]];
		for (var j=0; j<l.length; j++) {
			d=getDateFromFormat(val,l[j]);
			if (d!=0) { return new Date(d); }
			}
		}
	return null;
	}
