Skip to content
Browse files

Re-organizing code

Adding tests
Adding example for l10n
  • Loading branch information...
1 parent 57708ec commit 2213ad2ded4f6ee3d5efc3f02e2cba101a567bbd @jaymorrow committed Mar 11, 2013
Showing with 749 additions and 509 deletions.
  1. +6 −0 README.md
  2. +281 −250 dist/validatr.js
  3. +2 −2 dist/validatr.min.js
  4. +1 −1 docs
  5. +0 −1 src/index.html
  6. +23 −0 src/js/l10n/en.js
  7. +280 −249 src/js/validatr.js
  8. +3 −3 tests/tests_date.js
  9. +149 −0 tests/tests_widget.js
  10. +1 −0 tests/validatr.html
  11. +3 −3 validatr.jquery.json
View
6 README.md
@@ -9,6 +9,12 @@ View the [documentation][docs] to learn how to use Validatr.
## Changelog
+### Version 0.5.0 - 2013-03-11
+
+* Added unit tests for the jQuery instance and the widget
+* Fixed typo 'defualtOptions' - now 'defaultOptions'
+* Re-structured code to prep for more robust formatting and adding more input types
+
### Version 0.4.2 - 2013-03-03
* Default error message styles now inline - removed CSS file
View
531 dist/validatr.js
@@ -1,15 +1,15 @@
-/*! Validatr - v0.4.2 - 2013-03-03
+/*! Validatr - v0.5.0 - 2013-03-11
* http://jaymorrow.github.com/validatr/
* Copyright (c) 2013 Jay Morrow; Licensed MIT */
(function(window, document, $, undefined) {
"use strict";
- /*! Modernizr 2.6.2| MIT & BSD
+ /*! Inspired by Modernizr 2.6.2| MIT & BSD
* Build: http://modernizr.com/download/#-input-inputtypes
*/
var Support = (function() {
- var Modernizr = {},
+ var Support = {},
docElement = document.documentElement,
@@ -29,15 +29,15 @@
testElem;
- Modernizr.attributes = (function( props ) {
+ Support.attributes = (function( props ) {
for ( var i = 0, len = props.length; i < len; i++ ) {
attrs[ props[i] ] = !!(props[i] in inputElem);
}
return attrs;
})('max min multiple pattern required step'.split(' '));
- Modernizr.inputtypes = (function(props) {
+ Support.inputtypes = (function(props) {
for ( var i = 0, bool, inputElemType, defaultView, len = props.length; i < len; i++ ) {
inputElem.setAttribute('type', inputElemType = props[i]);
@@ -84,153 +84,268 @@
}
testElem.style.cssText = 'position:absolute;visibility:hidden;';
- Modernizr.inputtypes[ props[i] ] = !!testElem.checkValidity;
+ Support.inputtypes[ props[i] ] = !!testElem.checkValidity;
}
})('text password radio checkbox'.split(' '));
- Modernizr.inputtypes.select = !!selectElem.checkValidity;
- Modernizr.inputtypes.textarea = !!textareaElem.checkValidity;
+ Support.inputtypes.select = !!selectElem.checkValidity;
+ Support.inputtypes.textarea = !!textareaElem.checkValidity;
inputElem = null;
testElem = null;
selectElem = null;
textareaElem = null;
- return Modernizr;
+ return Support;
}()),
- Rules = {
- boxes: /checkbox|radio/i,
- color: /^#[0-9A-F]{6}$/i,
- date: {
- dd: '(0[1-9]|[12][0-9]|3[01])',
- mm: '(0[1-9]|1[012])',
- yyyy: '(\\d{4})'
+ Format = (function () {
+ var rules = {
+ isoDate: /^(\d{4})-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$/
},
- email: /^[a-zA-Z0-9.!#$%&’*+\/=?\^_`{|}~\-]+@[a-zA-Z0-9\-]+(?:\.[a-zA-Z0-9\-]+)*$/,
- isoDate: /^(\d{4})-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$/,
- isoMonth: /^(\d{4})-(0[1-9]|1[012])$/,
- leftright: /left|right/i,
- notInput: /select|textarea/i,
- number: /^-?\d*\.?\d*$/,
- separators: /(\/|\-|\.)/g,
- separatorsNoGroup: /\/|\-|\./g,
- spaces: /,\s*/,
- time: /^([01][0-9]|2[0-3])(:([0-5][0-9])){2}$/,
- topbottom: /top|bottom/i,
- url: /^\s*https?:\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?\s*$/
- },
- Tests = {
- checkbox: function (element) {
- return {
- valid: element.checked,
- message: $.validatr.messages.checkbox
- };
- },
+ utils = {
+ separators: /(\/|\-|\.)/g,
+ separatorsNoGroup: /\/|\-|\./g,
+ dateParts: {
+ dd: '(0[1-9]|[12][0-9]|3[01])',
+ mm: '(0[1-9]|1[012])',
+ yyyy: '(\\d{4})'
+ }
+ };
- color: function (element) {
- return {
- valid: Rules.color.test(element.value),
- message: $.validatr.messages.color
- };
- },
+ function indexOf(array, value) {
+ var index = -1,
+ length = array ? array.length : 0;
- date: function (element) {
- var $element = $(element),
- value = Support.inputtypes.date ? parseISODate(element.value) : parseDate(element),
- min = $element.attr('min') ? parseISODate($element.attr('min')) : false,
- max = $element.attr('max') ? parseISODate($element.attr('max')) : false,
- step = false;
-
- return minMax.call(element, value, min, max, step, 'date');
- },
- email: function (element) {
- var valid = true,
- msg = $.validatr.messages.email.single,
- multiple = Support.attributes.multiple ? element.multiple : $(element).is('[multiple]');
+ while (++index < length) {
+ if (array[index] === value) {
+ return index;
+ }
+ }
- if (multiple) {
- var values = element.value.split(Rules.spaces);
+ return -1;
+ }
- $.each(values, function (i, value) {
- if (!Rules.email.test(value)) {
- valid = false;
- msg = $.validatr.messages.email.multiple;
- return;
- }
- });
- } else {
- valid = Rules.email.test(element.value);
- }
- return {
- valid: valid,
- message: msg
- };
- },
+ function parseDate(element) {
+ var format = element.getAttribute('data-format') || $.fn.validatr.defaultOptions.dateFormat,
+ split = format.split(utils.separatorsNoGroup),
+ dateSplit = element.value.split(utils.separatorsNoGroup),
+ isoSplit = 'yyyy-mm-dd'.split('-'),
+ rule = format.replace(utils.separators, '\\$1')
+ .replace('yyyy', utils.dateParts.yyyy)
+ .replace('mm', utils.dateParts.mm)
+ .replace('dd', utils.dateParts.dd),
+ index = -1,
+ length = isoSplit.length,
+ iso = [];
+
+ rule = new RegExp(rule);
+ if (!rule.test(element.value)) {
+ return false;
+ }
- number: function (element) {
- var value = element.value.replace(',', ''),
- num = Rules.number.test(value) ? parseFloat(value) : false,
- min = Rules.number.test( element.getAttribute('min') ) ? parseFloat( element.getAttribute('min') ) : false,
- max = Rules.number.test( element.getAttribute('max') ) ? parseFloat( element.getAttribute('max') ) : false,
- step = Rules.number.test( element.getAttribute('step') ) ? parseFloat( element.getAttribute('step') ) : element.getAttribute('step') === 'any' ? 'any' : false;
+ while (++index < length) {
+ iso[index] = dateSplit[ indexOf(split, isoSplit[index]) ];
+ }
- if (step === false || step <= 0) {
- step = 1;
+ return parseISODate(iso.join('-'));
+ }
+
+ function parseISODate(dateString) {
+ if (!rules.isoDate.test(dateString)) {
+ return false;
}
- return minMax.call(element, value, min, max, step, 'number');
- },
+ var date = rules.isoDate.exec(dateString);
+ return new Date(parseInt(date[1], 10), parseInt(date[2], 10) - 1, parseInt(date[3], 10));
+ }
- pattern: function (element) {
- return {
- valid: new RegExp(element.getAttribute('pattern')).test(element.value),
- message: $.validatr.messages.pattern
- };
- },
+ function formatISODate(dateObj, element) {
+ function pad(n) {
+ return n < 10 ? '0' + n : n;
+ }
- radio: function (element) {
- return {
- valid: $(document.getElementsByName(element.name)).is(':checked'),
- message: $.validatr.messages.radio
- };
+ var date = pad(dateObj.getDate()),
+ month = pad(dateObj.getMonth() + 1),
+ year = dateObj.getFullYear(),
+ dateString = (element.getAttribute('data-format') || $.fn.validatr.defaultOptions.dateFormat).replace('mm', month).replace('yyyy', year).replace('dd', date);
+
+ return dateString;
+ }
+
+ return {
+ formatISODate: formatISODate,
+ parseDate: parseDate,
+ parseISODate: parseISODate
+ };
+ }()),
+
+ Tests = (function () {
+ var rules = {
+ color: /^#[0-9A-F]{6}$/i,
+ email: /^[a-zA-Z0-9.!#$%&’*+\/=?\^_`{|}~\-]+@[a-zA-Z0-9\-]+(?:\.[a-zA-Z0-9\-]+)*$/,
+ isoDate: /^(\d{4})-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$/,
+ number: /^-?\d*\.?\d*$/,
+ time: /^([01][0-9]|2[0-3])(:([0-5][0-9])){2}$/,
+ url: /^\s*https?:\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?\s*$/
},
- range: function (element) {
- return this.number(element);
+ utils = {
+ boxes: /checkbox|radio/i,
+ spaces: /,\s*/
},
- required: function (element) {
- if (Rules.boxes.test(element.type)) {
- return this[element.type](element);
+ minMax = function (value, min, max, step, type) {
+ var result = true,
+ msg = $.validatr.messages.range.base,
+ minString = min,
+ maxString = max;
+
+ if (type === 'date') {
+ minString = min && Format.formatISODate(min, this);
+ maxString = max && Format.formatISODate(max, this);
}
- return {
- valid: !!element.value.length,
- message: element.nodeName.toLowerCase() === 'select' ? $.validatr.messages.select : $.validatr.messages.required
- };
- },
+ if (value !== false) {
+ if (step !== false) {
+ result = step === 'any' ? true : (value - min) % step === 0;
+ msg = $.validatr.messages.range.invalid;
+ }
- time: function (element) {
- return {
- valid: Rules.time.test(element.value),
- message: $.validatr.messages.time
- };
- },
+ if (result) {
+ if (min !== false && max !== false) {
+ result = value >= min && value <= max;
+ msg = $.validatr.messages.range.overUnder;
+ } else if (min !== false) {
+ result = value >= min;
+ msg = $.validatr.messages.range.overflow;
+ } else if (max !== false) {
+ result = value <= max;
+ msg = $.validatr.messages.range.underflow;
+ }
+ }
+ }
- url: function (element) {
return {
- valid: Rules.url.test(element.value),
- message: $.validatr.messages.url
- };
- }
- },
+ valid: value !== false && result,
+ message: msg.replace('{{type}}', type).replace('{{min}}', minString).replace('{{max}}', maxString)
+ };
+ };
+
+ return {
+ checkbox: function (element) {
+ return {
+ valid: element.checked,
+ message: $.validatr.messages.checkbox
+ };
+ },
+
+ color: function (element) {
+ return {
+ valid: rules.color.test(element.value),
+ message: $.validatr.messages.color
+ };
+ },
+
+ date: function (element) {
+ var $element = $(element),
+ value = Support.inputtypes.date ? Format.parseISODate(element.value) : Format.parseDate(element),
+ min = $element.attr('min') ? Format.parseISODate($element.attr('min')) : false,
+ max = $element.attr('max') ? Format.parseISODate($element.attr('max')) : false,
+ step = false;
+
+ return minMax.call(element, value, min, max, step, 'date');
+ },
+
+ email: function (element) {
+ var valid = true,
+ msg = $.validatr.messages.email.single,
+ multiple = Support.attributes.multiple ? element.multiple : $(element).is('[multiple]');
+
+ if (multiple) {
+ var values = element.value.split(utils.spaces);
+
+ $.each(values, function (i, value) {
+ if (!rules.email.test(value)) {
+ valid = false;
+ msg = $.validatr.messages.email.multiple;
+ return;
+ }
+ });
+ } else {
+ valid = rules.email.test(element.value);
+ }
+
+ return {
+ valid: valid,
+ message: msg
+ };
+ },
+
+ number: function (element) {
+ var value = element.value.replace(',', ''),
+ num = rules.number.test(value) ? parseFloat(value) : false,
+ min = rules.number.test( element.getAttribute('min') ) ? parseFloat( element.getAttribute('min') ) : false,
+ max = rules.number.test( element.getAttribute('max') ) ? parseFloat( element.getAttribute('max') ) : false,
+ step = rules.number.test( element.getAttribute('step') ) ? parseFloat( element.getAttribute('step') ) : element.getAttribute('step') === 'any' ? 'any' : false;
+
+ if (step === false || step <= 0) {
+ step = 1;
+ }
+
+ return minMax.call(element, value, min, max, step, 'number');
+ },
- CustomTests = {
- as: function (element) {
+ pattern: function (element) {
+ return {
+ valid: new RegExp(element.getAttribute('pattern')).test(element.value),
+ message: $.validatr.messages.pattern
+ };
+ },
+
+ radio: function (element) {
+ return {
+ valid: $(document.getElementsByName(element.name)).is(':checked'),
+ message: $.validatr.messages.radio
+ };
+ },
+
+ range: function (element) {
+ return this.number(element);
+ },
+
+ required: function (element) {
+ if (utils.boxes.test(element.type)) {
+ return this[element.type](element);
+ }
+
+ return {
+ valid: !!element.value.length,
+ message: element.nodeName.toLowerCase() === 'select' ? $.validatr.messages.select : $.validatr.messages.required
+ };
+ },
+
+ time: function (element) {
+ return {
+ valid: rules.time.test(element.value),
+ message: $.validatr.messages.time
+ };
+ },
+
+ url: function (element) {
+ return {
+ valid: rules.url.test(element.value),
+ message: $.validatr.messages.url
+ };
+ }
+ };
+ }()),
+
+ CustomTests = (function () {
+ function as(element) {
if (element.type !== 'text') {
throw new Error('element must have a type of text');
}
@@ -240,16 +355,16 @@
if (Tests[type]) {
return Tests[type](element);
}
- },
+ }
- match: function (element) {
- var match = element.getAttribute('data-match'),
- source = document.getElementById(match) || document.getElementsByName(match)[0];
+ function match(element) {
+ var value = element.getAttribute('data-match'),
+ source = document.getElementById(value) || document.getElementsByName(value)[0];
if (!source) {
return {
valid: false,
- message: "'" + match + "' can not be found"
+ message: "'" + value + "' can not be found"
};
}
@@ -266,9 +381,21 @@
message: "'" + element.name + "' does not equal '" + source.name +"'"
};
}
+
+ return {
+ as: as,
+ match: match
+ };
+ }()),
+
+ filters = {
+ boxes: /checkbox|radio/i,
+ leftright: /left|right/i,
+ notInput: /select|textarea/i,
+ topbottom: /top|bottom/i
},
- KeyCodes = [
+ keyCodes = [
16, // shift
17, // control
18, // alt
@@ -282,111 +409,6 @@
39 //right arrow
],
- indexOf = function (array, value) {
- var index = -1,
- length = array ? array.length : 0;
-
-
- while (++index < length) {
- if (array[index] === value) {
- return index;
- }
- }
-
- return -1;
- },
-
- parseDate = function (element) {
- var format = element.getAttribute('data-format') || $.fn.validatr.defualtOptions.dateFormat,
- split = format.split(Rules.separatorsNoGroup),
- dateSplit = element.value.split(Rules.separatorsNoGroup),
- isoSplit = 'yyyy-mm-dd'.split('-'),
- rule = format.replace(Rules.separators, '\\$1')
- .replace('yyyy', Rules.date.yyyy)
- .replace('mm', Rules.date.mm)
- .replace('dd', Rules.date.dd),
- index = -1,
- length = isoSplit.length,
- iso = [];
-
- rule = new RegExp(rule);
- if (!rule.test(element.value)) {
- return false;
- }
-
- while (++index < length) {
- iso[index] = dateSplit[ indexOf(split, isoSplit[index]) ];
- }
-
- return parseISODate(iso.join('-'));
- },
-
- parseISODate = function (dateString) {
- if (!Rules.isoDate.test(dateString)) {
- return false;
- }
-
- var date = Rules.isoDate.exec(dateString);
- return new Date(parseInt(date[1], 10), parseInt(date[2], 10) - 1, parseInt(date[3], 10));
- },
-
- formatISODate = function (dateObj, element) {
- function pad(n) {
- return n < 10 ? '0' + n : n;
- }
-
- var date = pad(dateObj.getDate()),
- month = pad(dateObj.getMonth() + 1),
- year = dateObj.getFullYear(),
- dateString = (element.getAttribute('data-format') || $.fn.validatr.defualtOptions.dateFormat).replace('mm', month).replace('yyyy', year).replace('dd', date);
-
- return dateString;
- },
-
- minMax = function (value, min, max, step, type) {
- var result = true,
- msg = $.validatr.messages.range.base,
- minString = min,
- maxString = max;
-
- if (type === 'date') {
- minString = min && formatISODate(min, this);
- maxString = max && formatISODate(max, this);
- }
-
- if (value !== false) {
- if (step !== false) {
- result = step === 'any' ? true : (value - min) % step === 0;
- msg = $.validatr.messages.range.invalid;
- }
-
- if (result) {
- if (min !== false && max !== false) {
- result = value >= min && value <= max;
- msg = $.validatr.messages.range.overUnder;
- } else if (min !== false) {
- result = value >= min;
- msg = $.validatr.messages.range.overflow;
- } else if (max !== false) {
- result = value <= max;
- msg = $.validatr.messages.range.underflow;
- }
- }
- }
-
- return {
- valid: value !== false && result,
- message: msg.replace('{{type}}', type).replace('{{min}}', minString).replace('{{max}}', maxString)
- };
- },
-
- getNode = function (element) {
- if (element instanceof jQuery) {
- element = element[0];
- }
- return element;
- },
-
theme = {
bootstrap: 'alert alert-error',
jqueryui: 'ui-state-error ui-corner-all'
@@ -398,7 +420,6 @@
border: '1px solid #e4a6af',
padding: '2px 6px',
borderRadius: '2px'
-
},
supressError = false,
@@ -422,8 +443,8 @@
},
getElements: function (form) {
- if (this.elements) {
- return this.elements;
+ if (this.formElements) {
+ return this.formElements;
}
var elements = $(form).map(function () {
@@ -444,22 +465,22 @@
}
supressError = true;
- var valid = validateElement(getNode(element));
+ var valid = validateElement(element[0] || element);
supressError = false;
return valid;
},
validateForm: function (form) {
- var element = this.el || getNode(form),
+ var element = this.el || (form instanceof jQuery ? form[0] : form),
valid;
if (element.nodeName.toLowerCase() !== 'form') {
throw new Error('you must pass a form to this method');
}
supressError = true;
- valid = validateForm(this.elements || this.getElements(element));
+ valid = validateForm(this.formElements || this.getElements(element));
supressError = false;
return valid;
@@ -479,7 +500,7 @@
this.isSubmit = false;
this.firstError = false;
- this.options = $.extend({}, $.fn.validatr.defualtOptions, options);
+ this.options = $.extend({}, $.fn.validatr.defaultOptions, options);
this.template = (function (options) {
var template = $(options.template).addClass('validatr-message');
@@ -493,7 +514,19 @@
return template[0].outerHTML;
}(this.options));
- this.elements = this.getElements(this.el)
+ this.option = function (key, value) {
+ if (!arguments.length) {
+ return $.extend({}, this.options);
+ }
+
+ if (value === undefined) {
+ return this.options[key] === undefined ? null : this.options[key];
+ }
+
+ this.options[key] = value;
+ };
+
+ this.formElements = this.getElements(this.el)
.on('valid.' + 'validatr', $.proxy(validElement, this))
.on('invalid.' + 'validatr', $.proxy(invalidElement, this));
@@ -505,7 +538,7 @@
function bindElements() {
/*jshint validthis:true */
- this.elements.on({
+ this.formElements.on({
'focus.validatrelement': bindEvents,
'blur.validatrelement': unbindEvents
});
@@ -518,7 +551,7 @@
function unbindElements() {
/*jshint validthis:true */
- this.elements.off('.validatrelement');
+ this.formElements.off('.validatrelement');
}
function bindEvents (e) {
@@ -538,7 +571,7 @@
validateElement(target);
},
'keyup.validatrinput': function (event) {
- if (target.value.length && indexOf(KeyCodes, event.keyCode) === -1) {
+ if (target.value.length && $.inArray(keyCodes, event.keyCode) === -1) {
validateElement(target);
}
}
@@ -558,7 +591,7 @@
}
var $element = $(element),
- type = Rules.notInput.test(element.nodeName) ? element.nodeName.toLowerCase() : element.getAttribute('type'),
+ type = filters.notInput.test(element.nodeName) ? element.nodeName.toLowerCase() : element.getAttribute('type'),
required = Support.attributes.required ? element.required : $(element).is('[required]'),
check = {
valid: true,
@@ -573,7 +606,7 @@
check = Tests.required(element);
}
- if (check.valid && element.value.length && !Rules.boxes.test(type)) {
+ if (check.valid && element.value.length && !filters.boxes.test(type)) {
if (element.pattern) {
type = 'pattern';
}
@@ -623,7 +656,7 @@
this.isSubmit = true;
resetForm.call(this);
- var valid = validateForm(this.elements);
+ var valid = validateForm(this.formElements);
if (valid) {
return this.options.valid.call(this.el, this.el);
@@ -640,7 +673,7 @@
unbindElements.call(this);
this.firstError = false;
- this.elements.next('.validatr-message').remove();
+ this.formElements.next('.validatr-message').remove();
}
function invalidElement(e) {
@@ -687,7 +720,7 @@
var offset = $target.offset(),
location = $target[0].getAttribute('data-location') || this.options.location;
- if (Rules.topbottom.test(location)) {
+ if (filters.topbottom.test(location)) {
error.offset({left: offset.left});
if (location === 'top') {
@@ -697,7 +730,7 @@
if (location === 'bottom') {
error.offset({top: offset.top + error.outerHeight()});
}
- } else if (Rules.leftright.test(location)) {
+ } else if (filters.leftright.test(location)) {
error.offset({top: (offset.top + $target.outerHeight() / 2) - (error.outerHeight() / 2)});
if (location === 'left') {
@@ -753,7 +786,7 @@
return returnValue;
};
- $.fn.validatr.defualtOptions = {
+ $.fn.validatr.defaultOptions = {
dateFormat: 'yyyy-mm-dd',
location: 'right',
position: position,
@@ -797,14 +830,12 @@
this.Support = Support;
this.Tests = Tests;
this.CustomTests = CustomTests;
- this.parseDate = parseDate;
- this.parseISODate = parseISODate;
- this.formatISODate = formatISODate;
+ this.Format = Format;
};
// Custom selector.
$.expr[':'].validatr = function(elem) {
- return elem.textContent.indexOf('validatr') >= 0;
+ return !!$.data(elem, 'validatr');
};
}(this, this.document, jQuery));
View
4 dist/validatr.min.js
@@ -1,4 +1,4 @@
-/*! Validatr - v0.4.2 - 2013-03-03
+/*! Validatr - v0.5.0 - 2013-03-11
* http://jaymorrow.github.com/validatr/
* Copyright (c) 2013 Jay Morrow; Licensed MIT */
-(function(t,e,a,r){"use strict";function i(t,e){if(this.el=t,this.$el=a(t),!this.$el.length||!this.$el.is("form"))throw Error("validatr needs a form to work.");this.isSubmit=!1,this.firstError=!1,this.options=a.extend({},a.fn.validatr.defualtOptions,e),this.template=function(t){var e=a(t.template).addClass("validatr-message");return t.theme.length?e.addClass(P[t.theme]||t.theme):e.css(C),e[0].outerHTML}(this.options),this.elements=this.getElements(this.el).on("valid.validatr",a.proxy(f,this)).on("invalid.validatr",a.proxy(p,this)),this.el.noValidate=!0,this.$el.on("submit.validatr",a.proxy(m,this)),this.$el.on("reset.validatr",a.proxy(c,this))}function s(){this.elements.on({"focus.validatrelement":l,"blur.validatrelement":o}),a("input[type=radio], input[type=checkbox]").on("click.validatrelement",function(t){u(t.target)})}function n(){this.elements.off(".validatrelement")}function l(t){var e=t.target,r=e;"select"===e.nodeName.toLowerCase()&&r.on("change.validatrinput",function(){setTimeout(function(){u(e)},1)}),a(e).on({"blur.validatrinput":function(){u(e)},"keyup.validatrinput":function(t){e.value.length&&-1===w(x,t.keyCode)&&u(e)}})}function o(t){a(t.target).off(".validatrinput")}function u(t){if("radio"===t.type){var r=a(e.getElementsByName(t.name)).filter("[required]");r.length&&(t=r[0])}var i=a(t),s=g.notInput.test(t.nodeName)?t.nodeName.toLowerCase():t.getAttribute("type"),n=v.attributes.required?t.required:a(t).is("[required]"),l={valid:!0,message:""};if(v.inputtypes[s]?(l.valid=t.validity.valid,l.message=t.validationMessage):(n&&(l=y.required(t)),l.valid&&t.value.length&&!g.boxes.test(s)&&(t.pattern&&(s="pattern"),y[s]&&(l=y[s](t)))),l.valid)for(var o in b)if(b.hasOwnProperty(o)&&i.is("[data-"+o+"]")&&(l=b[o](t),!l.valid))break;return l.valid?(i.trigger("valid"),!0):(a.data(t,"validationMessage",l.message),i.trigger("invalid"),!1)}function d(t){var e=!0;return t.each(function(t,a){u(a)||(e=!1)}),e}function m(){this.isSubmit=!0,c.call(this);var t=d(this.elements);return t?this.options.valid.call(this.el,this.el):(s.call(this),this.isSubmit=!1,t)}function c(){n.call(this),this.firstError=!1,this.elements.next(".validatr-message").remove()}function p(t){if(!S){t.preventDefault();var e=t.target,i=a(e),s=this.options,n=e.getAttribute("data-message")||a.data(e,"validationMessage"),l=a(this.template.replace("{{message}}",n));return this.isSubmit&&!this.firstError?(this.firstError=i.after(l),s.position.call(this,l,i),r):((!this.isSubmit||s.showall)&&(f(t),i.after(l),s.position.call(this,l,i)),r)}}function f(t){S||a(t.target).next(".validatr-message").remove()}function h(t,e){t.css("position","absolute");var a=e.offset(),r=e[0].getAttribute("data-location")||this.options.location;g.topbottom.test(r)?(t.offset({left:a.left}),"top"===r&&t.offset({top:a.top-t.outerHeight()-2}),"bottom"===r&&t.offset({top:a.top+t.outerHeight()})):g.leftright.test(r)&&(t.offset({top:a.top+e.outerHeight()/2-t.outerHeight()/2}),"left"===r&&t.offset({left:a.left-t.outerWidth()-2}),"right"===r&&t.offset({left:a.left+e.outerWidth()+2}))}var v=function(){var t,a={},i=e.documentElement,s=e.createElement("input"),n=e.createElement("select"),l=e.createElement("textarea"),o=":)",u={},d={};return a.attributes=function(t){for(var e=0,a=t.length;a>e;e++)d[t[e]]=!!(t[e]in s);return d}("max min multiple pattern required step".split(" ")),a.inputtypes=function(t){for(var a,n,l,d=0,m=t.length;m>d;d++)s.setAttribute("type",n=t[d]),a="text"!==s.type,a&&(s.value=o,s.style.cssText="position:absolute;visibility:hidden;",/^range$/.test(n)&&s.style.WebkitAppearance!==r?(i.appendChild(s),l=e.defaultView,a=l.getComputedStyle&&"textfield"!==l.getComputedStyle(s,null).WebkitAppearance&&0!==s.offsetHeight,i.removeChild(s)):/^(search|tel)$/.test(n)||(a=/^(url|email)$/.test(n)?s.checkValidity&&s.checkValidity()===!1:s.value!==o)),u[t[d]]=!!a;return u}("search tel url email datetime date month week time datetime-local number range color".split(" ")),function(r){for(var i=0,n=r.length;n>i;i++){t=s;try{t.setAttribute("type",r[i])}catch(l){t=e.createElement('<input type="'+r[i]+'">')}t.style.cssText="position:absolute;visibility:hidden;",a.inputtypes[r[i]]=!!t.checkValidity}}("text password radio checkbox".split(" ")),a.inputtypes.select=!!n.checkValidity,a.inputtypes.textarea=!!l.checkValidity,s=null,t=null,n=null,l=null,a}(),g={boxes:/checkbox|radio/i,color:/^#[0-9A-F]{6}$/i,date:{dd:"(0[1-9]|[12][0-9]|3[01])",mm:"(0[1-9]|1[012])",yyyy:"(\\d{4})"},email:/^[a-zA-Z0-9.!#$%&’*+\/=?\^_`{|}~\-]+@[a-zA-Z0-9\-]+(?:\.[a-zA-Z0-9\-]+)*$/,isoDate:/^(\d{4})-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$/,isoMonth:/^(\d{4})-(0[1-9]|1[012])$/,leftright:/left|right/i,notInput:/select|textarea/i,number:/^-?\d*\.?\d*$/,separators:/(\/|\-|\.)/g,separatorsNoGroup:/\/|\-|\./g,spaces:/,\s*/,time:/^([01][0-9]|2[0-3])(:([0-5][0-9])){2}$/,topbottom:/top|bottom/i,url:/^\s*https?:\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?\s*$/},y={checkbox:function(t){return{valid:t.checked,message:a.validatr.messages.checkbox}},color:function(t){return{valid:g.color.test(t.value),message:a.validatr.messages.color}},date:function(t){var e=a(t),r=v.inputtypes.date?k(t.value):E(t),i=e.attr("min")?k(e.attr("min")):!1,s=e.attr("max")?k(e.attr("max")):!1,n=!1;return q.call(t,r,i,s,n,"date")},email:function(t){var e=!0,i=a.validatr.messages.email.single,s=v.attributes.multiple?t.multiple:a(t).is("[multiple]");if(s){var n=t.value.split(g.spaces);a.each(n,function(t,s){return g.email.test(s)?r:(e=!1,i=a.validatr.messages.email.multiple,r)})}else e=g.email.test(t.value);return{valid:e,message:i}},number:function(t){var e=t.value.replace(",",""),a=(g.number.test(e)?parseFloat(e):!1,g.number.test(t.getAttribute("min"))?parseFloat(t.getAttribute("min")):!1),r=g.number.test(t.getAttribute("max"))?parseFloat(t.getAttribute("max")):!1,i=g.number.test(t.getAttribute("step"))?parseFloat(t.getAttribute("step")):"any"===t.getAttribute("step")?"any":!1;return(i===!1||0>=i)&&(i=1),q.call(t,e,a,r,i,"number")},pattern:function(t){return{valid:RegExp(t.getAttribute("pattern")).test(t.value),message:a.validatr.messages.pattern}},radio:function(t){return{valid:a(e.getElementsByName(t.name)).is(":checked"),message:a.validatr.messages.radio}},range:function(t){return this.number(t)},required:function(t){return g.boxes.test(t.type)?this[t.type](t):{valid:!!t.value.length,message:"select"===t.nodeName.toLowerCase()?a.validatr.messages.select:a.validatr.messages.required}},time:function(t){return{valid:g.time.test(t.value),message:a.validatr.messages.time}},url:function(t){return{valid:g.url.test(t.value),message:a.validatr.messages.url}}},b={as:function(t){if("text"!==t.type)throw Error("element must have a type of text");var e=t.getAttribute("data-as");return y[e]?y[e](t):r},match:function(t){var r=t.getAttribute("data-match"),i=e.getElementById(r)||e.getElementsByName(r)[0];return i?(a(i).off("valid.validatrinput").on("valid.validatrinput",function(){t.value===i.value&&u(t)}),{valid:t.value===i.value,message:"'"+t.name+"' does not equal '"+i.name+"'"}):{valid:!1,message:"'"+r+"' can not be found"}}},x=[16,17,18,19,20,33,34,35,36,37,39],w=function(t,e){for(var a=-1,r=t?t.length:0;r>++a;)if(t[a]===e)return a;return-1},E=function(t){var e=t.getAttribute("data-format")||a.fn.validatr.defualtOptions.dateFormat,r=e.split(g.separatorsNoGroup),i=t.value.split(g.separatorsNoGroup),s="yyyy-mm-dd".split("-"),n=e.replace(g.separators,"\\$1").replace("yyyy",g.date.yyyy).replace("mm",g.date.mm).replace("dd",g.date.dd),l=-1,o=s.length,u=[];if(n=RegExp(n),!n.test(t.value))return!1;for(;o>++l;)u[l]=i[w(r,s[l])];return k(u.join("-"))},k=function(t){if(!g.isoDate.test(t))return!1;var e=g.isoDate.exec(t);return new Date(parseInt(e[1],10),parseInt(e[2],10)-1,parseInt(e[3],10))},A=function(t,e){function r(t){return 10>t?"0"+t:t}var i=r(t.getDate()),s=r(t.getMonth()+1),n=t.getFullYear(),l=(e.getAttribute("data-format")||a.fn.validatr.defualtOptions.dateFormat).replace("mm",s).replace("yyyy",n).replace("dd",i);return l},q=function(t,e,r,i,s){var n=!0,l=a.validatr.messages.range.base,o=e,u=r;return"date"===s&&(o=e&&A(e,this),u=r&&A(r,this)),t!==!1&&(i!==!1&&(n="any"===i?!0:0===(t-e)%i,l=a.validatr.messages.range.invalid),n&&(e!==!1&&r!==!1?(n=t>=e&&r>=t,l=a.validatr.messages.range.overUnder):e!==!1?(n=t>=e,l=a.validatr.messages.range.overflow):r!==!1&&(n=r>=t,l=a.validatr.messages.range.underflow))),{valid:t!==!1&&n,message:l.replace("{{type}}",s).replace("{{min}}",o).replace("{{max}}",u)}},$=function(t){return t instanceof jQuery&&(t=t[0]),t},P={bootstrap:"alert alert-error",jqueryui:"ui-state-error ui-corner-all"},C={color:"#f0444d",backgroundColor:"#ffcbcb",border:"1px solid #e4a6af",padding:"2px 6px",borderRadius:"2px"},S=!1,F=function(){};F.prototype={addTest:function(t){var e="string"!=typeof t,r=Array.prototype.slice.call(arguments,1)[0];if(e)a.extend(b,t);else{if(!r)throw Error("You must include a callback function");b[t]=r}},getElements:function(t){if(this.elements)return this.elements;var e=a(t).map(function(){return a.makeArray(this.elements)}).not("fieldset, button, input[type=submit], input[type=button], input[type=reset]");return t.id&&(e=e.add(a('[form="'+t.id+'"]'))),e},validateElement:function(t){if(!t)throw Error("method requires an element");S=!0;var e=u($(t));return S=!1,e},validateForm:function(t){var e,a=this.el||$(t);if("form"!==a.nodeName.toLowerCase())throw Error("you must pass a form to this method");return S=!0,e=d(this.elements||this.getElements(a)),S=!1,e}},a.fn.validatr=function(t){var e,s="string"==typeof t,n=Array.prototype.slice.call(arguments,1),l=this;if(s)this.each(function(){var i;if(e=a.data(this,"validatr"),!e)throw Error("cannot call methods on validatr prior to initialization; attempted to call method '"+t+"'");if(!a.isFunction(e[t]))throw Error("no such method '"+t+"' for validatr instance");return i=e[t].apply(e,n),i!==e&&i!==r?(l=i&&i.jquery?l.pushStack(i.get()):i,!1):r});else{var o;this.each(function(){e=a.data(this,"validatr"),e||(o=new F,i.call(o,this,t||{}),a.data(this,"validatr",o))})}return l},a.fn.validatr.defualtOptions={dateFormat:"yyyy-mm-dd",location:"right",position:h,showall:!1,template:"<div>{{message}}</div>",theme:"",valid:a.noop},a.validatr=new F,a.validatr.messages={checkbox:"Please check this box if you want to proceed.",color:"Please enter a color in the format #xxxxxx",email:{single:"Please enter an email address.",multiple:"Please enter a comma separated list of email addresses."},pattern:"Please match the requested format.",radio:"Please select one of these options.",range:{base:"Please enter a {{type}}",overflow:"Please enter a {{type}} greater than or equal to {{min}}.",overUnder:"Please enter a {{type}} greater than or equal to {{min}}<br> and less than or equal to {{max}}.",invalid:"Invalid {{type}}",underflow:"Please enter a {{type}} less than or equal to {{max}}."},required:"Please fill out this field.",select:"Please select an item in the list.",time:"Please enter a time in the format hh:mm:ss",url:"Please enter a url."},a.validatr.debug=function(){if(!QUnit)throw Error("QUnit is required for debugging");this.Support=v,this.Tests=y,this.CustomTests=b,this.parseDate=E,this.parseISODate=k,this.formatISODate=A},a.expr[":"].validatr=function(t){return t.textContent.indexOf("validatr")>=0}})(this,this.document,jQuery);
+(function(t,e,a,r){"use strict";function i(t,e){if(this.el=t,this.$el=a(t),!this.$el.length||!this.$el.is("form"))throw Error("validatr needs a form to work.");this.isSubmit=!1,this.firstError=!1,this.options=a.extend({},a.fn.validatr.defaultOptions,e),this.template=function(t){var e=a(t.template).addClass("validatr-message");return t.theme.length?e.addClass(w[t.theme]||t.theme):e.css(k),e[0].outerHTML}(this.options),this.option=function(t,e){return arguments.length?e===r?this.options[t]===r?null:this.options[t]:(this.options[t]=e,r):a.extend({},this.options)},this.formElements=this.getElements(this.el).on("valid.validatr",a.proxy(f,this)).on("invalid.validatr",a.proxy(c,this)),this.el.noValidate=!0,this.$el.on("submit.validatr",a.proxy(m,this)),this.$el.on("reset.validatr",a.proxy(p,this))}function s(){this.formElements.on({"focus.validatrelement":o,"blur.validatrelement":l}),a("input[type=radio], input[type=checkbox]").on("click.validatrelement",function(t){u(t.target)})}function n(){this.formElements.off(".validatrelement")}function o(t){var e=t.target,r=e;"select"===e.nodeName.toLowerCase()&&r.on("change.validatrinput",function(){setTimeout(function(){u(e)},1)}),a(e).on({"blur.validatrinput":function(){u(e)},"keyup.validatrinput":function(t){e.value.length&&-1===a.inArray(E,t.keyCode)&&u(e)}})}function l(t){a(t.target).off(".validatrinput")}function u(t){if("radio"===t.type){var r=a(e.getElementsByName(t.name)).filter("[required]");r.length&&(t=r[0])}var i=a(t),s=x.notInput.test(t.nodeName)?t.nodeName.toLowerCase():t.getAttribute("type"),n=v.attributes.required?t.required:a(t).is("[required]"),o={valid:!0,message:""};if(v.inputtypes[s]?(o.valid=t.validity.valid,o.message=t.validationMessage):(n&&(o=y.required(t)),o.valid&&t.value.length&&!x.boxes.test(s)&&(t.pattern&&(s="pattern"),y[s]&&(o=y[s](t)))),o.valid)for(var l in b)if(b.hasOwnProperty(l)&&i.is("[data-"+l+"]")&&(o=b[l](t),!o.valid))break;return o.valid?(i.trigger("valid"),!0):(a.data(t,"validationMessage",o.message),i.trigger("invalid"),!1)}function d(t){var e=!0;return t.each(function(t,a){u(a)||(e=!1)}),e}function m(){this.isSubmit=!0,p.call(this);var t=d(this.formElements);return t?this.options.valid.call(this.el,this.el):(s.call(this),this.isSubmit=!1,t)}function p(){n.call(this),this.firstError=!1,this.formElements.next(".validatr-message").remove()}function c(t){if(!A){t.preventDefault();var e=t.target,i=a(e),s=this.options,n=e.getAttribute("data-message")||a.data(e,"validationMessage"),o=a(this.template.replace("{{message}}",n));return this.isSubmit&&!this.firstError?(this.firstError=i.after(o),s.position.call(this,o,i),r):((!this.isSubmit||s.showall)&&(f(t),i.after(o),s.position.call(this,o,i)),r)}}function f(t){A||a(t.target).next(".validatr-message").remove()}function h(t,e){t.css("position","absolute");var a=e.offset(),r=e[0].getAttribute("data-location")||this.options.location;x.topbottom.test(r)?(t.offset({left:a.left}),"top"===r&&t.offset({top:a.top-t.outerHeight()-2}),"bottom"===r&&t.offset({top:a.top+t.outerHeight()})):x.leftright.test(r)&&(t.offset({top:a.top+e.outerHeight()/2-t.outerHeight()/2}),"left"===r&&t.offset({left:a.left-t.outerWidth()-2}),"right"===r&&t.offset({left:a.left+e.outerWidth()+2}))}var v=function(){var t,a={},i=e.documentElement,s=e.createElement("input"),n=e.createElement("select"),o=e.createElement("textarea"),l=":)",u={},d={};return a.attributes=function(t){for(var e=0,a=t.length;a>e;e++)d[t[e]]=!!(t[e]in s);return d}("max min multiple pattern required step".split(" ")),a.inputtypes=function(t){for(var a,n,o,d=0,m=t.length;m>d;d++)s.setAttribute("type",n=t[d]),a="text"!==s.type,a&&(s.value=l,s.style.cssText="position:absolute;visibility:hidden;",/^range$/.test(n)&&s.style.WebkitAppearance!==r?(i.appendChild(s),o=e.defaultView,a=o.getComputedStyle&&"textfield"!==o.getComputedStyle(s,null).WebkitAppearance&&0!==s.offsetHeight,i.removeChild(s)):/^(search|tel)$/.test(n)||(a=/^(url|email)$/.test(n)?s.checkValidity&&s.checkValidity()===!1:s.value!==l)),u[t[d]]=!!a;return u}("search tel url email datetime date month week time datetime-local number range color".split(" ")),function(r){for(var i=0,n=r.length;n>i;i++){t=s;try{t.setAttribute("type",r[i])}catch(o){t=e.createElement('<input type="'+r[i]+'">')}t.style.cssText="position:absolute;visibility:hidden;",a.inputtypes[r[i]]=!!t.checkValidity}}("text password radio checkbox".split(" ")),a.inputtypes.select=!!n.checkValidity,a.inputtypes.textarea=!!o.checkValidity,s=null,t=null,n=null,o=null,a}(),g=function(){function t(t,e){for(var a=-1,r=t?t.length:0;r>++a;)if(t[a]===e)return a;return-1}function e(e){var i=e.getAttribute("data-format")||a.fn.validatr.defaultOptions.dateFormat,s=i.split(n.separatorsNoGroup),o=e.value.split(n.separatorsNoGroup),l="yyyy-mm-dd".split("-"),u=i.replace(n.separators,"\\$1").replace("yyyy",n.dateParts.yyyy).replace("mm",n.dateParts.mm).replace("dd",n.dateParts.dd),d=-1,m=l.length,p=[];if(u=RegExp(u),!u.test(e.value))return!1;for(;m>++d;)p[d]=o[t(s,l[d])];return r(p.join("-"))}function r(t){if(!s.isoDate.test(t))return!1;var e=s.isoDate.exec(t);return new Date(parseInt(e[1],10),parseInt(e[2],10)-1,parseInt(e[3],10))}function i(t,e){function r(t){return 10>t?"0"+t:t}var i=r(t.getDate()),s=r(t.getMonth()+1),n=t.getFullYear(),o=(e.getAttribute("data-format")||a.fn.validatr.defaultOptions.dateFormat).replace("mm",s).replace("yyyy",n).replace("dd",i);return o}var s={isoDate:/^(\d{4})-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$/},n={separators:/(\/|\-|\.)/g,separatorsNoGroup:/\/|\-|\./g,dateParts:{dd:"(0[1-9]|[12][0-9]|3[01])",mm:"(0[1-9]|1[012])",yyyy:"(\\d{4})"}};return{formatISODate:i,parseDate:e,parseISODate:r}}(),y=function(){var t={color:/^#[0-9A-F]{6}$/i,email:/^[a-zA-Z0-9.!#$%&’*+\/=?\^_`{|}~\-]+@[a-zA-Z0-9\-]+(?:\.[a-zA-Z0-9\-]+)*$/,isoDate:/^(\d{4})-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$/,number:/^-?\d*\.?\d*$/,time:/^([01][0-9]|2[0-3])(:([0-5][0-9])){2}$/,url:/^\s*https?:\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?\s*$/},i={boxes:/checkbox|radio/i,spaces:/,\s*/},s=function(t,e,r,i,s){var n=!0,o=a.validatr.messages.range.base,l=e,u=r;return"date"===s&&(l=e&&g.formatISODate(e,this),u=r&&g.formatISODate(r,this)),t!==!1&&(i!==!1&&(n="any"===i?!0:0===(t-e)%i,o=a.validatr.messages.range.invalid),n&&(e!==!1&&r!==!1?(n=t>=e&&r>=t,o=a.validatr.messages.range.overUnder):e!==!1?(n=t>=e,o=a.validatr.messages.range.overflow):r!==!1&&(n=r>=t,o=a.validatr.messages.range.underflow))),{valid:t!==!1&&n,message:o.replace("{{type}}",s).replace("{{min}}",l).replace("{{max}}",u)}};return{checkbox:function(t){return{valid:t.checked,message:a.validatr.messages.checkbox}},color:function(e){return{valid:t.color.test(e.value),message:a.validatr.messages.color}},date:function(t){var e=a(t),r=v.inputtypes.date?g.parseISODate(t.value):g.parseDate(t),i=e.attr("min")?g.parseISODate(e.attr("min")):!1,n=e.attr("max")?g.parseISODate(e.attr("max")):!1,o=!1;return s.call(t,r,i,n,o,"date")},email:function(e){var s=!0,n=a.validatr.messages.email.single,o=v.attributes.multiple?e.multiple:a(e).is("[multiple]");if(o){var l=e.value.split(i.spaces);a.each(l,function(e,i){return t.email.test(i)?r:(s=!1,n=a.validatr.messages.email.multiple,r)})}else s=t.email.test(e.value);return{valid:s,message:n}},number:function(e){var a=e.value.replace(",",""),r=(t.number.test(a)?parseFloat(a):!1,t.number.test(e.getAttribute("min"))?parseFloat(e.getAttribute("min")):!1),i=t.number.test(e.getAttribute("max"))?parseFloat(e.getAttribute("max")):!1,n=t.number.test(e.getAttribute("step"))?parseFloat(e.getAttribute("step")):"any"===e.getAttribute("step")?"any":!1;return(n===!1||0>=n)&&(n=1),s.call(e,a,r,i,n,"number")},pattern:function(t){return{valid:RegExp(t.getAttribute("pattern")).test(t.value),message:a.validatr.messages.pattern}},radio:function(t){return{valid:a(e.getElementsByName(t.name)).is(":checked"),message:a.validatr.messages.radio}},range:function(t){return this.number(t)},required:function(t){return i.boxes.test(t.type)?this[t.type](t):{valid:!!t.value.length,message:"select"===t.nodeName.toLowerCase()?a.validatr.messages.select:a.validatr.messages.required}},time:function(e){return{valid:t.time.test(e.value),message:a.validatr.messages.time}},url:function(e){return{valid:t.url.test(e.value),message:a.validatr.messages.url}}}}(),b=function(){function t(t){if("text"!==t.type)throw Error("element must have a type of text");var e=t.getAttribute("data-as");return y[e]?y[e](t):r}function i(t){var r=t.getAttribute("data-match"),i=e.getElementById(r)||e.getElementsByName(r)[0];return i?(a(i).off("valid.validatrinput").on("valid.validatrinput",function(){t.value===i.value&&u(t)}),{valid:t.value===i.value,message:"'"+t.name+"' does not equal '"+i.name+"'"}):{valid:!1,message:"'"+r+"' can not be found"}}return{as:t,match:i}}(),x={boxes:/checkbox|radio/i,leftright:/left|right/i,notInput:/select|textarea/i,topbottom:/top|bottom/i},E=[16,17,18,19,20,33,34,35,36,37,39],w={bootstrap:"alert alert-error",jqueryui:"ui-state-error ui-corner-all"},k={color:"#f0444d",backgroundColor:"#ffcbcb",border:"1px solid #e4a6af",padding:"2px 6px",borderRadius:"2px"},A=!1,q=function(){};q.prototype={addTest:function(t){var e="string"!=typeof t,r=Array.prototype.slice.call(arguments,1)[0];if(e)a.extend(b,t);else{if(!r)throw Error("You must include a callback function");b[t]=r}},getElements:function(t){if(this.formElements)return this.formElements;var e=a(t).map(function(){return a.makeArray(this.elements)}).not("fieldset, button, input[type=submit], input[type=button], input[type=reset]");return t.id&&(e=e.add(a('[form="'+t.id+'"]'))),e},validateElement:function(t){if(!t)throw Error("method requires an element");A=!0;var e=u(t[0]||t);return A=!1,e},validateForm:function(t){var e,a=this.el||(t instanceof jQuery?t[0]:t);if("form"!==a.nodeName.toLowerCase())throw Error("you must pass a form to this method");return A=!0,e=d(this.formElements||this.getElements(a)),A=!1,e}},a.fn.validatr=function(t){var e,s="string"==typeof t,n=Array.prototype.slice.call(arguments,1),o=this;if(s)this.each(function(){var i;if(e=a.data(this,"validatr"),!e)throw Error("cannot call methods on validatr prior to initialization; attempted to call method '"+t+"'");if(!a.isFunction(e[t]))throw Error("no such method '"+t+"' for validatr instance");return i=e[t].apply(e,n),i!==e&&i!==r?(o=i&&i.jquery?o.pushStack(i.get()):i,!1):r});else{var l;this.each(function(){e=a.data(this,"validatr"),e||(l=new q,i.call(l,this,t||{}),a.data(this,"validatr",l))})}return o},a.fn.validatr.defaultOptions={dateFormat:"yyyy-mm-dd",location:"right",position:h,showall:!1,template:"<div>{{message}}</div>",theme:"",valid:a.noop},a.validatr=new q,a.validatr.messages={checkbox:"Please check this box if you want to proceed.",color:"Please enter a color in the format #xxxxxx",email:{single:"Please enter an email address.",multiple:"Please enter a comma separated list of email addresses."},pattern:"Please match the requested format.",radio:"Please select one of these options.",range:{base:"Please enter a {{type}}",overflow:"Please enter a {{type}} greater than or equal to {{min}}.",overUnder:"Please enter a {{type}} greater than or equal to {{min}}<br> and less than or equal to {{max}}.",invalid:"Invalid {{type}}",underflow:"Please enter a {{type}} less than or equal to {{max}}."},required:"Please fill out this field.",select:"Please select an item in the list.",time:"Please enter a time in the format hh:mm:ss",url:"Please enter a url."},a.validatr.debug=function(){if(!QUnit)throw Error("QUnit is required for debugging");this.Support=v,this.Tests=y,this.CustomTests=b,this.Format=g},a.expr[":"].validatr=function(t){return!!a.data(t,"validatr")}})(this,this.document,jQuery);
2 docs
@@ -1 +1 @@
-Subproject commit 7aeafac946c2e7152c96aef6546b41af3cfb30cd
+Subproject commit ea4b87f0dd88bfe5526b68cc25b1fcd06bbfee7d
View
1 src/index.html
@@ -21,7 +21,6 @@
width: 100px;
}
</style>
- <link rel="stylesheet" href="css/validatr.css">
<script src="../libs/jquery/jquery.js"></script>
<script src="js/validatr.js"></script>
<script>
View
23 src/js/l10n/en.js
@@ -0,0 +1,23 @@
+(function ($) {
+ $.extend($.validatr.messages, {
+ checkbox: 'Please check this box if you want to proceed.',
+ color: 'Please enter a color in the format #xxxxxx',
+ email: {
+ single: 'Please enter an email address.',
+ multiple: 'Please enter a comma separated list of email addresses.'
+ },
+ pattern: 'Please match the requested format.',
+ radio: 'Please select one of these options.',
+ range: {
+ base: 'Please enter a {{type}}',
+ overflow: 'Please enter a {{type}} greater than or equal to {{min}}.',
+ overUnder: 'Please enter a {{type}} greater than or equal to {{min}}<br> and less than or equal to {{max}}.',
+ invalid: 'Invalid {{type}}',
+ underflow: 'Please enter a {{type}} less than or equal to {{max}}.'
+ },
+ required: 'Please fill out this field.',
+ select: 'Please select an item in the list.',
+ time: 'Please enter a time in the format hh:mm:ss',
+ url: 'Please enter a url.'
+ });
+}(jQuery));
View
529 src/js/validatr.js
@@ -9,12 +9,12 @@
(function(window, document, $, undefined) {
"use strict";
- /*! Modernizr 2.6.2| MIT & BSD
+ /*! Inspired by Modernizr 2.6.2| MIT & BSD
* Build: http://modernizr.com/download/#-input-inputtypes
*/
var Support = (function() {
- var Modernizr = {},
+ var Support = {},
docElement = document.documentElement,
@@ -34,15 +34,15 @@
testElem;
- Modernizr.attributes = (function( props ) {
+ Support.attributes = (function( props ) {
for ( var i = 0, len = props.length; i < len; i++ ) {
attrs[ props[i] ] = !!(props[i] in inputElem);
}
return attrs;
})('max min multiple pattern required step'.split(' '));
- Modernizr.inputtypes = (function(props) {
+ Support.inputtypes = (function(props) {
for ( var i = 0, bool, inputElemType, defaultView, len = props.length; i < len; i++ ) {
inputElem.setAttribute('type', inputElemType = props[i]);
@@ -89,153 +89,268 @@
}
testElem.style.cssText = 'position:absolute;visibility:hidden;';
- Modernizr.inputtypes[ props[i] ] = !!testElem.checkValidity;
+ Support.inputtypes[ props[i] ] = !!testElem.checkValidity;
}
})('text password radio checkbox'.split(' '));
- Modernizr.inputtypes.select = !!selectElem.checkValidity;
- Modernizr.inputtypes.textarea = !!textareaElem.checkValidity;
+ Support.inputtypes.select = !!selectElem.checkValidity;
+ Support.inputtypes.textarea = !!textareaElem.checkValidity;
inputElem = null;
testElem = null;
selectElem = null;
textareaElem = null;
- return Modernizr;
+ return Support;
}()),
- Rules = {
- boxes: /checkbox|radio/i,
- color: /^#[0-9A-F]{6}$/i,
- date: {
- dd: '(0[1-9]|[12][0-9]|3[01])',
- mm: '(0[1-9]|1[012])',
- yyyy: '(\\d{4})'
+ Format = (function () {
+ var rules = {
+ isoDate: /^(\d{4})-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$/
},
- email: /^[a-zA-Z0-9.!#$%&’*+\/=?\^_`{|}~\-]+@[a-zA-Z0-9\-]+(?:\.[a-zA-Z0-9\-]+)*$/,
- isoDate: /^(\d{4})-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$/,
- isoMonth: /^(\d{4})-(0[1-9]|1[012])$/,
- leftright: /left|right/i,
- notInput: /select|textarea/i,
- number: /^-?\d*\.?\d*$/,
- separators: /(\/|\-|\.)/g,
- separatorsNoGroup: /\/|\-|\./g,
- spaces: /,\s*/,
- time: /^([01][0-9]|2[0-3])(:([0-5][0-9])){2}$/,
- topbottom: /top|bottom/i,
- url: /^\s*https?:\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?\s*$/
- },
- Tests = {
- checkbox: function (element) {
- return {
- valid: element.checked,
- message: $.validatr.messages.checkbox
- };
- },
+ utils = {
+ separators: /(\/|\-|\.)/g,
+ separatorsNoGroup: /\/|\-|\./g,
+ dateParts: {
+ dd: '(0[1-9]|[12][0-9]|3[01])',
+ mm: '(0[1-9]|1[012])',
+ yyyy: '(\\d{4})'
+ }
+ };
- color: function (element) {
- return {
- valid: Rules.color.test(element.value),
- message: $.validatr.messages.color
- };
- },
+ function indexOf(array, value) {
+ var index = -1,
+ length = array ? array.length : 0;
- date: function (element) {
- var $element = $(element),
- value = Support.inputtypes.date ? parseISODate(element.value) : parseDate(element),
- min = $element.attr('min') ? parseISODate($element.attr('min')) : false,
- max = $element.attr('max') ? parseISODate($element.attr('max')) : false,
- step = false;
-
- return minMax.call(element, value, min, max, step, 'date');
- },
- email: function (element) {
- var valid = true,
- msg = $.validatr.messages.email.single,
- multiple = Support.attributes.multiple ? element.multiple : $(element).is('[multiple]');
+ while (++index < length) {
+ if (array[index] === value) {
+ return index;
+ }
+ }
- if (multiple) {
- var values = element.value.split(Rules.spaces);
+ return -1;
+ }
- $.each(values, function (i, value) {
- if (!Rules.email.test(value)) {
- valid = false;
- msg = $.validatr.messages.email.multiple;
- return;
- }
- });
- } else {
- valid = Rules.email.test(element.value);
- }
- return {
- valid: valid,
- message: msg
- };
- },
+ function parseDate(element) {
+ var format = element.getAttribute('data-format') || $.fn.validatr.defaultOptions.dateFormat,
+ split = format.split(utils.separatorsNoGroup),
+ dateSplit = element.value.split(utils.separatorsNoGroup),
+ isoSplit = 'yyyy-mm-dd'.split('-'),
+ rule = format.replace(utils.separators, '\\$1')
+ .replace('yyyy', utils.dateParts.yyyy)
+ .replace('mm', utils.dateParts.mm)
+ .replace('dd', utils.dateParts.dd),
+ index = -1,
+ length = isoSplit.length,
+ iso = [];
+
+ rule = new RegExp(rule);
+ if (!rule.test(element.value)) {
+ return false;
+ }
- number: function (element) {
- var value = element.value.replace(',', ''),
- num = Rules.number.test(value) ? parseFloat(value) : false,
- min = Rules.number.test( element.getAttribute('min') ) ? parseFloat( element.getAttribute('min') ) : false,
- max = Rules.number.test( element.getAttribute('max') ) ? parseFloat( element.getAttribute('max') ) : false,
- step = Rules.number.test( element.getAttribute('step') ) ? parseFloat( element.getAttribute('step') ) : element.getAttribute('step') === 'any' ? 'any' : false;
+ while (++index < length) {
+ iso[index] = dateSplit[ indexOf(split, isoSplit[index]) ];
+ }
- if (step === false || step <= 0) {
- step = 1;
+ return parseISODate(iso.join('-'));
+ }
+
+ function parseISODate(dateString) {
+ if (!rules.isoDate.test(dateString)) {
+ return false;
}
- return minMax.call(element, value, min, max, step, 'number');
- },
+ var date = rules.isoDate.exec(dateString);
+ return new Date(parseInt(date[1], 10), parseInt(date[2], 10) - 1, parseInt(date[3], 10));
+ }
- pattern: function (element) {
- return {
- valid: new RegExp(element.getAttribute('pattern')).test(element.value),
- message: $.validatr.messages.pattern
- };
- },
+ function formatISODate(dateObj, element) {
+ function pad(n) {
+ return n < 10 ? '0' + n : n;
+ }
- radio: function (element) {
- return {
- valid: $(document.getElementsByName(element.name)).is(':checked'),
- message: $.validatr.messages.radio
- };
+ var date = pad(dateObj.getDate()),
+ month = pad(dateObj.getMonth() + 1),
+ year = dateObj.getFullYear(),
+ dateString = (element.getAttribute('data-format') || $.fn.validatr.defaultOptions.dateFormat).replace('mm', month).replace('yyyy', year).replace('dd', date);
+
+ return dateString;
+ }
+
+ return {
+ formatISODate: formatISODate,
+ parseDate: parseDate,
+ parseISODate: parseISODate
+ };
+ }()),
+
+ Tests = (function () {
+ var rules = {
+ color: /^#[0-9A-F]{6}$/i,
+ email: /^[a-zA-Z0-9.!#$%&’*+\/=?\^_`{|}~\-]+@[a-zA-Z0-9\-]+(?:\.[a-zA-Z0-9\-]+)*$/,
+ isoDate: /^(\d{4})-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$/,
+ number: /^-?\d*\.?\d*$/,
+ time: /^([01][0-9]|2[0-3])(:([0-5][0-9])){2}$/,
+ url: /^\s*https?:\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?\s*$/
},
- range: function (element) {
- return this.number(element);
+ utils = {
+ boxes: /checkbox|radio/i,
+ spaces: /,\s*/
},
- required: function (element) {
- if (Rules.boxes.test(element.type)) {
- return this[element.type](element);
+ minMax = function (value, min, max, step, type) {
+ var result = true,
+ msg = $.validatr.messages.range.base,
+ minString = min,
+ maxString = max;
+
+ if (type === 'date') {
+ minString = min && Format.formatISODate(min, this);
+ maxString = max && Format.formatISODate(max, this);
}
- return {
- valid: !!element.value.length,
- message: element.nodeName.toLowerCase() === 'select' ? $.validatr.messages.select : $.validatr.messages.required
- };
- },
+ if (value !== false) {
+ if (step !== false) {
+ result = step === 'any' ? true : (value - min) % step === 0;
+ msg = $.validatr.messages.range.invalid;
+ }
- time: function (element) {
- return {
- valid: Rules.time.test(element.value),
- message: $.validatr.messages.time
- };
- },
+ if (result) {
+ if (min !== false && max !== false) {
+ result = value >= min && value <= max;
+ msg = $.validatr.messages.range.overUnder;
+ } else if (min !== false) {
+ result = value >= min;
+ msg = $.validatr.messages.range.overflow;
+ } else if (max !== false) {
+ result = value <= max;
+ msg = $.validatr.messages.range.underflow;
+ }
+ }
+ }
- url: function (element) {
return {
- valid: Rules.url.test(element.value),
- message: $.validatr.messages.url
- };
- }
- },
+ valid: value !== false && result,
+ message: msg.replace('{{type}}', type).replace('{{min}}', minString).replace('{{max}}', maxString)
+ };
+ };
+
+ return {
+ checkbox: function (element) {
+ return {
+ valid: element.checked,
+ message: $.validatr.messages.checkbox
+ };
+ },
+
+ color: function (element) {
+ return {
+ valid: rules.color.test(element.value),
+ message: $.validatr.messages.color
+ };
+ },
+
+ date: function (element) {
+ var $element = $(element),
+ value = Support.inputtypes.date ? Format.parseISODate(element.value) : Format.parseDate(element),
+ min = $element.attr('min') ? Format.parseISODate($element.attr('min')) : false,
+ max = $element.attr('max') ? Format.parseISODate($element.attr('max')) : false,
+ step = false;
+
+ return minMax.call(element, value, min, max, step, 'date');
+ },
+
+ email: function (element) {
+ var valid = true,
+ msg = $.validatr.messages.email.single,
+ multiple = Support.attributes.multiple ? element.multiple : $(element).is('[multiple]');
+
+ if (multiple) {
+ var values = element.value.split(utils.spaces);
+
+ $.each(values, function (i, value) {
+ if (!rules.email.test(value)) {
+ valid = false;
+ msg = $.validatr.messages.email.multiple;
+ return;
+ }
+ });
+ } else {
+ valid = rules.email.test(element.value);
+ }
+
+ return {
+ valid: valid,
+ message: msg
+ };
+ },
+
+ number: function (element) {
+ var value = element.value.replace(',', ''),
+ num = rules.number.test(value) ? parseFloat(value) : false,
+ min = rules.number.test( element.getAttribute('min') ) ? parseFloat( element.getAttribute('min') ) : false,
+ max = rules.number.test( element.getAttribute('max') ) ? parseFloat( element.getAttribute('max') ) : false,
+ step = rules.number.test( element.getAttribute('step') ) ? parseFloat( element.getAttribute('step') ) : element.getAttribute('step') === 'any' ? 'any' : false;
+
+ if (step === false || step <= 0) {
+ step = 1;
+ }
+
+ return minMax.call(element, value, min, max, step, 'number');
+ },
- CustomTests = {
- as: function (element) {
+ pattern: function (element) {
+ return {
+ valid: new RegExp(element.getAttribute('pattern')).test(element.value),
+ message: $.validatr.messages.pattern
+ };
+ },
+
+ radio: function (element) {
+ return {
+ valid: $(document.getElementsByName(element.name)).is(':checked'),
+ message: $.validatr.messages.radio
+ };
+ },
+
+ range: function (element) {
+ return this.number(element);
+ },
+
+ required: function (element) {
+ if (utils.boxes.test(element.type)) {
+ return this[element.type](element);
+ }
+
+ return {
+ valid: !!element.value.length,
+ message: element.nodeName.toLowerCase() === 'select' ? $.validatr.messages.select : $.validatr.messages.required
+ };
+ },
+
+ time: function (element) {
+ return {
+ valid: rules.time.test(element.value),
+ message: $.validatr.messages.time
+ };
+ },
+
+ url: function (element) {
+ return {
+ valid: rules.url.test(element.value),
+ message: $.validatr.messages.url
+ };
+ }
+ };
+ }()),
+
+ CustomTests = (function () {
+ function as(element) {
if (element.type !== 'text') {
throw new Error('element must have a type of text');
}
@@ -245,16 +360,16 @@
if (Tests[type]) {
return Tests[type](element);
}
- },
+ }
- match: function (element) {
- var match = element.getAttribute('data-match'),
- source = document.getElementById(match) || document.getElementsByName(match)[0];
+ function match(element) {
+ var value = element.getAttribute('data-match'),
+ source = document.getElementById(value) || document.getElementsByName(value)[0];
if (!source) {
return {
valid: false,
- message: "'" + match + "' can not be found"
+ message: "'" + value + "' can not be found"
};
}
@@ -271,9 +386,21 @@
message: "'" + element.name + "' does not equal '" + source.name +"'"
};
}
+
+ return {
+ as: as,
+ match: match
+ };
+ }()),
+
+ filters = {
+ boxes: /checkbox|radio/i,
+ leftright: /left|right/i,
+ notInput: /select|textarea/i,
+ topbottom: /top|bottom/i
},
- KeyCodes = [
+ keyCodes = [
16, // shift
17, // control
18, // alt
@@ -287,111 +414,6 @@
39 //right arrow
],
- indexOf = function (array, value) {
- var index = -1,
- length = array ? array.length : 0;
-
-
- while (++index < length) {
- if (array[index] === value) {
- return index;
- }
- }
-
- return -1;
- },
-
- parseDate = function (element) {
- var format = element.getAttribute('data-format') || $.fn.validatr.defualtOptions.dateFormat,
- split = format.split(Rules.separatorsNoGroup),
- dateSplit = element.value.split(Rules.separatorsNoGroup),
- isoSplit = 'yyyy-mm-dd'.split('-'),
- rule = format.replace(Rules.separators, '\\$1')
- .replace('yyyy', Rules.date.yyyy)
- .replace('mm', Rules.date.mm)
- .replace('dd', Rules.date.dd),
- index = -1,
- length = isoSplit.length,
- iso = [];
-
- rule = new RegExp(rule);
- if (!rule.test(element.value)) {
- return false;
- }
-
- while (++index < length) {
- iso[index] = dateSplit[ indexOf(split, isoSplit[index]) ];
- }
-
- return parseISODate(iso.join('-'));
- },
-
- parseISODate = function (dateString) {
- if (!Rules.isoDate.test(dateString)) {
- return false;
- }
-
- var date = Rules.isoDate.exec(dateString);
- return new Date(parseInt(date[1], 10), parseInt(date[2], 10) - 1, parseInt(date[3], 10));
- },
-
- formatISODate = function (dateObj, element) {
- function pad(n) {
- return n < 10 ? '0' + n : n;
- }
-
- var date = pad(dateObj.getDate()),
- month = pad(dateObj.getMonth() + 1),
- year = dateObj.getFullYear(),
- dateString = (element.getAttribute('data-format') || $.fn.validatr.defualtOptions.dateFormat).replace('mm', month).replace('yyyy', year).replace('dd', date);
-
- return dateString;
- },
-
- minMax = function (value, min, max, step, type) {
- var result = true,
- msg = $.validatr.messages.range.base,
- minString = min,
- maxString = max;
-
- if (type === 'date') {
- minString = min && formatISODate(min, this);
- maxString = max && formatISODate(max, this);
- }
-
- if (value !== false) {
- if (step !== false) {
- result = step === 'any' ? true : (value - min) % step === 0;
- msg = $.validatr.messages.range.invalid;
- }
-
- if (result) {
- if (min !== false && max !== false) {
- result = value >= min && value <= max;
- msg = $.validatr.messages.range.overUnder;
- } else if (min !== false) {
- result = value >= min;
- msg = $.validatr.messages.range.overflow;
- } else if (max !== false) {
- result = value <= max;
- msg = $.validatr.messages.range.underflow;
- }
- }
- }
-
- return {
- valid: value !== false && result,
- message: msg.replace('{{type}}', type).replace('{{min}}', minString).replace('{{max}}', maxString)
- };
- },
-
- getNode = function (element) {
- if (element instanceof jQuery) {
- element = element[0];
- }
- return element;
- },
-
theme = {
bootstrap: 'alert alert-error',
jqueryui: 'ui-state-error ui-corner-all'
@@ -403,7 +425,6 @@
border: '1px solid #e4a6af',
padding: '2px 6px',
borderRadius: '2px'
-
},
supressError = false,
@@ -427,8 +448,8 @@
},
getElements: function (form) {
- if (this.elements) {
- return this.elements;
+ if (this.formElements) {
+ return this.formElements;
}
var elements = $(form).map(function () {
@@ -449,22 +470,22 @@
}
supressError = true;
- var valid = validateElement(getNode(element));
+ var valid = validateElement(element[0] || element);
supressError = false;
return valid;
},
validateForm: function (form) {
- var element = this.el || getNode(form),
+ var element = this.el || (form instanceof jQuery ? form[0] : form),
valid;
if (element.nodeName.toLowerCase() !== 'form') {
throw new Error('you must pass a form to this method');
}
supressError = true;
- valid = validateForm(this.elements || this.getElements(element));
+ valid = validateForm(this.formElements || this.getElements(element));
supressError = false;
return valid;
@@ -484,7 +505,7 @@
this.isSubmit = false;
this.firstError = false;
- this.options = $.extend({}, $.fn.validatr.defualtOptions, options);
+ this.options = $.extend({}, $.fn.validatr.defaultOptions, options);
this.template = (function (options) {
var template = $(options.template).addClass('validatr-message');
@@ -498,7 +519,19 @@
return template[0].outerHTML;
}(this.options));
- this.elements = this.getElements(this.el)
+ this.option = function (key, value) {
+ if (!arguments.length) {
+ return $.extend({}, this.options);
+ }
+
+ if (value === undefined) {
+ return this.options[key] === undefined ? null : this.options[key];
+ }
+
+ this.options[key] = value;
+ };
+
+ this.formElements = this.getElements(this.el)
.on('valid.' + 'validatr', $.proxy(validElement, this))
.on('invalid.' + 'validatr', $.proxy(invalidElement, this));
@@ -510,7 +543,7 @@
function bindElements() {
/*jshint validthis:true */
- this.elements.on({
+ this.formElements.on({
'focus.validatrelement': bindEvents,
'blur.validatrelement': unbindEvents
});
@@ -523,7 +556,7 @@
function unbindElements() {
/*jshint validthis:true */
- this.elements.off('.validatrelement');
+ this.formElements.off('.validatrelement');
}
function bindEvents (e) {
@@ -543,7 +576,7 @@
validateElement(target);
},
'keyup.validatrinput': function (event) {
- if (target.value.length && indexOf(KeyCodes, event.keyCode) === -1) {
+ if (target.value.length && $.inArray(keyCodes, event.keyCode) === -1) {
validateElement(target);
}
}
@@ -563,7 +596,7 @@
}
var $element = $(element),
- type = Rules.notInput.test(element.nodeName) ? element.nodeName.toLowerCase() : element.getAttribute('type'),
+ type = filters.notInput.test(element.nodeName) ? element.nodeName.toLowerCase() : element.getAttribute('type'),
required = Support.attributes.required ? element.required : $(element).is('[required]'),
check = {
valid: true,
@@ -578,7 +611,7 @@
check = Tests.required(element);
}
- if (check.valid && element.value.length && !Rules.boxes.test(type)) {
+ if (check.valid && element.value.length && !filters.boxes.test(type)) {
if (element.pattern) {
type = 'pattern';
}
@@ -628,7 +661,7 @@
this.isSubmit = true;
resetForm.call(this);
- var valid = validateForm(this.elements);
+ var valid = validateForm(this.formElements);
if (valid) {
return this.options.valid.call(this.el, this.el);
@@ -645,7 +678,7 @@
unbindElements.call(this);
this.firstError = false;
- this.elements.next('.validatr-message').remove();
+ this.formElements.next('.validatr-message').remove();
}
function invalidElement(e) {
@@ -692,7 +725,7 @@
var offset = $target.offset(),
location = $target[0].getAttribute('data-location') || this.options.location;
- if (Rules.topbottom.test(location)) {
+ if (filters.topbottom.test(location)) {
error.offset({left: offset.left});
if (location === 'top') {
@@ -702,7 +735,7 @@
if (location === 'bottom') {
error.offset({top: offset.top + error.outerHeight()});
}
- } else if (Rules.leftright.test(location)) {
+ } else if (filters.leftright.test(location)) {
error.offset({top: (offset.top + $target.outerHeight() / 2) - (error.outerHeight() / 2)});
if (location === 'left') {
@@ -758,7 +791,7 @@
return returnValue;
};
- $.fn.validatr.defualtOptions = {
+ $.fn.validatr.defaultOptions = {
dateFormat: 'yyyy-mm-dd',
location: 'right',
position: position,
@@ -802,14 +835,12 @@
this.Support = Support;
this.Tests = Tests;
this.CustomTests = CustomTests;
- this.parseDate = parseDate;
- this.parseISODate = parseISODate;
- this.formatISODate = formatISODate;
+ this.Format = Format;
};
// Custom selector.
$.expr[':'].validatr = function(elem) {
- return elem.textContent.indexOf('validatr') >= 0;
+ return !!$.data(elem, 'validatr');
};
}(this, this.document, jQuery));
View
6 tests/tests_date.js
@@ -23,8 +23,8 @@ Test assertions:
var InputTypes = $.validatr.Support.inputtypes,
Tests = $.validatr.Tests,
- parseISODate = $.validatr.parseISODate,
- formatISODate = $.validatr.formatISODate;
+ parseISODate = $.validatr.Format.parseISODate,
+ formatISODate = $.validatr.Format.formatISODate;
module('tests: date', {
setup: function () {
@@ -40,7 +40,7 @@ Test assertions:
}
if (!InputTypes.date) {
- $.fn.validatr.defualtOptions.dateFormat = 'mm/dd/yyyy';
+ $.fn.validatr.defaultOptions.dateFormat = 'mm/dd/yyyy';
test('no support', function () {
ok(InputTypes.date === false, '\'date\' type vaildation is not supported by your browser');
View
149 tests/tests_widget.js
@@ -0,0 +1,149 @@
+/*
+======== A Handy Little QUnit Reference ========
+http://api.qunitjs.com/
+
+Test methods:
+ module(name, {[setup][ ,teardown]})
+ test(name, callback)
+ expect(numberOfAssertions)
+ stop(increment)
+ start(decrement)
+Test assertions:
+ ok(value, [message])
+ equal(actual, expected, [message])
+ notEqual(actual, expected, [message])
+ deepEqual(actual, expected, [message])
+ notDeepEqual(actual, expected, [message])
+ strictEqual(actual, expected, [message])
+ notStrictEqual(actual, expected, [message])
+ throws(block, [expected], [message])
+*/
+
+(function($) {
+ var form = '<form><fieldset><legend>Legend</legend><input type="text" name="text" required></fieldset></form>',
+ validatr = $.validatr;
+
+ module('instance', {
+ setup: function () {
+ var fixture = $('#qunit-fixture').append(form);
+ this.form = fixture.find('form');
+ this.input = this.form.find('input');
+ }
+ });
+
+ test('method: getElements', function () {
+ var input = validatr.getElements(this.form);
+ ok(input.length === 1, 'returned one input');
+ ok(input.is(this.input), 'input elements are the same');
+ });
+
+ test('method: validateElement', function () {
+ var valid = validatr.validateElement(this.input);
+ ok(valid === false, 'jQuery input is invalid');
+
+ valid = validatr.validateElement(this.input[0]);
+ ok(valid === false, 'input is invalid');
+
+ this.input[0].value = 1;
+ valid = validatr.validateElement(this.input);
+ ok(valid === true, 'jQuery input is valid');
+
+ valid = validatr.validateElement(this.input[0]);
+ ok(valid === true, 'input is valid');
+ });
+
+ test('method: validateForm', function () {
+ var valid = validatr.validateForm(this.form);
+ ok(valid === false, 'jQuery form is invalid');
+
+ valid = validatr.validateForm(this.form[0]);
+ ok(valid === false, 'form is invalid');
+
+ this.input[0].value = 1;
+ valid = validatr.validateForm(this.form);
+ ok(valid === true, 'jQuery form is valid');
+
+ valid = validatr.validateForm(this.form[0]);
+ ok(valid === true, 'form is valid');
+ });
+
+ module('widget', {
+ setup: function () {
+ var fixture = $('#qunit-fixture').append(form);
+ this.form = fixture.find('form').validatr();
+ this.input = this.form.find('input');
+ }
+ });
+
+ test('init', function () {
+ ok(this.form.is(':validatr'), 'validatr initialized');
+ deepEqual(this.form.data('validatr').options, $.fn.validatr.defaultOptions, 'default options set');
+ });
+
+ test('method: option', function () {
+ deepEqual(this.form.validatr('option'), $.fn.validatr.defaultOptions, 'default options retrieved');
+
+ this.form.validatr('option', 'location', 'left');
+ equal(this.form.data('validatr').options.location, 'left', 'location set to left');
+ equal(this.form.validatr('option', 'location'), 'left', 'location option retrieved');
+ });
+
+ test('method: getElements', function () {
+ var input = this.form.validatr('getElements');
+ ok(input.length === 1, 'returned one input');
+ ok(input.is(this.input), 'input elements are the same');
+ });
+
+ test('method: validateElement', function () {
+ var valid = this.form.validatr('validateElement', this.input);
+ ok(valid === false, 'jQuery input is invalid');
+
+ valid = this.form.validatr('validateElement', this.input[0]);
+ ok(valid === false, 'input is invalid');
+
+ this.input[0].value = 1;
+ valid = this.form.validatr('validateElement', this.input);
+ ok(valid === true, 'jQuery input is valid');
+
+ valid = this.form.validatr('validateElement', this.input[0]);
+ ok(valid === true, 'input is valid');
+ });
+
+ test('method: validateForm', function () {
+ var valid = this.form.validatr('validateForm');
+ ok(valid === false, 'form is invalid');
+
+ this.input[0].value = 1;
+ valid = this.form.validatr('validateForm');
+ ok(valid === true, 'form is valid');
+ });
+
+ test('event: submit', function () {
+ expect(2);
+
+ this.form.validatr('option', 'valid', function () {
+ ok(1, 'form is valid');
+ return false;
+ })
+ .on('invalid', function () {
+ ok(1, 'form is invalid');
+ });
+
+ this.form.trigger('submit');
+
+ this.input[0].value = 1;
+ this.form.trigger('submit');
+ });
+
+ test('error message', function () {
+ this.form.trigger('submit');
+ ok(this.input.next().hasClass('validatr-message'), 'error message inserted after field');
+ });
+
+ test('event: reset', function () {
+ this.form.trigger('submit');
+ this.form.trigger('reset');
+ ok(this.input.next().length === 0, 'error message has been removed');
+ });
+
+}(jQuery));
View
1 tests/validatr.html
@@ -23,6 +23,7 @@
<script src="tests_pattern.js"></script>
<script src="tests_radio.js"></script>
<script src="tests_url.js"></script>
+ <script src="tests_widget.js"></script>
</head>
<body>
<div id="qunit"></div>
View
6 validatr.jquery.json
@@ -2,10 +2,10 @@
"name": "validatr",
"title": "Validatr",
"description": "Cross Browser HTML5 Form Validation.",
- "version": "0.4.2",
+ "version": "0.5.0",
"homepage": "http://jaymorrow.github.com/validatr/",
- "docs": "http://jaymorrow.github.com/validatr/",
- "demo": "http://jaymorrow.github.com/validatr/",
+ "docs": "http://jaymorrow.github.com/validatr/api.html",
+ "demo": "http://jaymorrow.github.com/validatr/fields.html",
"author": {
"name": "Jay Morrow",
"email": "jay.m.morrow@gmail.com"

0 comments on commit 2213ad2

Please sign in to comment.
Something went wrong with that request. Please try again.