/**
 * Additional xui-validation custom validations.
 * @version 1.1.1
 * 
 * Documentation on the extension format available online.
 * @see http://irama.org/web/dhtml/xui-validation/#extension
 */


vCount = validations.length;

// integer only fields
	validations[vCount++] = {
		'match'    : 'input.integer',
		'triggers' : ['keyup','change','blur','submit'],
		/**
		 * @param DOMElement this The DOMElement for the input that is being validated
		 * @param String arguments[0] The type of event that triggered this validation (example: 'keyup', 'change', etc...).
		 * @param EventObject arguments[1] The EventObject for the event that triggered this validation.
		 * @return Mixed true for success, String for failure (error message), null if validity cannot be determined.
		 */
		'validate' : function () {
				if ($(this).val().match(/[^0-9]/g)) {
					return 'can contain an integer only';
				}
				return true;
		}
	};
		
		
		
		
		
		
		
// alphanumeric characters only fields
	validations[vCount++] = {
		'match'    : 'input.alpha',
		'triggers' : ['keyup','change','blur','submit'],
		/**
		 * @param DOMElement this The DOMElement for the input that is being validated
		 * @param String arguments[0] The type of event that triggered this validation (example: 'keyup', 'change', etc...).
		 * @param EventObject arguments[1] The EventObject for the event that triggered this validation.
		 * @return Mixed true for success, String for failure (error message), null if validity cannot be determined.
		 */
		'validate' : function () {
				if ($(this).val().match(/[^0-9a-zA-Z ]/g)) {
					return 'can contain letters and numbers only';
				}
				return true;
		}
	};
	
		




// credit card type
// Not really a validation on this input.
// Purpose is to trigger an update of validation on the related credit card number field in this form.
	validations[vCount++] = {
		'match'    : 'input.creditcard-type',
		'triggers' : ['keyup','change','blur','submit'],
		/**
		 * @param DOMElement this The DOMElement for the input that is being validated
		 * @param String arguments[0] The type of event that triggered this validation (example: 'keyup', 'change', etc...).
		 * @param EventObject arguments[1] The EventObject for the event that triggered this validation.
		 * @return Mixed true for success, String for failure (error message), null if validity cannot be determined.
		 */
		'validate' : function () {
			
			// find first credit card field in this form
				ccnumElement = $(this).parents(xuivConf.formSelector+':lt(1)').find('.creditcard-number:lt(1)');
			
			// trigger validation of that field if not empty (pretend it was a blur event to ensure all validations are made)
				if (ccnumElement.val() != '') {
					validateControl.apply(ccnumElement, [arguments[1], 'blur']);
				}
			
			// this input will always be valid, return true
				return true;
		}
	};
	
	
	
	
// credit card number
	validations[vCount++] = {
		'match'    : 'input.creditcard-number',
		'triggers' : ['keyup','change','blur','submit'],
		/**
		 * @param DOMElement this The DOMElement for the input that is being validated
		 * @param String arguments[0] The type of event that triggered this validation (example: 'keyup', 'change', etc...).
		 * @param EventObject arguments[1] The EventObject for the event that triggered this validation.
		 * @return Mixed true for success, String for failure (error message), null if validity cannot be determined.
		 */
		'validate' : function (eventType, eventObj) {
			
			// check for invalid characters
				if ($(this).val().match(/[^0-9\- ]/g)) {
					return 'numbers spaces or - only';
				}
			
			// if empty, return true
				if ($(this).val()=='') {
					return true;
				}
			
			// init default values
				var validLength = 16;
			
			// extract all numbers (this becomes the parsed credit card number for further validation)
				var ccnumber = $(this).val().replace(/[^0-9]/g, '');
			
			// specify a function for checking the length of the number (for re-use later)
				checkLength = function (validLength, eventType) {
					
					// is number too long?
						if (ccnumber.length > validLength) {
							return 'must contain '+validLength+' digits (currently '+ccnumber.length+' digits)';
						}
					
					// is number too short?
						if (ccnumber.length < validLength) {
							
							// if too short, flag error only on blur or submit
								if (eventType == 'blur' || eventType == 'submit') {
									
									// blur or submit event occured, number is not long enough!
										return 'must contain '+validLength+' digits (currently '+ccnumber.length+' digits)';
									
								} else {
									
									// Validity cannot be determined until number is complete.
									// Don't indicate valid or invalid while user is still typing.
									// In other words, wait for blur or submit event.
									return null;
								}
						}
					
					// number must be the correct length
						return true;
				};
			
			// consider the type of credit card if 'credit card type' field exists
				cctype = $(this).parents(xuivConf.formSelector+':lt(1)').find('.creditcard-type:lt(1)');
				if (cctype.size() > 0) {
					
					// radio/checkboxes require special treatment
						if ($(cctype).is(xuivConf.checkControlSelector)) {
							
							// find selected option's value
								cctypeValue = $('input[name='+$(cctype).attr('name')+']:checked').val();
								
						} else {
							
							// simple input/control, just get value
								cctypeValue = cctype.val();
						}
					
					if (typeof cctypeValue != 'undefined') {
						
						// load the different requirements of the different card types
							switch (cctypeValue) {
								case 'Visa':
									//validLength = [13,16];
									validLength = 16;
									prefixes = [4];
								break;
								case 'Mastercard':
									//validLength = [16];
									validLength = 16;
									prefixes = [51,52,53,54,55];
								break;
								case '': // default values
									validLength = 16;
									prefixes = [''];
								break;
								default:
									return 'credit card type "'+cctypeValue+'" not recognised';
								break;
							}
						
						// check card number length
							if ((res = checkLength(validLength, eventType)) != true) {
								return res;
							}
						
						// check card number prefix
							validPrefix = false;
							for (n in prefixes) {
								if (ccnumber.substr(0,(prefixes[n]+'').length) == prefixes[n]+'') {
									validPrefix = true;
								}
							}
							
						// did any of the valid prefixes match?
							if (validPrefix == false) {
								return 'invalid '+cctypeValue+' number';
							}
					}
				}
				
				
			// check card number length (using default length)
				if ((res = checkLength(validLength, eventType)) != true) {
					return res;
				}
			
			
			
			// test Luhn checksum
				a = ccnumber.length / 2;
				b = Math.floor(a);
				c = Math.ceil(a) - b;
				d = 0;
				for (n = 0; n < b; n++) {
					e = ccnumber.charAt(n*2 + c) * 2;
					d += (e > 9) ? Math.floor(e/10 + e%10) : e;
				}
				for (n = 0; n < b + m; n++) {
					d += ccnumber.charAt(n*2 + 1-c) * 1;
				}
				if (d % 10 != 0) {
					return 'invalid number (check it was entered correctly)';
				}

			// no errors found above, yay!
				return true;
		}
	};
	
	
	
	
	
	
	
	
