if (Form == undefined) var Form = { };

Form.Validator = function(form) {
	this.form = form;
	this.list = new Array();
};

Form.Validator.VERSION = '0.20';

// Form.Validator Class
Form.Validator.prototype = {
	form:   null,
	list:   null,

	// define a constraint for a field
	set: function(field, constraintFunction, message) {
		var c     = new Object;
		c.field   = field;
		c.message = message;

		if (typeof(constraintFunction) == 'string') {
			// predefined validator
			c.constraint = this[constraintFunction];
		} else {
			// function reference w/ a custom validator
			c.constraint = constraintFunction;
		}

		this.list.push(c);
	},

	// get contraint info for a field. (this can return more than 1 constraint)
	get: function(field) {
		var constraints = new Array();
		for (i in this.list) {
			var c = this.list[i];
			if (c.field == field) {
				constraints.push(c);
			}
		}
		return constraints;
	},

	// run all validation tests and return all error messages in an array
	validate: function() {
		var status;
		var i, c;
		var messages = new Array();
		for (i in this.list) {
			c = this.list[i];

			// somehow, this.list is getting polluted w/ an extra element.
			if (typeof(c.constraint) != 'function') {
				continue;
			}

			ok = c.constraint(this, c.field);
			if (!ok) {
				messages.push(c.message);
			}
		}
		return messages;
	},

	// like validate() but displays an alert box telling you what went wrong
	validateAndAlert: function() {
		var messages = this.validate();
		if (messages.length > 0) {
			alert(messages.join("\n"));
			return false;
		}
	},


	// VALIDATION FUNCTIONS

	// false is bad
	// true  is good

	// Make sure a field's value is not blank
	notBlank: function(self, field) {
		var blankness = new RegExp(/^\s*$/);
		if (blankness.test(self.form[field].value)) {
			return false;
		} else {
			return true;
		}
	},
	// ...meaning, you can't be Blank; if you are, you messed up;
 
	// Make sure a field's value is not 0
	notZero: function(self, field) {
		if (self.form[field].value == "0") {
			return false;
		} else {
			return true;
		}
	},

	// METHODS THAT GENERATE VALIDATION CLOSURES

	// Make sure field is at least n characters long
	makeLengthMin: function(n) {
		return function(fv, field) {
			if (fv.form[field].value.length < n) {
				return false;
			} else {
				return true;
			}
		}
	},

	// Make sure field is not more than n characters long
	makeLengthMax: function(n) {
		return function(fv, field) {
			if (fv.form[field].value.length > n) {
				return false;
			} else {
				return true;
			}
		}
	},

	// Make sure field is at least n (or greater)
	makeValueMin: function(n) {
		return function(fv, field) {
			if (fv.form[field].value < n) {
				return false;
			} else {
				return true;
			}
		}
	},

	// Make sure field is at most n (or less).
	makeValueMax: function(n) {
		return function(fv, field) {
			if (fv.form[field].value > n) {
				return false;
			} else {
				return true;
			}
		}
	},

	// Make sure a field matches the given regex
	makeMatchRegex: function(regex) {
		return function(fv, field) {
			return regex.test(fv.form[field].value);
		}
	},

	// Make sure a field does not match the given regex
	makeNotMatchRegex: function(regex) {
		return function(fv, field) {
			return !regex.test(fv.form[field].value);
		}
	}

};

