Skip to content

Commit

Permalink
* New feature: validate emits "validated" event. Sends validity
Browse files Browse the repository at this point in the history
  object with element attribute.
* New feature: allValid events "formValidated" event. Sends
  validity objects with element attributes.

* Fixed issue ericelliott#26 Add support for .novalidate
* Fixed bug ericelliott#29 - Validate tries to validate disabled fields
* Fixed bug - Validate tries to validate buttons
  • Loading branch information
Eric Hamilton committed Dec 28, 2011
1 parent 2583d90 commit f34c0da
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 40 deletions.
94 changes: 61 additions & 33 deletions jquery.h5validate.js
@@ -1,6 +1,6 @@
/**
* h5Validate
* @version v0.7.1
* @version v0.8.0
* Using semantic versioning: http://semver.org/
* @author Eric Hamilton http://ericleads.com/
* @copyright 2010 - 2011 Eric Hamilton
Expand Down Expand Up @@ -62,15 +62,15 @@
},

// Setup KB event delegation.
kbSelectors: ':input',
kbSelectors: ':input:not(:button):not(:disabled):not(.novalidate)',
focusout: true,
focusin: false,
change: true,
keyup: false,
activeKeyup: true,

// Setup mouse event delegation.
mSelectors: '[type="range"], :radio, :checkbox, select, option',
mSelectors: '[type="range"]:not(:disabled):not(.novalidate), :radio:not(:disabled):not(.novalidate), :checkbox:not(:disabled):not(.novalidate), select:not(:disabled):not(.novalidate), option:not(:disabled):not(.novalidate)',
click: true,

// What do we name the required .data variable?
Expand All @@ -92,7 +92,7 @@
validateOnSubmit: true,

// Elements to validate with allValid (only validating visible elements)
allValidSelectors: 'input:visible, textarea:visible, select:visible',
allValidSelectors: ':input:visible:not(:button):not(:disabled):not(.novalidate)',

// Mark field invalid.
// ** TODO: Highlight labels
Expand All @@ -113,7 +113,7 @@
$errorID.show();
}
$element.data('valid', false);
options.settings.invalidCallback.call(options.element);
options.settings.invalidCallback.call(options.element, options.validity);
return $element;
},

Expand All @@ -127,7 +127,7 @@
$errorID.hide();
}
$element.data('valid', true);
options.settings.validCallback.call(options.element);
options.settings.validCallback.call(options.element, options.validity);
return $element;
},

Expand All @@ -144,6 +144,20 @@
defaults = h5.defaults,
patternLibrary = defaults.patternLibrary,

createValidity = function createValidity(validity) {
return $.extend({
customError: validity.customError || false,
patternMismatch: validity.patternMismatch || false,
rangeOverflow: validity.rangeOverflow || false,
rangeUnderflow: validity.rangeUnderflow || false,
stepMismatch: validity.stepMismatch || false,
tooLong: validity.tooLong || false,
typeMismatch: validity.typeMismatch || false,
valid: validity.valid || true,
valueMissing: validity.valueMissing || false
}, validity);
},

methods = {
isValid: function () {
var $this = $(this);
Expand All @@ -153,10 +167,18 @@
return $this.data('valid'); // get the validation result
},
allValid: function (settings) {
var valid = true;
$(this).find(settings.allValidSelectors).each(function () {
var valid = true,
formValidity = [],
$this = $(this);
$this.find(settings.allValidSelectors).each(function () {
var $this = $(this);
$this.bind('validated', function (e, data) {
data.e = e;
formValidity.push(data);
});
valid = $(this).h5Validate('isValid') && valid;
});
$this.trigger('formValidated', {elements: formValidity});
return valid;
},
validate: function (settings) {
Expand All @@ -175,8 +197,7 @@
errorIDbare = $this.attr(settings.errorAttribute) || false, // Get the ID of the error element.
errorID = errorIDbare ? '#' + errorIDbare : false, // Add the hash for convenience. This is done in two steps to avoid two attribute lookups.
required = false,
isValid = true,
reason = '',
validity = createValidity({element: this, valid: true}),
$checkRequired = $('<input required>');

/* If the required attribute exists, set it required to true, unless it's set 'false'.
Expand All @@ -187,9 +208,9 @@
*/
// Feature fork
if ($checkRequired.filter('[required]') && $checkRequired.filter('[required]').length) {
required = ($this.filter('[required]').length && $this.attr('required') !== 'false') ? true : false;
required = ($this.filter('[required]').length && $this.attr('required') !== 'false');
} else {
required = ($this.attr('required') !== undefined) ? true : false;
required = ($this.attr('required') !== undefined);
}

