(function($){  
  
    $.fn.extend({   
          
        //pass the options variable to the function  
        DreamForm: function(rowModel, options) {  
  
            var app_path = window.location.href.substring(0, window.location.href.lastIndexOf("/")+1);
            var $form = this;
			var validate = true;

			//Set the default values, use comma to separate the settings, example:  
            var defaults = {
				w: 200,
				p: 3,
				m: 1
			};
            var options =  $.extend(defaults, options);  
			
			//Public Functions
			this.deactivate = function (target) {
				var o = options;
//				$(target).children("label").css({'margin': '0', 'padding':'0'});
//				$(".tooltip", target).css({'border-style': 'none', 'margin': '0', 'padding':'0'});
				$(".tooltip", target).css({'border-style': 'none', 'margin': o.m+1, 'padding': o.p});
//				$(":input", target).css({'border-style': 'none', 'margin': '0', 'padding':'0'});
			}
			
			this.checkForm = function(target) {
			
				$(".errormsg", target).hide();
				var result = $(":input[value]", target).serialize(); 
				if (result == "") { // empty form - show warning
			
				} else { /// there are field values
					
					//		  /^4[0-9]{12}([0-9]{3})?$/ 'Visa';
					//		  /^5[1-5][0-9]{14}$/ 'Master Card';
					//		  /^3[47][0-9]{13}$/ 'American Express';
					//		  /^3(0[0-5]|[68][0-9])[0-9]{11}$/ 'Diners Club';
					//		  /^6011[0-9]{12}$/ 'Discover';
					//		  /^6[0-9]{18}$/ 'Laser/Maestro/Electron';
					//		  /^6[0-9]{15}$/ 'Laser';
					
					// This breaks internet explorer - the fact that it's not a string
					var v = { 
						v_number: /^[\d]+$/, 
						v_phone: /^(\+|0)[\d]{8,13}$/, 
						v_firstname: /^([a-z]+(\'|-|\.\s|\s)?[a-z]*){1,2}$/i,
						v_surname: /^([a-z]+(\'|-|\.\s|\s)?[a-z]*){1,2}$/i,
						v_email: /^(([A-Z0-9]+_+)|([A-Z0-9]+\-+)|([A-Z0-9]+\.+)|([A-Z0-9]+\++))*[A-Z0-9]+@((\w+\-+)|(\w+\.))*\w{1,63}\.[A-Z]{2,6}$/i,
						v_url: /^([A-Z0-9]([A-Z0-9\-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}$/i,
						v_address: /^(\w(\s|\.|\.\s|,|,\s)?)+$/,
						v_ccn: /^((4\d{3})|(5[1-5]\d{2})|(6011))-?\d{4}-?\d{4}-?\d{4}|3[4,7]\d{13}|6\d{18}|6\d{15}$/,
						v_ccv: /^\d{3}$/,
						v_cce: /^((0[1-9])|(1[0-2]))\/20(\d{2})$/
					}; //' Comment to correct colour coding
		
					validate = true;			
					$.each(v, function(i, val) {
						$(":input:text."+i, target).removeClass("error").filter(function() { 
							var valid = this.value.match(val);
							if ( valid && ($(this).hasClass('v_ccn')) ) { valid = ccn_check($(this).val()); }
							return (!valid); }).addClass("error").each(function() { validate = false; });	
					});
					$(target).children("label").removeClass("error");
					$(":input.error", target).parents(".formrow").prev("label").addClass("error"); 
					
					if (validate) { $form.deactivate(target); $form.addClass("valid"); } 
					else $form.removeClass("valid");
					
					return validate;
					
				}
			}

			//Private Functions
			drawForm = function(target, rowModel) {
			
				var o = options;
				$.each(rowModel, function(i, val) { 
		
					var emsg = val.err, hmsg = val.hint;
					switch(val.class_name) {
						case "v_firstname":
							if (typeof(hmsg) === 'undefined') 
								hmsg = "A valid name is at least 1 character long and contains no digits";
							if (typeof(emsg) === 'undefined') emsg = "Please enter a valid first name";
							break;
						case "v_surname":
							if (typeof(hmsg) === 'undefined') 
								hmsg = "A valid name is at least 1 character long and contains no digits";
							if (typeof(emsg) === 'undefined') emsg = "Please enter a valid last name";
							break;
						case "v_email":
							if (typeof(emsg) === 'undefined') emsg = "Please enter a valid email address";
							break;
						case "v_phone":
							if (typeof(hmsg) === 'undefined') 
								hmsg = "An area code (starting with '0') is required. Please use digits only - no spaces, dashes or brackets. '+' and country code (353 for ireland) are optional.";
							if (typeof(emsg) === 'undefined') emsg = "Please enter a valid phone number";
							break;
						case "v_phone":
							if (typeof(emsg) === 'undefined') emsg = "Please enter a valid email address";
							break;
						case "v_ccn":
							if (typeof(emsg) === 'undefined') emsg = "Please enter a valid credit card number. Please use digits only - no spaces or dashes.";
							break;
						case "v_ccv":
							if (typeof(emsg) === 'undefined') emsg = "Please enter a valid ccv number. For Visa/Mastercard, the three-digit CVV number is printed on the signature panel on the back of the card immediately after the card's account number.";
							break;
						case "v_cce":
							if (typeof(emsg) === 'undefined') emsg = "Format mm/yyyy";
							break;
						default:
					}
		
					var optns = "";
					var typ = val.type; if (typ === undefined) typ = "text";
					var vv = val.value; if (vv === undefined) vv = ""; else optns= "value='"+vv+"'";
					var vl; if (val.label == "") vl = "&nbsp;"; else vl = val.label;
					if ((typ == 'checkbox') && (vv == 'checked')) optns = " checked='checked' ";
					if ((typ == 'select') && (vv == 'checked')) optns = " checked='checked' ";
					var id = i; if (typeof val.id != 'undefined') id = val.id;
					
					var thisrow = $(target); var cl = "";
					if (typeof val.label != 'undefined') $(target).append("<label for='"+id+"'>"+vl+"</label>");			
					if ((typeof val.class_name != 'undefined') || (typeof val.hint != 'undefined')) {
						$(target).append("<div class='formrow'><div class='tooltip'></div></div>");
						thisrow = $(target).find("div:last");
						cl = "class='"+val.class_name+"'";
					} else {
						$(target).append("<div class='formrow'><div class='tooltip'></div></div>");
						thisrow = $(target).find("div:last");
					}
					
					if (typ == "select") {
						$(thisrow).append("<select id='"+id+"' name='"+id+"' "+cl+optns+"></select>");
						$.each(val.options, function(key, opt) {
							$("<option value='"+key+"'>"+opt+"</option>").appendTo("#"+i, thisrow);
						});
					} else if (typ == "textarea") {
						$(thisrow).append("<textarea id='"+id+"' name='"+id+"' "+cl+optns+">"+vv+"</textarea>");
					} else {
						$(thisrow).append("<input id='"+id+"' name='"+id+"' value='"+vv+"' "+cl+"' type='"+typ+"'"+optns+"/>");
					}
					if (typeof(hmsg) !== 'undefined') 
						$(thisrow).append("<label for='"+id+"' class='hint'>"+hmsg+"</label>");
					if (typeof(emsg) !== 'undefined') 
						$(thisrow).append("<label for='"+id+"' class='errormsg'>"+emsg+"</label>");
				});
		
				$(".tooltip").width(o.w+(o.p+o.m+1)*2);
				$(".tooltip label", target).hide();

				$(":input", target).each( function () { 
					$.data(this, "focused", false); 
				}).focus( function () {
					$(this).parents(".tooltip").css({'border-style': 'solid', 'margin': o.m});
					$.data(this, "focused", true); activate(target,o.m,o.p); 
					if ( $(this).hasClass("error") ) {
						$("label.hint[for='"+$(this).attr('id')+"']").fadeIn("slow"); 
						$("label.errormsg[for='"+$(this).attr('id')+"']").fadeIn("slow"); 
					}
				}).blur( function () { 
					$.data(this, "focused", false); 
					$("label.hint[for='"+$(this).attr('id')+"']").fadeOut("slow"); 
					$("label.errormsg[for='"+$(this).attr('id')+"']").fadeOut("slow"); 
					$(this).parents(".tooltip").css({'border-style': 'none', 'margin': o.m+1});
					if (!checkFocus(target)) $form.deactivate(target);
				}).hover( 
					function() { 
						 $(this).parents(".tooltip").css({'border-style': 'solid', 'margin': o.m});
					},
					function() { 
						if (!$.data(this, "focused")) 
							$(this).parents(".tooltip").css({'border-style': 'none', 'margin': o.m+1});
					}
				);
		
				$(target).hover( 
					function () { activate(target,o.m,o.p); }, 
					function () { if (!checkFocus(target)) $form.deactivate(target); }
				);
				if (o.w == 0) { setWidth(target); $(":input", target).keyup( function() { setWidth(target); }); }
				$form.deactivate(target); 
					
				return $(target);
			}

			setWidth = function (target) {
				var aspect = 1.65; var fsz = parseInt($(":input:first", target).css('fontSize')) / aspect;
				$(":input", target).each( function() { $(this).width( ($(this).val().length+1)*fsz ); });
			}
			
			checkFocus = function (target) {
				var checkall = false;
				$(":input", target).each(function () { if ($.data(this, "focused")) checkall = true; });
				return checkall;
			}
			
			activate = function (target, m, p) {
//				$(target).children("label").css({'margin': m, 'padding': p});
//				$(".tooltip", target).css({'margin': m, 'padding': p});
//				$(":input", target).css({'border-style': 'inset', 'margin': m, 'padding': p});
			}
			
			ccn_check = function (ccNumb) {
				
				var len = ccNumb.length;
				var iCCN = parseInt(ccNumb);  // integer of ccNumb	
				var iTotal = 0;  // integer total set at zero
				var bNum = true;  // by default assume it is a number
				var bResult = false;  // by default assume it is NOT a valid cc
				var calc;  // used for calculation of each digit
				
				if ( (ccNumb.substr(0,4) != "6011") && (ccNumb.substr(0,1) == "6") ) {
					// Laser cards are prefixed with either "6304", "6706", "6771" or "6709", 
					// and may be between 16 and 19 (inclusive) characters in length.
					$("#cctype").val("Laser");
					$("#ccv").val("000"); bResult = true;
				} else {
					if ($("#ccv").val() == "000") $("#ccv").val(""); 
					if ( (ccNumb.substr(0,1) == "4") ) $("#cctype").val("Visa");
					else if ( (ccNumb.substr(0,1) == "5") ) $("#cctype").val("Mastercard");
					else if ( (ccNumb.substr(0,4) == "6011") ) $("#cctype").val("Discover");
					else if ( (ccNumb.substr(0,2) == "34") || (ccNumb.substr(0,2) == "37") ) 
						$("#cctype").val("American Express");
					
					for(var i=len;i>0;i--){  // LOOP throught the digits of the card
					  calc = parseInt(iCCN) % 10;  // right most digit
					  calc = parseInt(calc);  // assure it is an integer
					  iTotal += calc;  // running total of the card number as we loop - Do Nothing to first digit
					  i--;  // decrement the count - move to the next digit in the card
					  iCCN = iCCN / 10;                               // subtracts right most digit from ccNumb
					  calc = parseInt(iCCN) % 10 ;    // NEXT right most digit
					  calc = calc *2;                                 // multiply the digit by two
					  // I use a simple switch statement to change the value of calc2 to 7 if 16 is the multiple.
					  switch(calc){
						case 10: calc = 1; break;       //5*2=10 & 1+0 = 1
						case 12: calc = 3; break;       //6*2=12 & 1+2 = 3
						case 14: calc = 5; break;       //7*2=14 & 1+4 = 5
						case 16: calc = 7; break;       //8*2=16 & 1+6 = 7
						case 18: calc = 9; break;       //9*2=18 & 1+8 = 9
						default: calc = calc;           //4*2= 8 &   8 = 8  -same for all lower numbers
					  }                                               
					  iCCN = iCCN / 10;  // subtracts right most digit from ccNum
					  iTotal += calc;  // running total of the card number as we loop
					}  // END OF LOOP
					if ((iTotal%10)==0){  // check to see if the sum Mod 10 is zero
					  bResult = true;  // This IS (or could be) a valid credit card number.
					} else {
					  bResult = false;  // This could NOT be a valid credit card number
					}
				}
				return bResult;
			}
			
			return this.each(function() {
				var o = options; 
                var obj = $(this);
                $form.addClass("DreamForm");  
				drawForm(this, rowModel); 
				if ($.browser.msie && $.browser.version <= 7) $(":input:first", obj).focus();
				$form.checkForm(obj);
				$(":input",this).change(function () { $form.checkForm(obj); });
			});
			

		}
	});
})(jQuery);