/*

=head1 NAME

Form.Validator - a class for form validation

=head1 SYNOPSIS

Make a form with an C<onSubmit> handler:

  <form name="stuff"
    action="process_stuff.mas"
    method="POST"
    onSubmit="return fv.validateAndAlert()">

    name: 
      <input name="name" type="text"/><br/>

    category:
      <select name="category_id">
	<option value="0">Select a category</option>
	<option value="1"> cat 1 </option>
	<option value="2"> cat 2 </option>
	<option value="3"> cat 3 </option>
      </select>
      <br/>

    cost:
      <input name="cost" type="text"/><br/>

    <input type="submit" name="submit" value="Save" />
  </form>

Add in JavaScript later on:

  <script>
    fv = new Form.Validator(document.stuff);
    fv.set('name',        'notBlank',           "Name is required.");
    fv.set('name',        fv,makeLengthMax(32), "Name is too long.");
    fv.set('category_id', 'notZero',            "Please pick a category.");
    fv.set('cost',        fv.makeValueMin(5),   "Cost must be 5 or greater.");
    fv.set('cost',        fv.makeValueMax(100), "Cost may not exceed 100.");
  </script>

=head1 DESCRIPTION

Client-side form validation is no longer a pain.

=head1 METHODS

=head2 Form.Validator

This is the constructor.  It takes a form object as its only parameter.

B<Example>:

  var fv = new Form.Validator(document.stuff);
  var fv2 = new Form.Validator(document.forms[0]);

=head2 set

This method associates a validation function and error message to
an input field in the form.

B<Example>:

  // using a built-in validator function
  fv.set('name', 'notBlank', "Name must be filled in.");

  // using a custom validator
  fv.set('category_id',
    function(fv, field) {
      if (fv.form[field].value == 3) {
	return false;
      } else {
	return true;
      }
    },
    "Problems always come in 3s.  Bad choice, my friend."
  );

=head2 get

This method returns a list of the validation settings for a given input field.

=head2 validate

This method runs through the list of validation settings in the order they were
defined and returns an Array of strings for every constraint that failed.

=head2 validateAndAlert

This function is intended to be used as an C<onSubmit> handler on a form.  It
runs through the validation tests in the order they were defined and displays
an alert box if any of the validation tests failed.  Then it will return
C<false> to prevent the form from being submitted.

B<Example>:

  <form name="stuff"
    action="process_stuff.mas"
    method="POST"
    onSubmit="return fv.validateAndAlert()">

=head1 BUILT-IN VALIDATION TESTS

=head2 notBlank

For inputs that shouldn't be blank, use this test function.

=head2 notZero

For inputs that shouldn't be 0, use this test function.

=head1 VALIDATION FUNCTION GENERATORS

=head2 makeLengthMin

This method returns a function that will ensure that the contents
of a field are at least n characters long.

B<Example>:

  fv.set(
    'name',
    fv.makeLengthMax(3),
    "The name field must be at least 3 characters long"
  );

=head2 makeLengthMax

This method returns a function that will ensure that the contents
of a field are not more than n characters long.

B<Example>:

  fv.set(
    'name',
    fv.makeLengthMax(10),
    "The name field may not be more than 10 characters long"
  );

=head2 makeValueMin

This method returns a function that will ensure that the value
of a field is at least n.

=head2 makeValueMax

This method returns a function that will ensure that the value
of a field is less than or equal to n.

=head2 makeMatchRegex

This method returns a function that will match the regex passed
to it.

B<Example>:

  fv.set(
    'country',
    fv.makeMatchRegex(/CA|US/),
    "Sorry, we do not ship outside of Canada or the US."
  );

=head2 makeNotMatchRegex

This method returns a function that will make sure that the
specified input does not match the given regex.

B<Example>:

  fv.set(
    'do_i_look_fat_in_this',
    fv.makeNotMatchRegex(/[Yy]es|[Yy]eah/),
    ";_; *cry*"
  );


=head1 WRITING YOUR OWN VALIDATION FUNCTIONS

If the provided validation tests are not enough to fulfill your needs, you may
add validation functions of your own.  They should expect to receive 2
parameters.  The first is the Form.Validator object (fv) and the second is the
name of the field that the function needs to test.  To get at the actual form,
access C<fv.form>.

B<Example>:

  function notOdd (fv, field) {
    var form = fv.form;
    if (form.field.value > 0 && form.field.value & 0x01) {
      return false;
    } else {
      return true;
    }
  };

  fv.set('number', notOdd, "Only positive, even numbers are allowed.");

Furthermore, if you feel compelled to add this method to the Form.Validator
class, you can do this:

  Form.Validator.prototype.notOdd = notOdd;

Now that we've added the C<notOdd> method, all Form.Validator objects 
can test to see whether a field has an odd value or not.

=head1 TO DO

=over 2

=item * Add more validation functions.

C<notBlank> and C<notZero> alone can take you pretty far, but a few
more validation functions would be nice to have.

UPDATE: Many validation function generators were added.

=item * Add hooks for pre- and post- validation actions.

I think these might be helpful to those who want to do fancy AJAX-ish
along with validation.

=back

=head1 AUTHOR

John Beppu (beppu@cpan.org)

=cut

*/
