﻿/*****************************************************
    JQuery Ajax Form 
  */
(function($) {

  $.fn.upgradeForm = function(opt, schemes) {
    var formEvents = {
        validate:function(e) {
			var form = this;
			var formIsValid = true;
            var fields = $(":input[validation]", form).get();
			
			$(":input", form).trigger("resetValidation", [form]);
			
			//Validate All Fields
			for(var i in fields) {
				var field = fields[i];
				var fieldIsValid = true;
				var fieldSchemes = $(field).attr("validation").split(" ");
				
                for(var i in fieldSchemes) {
                    var scheme = fieldSchemes[i];
                    if(validationSchemes[scheme]) { //Scheme Exists
                        var result = validationSchemes[scheme].call(field, form);
                        if(result !== true) { //Validation Failed
							fieldIsValid = false;
							formIsValid	 = false;
							$(field).trigger("invalid", [form, result]);
                        }
                    }
                    else { //Scheme Does Not Exist
                        throw new Error("Cannot locate validation scheme '" + scheme + "'");
                    }
                }
				
				if(fieldIsValid)
					$(field).trigger("valid", [form]);
			}
            
			
			if(formIsValid) {
				if(options.submitWithAjax) {
					$(this).trigger("serialize", [options]);
					return false; //NOTE: Stop the submit event, the request is handled via AJAX
				}
                return true;
			}
			
			return false; //NOTE: Stop the submit event, the form has validation errors
        },
        serialize:function(e) {
			var data = new Object();
            var form = this;
			var fields = $(":input", form).get();
			
			for(var i in fields)
			{
				var field = fields[i];
				var n = $(field).attr("name");
                var v = $(field).val();
            
                if(n != undefined) 
                {
					data[n] = (data[n] == undefined ? new Array() : data[n]);

	                if(v != undefined)
	                {
	                    switch( $(field).attr("type") )
	                    {
	                        //NOTE: Only add checkbox or radio field values if they are checked
	                        case "checkbox" :
	                        case "radio" :
	                            if( $(field).attr("checked") )
	                                data[n].push(v);
	                            break;
	                        default:
	                            data[n].push(v);
	                    }
	                }
				}   
			}

            $(form).trigger("send", [data]);
        },
        send:function(e, data) { 
			//Assign this form an ID if it doesnt't have one
			var form   = this;
			var formID = $(form).attr("id");
			if(formID == null) {
				var idx = 0;
				while(true) {
					formID = "form_" + idx;
					if($("#"+formID).get(0))
						idx++;
					else {
						$(form).attr("id", formID);
						break;
					}
				}
			}
			
			eval("var errorCode   = function (response, description, error)   { $('#" + formID + "').trigger('serverError', [response, description, error]); };");
	        eval("var timeoutCode = function () 							  { $('#" + formID + "').trigger('timeoutError'); };");
	        eval("var successCode = function (response)	 				      { $('#" + formID + "').trigger('receive', [response]); };");     

	        var ajaxOptions = {
	               data: data,
	           dataType: (window.ActiveXObject ? 'text' : 'xml'),
	               type: $(form).attr("method"),
	                url: $(form).attr("action"),
	              error: errorCode,
	            timeout: timeoutCode,
	            success: successCode
	        };
	        $.ajax(ajaxOptions);
	        $(form).trigger("wait");
        },
        receive:function(e, r) {
            var response = r;
	        if(window.ActiveXObject)
	        {
	            response = new ActiveXObject("Microsoft.XMLDOM");
	            response.loadXML(r); 
	        }
	
			switch(response.documentElement.nodeName)
	        {
	            case "ArrayOfClientError" :
					$(this).trigger("clientError", [response]);
					break;
					
				default :
					$(this).trigger("success", [response]);
					break;
			}
        },
        clientError:function(e, response) {
			var form   = this;
			var errors = $("ClientError", response).get();
			for(var i in errors) {
				var error = errors[i];
				var fieldName 	  = $("Context", error).text();
				var errorMessage  = $("Description", error).text();
	            var fieldSelector = ":input[name='" + fieldName + "']";
				$(fieldSelector, form).trigger("invalid", [form, errorMessage]);
			}
        }     
    };
    
	var fieldEvents = {
		resetValidation:function(e, form) {
			var name = $(this).attr("name");
			var byName  = "[name='" + name + "']";
			var byTitle = "[title='" + name + "']";
			var messageField = "[class*='" + options.errorMessageClassName + "'][title='" + name + "']";
			
			$(byName, form).removeClass(options.invalidFieldClassName);
			$(byTitle, form).removeClass(options.invalidFieldClassName);
			$(messageField, form).text(""); //TODO: Handle OL, UL
		},
		valid:function(e, form) {
			//Do Nothing
		},
		invalid:function(e, form, errorMessage) {
			var name = $(this).attr("name");
			var byName  = "[name='" + name + "']";
			var byTitle = "[title='" + name + "']";
			var messageField = "[class*='" + options.errorMessageClassName + "'][title='" + name + "']";
			
			$(byName, form).addClass(options.invalidFieldClassName);
			$(byTitle, form).addClass(options.invalidFieldClassName);
			$(messageField, form).append(errorMessage + "\n"); //TODO: Handle OL, UL
		}
	};
	
    var options = $.extend({
		/** Options */
		submitWithAjax:true,
		invalidFieldClassName:"invalid",
		errorMessageClassName:"error-message",
		
		/** Default Event Handlers */
        wait:function(e) {
			$(".result", this).hide("fast").text("");
            $(".waiting", this).fadeIn("fast");
        },        
        timeoutError:function(e) {
            $(".waiting", this).fadeOut("fast");
			$(".error", this).fadeIn("fast");
        },
        serverError:function(e, response, description, error) {
            $(".waiting", this).fadeOut("fast");
			$(".error", this).fadeIn("fast");
        },
		clientError:function(e, response) {
            $(".waiting", this).fadeOut("fast");
        },
        success:function(e, response) {
			var formID = "#" + $(this).attr("id");
            $(".waiting", this).fadeOut("fast");
			$(".success", this).show("fast");
			$(".result", this).text("success").show("fast");
			
			setTimeout("$('.success', '" + formID + "').hide('fast');", 4000);
        }
    }, opt);
  
    //Return turn if passes scheme or an error message if invalid
    var validationSchemes = $.extend({
        required:function(form) {
				var sv = $(this).val();
				if(sv == null || sv.length == 0)
					return "This field is required.";
				return true; 
			},
			
		number:function(form) { return "'number' is not implemented."; },

         integer:function(form) { 
				var sv = $(this).val();
				if(isNaN(sv)) return "'" +  sv + "' is not an integer.";
				var n = new Number(sv);
				$(this).val( Math.round(n) );
				return true;
			},
			
		  email:function(form) {
				var sv = $(this).val();
				if(sv == null || sv.length == 0) return true;
				
				if( sv.match(/^[\w-]+(?:\.[\w-]+)*@(?:[\w-]+\.)+[a-zA-Z]{2,7}$/) )
					return true;
				
				return "'" + sv + "' is not an email address.";
			},
		phone:function(form) {
			var sv = $(this).val();
			if(sv == null || sv.length == 0) return true;
			
			if( sv.match(/^1?\D*\d{3}\D*\d{3}\D*\d{4}(\D+\d+)?$/) )
				return true;
			
			return "'" + sv + "' is not a phone number.";	
		}
			
    }, schemes);
    
  return this.each(function(){
    //Bind Required Form Event Handlers   
	$(this).bind("submit", 			formEvents.validate);
	$(this).bind("serialize",       formEvents.serialize);
	$(this).bind("send",            formEvents.send); 
	$(this).bind("receive",         formEvents.receive);
    $(this).bind("clientError",     formEvents.clientError);
    
	//Bind Required Field Event Handlers
	$(":input", this).bind("resetValidation", fieldEvents.resetValidation);
	$(":input", this).bind("valid", 		  fieldEvents.valid);
	$(":input", this).bind("invalid", 		  fieldEvents.invalid);

    //Bind Default Event Handlers
    $(this).bind("wait",            options.wait);    
    $(this).bind("timeoutError",    options.timeoutError);
    $(this).bind("serverError",     options.serverError);
	$(this).bind("clientError",     options.clientError);   
    $(this).bind("success",         options.success);
  });
};

})(jQuery);