// credit card expiry date
	validations[vCount++] = {
		'match'    : 'input.creditcard-expiry',
		'triggers' : ['keyup','change','blur','submit'],
		/**
		 * @param DOMElement this The DOMElement for the input that is being validated
		 * @param String arguments[0] The type of event that triggered this validation (example: 'keyup', 'change', etc...).
		 * @param EventObject arguments[1] The EventObject for the event that triggered this validation.
		 * @return Mixed true for success, String for failure (error message), null if validity cannot be determined.
		 */
		'validate' : function (eventType) {
			
			// check for invalid characters
				if ($(this).val().match(/[^0-9\/ ]/g)) {
					updateConfirmation.apply(this, ['']);
					return 'can contain numbers, spaces or / only';
				}
			
			// init function to format readable dates
				readableDate = function (month, year) {
					if (month == '') return '';
					months = ['January','February','March','April','May','June','July','August','September','October','November','December'];
					month = months[parseInt(month, 10)-1];
					year = year||'?';
					return month+' '+year;
				};
			
			// extract all numbers (this becomes the parsed expiry date number for further validation)
				ccexpnumber = $(this).val().replace(/[^0-9]/g, '');
			
			
			// parse and validate 1-digit month value
				if (ccexpnumber.length == 1) {
					updateConfirmation.apply(this, ['']);
					if (ccexpnumber.substr(0,1) > '1' || eventType == 'blur' || eventType == 'submit') {
						return 'must be of format mm/yy or mm/yyyy (i.e: Feb = 02)';
					} else {
						// reserve judgement for now
							return null;
					}
				}
				
			// parse and validate 2-digit month value
				if (ccexpnumber.length > 1) {
					month = ccexpnumber.substr(0,2);
					if (month == '00') {
						updateConfirmation.apply(this, ['']);
						return 'must contain a valid month (format mm/yy or mm/yyyy)';
					}
					
					if (month > '12') {
						updateConfirmation.apply(this, ['']);
						return 'must contain a valid month (format mm/yy or mm/yyyy)';
					}
				}
			
			// is number too long?
				if (ccexpnumber.length > 6) {
						updateConfirmation.apply(this, [readableDate(month)]);
						return 'must be of the format mm/yy or mm/yyyy';// (currently '+ccexpnumber.length+' digits)';
				}
			
			// is number too short?
				if (ccexpnumber.length < 4 || ccexpnumber.length == 5) {
					if (ccexpnumber.length == 0) {
						return true;
					}
					
					if (eventType == 'blur' || eventType == 'submit') {
						// blur or submit event occured, number is not long enough!
							updateConfirmation.apply(this, [readableDate(month)]);
							return 'must be of the format mm/yy or mm/yyyy';// (currently '+ccexpnumber.length+' digits)';
						
					} else {
						
						// Validity cannot be determined until number is complete.
						// Don't indicate valid or invalid while user is still typing.
						// In other words, wait for blur or submit event.
							updateConfirmation.apply(this, [readableDate(month)]);
							return null;
					}
				}
			
			// obtain current date information (for later comparison)
				currentDate = new Date();
				currentMonth = currentDate.getMonth()+1;
				currentYear = currentDate.getFullYear()+'';
						
			// parse and normalise year (prepend current year prefix if only 2 digits specified)
				year = ccexpnumber.substr(2);
				if (year.length < 4) {
					year = currentYear.substr(0,2)+year;
				}
			
			// ensure date occurs in the future
				if (year < currentYear ||
					(year == currentYear && month < currentMonth)) {
					updateConfirmation.apply(this, [readableDate(month,year)]);
					return 'must be in the future (has your card expired?)';
				}
				
			// limit expiry date to 15 years in the future
				limitYears = 15;
				limitYear = parseInt(currentYear,10)+limitYears;
				
			// ensure date is not too far in the future
				if (parseInt(year,10) > limitYear) {
					updateConfirmation.apply(this, [readableDate(month,year)]);
					return 'must be within '+limitYears+' years';
				}
			
			// no errors, yay!
				updateConfirmation.apply(this, [readableDate(month,year)]);
				return true;
		}
	};