if (settings.debug && window.console) {
Expand All @@ -198,32 +219,39 @@
}

if (required && !value) {
isValid = false;
reason = 'required';
validity.valid = false;
validity.valueMissing = true;
} else if (pattern && !re.test(value) && value) {
isValid = false;
reason = 'pattern';
validity.valid = false;
validity.patternMismatch = true;
} else {
isValid = true;
settings.markValid({
element: this,
errorClass: errorClass,
validClass: validClass,
errorID: errorID,
settings: settings
});
validity.valid = true; // redundant?

if (!settings.RODom) {
settings.markValid({
element: this,
validity: validity,
errorClass: errorClass,
validClass: validClass,
errorID: errorID,
settings: settings
});
}
}

if (!isValid) {
settings.markInvalid({
element: this,
reason: reason,
errorClass: errorClass,
validClass: validClass,
errorID: errorID,
settings: settings
});
if (!validity.valid) {
if (!settings.RODom) {
settings.markInvalid({
element: this,
validity: validity,
errorClass: errorClass,
validClass: validClass,
errorID: errorID,
settings: settings
});
}
}
$this.trigger('validated', validity);
},

/**
Expand Down
75 changes: 68 additions & 7 deletions test/test.h5validate.js
@@ -1,4 +1,7 @@

/*global window, $, module, test, ok, equal, exports */
(function (exports) {
'use strict';
function runTests() {
module('h5Validate');

Expand All @@ -18,15 +21,15 @@
var $input = $('#birthdate');
ok(($input.h5Validate('isValid')), 'Optional input should be valid when empty.');
$input.val('01/01/2010');
ok(($input.h5Validate('isValid')), 'Input should be valid when given valid input.');
ok(($input.h5Validate('isValid')), 'Input should be valid when given valid input.');
$input.val('foo');
ok((!$input.h5Validate('isValid')), 'Input should be invalid when given invalid input.');
});

test('Pattern library:', function () {
var $input = $('#email');
$input.val('test@example.com');
ok(($input.h5Validate('isValid')), 'Input should be valid when given valid input.');
ok(($input.h5Validate('isValid')), 'Input should be valid when given valid input.');
$input.val('invalid email');
ok((!$input.h5Validate('isValid')), 'Input should be invalid when given invalid input.');
});
Expand All @@ -41,10 +44,10 @@

test('Instance safe for method calls:', function () {
var $form = $('<form>', {
id: 'instanceTest',
id: 'instanceTest'
}),
test1;

$form.html('<input required id="instance-input"/>'
+ '<select type="select" name="BillingCountry" id="BillingCountry" required="required">'
+ '<option value="">-- Choose Country --</option>'
Expand All @@ -70,11 +73,69 @@
ok(test1, 'Methods are instance safe.');
});

//TODO: Don't attempt to validate disabled fields

//TODO: Validate radio buttons correctly. (Any checked field satisfies required)
test('Validated events:', function () {
var $form = $('<form>', {
id: 'eventTest'
}),
$input;

$form.html('<input required id="event-input"/>')
.appendTo('body');

$input = $('#event-input');

$form.h5Validate();

$input.bind('validated', function (event, data) {
ok(data, 'Validated event triggers.');
equal(data.element, $input[0], 'Element is correct.');
equal(data.valid, true, 'Element is valid.');
});

$form.bind('formValidated', function (event, data) {
ok(data, 'formValidated triggers.');
equal(data.elements[0].element, $input[0], 'Form element 0 is correct.');
equal(data.elements[0].valid, true, 'Element is valid.');
});

$input.val('test');
$form.h5Validate('allValid');
});

test('Issue #29: Disabled fields gum up the works.', function () {
var $form = $('<form>', {
id: 'disabledTest'
}),
$input;

$form.html('<input required id="disabled-input" disabled required /><input />')
.appendTo('body');

$input = $('#disabled-input');

$form.h5Validate();

ok($form.h5Validate('allValid'), 'Disabled fields get skipped.');
});

test('Issue #26: Need a .novalidate class.', function () {
var $form = $('<form>', {
id: 'novalidateTest'
}),
$input;

$form.html('<input required id="novalidate-input" class="novalidate" required /><input />')
.appendTo('body');

$input = $('#novalidate-input');

$form.h5Validate();

ok($form.h5Validate('allValid'), '.novalidate fields get skipped.');

});

//TODO: Validate radio buttons correctly. (Any checked field satisfies required)
}
exports.runTests = runTests;
}((typeof exports !== 'undefined') ? exports : window));

0 comments on commit f34c0da

Please sign in to comment.