diff --git a/js/foundation/foundation.abide.js b/js/foundation/foundation.abide.js
index 8cb4b7872..021268a8a 100644
--- a/js/foundation/foundation.abide.js
+++ b/js/foundation/foundation.abide.js
@@ -39,6 +39,15 @@
// #FFF or #FFFFFF
color: /^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/
+ },
+ validators : {
+ equalTo: function(el, required, parent) {
+ var from = document.getElementById(el.getAttribute(this.add_namespace('data-equalto'))).value,
+ to = el.value,
+ valid = (from === to);
+
+ return valid;
+ }
}
},
@@ -135,7 +144,7 @@
} else if (pattern.length > 0) {
return [el, new RegExp(pattern), required];
}
-
+
if (this.settings.patterns.hasOwnProperty(type)) {
return [el, this.settings.patterns[type], required];
}
@@ -154,13 +163,16 @@
required = el_patterns[i][2],
value = el.value,
direct_parent = this.S(el).parent(),
- is_equal = el.getAttribute(this.add_namespace('data-equalto')),
+ validator = el.getAttribute(this.add_namespace('data-abide-validator')),
is_radio = el.type === "radio",
is_checkbox = el.type === "checkbox",
label = this.S('label[for="' + el.getAttribute('id') + '"]'),
valid_length = (required) ? (el.value.length > 0) : true;
- var parent;
+ var parent, valid;
+
+ // support old way to do equalTo validations
+ if(el.getAttribute(this.add_namespace('data-equalto'))) { validator = "equalTo" }
if (!direct_parent.is('label')) {
parent = direct_parent;
@@ -172,8 +184,17 @@
validations.push(this.valid_radio(el, required));
} else if (is_checkbox && required) {
validations.push(this.valid_checkbox(el, required));
- } else if (is_equal && required) {
- validations.push(this.valid_equal(el, required, parent));
+ } else if (validator) {
+ valid = this.settings.validators[validator].apply(this, [el, required, parent])
+ validations.push(valid);
+
+ if (valid) {
+ this.S(el).removeAttr(this.invalid_attr);
+ parent.removeClass('error');
+ } else {
+ this.S(el).attr(this.invalid_attr, '');
+ parent.addClass('error');
+ }
} else {
if (el_patterns[i][1].test(value) && valid_length ||
@@ -231,22 +252,6 @@
}
}
- return valid;
- },
-
- valid_equal: function(el, required, parent) {
- var from = document.getElementById(el.getAttribute(this.add_namespace('data-equalto'))).value,
- to = el.value,
- valid = (from === to);
-
- if (valid) {
- this.S(el).removeAttr(this.invalid_attr);
- parent.removeClass('error');
- } else {
- this.S(el).attr(this.invalid_attr, '');
- parent.addClass('error');
- }
-
return valid;
}
};
diff --git a/spec/abide/abide.js b/spec/abide/abide.js
index 23a219e39..5d8f14d74 100644
--- a/spec/abide/abide.js
+++ b/spec/abide/abide.js
@@ -90,4 +90,84 @@ describe('abide:', function() {
expect('invalid').not.toHaveBeenTriggeredOn('input[name="user_email"]');
});
});
+
+ describe('advanced validation', function() {
+ beforeEach(function() {
+ document.body.innerHTML = __html__['spec/abide/advanced.html'];
+ });
+
+ it('should support builtin equalTo validator', function() {
+ $(document).foundation({
+ abide: {
+ validators: {
+ range: function(){ return true; }
+ }
+ }
+ });
+
+
+ expect($('input[name="user_password"]')).not.toHaveAttr('data-invalid');
+ expect($('input[name="user_password_confirmation"]')).not.toHaveAttr('data-invalid');
+
+ $('input[name="user_password"]').val("foobarbaz");
+ // now they're not equal
+ $('form').submit();
+
+ var invalid_fields = $('form').find('[data-invalid]');
+ expect(invalid_fields.length).toBe(1);
+ expect($('input[name="user_password_confirmation"]')).toHaveAttr('data-invalid');
+
+ $('input[name="user_password_confirmation"]').val("foobarbaz");
+ // now they're equal
+ spyOnEvent('form', 'invalid');
+ spyOnEvent('form', 'valid');
+
+ $('form').submit();
+
+ expect('valid').toHaveBeenTriggeredOn('form');
+ expect($('input[name="user_password"]')).not.toHaveAttr('data-invalid');
+ expect($('input[name="user_password_confirmation"]')).not.toHaveAttr('data-invalid');
+ });
+
+ it('should support custom validators', function() {
+ $(document).foundation({
+ abide: {
+ validators: {
+ range: function(el, required, parent) {
+ var start = parseInt(this.S("[name='user_start_num']").val()),
+ end = parseInt(el.value);
+
+ return start < end;
+ }
+ }
+ }
+ });
+
+ expect($('input[name="user_start_num"]')).not.toHaveData('invalid');
+ expect($('input[name="user_end_num"]')).not.toHaveData('invalid');
+
+ // invalid
+ $('input[name="user_start_num"]').val("10");
+ $('input[name="user_end_num"]').val("2");
+
+ $('form').submit();
+
+ var invalid_fields = $('form').find('[data-invalid]');
+ expect(invalid_fields.length).toBe(1);
+
+ expect($('input[name="user_start_num"]')).not.toHaveAttr('data-invalid');
+ expect($('input[name="user_end_num"]')).toHaveAttr('data-invalid');
+
+ // valid now
+ $('input[name="user_end_num"]').val("12");
+ spyOnEvent('form', 'invalid');
+ spyOnEvent('form', 'valid');
+
+ $('form').submit();
+
+ expect('valid').toHaveBeenTriggeredOn('form');
+ expect($('input[name="user_start_num"]')).not.toHaveAttr('data-invalid');
+ expect($('input[name="user_end_num"]')).not.toHaveAttr('data-invalid');
+ });
+ });
});
diff --git a/spec/abide/advanced.html b/spec/abide/advanced.html
new file mode 100644
index 000000000..718f6e3d5
--- /dev/null
+++ b/spec/abide/advanced.html
@@ -0,0 +1,22 @@
+