jQuery.fn.think_validation = function(vOptions) {
    //validation
    function isValid(fieldToValidate) {
        var postData = jQuery(vOptions.formId).serialize() + "&validate_json_request=true&validate_json_field=" + fieldToValidate; //live data
        jQuery.ajax({
            type: "POST",
            contentType: "application/json; charset=utf-8",
            url: vOptions.urlString,
            data: postData,
            dataType: "json",
            contentType: "application/x-www-form-urlencoded",
            error: function(errorData, errorData1) {
                //document.forms[0].submit(); //do a postback here
            },
            success: function(returnedData) {
                //loop JSON
                var i = returnedData.validation.field.length;
                while (i--) {
                    var mainMessage = returnedData.validation.error_message_info;
                    var fieldId = returnedData.validation.field[i].field_id;
                    var inlineMess = returnedData.validation.field[i].message;
                    var success = returnedData.validation.field[i].sucess;
                    if (success) {
                    }
                    if (!success && jQuery("#" + fieldId).val()) {
                        addInlineMessage(fieldId, inlineMess);
                        addMainError(fieldId, mainMessage);
                    }
                    else if (success) {
                        clearErrors(jQuery("#" + fieldId), fieldId)
                    }
                }
            }
        });
    };

    //display inline message next to field
    function addInlineMessage(fieldId, inlineMess) {
        var rowParent = jQuery("#" + fieldId).parents(".frmRow:first");
        var messageExists = rowParent.find(".errorMessage:has(label[for='" + fieldId + "'])").filter(function() { return jQuery(this).html() == jQuery(inlineMess).html(); });

        if (rowParent.hasClass("error")) {
            if (messageExists.length == 0) { rowParent.append(" " + inlineMess); }
        }
        else if (rowParent.hasClass("servererror")) {
            if (messageExists.length > 0) { messageExists.removeClass("js-hide"); }
            else { rowParent.append(" " + inlineMess); }

            if (rowParent.find(".errorMessage:not(.js-hide)").length > 0) {
                rowParent.addClass("error"); 
            }
        }
        else {
            //no message exists, so add it
            rowParent.addClass("error");
            rowParent.append(" " + inlineMess);
        }
    };

    function clearErrors(field, fieldId) {
        var rowParent = jQuery(field).parents(".frmRow:first");

        if (rowParent.hasClass("servererror")) {
            rowParent.find(".errorMessage:has(label[for='" + fieldId + "'])").addClass("js-hide");
        }
        else if (rowParent.hasClass("error")) {
            rowParent.find(".errorMessage:has(label[for='" + fieldId + "'])").remove();
        }

        if (rowParent.find(".errorMessage:not(.js-hide)").length == 0) { rowParent.removeClass("error"); }

        //check if other errors exist on page, true: update main error message at top of page
        if (jQuery("#iError").length && jQuery(".frmRow").hasClass(".error") == false) {
            jQuery("#iError").remove(); //remove if main message exists and no inline errors exist
        }
        else if (jQuery("#iError").length && jQuery(".frmRow").hasClass(".error") == true) {
            //validate all fields
            isValid();
        }
    };

    function addMainError(fieldId, mainMessage) {
        //check if main message exists
        if (jQuery("#iError").length) {
            //check if main message has changed
            if (jQuery("#iError").html() !== jQuery(mainMessage).html()) {
                jQuery("#iError").replaceWith(mainMessage); //if changed replace
            }
        }
        else //append message
        {
            jQuery("#" + fieldId).parents("div.form").prepend(mainMessage);
        }
        jQuery("#iError").roundedCorners(true);
    };

    //set event on fields
    jQuery(this).blur(function() {
        //check if field is null or empty
        if (!(jQuery(this).val())) {
            // Get the control ID
            var fieldId = jQuery(this).attr("id");
            var rowParent = jQuery(this).parents(".frmRow:first");

            if (jQuery(this).parent().hasClass("frmElement")) {
                if (!rowParent.hasClass("servererror")) {
                    //unless current field is the forename field
                    if (!jQuery(this).parent(".frmElement").prev().hasClass("title")) {
                        //remove associated inline error message and parent class
                        clearErrors(jQuery(this), jQuery(this).attr("id"));
                    }
                }
            }
            else {
                if (!rowParent.hasClass("servererror")) {
                    //remove associated inline error message and parent class
                    clearErrors(jQuery(this), jQuery(this).attr("id"));
                }
            }

            if (rowParent.hasClass("servererror")) {
                // Check if this field was the one that caused the error on postback (servererror)
                var exists = rowParent.find(".errorMessage:has(label[for='" + fieldId + "'])");
                if (exists.length > 0) {
                    // Set the status back to how it was before any js interaction
                    rowParent.addClass("error")
                    exists.removeClass("js-hide");
                }
            }
        }
        else //if field contains data validate it
        {
            isValid(escape(this.name)); //validate passing in id of field
        }
    });

    /*
    end validation
    */
};
