diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..67d713f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+.idea
+node_modules
+npm-debug.log
diff --git a/Gruntfile.js b/Gruntfile.js
new file mode 100644
index 0000000..8fbfdca
--- /dev/null
+++ b/Gruntfile.js
@@ -0,0 +1,137 @@
+module.exports = function (grunt) {
+
+ grunt.initConfig({
+
+ pkg: grunt.file.readJSON('package.json'),
+
+ banner: '/*!\n' +
+ ' * jQuery Form Validation\n' +
+ ' * Copyright (C) 2015 RunningCoder.org\n' +
+ ' * Licensed under the MIT license\n' +
+ ' *\n' +
+ ' * @author <%= pkg.author.name %>\n' +
+ ' * @version <%= pkg.version %> (<%= grunt.template.today("yyyy-mm-dd") %>)\n' +
+ ' * @link http://www.runningcoder.org/jqueryvalidation/\n' +
+ '*/\n',
+
+ clean: {
+ dist: ["dist"]
+ },
+
+ copy: {
+ dist: {
+ files: [
+ {
+ src: ['src/jquery.validation.js'],
+ dest: 'dist/jquery.validation.js'
+ },
+ {
+ src: ['src/jquery.validation.js'],
+ dest: 'dist/jquery.validation.min.js'
+ }
+ ]
+ }
+ },
+
+ comments: {
+ dist: {
+ options: {
+ singleline: true,
+ multiline: true
+ },
+ src: [ 'dist/jquery.validation.js']
+ }
+ },
+
+ replace: {
+ banner: {
+ options: {
+ patterns: [
+ {
+ match: /\/\*![\S\s]+?\*\/[\r\n]*/,
+ replacement: '<%= banner %>'
+ }
+ ]
+ },
+ files: [
+ {
+ src: ['src/jquery.validation.js'],
+ dest: 'src/jquery.validation.js'
+ }
+ ]
+ },
+ removeDebug: {
+ options: {
+ patterns: [
+ {
+ match: /\/\/\s?\{debug}[\s\S]*?\{\/debug}/g,
+ replacement: ''
+ }
+ ]
+ },
+ files: [
+ {
+ src: ['dist/jquery.validation.min.js'],
+ dest: 'dist/jquery.validation.min.js'
+ }
+ ]
+ },
+ removeComments: {
+ options: {
+ patterns: [
+ {
+ match: /\/\*[^!][\S\s]+?\*\//gm,
+ replacement: ''
+ }
+ ]
+ },
+ files: [
+ {
+ src: ['dist/jquery.validation.js'],
+ dest: 'dist/jquery.validation.js'
+ }
+ ]
+ }
+ },
+
+ jsbeautifier : {
+ files : ['dist/jquery.validation.js'],
+ options : {
+ }
+ },
+
+ uglify: {
+ dist: {
+ options: {
+ mangle: true,
+ compress: true,
+ banner: '<%= banner %>'
+ },
+ files: {
+ 'dist/jquery.validation.min.js': ['dist/jquery.validation.min.js']
+ }
+ }
+
+ }
+
+ });
+
+ grunt.loadNpmTasks('grunt-contrib-clean');
+ grunt.loadNpmTasks('grunt-contrib-copy');
+ grunt.loadNpmTasks('grunt-stripcomments');
+ grunt.loadNpmTasks('grunt-replace');
+ grunt.loadNpmTasks("grunt-jsbeautifier");
+ grunt.loadNpmTasks('grunt-contrib-uglify');
+
+ grunt.registerTask('default', [
+ 'clean:dist',
+ 'replace:banner',
+ 'copy:dist',
+ 'comments',
+ 'replace:removeComments',
+ 'jsbeautifier',
+ 'replace:removeDebug',
+ 'uglify'
+ ]);
+
+};
diff --git a/README.md b/README.md
index 92ff012..4ff9da1 100644
--- a/README.md
+++ b/README.md
@@ -7,3 +7,18 @@ It is a simple clientside library that will save you a lot of time when it comes
The jQuery form Validation plugin is released under the MIT License.
The complete documentation, demo and further instructions can be found at www.runningcoder.org
+
+Documentation
+======================
+
+www.runningcoder.org/jqueryvalidation/documentation/
+
+Demos
+======================
+
+www.runningcoder.org/jqueryvalidation/demo/
+
+Patch Notes
+======================
+
+www.runningcoder.org/jqueryvalidation/version/
diff --git a/bower.json b/bower.json
new file mode 100644
index 0000000..5e42e12
--- /dev/null
+++ b/bower.json
@@ -0,0 +1,26 @@
+{
+ "name": "html5-form-validation",
+ "version": "1.5.3",
+ "authors": [
+ "Tom Bertrand"
+ ],
+ "description": "jQuery plugin that provides a client site form validation with builtin options and deep customization.",
+ "main": "jquery.validation.min.js",
+ "keywords": [
+ "form",
+ "html5",
+ "validate",
+ "validation",
+ "input"
+ ],
+ "ignore": [],
+ "licenses": "MIT",
+ "homepage": "http://www.runningcoder.org/jqueryvalidation/",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/running-coder/jquery-form-validation.git"
+ },
+ "dependencies": {
+ "jquery": ">=1.7.2"
+ }
+}
\ No newline at end of file
diff --git a/dist/jquery.validation.js b/dist/jquery.validation.js
new file mode 100644
index 0000000..0d16d61
--- /dev/null
+++ b/dist/jquery.validation.js
@@ -0,0 +1,1559 @@
+/*!
+ * jQuery Form Validation
+ * Copyright (C) 2015 RunningCoder.org
+ * Licensed under the MIT license
+ *
+ * @author Tom Bertrand
+ * @version 1.5.3 (2015-12-02)
+ * @link http://www.runningcoder.org/jqueryvalidation/
+ */
+;
+(function(window, document, $, undefined) {
+
+ window.Validation = {
+ form: [],
+ labels: {},
+ hasScrolled: false
+ };
+
+ if (typeof Object.preventExtensions !== "function") {
+ Object.preventExtensions = function(obj) {
+ return obj;
+ };
+ }
+ var _rules = {
+ NOTEMPTY: /\S/,
+ INTEGER: /^\d+$/,
+ NUMERIC: /^\d+(?:[,\s]\d{3})*(?:\.\d+)?$/,
+ MIXED: /^[\w\s-]+$/,
+ NAME: /^['a-zãàáäâẽèéëêìíïîõòóöôùúüûñç\s-]+$/i,
+ NOSPACE: /^(?!\s)\S*$/,
+ TRIM: /^[^\s].*[^\s]$/,
+ DATE: /^\d{4}-\d{2}-\d{2}(\s\d{2}:\d{2}(:\d{2})?)?$/,
+ EMAIL: /^([^@]+?)@(([a-z0-9]-*)*[a-z0-9]+\.)+([a-z0-9]+)$/i,
+ URL: /^(https?:\/\/)?((([a-z0-9]-*)*[a-z0-9]+\.?)*([a-z0-9]+))(\/[\w?=\.-]*)*$/,
+ PHONE: /^(\()?\d{3}(\))?(-|\s)?\d{3}(-|\s)\d{4}$/,
+ OPTIONAL: /\S/,
+ COMPARISON: /^\s*([LV])\s*([<>]=?|==|!=)\s*([^<>=!]+?)\s*$/
+ };
+
+ var _messages = {
+ 'default': '$ contain error(s).',
+ 'NOTEMPTY': '$ must not be empty.',
+ 'INTEGER': '$ must be an integer.',
+ 'NUMERIC': '$ must be numeric.',
+ 'MIXED': '$ must be letters or numbers (no special characters).',
+ 'NAME': '$ must not contain special characters.',
+ 'NOSPACE': '$ must not contain spaces.',
+ 'TRIM': '$ must not start or end with space character.',
+ 'DATE': '$ is not a valid with format YYYY-MM-DD.',
+ 'EMAIL': '$ is not valid.',
+ 'URL': '$ is not valid.',
+ 'PHONE': '$ is not a valid phone number.',
+ '<': '$ must be less than % characters.',
+ '<=': '$ must be less or equal to % characters.',
+ '>': '$ must be greater than % characters.',
+ '>=': '$ must be greater or equal to % characters.',
+ '==': '$ must be equal to %',
+ '!=': '$ must be different than %'
+ };
+
+ var _data = {
+ validation: 'data-validation',
+ validationMessage: 'data-validation-message',
+ regex: 'data-validation-regex',
+ regexReverse: 'data-validation-regex-reverse',
+ regexMessage: 'data-validation-regex-message',
+ group: 'data-validation-group',
+ label: 'data-validation-label',
+ errorList: 'data-error-list'
+ }
+
+ var _options = {
+ submit: {
+ settings: {
+ form: null,
+ display: "inline",
+ insertion: "append",
+ allErrors: false,
+ trigger: "click",
+ button: "[type='submit']",
+ errorClass: "error",
+ errorListClass: "error-list",
+ errorListContainer: null,
+ errorTemplate: null,
+ inputContainer: null,
+ clear: "focusin",
+ scrollToError: false
+ },
+ callback: {
+ onInit: null,
+ onValidate: null,
+ onError: null,
+ onBeforeSubmit: null,
+ onSubmit: null,
+ onAfterSubmit: null
+ }
+ },
+ dynamic: {
+ settings: {
+ trigger: null,
+ delay: 300
+ },
+ callback: {
+ onSuccess: null,
+ onError: null,
+ onComplete: null
+ }
+ },
+ rules: {},
+ messages: {},
+ labels: {},
+ debug: false
+ };
+
+ var _supported = {
+ submit: {
+ settings: {
+ display: ["inline", "block"],
+ insertion: ["append", "prepend"], //"before", "insertBefore", "after", "insertAfter"
+ allErrors: [true, false],
+ clear: ["focusin", "keypress", false],
+ trigger: [
+ "click", "dblclick", "focusout",
+ "hover", "mousedown", "mouseenter",
+ "mouseleave", "mousemove", "mouseout",
+ "mouseover", "mouseup", "toggle"
+ ]
+ }
+ },
+ dynamic: {
+ settings: {
+ trigger: ["focusout", "keydown", "keypress", "keyup"]
+ }
+ },
+ debug: [true, false]
+ };
+
+ var Validation = function(node, options) {
+
+ var errors = [],
+ messages = {},
+ formData = {},
+ delegateSuffix = ".vd", // validation.delegate
+ resetSuffix = ".vr"; // validation.resetError
+
+ window.Validation.hasScrolled = false;
+
+ function extendRules() {
+ options.rules = $.extend(
+ true, {},
+ _rules,
+ options.rules
+ );
+ }
+
+ function extendMessages() {
+ options.messages = $.extend(
+ true, {},
+ _messages,
+ options.messages
+ );
+ }
+
+ function extendOptions() {
+
+ if (!(options instanceof Object)) {
+ options = {};
+ }
+
+ var tpmOptions = Object.preventExtensions($.extend(true, {}, _options));
+
+ for (var method in options) {
+
+ if (!options.hasOwnProperty(method) || method === "debug") {
+ continue;
+ }
+
+ if (~["labels", "messages", "rules"].indexOf(method) && options[method] instanceof Object) {
+ tpmOptions[method] = options[method];
+ continue;
+ }
+
+ if (!_options[method] || !(options[method] instanceof Object)) {
+ options.debug && window.Debug.log({
+ 'node': node,
+ 'function': 'extendOptions()',
+ 'arguments': '{' + method + ': ' + JSON.stringify(options[method]) + '}',
+ 'message': 'WARNING - ' + method + ' - invalid option'
+ });
+
+ continue;
+ }
+
+ for (var type in options[method]) {
+ if (!options[method].hasOwnProperty(type)) {
+ continue;
+ }
+
+ if (!_options[method][type] || !(options[method][type] instanceof Object)) {
+ options.debug && window.Debug.log({
+ 'node': node,
+ 'function': 'extendOptions()',
+ 'arguments': '{' + type + ': ' + JSON.stringify(options[method][type]) + '}',
+ 'message': 'WARNING - ' + type + ' - invalid option'
+ });
+
+ continue;
+ }
+
+ for (var option in options[method][type]) {
+ if (!options[method][type].hasOwnProperty(option)) {
+ continue;
+ }
+
+ if (_supported[method] &&
+ _supported[method][type] &&
+ _supported[method][type][option] &&
+ $.inArray(options[method][type][option], _supported[method][type][option]) === -1) {
+ options.debug && window.Debug.log({
+ 'node': node,
+ 'function': 'extendOptions()',
+ 'arguments': '{' + option + ': ' + JSON.stringify(options[method][type][option]) + '}',
+ 'message': 'WARNING - ' + option.toString() + ': ' + JSON.stringify(options[method][type][option]) + ' - unsupported option'
+ });
+
+ delete options[method][type][option];
+ }
+
+ }
+ if (tpmOptions[method] && tpmOptions[method][type]) {
+ tpmOptions[method][type] = $.extend(Object.preventExtensions(tpmOptions[method][type]), options[method][type]);
+ }
+ }
+ }
+ if (options.debug && $.inArray(options.debug, _supported.debug !== -1)) {
+ tpmOptions.debug = options.debug;
+ }
+ if (tpmOptions.dynamic.settings.trigger) {
+ if (tpmOptions.dynamic.settings.trigger === "keypress" && tpmOptions.submit.settings.clear === "keypress") {
+ tpmOptions.dynamic.settings.trigger = "keydown";
+ }
+ }
+
+ options = tpmOptions;
+
+ }
+
+ function delegateDynamicValidation() {
+
+ if (!options.dynamic.settings.trigger) {
+ return false;
+ }
+ options.debug && window.Debug.log({
+ 'node': node,
+ 'function': 'delegateDynamicValidation()',
+ 'message': 'OK - Dynamic Validation activated on ' + node.length + ' form(s)'
+ });
+
+ if (!node.find('[' + _data.validation + '],[' + _data.regex + ']')[0]) {
+ options.debug && window.Debug.log({
+ 'node': node,
+ 'function': 'delegateDynamicValidation()',
+ 'arguments': 'node.find([' + _data.validation + '],[' + _data.regex + '])',
+ 'message': 'ERROR - [' + _data.validation + '] not found'
+ });
+
+ return false;
+ }
+
+ var event = options.dynamic.settings.trigger + delegateSuffix;
+ if (options.dynamic.settings.trigger !== "focusout") {
+ event += " change" + delegateSuffix + " paste" + delegateSuffix;
+ }
+
+ $.each(
+ node.find('[' + _data.validation + '],[' + _data.regex + ']'),
+ function(index, input) {
+
+ $(input).unbind(event).on(event, function(e) {
+
+ if ($(this).is(':disabled')) {
+ return false;
+ }
+
+ var input = this,
+ keyCode = e.keyCode || null;
+
+ _typeWatch(function() {
+
+ if (!validateInput(input)) {
+
+ displayOneError(input.name);
+ _executeCallback(options.dynamic.callback.onError, [node, input, keyCode, errors[input.name]]);
+
+ } else {
+
+ _executeCallback(options.dynamic.callback.onSuccess, [node, input, keyCode]);
+
+ }
+
+ _executeCallback(options.dynamic.callback.onComplete, [node, input, keyCode]);
+
+ }, options.dynamic.settings.delay);
+
+ });
+ }
+ );
+ }
+
+ function delegateValidation() {
+
+ _executeCallback(options.submit.callback.onInit, [node]);
+
+ var event = options.submit.settings.trigger + '.vd';
+ options.debug && window.Debug.log({
+ 'node': node,
+ 'function': 'delegateValidation()',
+ 'message': 'OK - Validation activated on ' + node.length + ' form(s)'
+ });
+
+ if (!node.find(options.submit.settings.button)[0]) {
+ options.debug && window.Debug.log({
+ 'node': node,
+ 'function': 'delegateValidation()',
+ 'arguments': '{button: ' + options.submit.settings.button + '}',
+ 'message': 'ERROR - node.find("' + options.submit.settings.button + '") not found'
+ });
+
+ return false;
+
+ }
+
+ node.on("submit", false);
+ node.find(options.submit.settings.button).off('.vd').on(event, function(e) {
+
+ e.preventDefault();
+
+ resetErrors();
+
+ _executeCallback(options.submit.callback.onValidate, [node]);
+
+ if (!validateForm()) {
+
+ displayErrors();
+ _executeCallback(options.submit.callback.onError, [node, errors, formData]);
+
+ } else {
+
+ _executeCallback(options.submit.callback.onBeforeSubmit, [node]);
+
+ if (typeof options.submit.callback.onSubmit === "function") {
+ if (_executeCallback(options.submit.callback.onSubmit, [node, formData]) === true) {
+ submitForm();
+ }
+ } else {
+ submitForm();
+ }
+
+ _executeCallback(options.submit.callback.onAfterSubmit, [node]);
+
+ }
+ options.debug && window.Debug.print();
+
+ return false;
+
+ });
+
+ }
+
+ function validateForm() {
+
+ var isValid = isEmpty(errors);
+
+ formData = {};
+
+ $.each(
+ node.find('input:not([type="submit"]), select, textarea').not(':disabled'),
+ function(index, input) {
+
+ input = $(input);
+
+ var value = _getInputValue(input[0]),
+ inputName = input.attr('name');
+
+ if (inputName) {
+ if (/\[]$/.test(inputName)) {
+ inputName = inputName.replace(/\[]$/, '');
+ if (!(formData[inputName] instanceof Array)) {
+ formData[inputName] = [];
+ }
+ formData[inputName].push(value)
+ } else {
+ formData[inputName] = value;
+ }
+ }
+
+ if (!!input.attr(_data.validation) || !!input.attr(_data.regex)) {
+ if (!validateInput(input[0], value)) {
+ isValid = false;
+ }
+ }
+ }
+ );
+
+ prepareFormData();
+
+ return isValid;
+
+ }
+
+ function prepareFormData() {
+
+ var data = {},
+ matches,
+ index;
+
+ for (var i in formData) {
+ if (!formData.hasOwnProperty(i)) continue;
+
+ index = 0;
+ matches = i.split(/\[(.+?)]/g);
+
+ var tmpObject = {},
+ tmpArray = [];
+
+ for (var k = matches.length - 1; k >= 0; k--) {
+ if (matches[k] === "") {
+ matches.splice(k, 1);
+ continue;
+ }
+
+ if (tmpArray.length < 1) {
+ tmpObject[matches[k]] = Number(formData[i]) || formData[i];
+ } else {
+ tmpObject = {};
+ tmpObject[matches[k]] = tmpArray[tmpArray.length - 1];
+ }
+ tmpArray.push(tmpObject)
+
+ }
+
+ data = $.extend(true, data, tmpObject);
+
+ }
+
+ formData = data;
+
+ }
+
+ function validateInput(input, value) {
+
+ var inputName = $(input).attr('name'),
+ value = value || _getInputValue(input);
+
+ if (!inputName) {
+ options.debug && window.Debug.log({
+ 'node': node,
+ 'function': 'validateInput()',
+ 'arguments': '$(input).attr("name")',
+ 'message': 'ERROR - Missing input [name]'
+ });
+
+ return false;
+ }
+
+ var matches = inputName.replace(/]$/, '').split(/]\[|[[\]]/g),
+ inputShortName = window.Validation.labels[inputName] ||
+ options.labels[inputName] ||
+ $(input).attr(_data.label) ||
+ matches[matches.length - 1],
+
+ validationArray = $(input).attr(_data.validation),
+ validationMessage = $(input).attr(_data.validationMessage),
+ validationRegex = $(input).attr(_data.regex),
+ validationRegexReverse = !($(input).attr(_data.regexReverse) === undefined),
+ validationRegexMessage = $(input).attr(_data.regexMessage),
+
+ validateOnce = false;
+
+ if (validationArray) {
+ validationArray = _api._splitValidation(validationArray);
+ }
+ if (validationArray instanceof Array && validationArray.length > 0) {
+ if ($.trim(value) === '' && ~validationArray.indexOf('OPTIONAL')) {
+ return true;
+ }
+
+ $.each(validationArray, function(i, rule) {
+
+ if (validateOnce === true) {
+ return true;
+ }
+
+ try {
+
+ validateRule(value, rule);
+
+ } catch (error) {
+
+ if (validationMessage || !options.submit.settings.allErrors) {
+ validateOnce = true;
+ }
+
+ error[0] = validationMessage || error[0];
+
+ registerError(inputName, error[0].replace('$', inputShortName).replace('%', error[1]));
+
+ }
+
+ });
+
+ }
+ if (validationRegex) {
+
+ var rule = _buildRegexFromString(validationRegex);
+ if (!(rule instanceof RegExp)) {
+ return true;
+ }
+
+ try {
+
+ validateRule(value, rule, validationRegexReverse);
+
+ } catch (error) {
+
+ error[0] = validationRegexMessage || error[0];
+
+ registerError(inputName, error[0].replace('$', inputShortName));
+
+ }
+
+ }
+
+ return !errors[inputName] || errors[inputName] instanceof Array && errors[inputName].length === 0;
+
+ }
+
+ function validateRule(value, rule, reversed) {
+ if (rule instanceof RegExp) {
+ var isValid = rule.test(value);
+
+ if (reversed) {
+ isValid = !isValid;
+ }
+
+ if (!isValid) {
+ throw [options.messages['default'], ''];
+ }
+ return;
+ }
+
+ if (options.rules[rule]) {
+ if (!options.rules[rule].test(value)) {
+ throw [options.messages[rule], ''];
+ }
+ return;
+ }
+ var comparison = rule.match(options.rules.COMPARISON);
+
+ if (!comparison || comparison.length !== 4) {
+ options.debug && window.Debug.log({
+ 'node': node,
+ 'function': 'validateRule()',
+ 'arguments': 'value: ' + value + ' rule: ' + rule,
+ 'message': 'WARNING - Invalid comparison'
+ });
+
+ return;
+ }
+
+ var type = comparison[1],
+ operator = comparison[2],
+ compared = comparison[3],
+ comparedValue;
+
+ switch (type) {
+ case "L":
+ if (isNaN(compared)) {
+ options.debug && window.Debug.log({
+ 'node': node,
+ 'function': 'validateRule()',
+ 'arguments': 'compare: ' + compared + ' rule: ' + rule,
+ 'message': 'WARNING - Invalid rule, "L" compare must be numeric'
+ });
+
+ return false;
+
+ } else {
+
+ if (!value || eval(value.length + operator + parseFloat(compared)) === false) {
+ throw [options.messages[operator], compared];
+ }
+
+ }
+
+ break;
+ case "V":
+ default:
+ if (isNaN(compared)) {
+
+ comparedValue = node.find('[name="' + compared + '"]').val();
+ if (!comparedValue) {
+ options.debug && window.Debug.log({
+ 'node': node,
+ 'function': 'validateRule()',
+ 'arguments': 'compare: ' + compared + ' rule: ' + rule,
+ 'message': 'WARNING - Unable to find compared field [name="' + compared + '"]'
+ });
+
+ return false;
+ }
+
+ if (!value || !eval('"' + encodeURIComponent(value) + '"' + operator + '"' + encodeURIComponent(comparedValue) + '"')) {
+ throw [options.messages[operator].replace(' characters', ''), compared];
+ }
+
+ } else {
+ if (!value || isNaN(value) || !eval(value + operator + parseFloat(compared))) {
+ throw [options.messages[operator].replace(' characters', ''), compared];
+ }
+
+ }
+ break;
+
+ }
+
+ }
+
+ function registerError(inputName, error) {
+
+ if (!errors[inputName]) {
+ errors[inputName] = [];
+ }
+
+ error = error.capitalize();
+
+ var hasError = false;
+ for (var i = 0; i < errors[inputName].length; i++) {
+ if (errors[inputName][i] === error) {
+ hasError = true;
+ break;
+ }
+ }
+
+ if (!hasError) {
+ errors[inputName].push(error);
+ }
+
+ }
+
+ function displayOneError(inputName) {
+
+ var input,
+ inputId,
+ errorContainer,
+ label,
+ html = '
',
+ group,
+ groupInput;
+
+ if (!errors.hasOwnProperty(inputName)) {
+ return false;
+ }
+
+ input = node.find('[name="' + inputName + '"]');
+
+ label = null;
+
+ if (!input[0]) {
+ options.debug && window.Debug.log({
+ 'node': node,
+ 'function': 'displayOneError()',
+ 'arguments': '[name="' + inputName + '"]',
+ 'message': 'ERROR - Unable to find input by name "' + inputName + '"'
+ });
+
+ return false;
+ }
+
+ group = input.attr(_data.group);
+
+ if (group) {
+
+ groupInput = node.find('[name="' + inputName + '"]');
+ label = node.find('[id="' + group + '"]');
+
+ if (label[0]) {
+ label.addClass(options.submit.settings.errorClass);
+ errorContainer = label;
+ }
+
+ } else {
+
+ input.addClass(options.submit.settings.errorClass);
+
+ if (options.submit.settings.inputContainer) {
+ input.parentsUntil(node, options.submit.settings.inputContainer).addClass(options.submit.settings.errorClass);
+ }
+
+ inputId = input.attr('id');
+
+ if (inputId) {
+ label = node.find('label[for="' + inputId + '"]')[0];
+ }
+
+ if (!label) {
+ label = input.parentsUntil(node, 'label')[0];
+ }
+
+ if (label) {
+ label = $(label);
+ label.addClass(options.submit.settings.errorClass);
+ }
+ }
+
+ if (options.submit.settings.display === 'inline') {
+ if (options.submit.settings.errorListContainer) {
+ errorContainer = input.parentsUntil(node, options.submit.settings.errorListContainer);
+ } else {
+ errorContainer = errorContainer || input.parent();
+ }
+ } else if (options.submit.settings.display === 'block') {
+ errorContainer = node;
+ }
+ if (options.submit.settings.display === 'inline' && errorContainer.find('[' + _data.errorList + ']')[0]) {
+ return false;
+ }
+
+ if (options.submit.settings.display === "inline" ||
+ (options.submit.settings.display === "block" && !errorContainer.find('[' + _data.errorList + ']')[0])
+ ) {
+ if (options.submit.settings.insertion === 'append') {
+ errorContainer.append(html);
+ } else if (options.submit.settings.insertion === 'prepend') {
+ errorContainer.prepend(html);
+ }
+ }
+
+ for (var i = 0; i < errors[inputName].length; i++) {
+ if (options.submit.settings.errorTemplate) {
+ errorContainer.find('[' + _data.errorList + '] ul')
+ .append('' + options.submit.settings.errorTemplate.replace('{{validation-message}}', errors[inputName][i]) + '');
+ } else {
+ errorContainer.find('[' + _data.errorList + '] ul').append('' + errors[inputName][i] + '');
+ }
+ }
+
+ if (options.submit.settings.clear || options.dynamic.settings.trigger) {
+
+ if (group && groupInput) {
+ input = groupInput;
+ }
+
+ var event = "coucou" + resetSuffix;
+ if (options.submit.settings.clear) {
+ event += " " + options.submit.settings.clear + resetSuffix;
+ if (~['radio', 'checkbox'].indexOf(input[0].type)) {
+ event += " change" + resetSuffix;
+ }
+ }
+ if (options.dynamic.settings.trigger) {
+ event += " " + options.dynamic.settings.trigger + resetSuffix;
+ if (options.dynamic.settings.trigger !== "focusout" && !~['radio', 'checkbox'].indexOf(input[0].type)) {
+ event += " change" + resetSuffix + " paste" + resetSuffix;
+ }
+ }
+
+ input.unbind(event).on(event, function(a, b, c, d, e) {
+
+ return function() {
+ if (e) {
+ if ($(c).hasClass(options.submit.settings.errorClass)) {
+ resetOneError(a, b, c, d, e);
+ }
+ } else if ($(b).hasClass(options.submit.settings.errorClass)) {
+ resetOneError(a, b, c, d);
+ }
+ };
+
+ }(inputName, input, label, errorContainer, group));
+ }
+
+ if (options.submit.settings.scrollToError && !window.Validation.hasScrolled) {
+
+ window.Validation.hasScrolled = true;
+
+ var offset = parseFloat(options.submit.settings.scrollToError.offset) || 0,
+ duration = parseFloat(options.submit.settings.scrollToError.duration) || 500,
+ handle = (options.submit.settings.display === 'block') ? errorContainer : input;
+
+ $('html, body').animate({
+ scrollTop: handle.offset().top + offset
+ }, duration);
+
+ }
+
+ }
+
+ function displayErrors() {
+
+ for (var inputName in errors) {
+ if (!errors.hasOwnProperty(inputName)) continue;
+ displayOneError(inputName);
+ }
+
+ }
+
+ function resetOneError(inputName, input, label, container, group) {
+
+ delete errors[inputName];
+
+ if (container) {
+
+ if (options.submit.settings.inputContainer) {
+ (group ? label : input).parentsUntil(node, options.submit.settings.inputContainer).removeClass(options.submit.settings.errorClass);
+ }
+
+ label && label.removeClass(options.submit.settings.errorClass);
+
+ input.removeClass(options.submit.settings.errorClass);
+
+ if (options.submit.settings.display === 'inline') {
+ container.find('[' + _data.errorList + ']').remove();
+ }
+
+ } else {
+
+ if (!input) {
+ input = node.find('[name="' + inputName + '"]');
+
+ if (!input[0]) {
+ options.debug && window.Debug.log({
+ 'node': node,
+ 'function': 'resetOneError()',
+ 'arguments': '[name="' + inputName + '"]',
+ 'message': 'ERROR - Unable to find input by name "' + inputName + '"'
+ });
+
+ return false;
+ }
+ }
+
+ input.trigger('coucou' + resetSuffix);
+ }
+
+ }
+
+ function resetErrors() {
+
+ errors = [];
+ window.Validation.hasScrolled = false;
+
+ node.find('[' + _data.errorList + ']').remove();
+ node.find('.' + options.submit.settings.errorClass).removeClass(options.submit.settings.errorClass);
+
+ }
+
+ function submitForm() {
+
+ node[0].submit()
+
+ }
+
+ function destroy() {
+
+ resetErrors();
+ node.find('[' + _data.validation + '],[' + _data.regex + ']').off(delegateSuffix + ' ' + resetSuffix);
+
+ node.find(options.submit.settings.button).off(delegateSuffix).on('click' + delegateSuffix, function() {
+ $(this).closest('form')[0].submit();
+ });
+
+ return true;
+
+ }
+
+ var _getInputValue = function(input) {
+
+ var value;
+ switch ($(input).attr('type')) {
+ case 'checkbox':
+ value = ($(input).is(':checked')) ? 1 : '';
+ break;
+ case 'radio':
+ value = node.find('input[name="' + $(input).attr('name') + '"]:checked').val() || '';
+ break;
+ default:
+ value = $(input).val();
+ break;
+ }
+
+ return value;
+
+ };
+
+ var _typeWatch = (function() {
+ var timer = 0;
+ return function(callback, ms) {
+ clearTimeout(timer);
+ timer = setTimeout(callback, ms);
+ };
+ })();
+
+ var _executeCallback = function(callback, extraParams) {
+
+ if (!callback) {
+ return false;
+ }
+
+ var _callback;
+
+ if (typeof callback === "function") {
+
+ _callback = callback;
+
+ } else if (typeof callback === "string" || callback instanceof Array) {
+
+ _callback = window;
+
+ if (typeof callback === "string") {
+ callback = [callback, []];
+ }
+
+ var _exploded = callback[0].split('.'),
+ _params = callback[1],
+ _isValid = true,
+ _splitIndex = 0;
+
+ while (_splitIndex < _exploded.length) {
+
+ if (typeof _callback !== 'undefined') {
+ _callback = _callback[_exploded[_splitIndex++]];
+ } else {
+ _isValid = false;
+ break;
+ }
+ }
+
+ if (!_isValid || typeof _callback !== "function") {
+ options.debug && window.Debug.log({
+ 'node': node,
+ 'function': '_executeCallback()',
+ 'arguments': JSON.stringify(callback),
+ 'message': 'WARNING - Invalid callback function"'
+ });
+
+ return false;
+ }
+
+ }
+
+ return _callback.apply(this, $.merge(_params || [], (extraParams) ? extraParams : []));
+
+ };
+
+ this.__construct = function() {
+
+ extendOptions();
+ extendRules();
+ extendMessages();
+
+ delegateDynamicValidation();
+ delegateValidation();
+ options.debug && window.Debug.print();
+
+ }();
+
+ return {
+
+ registerError: registerError,
+
+ displayOneError: displayOneError,
+
+ displayErrors: displayErrors,
+
+ resetOneError: resetOneError,
+
+ resetErrors: resetErrors,
+
+ destroy: destroy
+
+ };
+
+ };
+
+ $.fn.validate = $.validate = function(options) {
+
+ return _api.validate(this, options);
+
+ };
+
+ $.fn.addValidation = function(validation) {
+
+ return _api.addValidation(this, validation);
+
+ };
+
+ $.fn.removeValidation = function(validation) {
+
+ return _api.removeValidation(this, validation);
+
+ };
+
+ $.fn.addError = function(error) {
+
+ return _api.addError(this, error);
+
+ };
+
+ $.fn.removeError = function(error) {
+
+ return _api.removeError(this, error);
+
+ };
+
+ $.fn.alterValidationRules = $.alterValidationRules = function(rules) {
+
+ if (!(rules instanceof Array)) {
+ rules = [rules];
+ }
+
+ for (var i = 0; i < rules.length; i++) {
+ _api.alterValidationRules(rules[i]);
+ }
+
+ };
+
+ var _api = {
+
+ _formatValidation: function(validation) {
+
+ validation = validation.toString().replace(/\s/g, '');
+
+ if (validation.charAt(0) === "[" && validation.charAt(validation.length - 1) === "]") {
+ validation = validation.replace(/^\[|]$/g, '');
+ }
+
+ return validation;
+
+ },
+
+ _splitValidation: function(validation) {
+
+ var validationArray = this._formatValidation(validation).split(','),
+ oneValidation;
+
+ for (var i = 0; i < validationArray.length; i++) {
+ oneValidation = validationArray[i];
+ if (/^[a-z]+$/i.test(oneValidation)) {
+ validationArray[i] = oneValidation.toUpperCase();
+ }
+ }
+
+ return validationArray;
+ },
+
+ _joinValidation: function(validation) {
+
+ return '[' + validation.join(', ') + ']';
+
+ },
+
+ validate: function(node, options) {
+
+ if (typeof node === "function") {
+
+ if (!options.submit.settings.form) {
+ window.Debug.log({
+ 'node': node,
+ 'function': '$.validate()',
+ 'arguments': '',
+ 'message': 'Undefined property "options.submit.settings.form - Validation dropped'
+ });
+
+ window.Debug.print();
+
+ return;
+ }
+
+ node = $(options.submit.settings.form);
+
+ if (!node[0] || node[0].nodeName.toLowerCase() !== "form") {
+ window.Debug.log({
+ 'function': '$.validate()',
+ 'arguments': options.submit.settings.form,
+ 'message': 'Unable to find jQuery form element - Validation dropped'
+ });
+
+ window.Debug.print();
+
+ return;
+ }
+
+ } else if (typeof node[0] === 'undefined') {
+ window.Debug.log({
+ 'node': node,
+ 'function': '$.validate()',
+ 'arguments': '$("' + node.selector + '").validate()',
+ 'message': 'Unable to find jQuery form element - Validation dropped'
+ });
+
+ window.Debug.print();
+
+ return;
+ }
+
+ if (options === "destroy") {
+
+ if (!window.Validation.form[node.selector]) {
+ window.Debug.log({
+ 'node': node,
+ 'function': '$.validate("destroy")',
+ 'arguments': '',
+ 'message': 'Unable to destroy "' + node.selector + '", perhaps it\'s already destroyed?'
+ });
+
+ window.Debug.print();
+
+ return;
+ }
+
+ window.Validation.form[node.selector].destroy();
+
+ return;
+
+ }
+
+ return node.each(function() {
+ window.Validation.form[node.selector] = new Validation($(this), options);
+ });
+
+ },
+
+ addValidation: function(node, validation) {
+
+ var self = this;
+
+ validation = self._splitValidation(validation);
+
+ if (!validation) {
+ return false;
+ }
+
+ return node.each(function() {
+
+ var $this = $(this),
+ validationData = $this.attr(_data.validation),
+ validationArray = (validationData && validationData.length) ? self._splitValidation(validationData) : [],
+ oneValidation;
+
+ for (var i = 0; i < validation.length; i++) {
+
+ oneValidation = self._formatValidation(validation[i]);
+
+ if ($.inArray(oneValidation, validationArray) === -1) {
+ validationArray.push(oneValidation);
+ }
+ }
+
+ if (validationArray.length) {
+ $this.attr(_data.validation, self._joinValidation(validationArray));
+ }
+
+ });
+
+ },
+
+ removeValidation: function(node, validation) {
+
+ var self = this;
+
+ validation = self._splitValidation(validation);
+ if (!validation) {
+ return false;
+ }
+
+ return node.each(function() {
+
+ var $this = $(this),
+ validationData = $this.attr(_data.validation),
+ validationArray = (validationData && validationData.length) ? self._splitValidation(validationData) : [],
+ oneValidation,
+ validationIndex;
+
+ if (!validationArray.length) {
+ $this.removeAttr(_data.validation);
+ return true;
+ }
+
+ for (var i = 0; i < validation.length; i++) {
+ oneValidation = self._formatValidation(validation[i]);
+ validationIndex = $.inArray(oneValidation, validationArray);
+ if (validationIndex !== -1) {
+ validationArray.splice(validationIndex, 1);
+ }
+
+ }
+
+ if (!validationArray.length) {
+ $this.removeAttr(_data.validation);
+ return true;
+ }
+
+ $this.attr(_data.validation, self._joinValidation(validationArray));
+
+ });
+
+ },
+
+ addError: function(node, error) {
+
+ if (!window.Validation.form[node.selector]) {
+ window.Debug.log({
+ 'node': node,
+ 'function': '$.addError()',
+ 'arguments': 'window.Validation.form[' + node.selector + ']',
+ 'message': 'ERROR - Invalid node selector'
+ });
+
+ window.Debug.print();
+
+ return false;
+ }
+
+ if (typeof error !== "object" || Object.prototype.toString.call(error) !== "[object Object]") {
+ window.Debug.log({
+ 'node': node,
+ 'function': '$.addError()',
+ 'arguments': 'window.Validation.form[' + node.selector + ']',
+ 'message': 'ERROR - Invalid argument, must be type object'
+ });
+
+ window.Debug.print();
+
+ return false;
+ }
+
+ var input,
+ onlyOnce = true;
+ for (var inputName in error) {
+
+ if (!error.hasOwnProperty(inputName)) {
+ continue;
+ }
+
+ if (!(error[inputName] instanceof Array)) {
+ error[inputName] = [error[inputName]];
+ }
+
+ input = $(node.selector).find('[name="' + inputName + '"]');
+ if (!input[0]) {
+ window.Debug.log({
+ 'node': node,
+ 'function': '$.addError()',
+ 'arguments': inputName,
+ 'message': 'ERROR - Unable to find ' + '$(' + node.selector + ').find("[name="' + inputName + '"]")'
+ });
+
+ window.Debug.print();
+
+ continue;
+ }
+
+ if (onlyOnce) {
+ window.Validation.hasScrolled = false;
+ onlyOnce = false;
+ }
+
+ window.Validation.form[node.selector].resetOneError(inputName, input);
+
+ for (var i = 0; i < error[inputName].length; i++) {
+
+ if (typeof error[inputName][i] !== "string") {
+ window.Debug.log({
+ 'node': node,
+ 'function': '$.addError()',
+ 'arguments': error[inputName][i],
+ 'message': 'ERROR - Invalid error object property - Accepted format: {"inputName": "errorString"} or {"inputName": ["errorString", "errorString"]}'
+ });
+
+ window.Debug.print();
+
+ continue;
+ }
+
+ window.Validation.form[node.selector].registerError(inputName, error[inputName][i]);
+
+ }
+
+ window.Validation.form[node.selector].displayOneError(inputName);
+
+ }
+
+ },
+
+ removeError: function(node, inputName) {
+
+ if (!window.Validation.form[node.selector]) {
+ window.Debug.log({
+ 'node': node,
+ 'function': '$.removeError()',
+ 'arguments': 'window.Validation.form[' + node.selector + ']',
+ 'message': 'ERROR - Invalid node selector'
+ });
+
+ window.Debug.print();
+
+ return false;
+ }
+
+ if (!inputName) {
+ window.Validation.form[node.selector].resetErrors();
+ return false;
+ }
+
+ if (typeof inputName === "object" && Object.prototype.toString.call(inputName) !== "[object Array]") {
+ window.Debug.log({
+ 'node': node,
+ 'function': '$.removeError()',
+ 'arguments': inputName,
+ 'message': 'ERROR - Invalid inputName, must be type String or Array'
+ });
+
+ window.Debug.print();
+
+ return false;
+ }
+
+ if (!(inputName instanceof Array)) {
+ inputName = [inputName];
+ }
+
+ var input;
+ for (var i = 0; i < inputName.length; i++) {
+
+ input = $(node.selector).find('[name="' + inputName[i] + '"]');
+ if (!input[0]) {
+ window.Debug.log({
+ 'node': node,
+ 'function': '$.removeError()',
+ 'arguments': inputName[i],
+ 'message': 'ERROR - Unable to find ' + '$(' + node.selector + ').find("[name="' + inputName[i] + '"]")'
+ });
+
+ window.Debug.print();
+
+ continue;
+ }
+
+ window.Validation.form[node.selector].resetOneError(inputName[i], input);
+
+ }
+
+ },
+
+ alterValidationRules: function(ruleObj) {
+
+ if (!ruleObj.rule || (!ruleObj.regex && !ruleObj.message)) {
+ window.Debug.log({
+ 'function': '$.alterValidationRules()',
+ 'message': 'ERROR - Missing one or multiple parameter(s) {rule, regex, message}'
+ });
+ window.Debug.print();
+ return false;
+ }
+
+ ruleObj.rule = ruleObj.rule.toUpperCase();
+
+ if (ruleObj.regex) {
+
+ var regex = _buildRegexFromString(ruleObj.regex);
+
+ if (!(regex instanceof RegExp)) {
+ window.Debug.log({
+ 'function': '$.alterValidationRules(rule)',
+ 'arguments': regex.toString(),
+ 'message': 'ERROR - Invalid rule'
+ });
+ window.Debug.print();
+ return false;
+ }
+
+ _rules[ruleObj.rule] = regex;
+ }
+
+ if (ruleObj.message) {
+ _messages[ruleObj.rule] = ruleObj.message;
+ }
+
+ return true;
+ }
+
+ };
+
+ function _buildRegexFromString(regex) {
+
+ if (!regex || (typeof regex !== "string" && !(regex instanceof RegExp))) {
+ _regexDebug();
+ return false;
+ }
+
+ if (typeof regex !== 'string') {
+ regex = regex.toString();
+ }
+
+ var separator = regex.charAt(0),
+ index = regex.length - 1,
+ pattern,
+ modifier,
+ rule;
+
+ while (index > 0) {
+ if (/[gimsxeU]/.test(regex.charAt(index))) {
+ index--;
+ } else {
+ break;
+ }
+ }
+
+ if (regex.charAt(index) !== separator) {
+ separator = null;
+ }
+
+ if (separator && index !== regex.length - 1) {
+ modifier = regex.substr(index + 1, regex.length - 1);
+ }
+
+ if (separator) {
+ pattern = regex.substr(1, index - 1);
+ } else {
+ pattern = regex;
+ }
+
+ try {
+ rule = new RegExp(pattern, modifier);
+ } catch (error) {
+ _regexDebug();
+ return false;
+ }
+
+ return rule;
+
+ function _regexDebug() {
+ window.Debug.log({
+ 'function': '_buildRegexFromString()',
+ 'arguments': '{pattern: {' + (pattern || '') + '}, modifier: {' + (modifier || '') + '}',
+ 'message': 'WARNING - Invalid regex given: ' + regex
+ });
+ window.Debug.print();
+ }
+
+ }
+ window.Debug = {
+
+ table: {},
+ log: function(debugObject) {
+
+ if (!debugObject.message || typeof debugObject.message !== "string") {
+ return false;
+ }
+
+ this.table[debugObject.message] = $.extend(
+ Object.preventExtensions({
+ 'node': '',
+ 'function': '',
+ 'arguments': ''
+ }), debugObject
+ );
+
+ },
+ print: function() {
+
+ if (isEmpty(this.table)) {
+ return false;
+ }
+
+ if (console.group !== undefined || console.table !== undefined) {
+
+ console.groupCollapsed('--- jQuery Form Validation Debug ---');
+
+ if (console.table) {
+ console.table(this.table);
+ } else {
+ $.each(this.table, function(index, data) {
+ console.log(data['Name'] + ': ' + data['Execution Time'] + 'ms');
+ });
+ }
+
+ console.groupEnd();
+
+ } else {
+ console.log('Debug is not available on your current browser, try the most recent version of Chrome or Firefox.');
+ }
+
+ this.table = {};
+
+ }
+
+ };
+
+ String.prototype.capitalize = function() {
+ return this.charAt(0).toUpperCase() + this.slice(1);
+ };
+
+ function isEmpty(obj) {
+ for (var prop in obj) {
+ if (obj.hasOwnProperty(prop))
+ return false;
+ }
+
+ return true;
+ }
+
+ if (!Array.prototype.indexOf) {
+ Array.prototype.indexOf = function(elt) {
+ var len = this.length >>> 0;
+
+ var from = Number(arguments[1]) || 0;
+ from = (from < 0) ? Math.ceil(from) : Math.floor(from);
+ if (from < 0)
+ from += len;
+
+ for (; from < len; from++) {
+ if (from in this &&
+ this[from] === elt)
+ return from;
+ }
+ return -1;
+ };
+ }
+ if (!JSON && !JSON.stringify) {
+ JSON.stringify = function(obj) {
+ var t = typeof(obj);
+ if (t !== "object" || obj === null) {
+ if (t === "string") {
+ obj = '"' + obj + '"';
+ }
+ return String(obj);
+ } else {
+ var n, v, json = [],
+ arr = (obj && obj.constructor === Array);
+ for (n in obj) {
+ if (true) {
+ v = obj[n];
+ t = typeof(v);
+ if (t === "string") {
+ v = '"' + v + '"';
+ } else if (t === "object" && v !== null) {
+ v = JSON.stringify(v);
+ }
+ json.push((arr ? "" : '"' + n + '": ') + String(v));
+ }
+ }
+ return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}");
+ }
+ };
+ }
+
+}(window, document, window.jQuery));
diff --git a/dist/jquery.validation.min.js b/dist/jquery.validation.min.js
new file mode 100644
index 0000000..95e1d6b
--- /dev/null
+++ b/dist/jquery.validation.min.js
@@ -0,0 +1,10 @@
+/*!
+ * jQuery Form Validation
+ * Copyright (C) 2015 RunningCoder.org
+ * Licensed under the MIT license
+ *
+ * @author Tom Bertrand
+ * @version 1.5.3 (2015-12-02)
+ * @link http://www.runningcoder.org/jqueryvalidation/
+*/
+!function(window,document,$,undefined){function _buildRegexFromString(a){function b(){}if(!a||"string"!=typeof a&&!(a instanceof RegExp))return b(),!1;"string"!=typeof a&&(a=a.toString());for(var c,d,e,f=a.charAt(0),g=a.length-1;g>0&&/[gimsxeU]/.test(a.charAt(g));)g--;a.charAt(g)!==f&&(f=null),f&&g!==a.length-1&&(d=a.substr(g+1,a.length-1)),c=f?a.substr(1,g-1):a;try{e=new RegExp(c,d)}catch(h){return b(),!1}return e}function isEmpty(a){for(var b in a)if(a.hasOwnProperty(b))return!1;return!0}window.Validation={form:[],labels:{},hasScrolled:!1},"function"!=typeof Object.preventExtensions&&(Object.preventExtensions=function(a){return a});var _rules={NOTEMPTY:/\S/,INTEGER:/^\d+$/,NUMERIC:/^\d+(?:[,\s]\d{3})*(?:\.\d+)?$/,MIXED:/^[\w\s-]+$/,NAME:/^['a-zãàáäâẽèéëêìíïîõòóöôùúüûñç\s-]+$/i,NOSPACE:/^(?!\s)\S*$/,TRIM:/^[^\s].*[^\s]$/,DATE:/^\d{4}-\d{2}-\d{2}(\s\d{2}:\d{2}(:\d{2})?)?$/,EMAIL:/^([^@]+?)@(([a-z0-9]-*)*[a-z0-9]+\.)+([a-z0-9]+)$/i,URL:/^(https?:\/\/)?((([a-z0-9]-*)*[a-z0-9]+\.?)*([a-z0-9]+))(\/[\w?=\.-]*)*$/,PHONE:/^(\()?\d{3}(\))?(-|\s)?\d{3}(-|\s)\d{4}$/,OPTIONAL:/\S/,COMPARISON:/^\s*([LV])\s*([<>]=?|==|!=)\s*([^<>=!]+?)\s*$/},_messages={"default":"$ contain error(s).",NOTEMPTY:"$ must not be empty.",INTEGER:"$ must be an integer.",NUMERIC:"$ must be numeric.",MIXED:"$ must be letters or numbers (no special characters).",NAME:"$ must not contain special characters.",NOSPACE:"$ must not contain spaces.",TRIM:"$ must not start or end with space character.",DATE:"$ is not a valid with format YYYY-MM-DD.",EMAIL:"$ is not valid.",URL:"$ is not valid.",PHONE:"$ is not a valid phone number.","<":"$ must be less than % characters.","<=":"$ must be less or equal to % characters.",">":"$ must be greater than % characters.",">=":"$ must be greater or equal to % characters.","==":"$ must be equal to %","!=":"$ must be different than %"},_data={validation:"data-validation",validationMessage:"data-validation-message",regex:"data-validation-regex",regexReverse:"data-validation-regex-reverse",regexMessage:"data-validation-regex-message",group:"data-validation-group",label:"data-validation-label",errorList:"data-error-list"},_options={submit:{settings:{form:null,display:"inline",insertion:"append",allErrors:!1,trigger:"click",button:"[type='submit']",errorClass:"error",errorListClass:"error-list",errorListContainer:null,errorTemplate:null,inputContainer:null,clear:"focusin",scrollToError:!1},callback:{onInit:null,onValidate:null,onError:null,onBeforeSubmit:null,onSubmit:null,onAfterSubmit:null}},dynamic:{settings:{trigger:null,delay:300},callback:{onSuccess:null,onError:null,onComplete:null}},rules:{},messages:{},labels:{},debug:!1},_supported={submit:{settings:{display:["inline","block"],insertion:["append","prepend"],allErrors:[!0,!1],clear:["focusin","keypress",!1],trigger:["click","dblclick","focusout","hover","mousedown","mouseenter","mouseleave","mousemove","mouseout","mouseover","mouseup","toggle"]}},dynamic:{settings:{trigger:["focusout","keydown","keypress","keyup"]}},debug:[!0,!1]},Validation=function(node,options){function extendRules(){options.rules=$.extend(!0,{},_rules,options.rules)}function extendMessages(){options.messages=$.extend(!0,{},_messages,options.messages)}function extendOptions(){options instanceof Object||(options={});var a=Object.preventExtensions($.extend(!0,{},_options));for(var b in options)if(options.hasOwnProperty(b)&&"debug"!==b)if(~["labels","messages","rules"].indexOf(b)&&options[b]instanceof Object)a[b]=options[b];else if(_options[b]&&options[b]instanceof Object)for(var c in options[b])if(options[b].hasOwnProperty(c)&&_options[b][c]&&options[b][c]instanceof Object){for(var d in options[b][c])options[b][c].hasOwnProperty(d)&&_supported[b]&&_supported[b][c]&&_supported[b][c][d]&&-1===$.inArray(options[b][c][d],_supported[b][c][d])&&delete options[b][c][d];a[b]&&a[b][c]&&(a[b][c]=$.extend(Object.preventExtensions(a[b][c]),options[b][c]))}a.dynamic.settings.trigger&&"keypress"===a.dynamic.settings.trigger&&"keypress"===a.submit.settings.clear&&(a.dynamic.settings.trigger="keydown"),options=a}function delegateDynamicValidation(){if(!options.dynamic.settings.trigger)return!1;if(!node.find("["+_data.validation+"],["+_data.regex+"]")[0])return!1;var a=options.dynamic.settings.trigger+delegateSuffix;"focusout"!==options.dynamic.settings.trigger&&(a+=" change"+delegateSuffix+" paste"+delegateSuffix),$.each(node.find("["+_data.validation+"],["+_data.regex+"]"),function(b,c){$(c).unbind(a).on(a,function(a){if($(this).is(":disabled"))return!1;var b=this,c=a.keyCode||null;_typeWatch(function(){validateInput(b)?_executeCallback(options.dynamic.callback.onSuccess,[node,b,c]):(displayOneError(b.name),_executeCallback(options.dynamic.callback.onError,[node,b,c,errors[b.name]])),_executeCallback(options.dynamic.callback.onComplete,[node,b,c])},options.dynamic.settings.delay)})})}function delegateValidation(){_executeCallback(options.submit.callback.onInit,[node]);var a=options.submit.settings.trigger+".vd";return node.find(options.submit.settings.button)[0]?(node.on("submit",!1),void node.find(options.submit.settings.button).off(".vd").on(a,function(a){return a.preventDefault(),resetErrors(),_executeCallback(options.submit.callback.onValidate,[node]),validateForm()?(_executeCallback(options.submit.callback.onBeforeSubmit,[node]),"function"==typeof options.submit.callback.onSubmit?_executeCallback(options.submit.callback.onSubmit,[node,formData])===!0&&submitForm():submitForm(),_executeCallback(options.submit.callback.onAfterSubmit,[node])):(displayErrors(),_executeCallback(options.submit.callback.onError,[node,errors,formData])),!1})):!1}function validateForm(){var a=isEmpty(errors);return formData={},$.each(node.find('input:not([type="submit"]), select, textarea').not(":disabled"),function(b,c){c=$(c);var d=_getInputValue(c[0]),e=c.attr("name");e&&(/\[]$/.test(e)?(e=e.replace(/\[]$/,""),formData[e]instanceof Array||(formData[e]=[]),formData[e].push(d)):formData[e]=d),(c.attr(_data.validation)||c.attr(_data.regex))&&(validateInput(c[0],d)||(a=!1))}),prepareFormData(),a}function prepareFormData(){var a,b,c={};for(var d in formData)if(formData.hasOwnProperty(d)){b=0,a=d.split(/\[(.+?)]/g);for(var e={},f=[],g=a.length-1;g>=0;g--)""!==a[g]?(f.length<1?e[a[g]]=Number(formData[d])||formData[d]:(e={},e[a[g]]=f[f.length-1]),f.push(e)):a.splice(g,1);c=$.extend(!0,c,e)}formData=c}function validateInput(a,b){var c=$(a).attr("name"),b=b||_getInputValue(a);if(!c)return!1;var d=c.replace(/]$/,"").split(/]\[|[[\]]/g),e=window.Validation.labels[c]||options.labels[c]||$(a).attr(_data.label)||d[d.length-1],f=$(a).attr(_data.validation),g=$(a).attr(_data.validationMessage),h=$(a).attr(_data.regex),i=!($(a).attr(_data.regexReverse)===undefined),j=$(a).attr(_data.regexMessage),k=!1;if(f&&(f=_api._splitValidation(f)),f instanceof Array&&f.length>0){if(""===$.trim(b)&&~f.indexOf("OPTIONAL"))return!0;$.each(f,function(a,d){if(k===!0)return!0;try{validateRule(b,d)}catch(f){(g||!options.submit.settings.allErrors)&&(k=!0),f[0]=g||f[0],registerError(c,f[0].replace("$",e).replace("%",f[1]))}})}if(h){var l=_buildRegexFromString(h);if(!(l instanceof RegExp))return!0;try{validateRule(b,l,i)}catch(m){m[0]=j||m[0],registerError(c,m[0].replace("$",e))}}return!errors[c]||errors[c]instanceof Array&&0===errors[c].length}function validateRule(value,rule,reversed){if(rule instanceof RegExp){var isValid=rule.test(value);if(reversed&&(isValid=!isValid),!isValid)throw[options.messages["default"],""]}else if(options.rules[rule]){if(!options.rules[rule].test(value))throw[options.messages[rule],""]}else{var comparison=rule.match(options.rules.COMPARISON);if(comparison&&4===comparison.length){var type=comparison[1],operator=comparison[2],compared=comparison[3],comparedValue;switch(type){case"L":if(isNaN(compared))return!1;if(!value||eval(value.length+operator+parseFloat(compared))===!1)throw[options.messages[operator],compared];break;case"V":default:if(isNaN(compared)){if(comparedValue=node.find('[name="'+compared+'"]').val(),!comparedValue)return!1;if(!value||!eval('"'+encodeURIComponent(value)+'"'+operator+'"'+encodeURIComponent(comparedValue)+'"'))throw[options.messages[operator].replace(" characters",""),compared]}else if(!value||isNaN(value)||!eval(value+operator+parseFloat(compared)))throw[options.messages[operator].replace(" characters",""),compared]}}}}function registerError(a,b){errors[a]||(errors[a]=[]),b=b.capitalize();for(var c=!1,d=0;d";if(!errors.hasOwnProperty(a))return!1;if(b=node.find('[name="'+a+'"]'),e=null,!b[0])return!1;if(f=b.attr(_data.group),f?(g=node.find('[name="'+a+'"]'),e=node.find('[id="'+f+'"]'),e[0]&&(e.addClass(options.submit.settings.errorClass),d=e)):(b.addClass(options.submit.settings.errorClass),options.submit.settings.inputContainer&&b.parentsUntil(node,options.submit.settings.inputContainer).addClass(options.submit.settings.errorClass),c=b.attr("id"),c&&(e=node.find('label[for="'+c+'"]')[0]),e||(e=b.parentsUntil(node,"label")[0]),e&&(e=$(e),e.addClass(options.submit.settings.errorClass))),"inline"===options.submit.settings.display?d=options.submit.settings.errorListContainer?b.parentsUntil(node,options.submit.settings.errorListContainer):d||b.parent():"block"===options.submit.settings.display&&(d=node),"inline"===options.submit.settings.display&&d.find("["+_data.errorList+"]")[0])return!1;("inline"===options.submit.settings.display||"block"===options.submit.settings.display&&!d.find("["+_data.errorList+"]")[0])&&("append"===options.submit.settings.insertion?d.append(h):"prepend"===options.submit.settings.insertion&&d.prepend(h));for(var i=0;i"+options.submit.settings.errorTemplate.replace("{{validation-message}}",errors[a][i])+"":""+errors[a][i]+"");if(options.submit.settings.clear||options.dynamic.settings.trigger){f&&g&&(b=g);var j="coucou"+resetSuffix;options.submit.settings.clear&&(j+=" "+options.submit.settings.clear+resetSuffix,~["radio","checkbox"].indexOf(b[0].type)&&(j+=" change"+resetSuffix)),options.dynamic.settings.trigger&&(j+=" "+options.dynamic.settings.trigger+resetSuffix,"focusout"===options.dynamic.settings.trigger||~["radio","checkbox"].indexOf(b[0].type)||(j+=" change"+resetSuffix+" paste"+resetSuffix)),b.unbind(j).on(j,function(a,b,c,d,e){return function(){e?$(c).hasClass(options.submit.settings.errorClass)&&resetOneError(a,b,c,d,e):$(b).hasClass(options.submit.settings.errorClass)&&resetOneError(a,b,c,d)}}(a,b,e,d,f))}if(options.submit.settings.scrollToError&&!window.Validation.hasScrolled){window.Validation.hasScrolled=!0;var k=parseFloat(options.submit.settings.scrollToError.offset)||0,l=parseFloat(options.submit.settings.scrollToError.duration)||500,m="block"===options.submit.settings.display?d:b;$("html, body").animate({scrollTop:m.offset().top+k},l)}}function displayErrors(){for(var a in errors)errors.hasOwnProperty(a)&&displayOneError(a)}function resetOneError(a,b,c,d,e){if(delete errors[a],d)options.submit.settings.inputContainer&&(e?c:b).parentsUntil(node,options.submit.settings.inputContainer).removeClass(options.submit.settings.errorClass),c&&c.removeClass(options.submit.settings.errorClass),b.removeClass(options.submit.settings.errorClass),"inline"===options.submit.settings.display&&d.find("["+_data.errorList+"]").remove();else{if(!b&&(b=node.find('[name="'+a+'"]'),!b[0]))return!1;b.trigger("coucou"+resetSuffix)}}function resetErrors(){errors=[],window.Validation.hasScrolled=!1,node.find("["+_data.errorList+"]").remove(),node.find("."+options.submit.settings.errorClass).removeClass(options.submit.settings.errorClass)}function submitForm(){node[0].submit()}function destroy(){return resetErrors(),node.find("["+_data.validation+"],["+_data.regex+"]").off(delegateSuffix+" "+resetSuffix),node.find(options.submit.settings.button).off(delegateSuffix).on("click"+delegateSuffix,function(){$(this).closest("form")[0].submit()}),!0}var errors=[],messages={},formData={},delegateSuffix=".vd",resetSuffix=".vr";window.Validation.hasScrolled=!1;var _getInputValue=function(a){var b;switch($(a).attr("type")){case"checkbox":b=$(a).is(":checked")?1:"";break;case"radio":b=node.find('input[name="'+$(a).attr("name")+'"]:checked').val()||"";break;default:b=$(a).val()}return b},_typeWatch=function(){var a=0;return function(b,c){clearTimeout(a),a=setTimeout(b,c)}}(),_executeCallback=function(a,b){if(!a)return!1;var c;if("function"==typeof a)c=a;else if("string"==typeof a||a instanceof Array){c=window,"string"==typeof a&&(a=[a,[]]);for(var d=a[0].split("."),e=a[1],f=!0,g=0;g>>0,c=Number(arguments[1])||0;for(c=0>c?Math.ceil(c):Math.floor(c),0>c&&(c+=b);b>c;c++)if(c in this&&this[c]===a)return c;return-1})}(window,document,window.jQuery);
\ No newline at end of file
diff --git a/example/demo.html b/example/demo.html
new file mode 100644
index 0000000..3b9e166
--- /dev/null
+++ b/example/demo.html
@@ -0,0 +1,169 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Signup_v1 Demo
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/example/jquery.validation.css b/example/jquery.validation.css
new file mode 100644
index 0000000..c2856a7
--- /dev/null
+++ b/example/jquery.validation.css
@@ -0,0 +1,385 @@
+/*------------------------------------*\
+ CONTENTS
+\*------------------------------------*/
+/*
+NORMALIZE BUTTON & INPUT - https://github.com/necolas/normalize.css
+LAYOUT
+INPUT, BUTTON & LABEL
+ERROR
+*/
+
+/*------------------------------------*\
+ NORMALIZE BUTTON & INPUT
+\*------------------------------------*/
+
+/**
+ * Known limitation: by default, Chrome and Safari on OS X allow very limited
+ * styling of `select`, unless a `border` property is set.
+ */
+
+/**
+ * 1. Correct color not being inherited.
+ * Known issue: affects color of disabled elements.
+ * 2. Correct font properties not being inherited.
+ * 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
+ */
+
+button,
+input,
+optgroup,
+select,
+textarea {
+ color: inherit; /* 1 */
+ font: inherit; /* 2 */
+ margin: 0; /* 3 */
+}
+
+/**
+ * Address `overflow` set to `hidden` in IE 8/9/10/11.
+ */
+
+button {
+ overflow: visible;
+}
+
+/**
+ * Address inconsistent `text-transform` inheritance for `button` and `select`.
+ * All other form control elements do not inherit `text-transform` values.
+ * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
+ * Correct `select` style inheritance in Firefox.
+ */
+
+button,
+select {
+ text-transform: none;
+}
+
+/**
+ * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
+ * and `video` controls.
+ * 2. Correct inability to style clickable `input` types in iOS.
+ * 3. Improve usability and consistency of cursor style between image-type
+ * `input` and others.
+ */
+
+button,
+html input[type="button"], /* 1 */
+input[type="reset"],
+input[type="submit"] {
+ -webkit-appearance: button; /* 2 */
+ cursor: pointer; /* 3 */
+}
+
+/**
+ * Re-set default cursor for disabled elements.
+ */
+
+button[disabled],
+html input[disabled] {
+ cursor: default;
+}
+
+/**
+ * Remove inner padding and border in Firefox 4+.
+ */
+
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+ border: 0;
+ padding: 0;
+}
+
+/**
+ * Address Firefox 4+ setting `line-height` on `input` using `!important` in
+ * the UA stylesheet.
+ */
+
+input {
+ line-height: normal;
+}
+
+
+/*------------------------------------*\
+ LAYOUT
+\*------------------------------------*/
+
+.validation-form-container {
+ position: relative;
+ background-color: #fff;
+ font-family: "Helvetica Neue", "Helvetica", Arial;
+ color: rgba(0, 0, 0, 0.7);
+ font-size: 16px;
+ padding: 16px;
+ width: 100%;
+ max-width: 500px;
+ border-radius: 4px;
+ -webkit-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1) inset;
+ box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1) inset;
+ line-height: 16px;
+}
+
+.validation-form-container * {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.validation-form-container :last-child {
+ margin-bottom: 0em;
+}
+
+.validation-form-container .field {
+ clear: both;
+ margin: 0em 0em 1em;
+}
+
+.validation-form-container ul {
+ list-style: none;
+ margin: 0.2em 0;
+ padding: 0;
+}
+
+.validation-form-container .ui.loader.active {
+ display: block;
+}
+.validation-form-container .ui.loader {
+ width: 32px;
+ height: 32px;
+ background: url(loader-medium.gif) no-repeat;
+ background-position: 48% 0px;
+ display: none;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ margin: 0px;
+ z-index: 1000;
+ -webkit-transform: translateX(-50%) translateY(-50%);
+ -ms-transform: translateX(-50%) translateY(-50%);
+ transform: translateX(-50%) translateY(-50%);
+}
+
+
+/*------------------------------------*\
+ INPUT, BUTTON & LABEL
+\*------------------------------------*/
+
+.validation-form-container .ui.button {
+ cursor: pointer;
+ display: inline-block;
+ vertical-align: middle;
+ min-height: 1em;
+ outline: none;
+ border: none;
+ background-color: #FAFAFA;
+ color: #808080;
+ margin: 0em;
+ padding: 0.8em 1.5em;
+ font-size: 1rem;
+ text-transform: uppercase;
+ line-height: 1;
+ font-weight: bold;
+ font-style: normal;
+ text-align: center;
+ text-decoration: none;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 0)), to(rgba(0, 0, 0, 0.05)));
+ background-image: -webkit-linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.05));
+ background-image: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.05));
+ border-radius: 0.25em;
+ -webkit-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.08) inset;
+ box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.08) inset;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ box-sizing: border-box;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+ -webkit-transition: opacity 0.25s ease, background-color 0.25s ease, color 0.25s ease, background 0.25s ease, -webkit-box-shadow 0.25s ease;
+ transition: opacity 0.25s ease, background-color 0.25s ease, color 0.25s ease, background 0.25s ease, box-shadow 0.25s ease;
+}
+
+.validation-form-container .ui.blue.button {
+ background-color: #6ECFF5;
+ color: #FFFFFF;
+}
+
+.validation-form-container .ui.blue.button:hover,
+.validation-form-container .ui.blue.button.active {
+ background-color: #1AB8F3;
+ color: #FFFFFF;
+}
+
+.validation-form-container .ui.blue.button:active {
+ background-color: #0AA5DF;
+ color: #FFFFFF;
+}
+
+.validation-form-container .ui.mini.button {
+ font-size: 0.8rem;
+ padding: 0.6em 0.8em;
+}
+
+.validation-form-container .ui.basic.button {
+ background-color: transparent !important;
+ background-image: none;
+ color: #808080 !important;
+ font-weight: normal;
+ text-transform: none;
+ -webkit-box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1) inset;
+ box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.1) inset;
+}
+
+.validation-form-container .ui.input {
+ width: 100%;
+ font-size: 1em;
+ display: inline-block;
+ position: relative;
+ color: rgba(0, 0, 0, 0.7);
+}
+
+.validation-form-container .ui.labeled.input input {
+ padding-right: 2.5em !important;
+}
+
+.validation-form-container textarea,
+.validation-form-container input[type="text"],
+.validation-form-container input[type="password"] {
+ width: 100%;
+ margin: 0em;
+ padding: 0.65em 1em;
+ font-size: 1em;
+ background-color: #FFFFFF;
+ border: 1px solid rgba(0, 0, 0, 0.15);
+ outline: none;
+ color: rgba(0, 0, 0, 0.7);
+ border-radius: 0.3125em;
+ -webkit-transition: background-color 0.3s ease-out, -webkit-box-shadow 0.2s ease, border-color 0.2s ease;
+ transition: background-color 0.3s ease-out, box-shadow 0.2s ease, border-color 0.2s ease;
+ -webkit-box-shadow: 0em 0em 0em 0em rgba(0, 0, 0, 0.3) inset;
+ box-shadow: 0em 0em 0em 0em rgba(0, 0, 0, 0.3) inset;
+ -webkit-appearance: none;
+ -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
+}
+
+.validation-form-container textarea:focus,
+.validation-form-container input[type="text"]:focus,
+.validation-form-container input[type="password"]:focus {
+ color: rgba(0, 0, 0, 0.85);
+ border-color: rgba(0, 0, 0, 0.2);
+ border-bottom-left-radius: 0;
+ border-top-left-radius: 0;
+ -webkit-appearance: none;
+ -webkit-box-shadow: 0.3em 0em 0em 0em rgba(0, 0, 0, 0.2) inset;
+ box-shadow: 0.3em 0em 0em 0em rgba(0, 0, 0, 0.2) inset;
+}
+
+.validation-form-container textarea[readonly],
+.validation-form-container textarea[disabled],
+.validation-form-container input[readonly],
+.validation-form-container input[disabled] {
+ cursor: not-allowed;
+ background-color: #f7f7f7;
+ color: #999;
+}
+
+.validation-form-container .field > label {
+ margin: 0em 0em 0.3em;
+ display: block;
+ color: #555555;
+ font-size: 0.875em;
+ position: relative;
+}
+
+.validation-form-container .ui.label {
+ display: inline-block;
+ vertical-align: middle;
+ margin: -0.25em 0.25em 0em;
+ background-color: #E8E8E8;
+ border-color: #E8E8E8;
+ padding: 0.5em 0.8em;
+ color: rgba(0, 0, 0, 0.65);
+ text-transform: uppercase;
+ font-weight: normal;
+ border-radius: 0.325em;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ box-sizing: border-box;
+ -webkit-transition: background 0.1s linear;
+ transition: background 0.1s linear;
+}
+
+.validation-form-container .ui.corner.label {
+ top: 1px;
+ right: 1px;
+ overflow: hidden;
+ font-size: 0.7em;
+ border-radius: 0 0.3125em;
+ background-color: transparent;
+ position: absolute;
+ z-index: 10;
+ margin: 0em;
+ width: 3em;
+ height: 3em;
+ padding: 0em;
+ text-align: center;
+ -webkit-transition: color 0.2s ease;
+ transition: color 0.2s ease;
+}
+
+.validation-form-container .ui.corner.label:after {
+ position: absolute;
+ content: "";
+ right: 0em;
+ top: 0em;
+ z-index: -1;
+ width: 0em;
+ height: 0em;
+ border-top: 0em solid transparent;
+ border-right: 3em solid transparent;
+ border-bottom: 3em solid transparent;
+ border-left: 0em solid transparent;
+ border-right-color: inherit;
+ -webkit-transition: border-color 0.2s ease;
+ transition: border-color 0.2s ease;
+}
+
+.validation-form-container .ui.corner.label .icon {
+ font-size: 2em;
+ margin: 0.25em 0 0 0.5em;
+ width: auto;
+ display: inline-block;
+ height: 1em;
+ font-style: normal;
+ line-height: 1;
+ font-weight: normal;
+ text-decoration: inherit;
+ text-align: center;
+ speak: none;
+ -webkit-font-smoothing: antialiased;
+ -moz-font-smoothing: antialiased;
+ font-smoothing: antialiased;
+}
+
+
+/*------------------------------------*\
+ ERROR
+\*------------------------------------*/
+
+div.error,
+div.error-list,
+label.error,
+input.error,
+select.error,
+textarea.error {
+ color: #D95C5C !important;
+ border-color: #D95C5C !important;
+}
+
+
+.validation-form-container .error .corner.label {
+ border-color: #D95C5C;
+ color: #FFFFFF;
+}
\ No newline at end of file
diff --git a/example/loader-medium.gif b/example/loader-medium.gif
new file mode 100644
index 0000000..386eb5a
Binary files /dev/null and b/example/loader-medium.gif differ
diff --git a/html5-form-validation.jquery.json b/html5-form-validation.jquery.json
index 927d811..a041b20 100644
--- a/html5-form-validation.jquery.json
+++ b/html5-form-validation.jquery.json
@@ -1,30 +1,30 @@
-{
- "name": "html5-form-validation",
- "title": "jQuery Form Validation",
- "description": "jQuery plugin that provides a client site form validation with builtin options and deep customization.",
- "keywords": [
- "form",
- "html5",
- "validate",
- "validation",
- "input"
- ],
- "version": "1.1.0",
- "author": {
- "name": "Tom Bertrand",
- "url": "http://www.runningcoder.org/jqueryvalidation/"
- },
- "licenses": [
- {
- "type": "MIT",
- "url": "https://github.com/running-coder/jquery-form-validation/blob/master/LICENSE"
- }
- ],
- "bugs": "https://github.com/running-coder/jquery-form-validation/issues",
- "homepage": "http://www.runningcoder.org/jqueryvalidation/",
- "docs": "http://www.runningcoder.org/jqueryvalidation/documentation/",
- "demo": "http://www.runningcoder.org/jqueryvalidation/demo/",
- "dependencies": {
- "jquery": ">=1.7.2"
- }
+{
+ "name": "html5-form-validation",
+ "title": "jQuery Form Validation",
+ "description": "jQuery plugin that provides a client site form validation with builtin options and deep customization.",
+ "keywords": [
+ "form",
+ "html5",
+ "validate",
+ "validation",
+ "input"
+ ],
+ "version": "1.5.3",
+ "author": {
+ "name": "Tom Bertrand",
+ "url": "http://www.runningcoder.org/jqueryvalidation/"
+ },
+ "licenses": [
+ {
+ "type": "MIT",
+ "url": "https://github.com/running-coder/jquery-form-validation/blob/master/LICENSE"
+ }
+ ],
+ "bugs": "https://github.com/running-coder/jquery-form-validation/issues",
+ "homepage": "http://www.runningcoder.org/jqueryvalidation/",
+ "docs": "http://www.runningcoder.org/jqueryvalidation/documentation/",
+ "demo": "http://www.runningcoder.org/jqueryvalidation/demo/",
+ "dependencies": {
+ "jquery": ">=1.7.2"
+ }
}
\ No newline at end of file
diff --git a/jquery.validation-1.1.0.js b/jquery.validation-1.1.0.js
deleted file mode 100644
index 4fbe637..0000000
--- a/jquery.validation-1.1.0.js
+++ /dev/null
@@ -1,1333 +0,0 @@
-/**
- * jQuery Form Validation
- *
- * @author Tom Bertrand
- * @version 1.1.0 (2014-05-26)
- *
- * @copyright
- * Copyright (C) 2014 Tom Bertrand.
- *
- * @link
- * http://www.runningcoder.org/jqueryvalidation/
- *
- * @license
- * Licensed under the MIT license.
- */
-(function (window, document, $, undefined)
-{
-
- window.Validation = {};
-
- /**
- * Fail-safe preventExtensions function for older browsers
- */
- if (typeof Object.preventExtensions !== "function") {
- Object.preventExtensions = function (obj) { return obj; }
- }
-
- // Not using strict to avoid throwing a window error on bad config extend.
- // console.debug is used instead to debug Validation
- //"use strict";
-
- // =================================================================================================================
- /**
- * @private
- * RegExp rules
- */
- var _rules = {
- // Validate not empty
- NOTEMPTY: /./,
- // Validate a numeric
- NUMERIC: /^[0-9]+$/,
- // Validate an alphanumeric string (no special chars)
- MIXED: /^[\w\s-]+$/,
- // Validate a spaceless string
- NOSPACE: /^[^\s]+$/,
- // Validate a spaceless string at start or end
- TRIM: /^[^\s].*[^\s]$/,
- // Validate a date YYYY-MM-DD
- DATE: /^\d{4}-\d{2}-\d{2}(\s\d{2}:\d{2}(:\d{2})?)?$/,
- // Validate an email
- EMAIL: /^([^@]+?)@(([a-z0-9]-*)*[a-z0-9]+\.)+([a-z0-9]+)$/i,
- // Validate an url
- URL: /^(https?:\/\/)?((([a-z0-9]-*)*[a-z0-9]+\.?)*([a-z0-9]+))(\/[\w?=\.-]*)*$/,
- // Validate a north american phone number
- PHONE: /^(\()?\d{3}(\))?(-|\s)?\d{3}(-|\s)\d{4}$/,
- // Validate value if it is not empty
- OPTIONAL: /^.*$/,
- // Validate values or length by comparison
- COMPARISON: /^\s*([LV])\s*([<>]=?|==|!=)\s*([^<>=!]+?)\s*$/,
- // Validate credit card number
- LUHN: "_validateLuhn"
- };
-
- /**
- * @private
- * Error messages
- */
- var _messages = Object.preventExtensions({
- 'default': '$ contain error(s).',
- 'NOTEMPTY': '$ must not be empty.',
- 'NUMERIC': '$ must be numeric.',
- 'STRING': '$ must be a string.',
- 'NOSPACE': '$ must not contain spaces.',
- 'TRIM': '$ must not start or end with space character.',
- 'MIXED': '$ must be letters or numbers (no special characters).',
- 'DATE': '$ is not a valid with format YYYY-MM-DD.',
- 'EMAIL': '$ is not valid.',
- 'URL': '$ is not valid.',
- 'PHONE': '$ is not a valid phone number.',
- //'INARRAY': '$ is not a valid option.',
- '<': '$ must be less than % characters.',
- '<=': '$ must be less or equal to % characters.',
- '>': '$ must be greater than % characters.',
- '>=': '$ must be greater or equal to % characters.',
- '==': '$ must be equal to %',
- '!=': '$ must be different than %'
- }),
- _extendedMessages = false;
-
- /**
- * @private
- * HTML5 data attributes
- */
- var _data = {
- validation: 'data-validation',
- validationMessage: 'data-validation-message',
- regex: 'data-validation-regex',
- regexMessage: 'data-validation-regex-message',
- validationGroup: 'data-validation-group',
- errorList: 'data-error-list'
- };
-
- /**
- * @private
- * Default options
- *
- * @link http://www.runningcoder.org/jqueryvalidation/documentation/
- */
- var _options = {
- submit: {
- settings: {
- form: null,
- display: "inline",
- insertion: "append",
- allErrors: false,
- trigger: "click",
- button: "input[type='submit']",
- errorClass: "error",
- errorListClass: "error-list",
- inputContainer: null,
- clear: "focusin",
- scrollToError: false
- },
- callback: {
- onInit: null,
- onValidate: null,
- onError: null,
- onBeforeSubmit: null,
- onSubmit: null,
- onAfterSubmit: null
- }
- },
- dynamic: {
- settings: {
- trigger: null,
- delay: 300
- },
- callback: {
- onSuccess: null,
- onError: null,
- onComplete: null
- }
- },
- messages: {}
- };
-
- /**
- * @private
- * Limit the supported options on matching keys
- */
- var _supported = {
- submit: {
- settings: {
- display: ["inline", "block"],
- insertion: ["append", "prepend"], //"before", "insertBefore", "after", "insertAfter"
- allErrors: [true, false],
- clear: ["focusin", "keypress", false],
- trigger: [
- "click", "dblclick", "focusout",
- "hover", "mousedown", "mouseenter",
- "mouseleave", "mousemove", "mouseout",
- "mouseover", "mouseup", "toggle"
- ],
- scrollToError: [true, false]
- }
- },
- dynamic: {
- settings: {
- trigger: ["focusout", "keydown", "keypress", "keyup"]
- }
- }
- };
-
- // =================================================================================================================
-
- /**
- * @constructor
- * Validation Class
- *
- * @param {object} node jQuery form object
- * @param {object} options User defined options
- */
- var Validation = function (node, options) {
-
- var errors = [],
- hasScrolled = false;
-
- /**
- * Extends user-defined "message" into the default Validation "_message".
- * Notes:
- * - preventExtensions prevents from modifying the Validation "_message" object structure
- */
- function extendMessages () {
-
- if (!window.Validation.messages || _extendedMessages) {
- return false;
- }
-
- _messages = $.extend(_messages, window.Validation.messages);
-
- _extendedMessages = true;
-
- }
-
- /**
- * Extends user-defined "options" into the default Validation "_options".
- * Notes:
- * - preventExtensions prevents from modifying the Validation "_options" object structure
- * - filter through the "_supported" to delete unsupported "options"
- */
- function extendOptions () {
-
- if (!(options instanceof Object)) {
- options = {};
- }
-
- var tpmOptions = Object.preventExtensions($.extend(true, {}, _options)),
- tmpMessages = Object.preventExtensions($.extend(true, {}, _messages));
-
- tpmOptions.messages = $.extend(tmpMessages, options.messages || {});
-
- for (var method in options) {
- if (!options.hasOwnProperty(method) || !(options[method] instanceof Object)) {
- continue;
- }
-
- for (var type in options[method]) {
- if (!options[method].hasOwnProperty(type) || !(options[method][type] instanceof Object)) {
- continue;
- }
-
- for (var option in options[method][type]) {
- if (!options[method][type].hasOwnProperty(option)) {
- continue;
- }
-
- if (_supported[method] &&
- _supported[method][type] &&
- _supported[method][type][option] &&
- $.inArray(options[method][type][option], _supported[method][type][option]) === -1) {
-
- window.debug(
- 'Validation.extendOptions - Delete unsupported property - ' + type +
- ': {' + option.toString() + ': ' + options[method][type][option].toString() + '}'
- );
-
- delete options[method][type][option];
- }
-
- }
- if (tpmOptions[method] && tpmOptions[method][type]) {
- tpmOptions[method][type] = $.extend(Object.preventExtensions(tpmOptions[method][type]), options[method][type]);
- }
- }
- }
-
- // @TODO Would there be a better fix to solve event conflict?
- if (tpmOptions.dynamic.settings.trigger) {
- if (tpmOptions.dynamic.settings.trigger === "keypress" && tpmOptions.submit.settings.clear === "keypress") {
- tpmOptions.dynamic.settings.trigger = "keydown";
- }
- }
-
- options = tpmOptions;
-
- }
-
- /**
- * Delegates the dynamic validation on data-validation and data-validation-regex attributes based on trigger.
- *
- * @returns {boolean} false if the option is not set
- */
- function delegateDynamicValidation() {
-
- if (!options.dynamic.settings.trigger) {
- return false;
- }
-
- $.each(
- $(node).find('[' + _data.validation + '],[' + _data.regex + ']'),
- function (index, input) {
-
- $(input).unbind(options.dynamic.settings.trigger).on(options.dynamic.settings.trigger, function (e) {
-
- if ($(this).is(':disabled')) {
- return false;
- }
-
- //e.preventDefault();
-
- var input = this;
-
- _typeWatch(function () {
-
- if (!validateInput(input)) {
-
- displayOneError(input.name);
- _executeCallback(options.dynamic.callback.onError, [node, input, errors[input.name]]);
-
- } else {
-
- _executeCallback(options.dynamic.callback.onSuccess, [node, input]);
-
- }
-
- _executeCallback(options.dynamic.callback.onComplete, [node, input]);
-
- }, options.dynamic.settings.delay);
-
- });
- }
- )
- }
-
- var _typeWatch = (function(){
- var timer = 0;
- return function(callback, ms){
- clearTimeout (timer);
- timer = setTimeout(callback, ms);
- }
- })();
-
- /**
- * Delegates the submit validation on data-validation and data-validation-regex attributes based on trigger.
- * Note: Disable the form submit function so the callbacks are not by-passed
- */
- function delegateValidation () {
-
- _executeCallback(options.submit.callback.onInit, [node]);
-
- $(node).on("submit", false );
- $(node).find(options.submit.settings.button).unbind(options.submit.settings.trigger).on(options.submit.settings.trigger, function (e) {
-
- e.preventDefault();
-
- resetErrors();
-
- _executeCallback(options.submit.callback.onValidate, [node]);
-
- if (!validateForm()) {
-
- // OnError function receives the "errors" object as the last "extraParam"
- _executeCallback(options.submit.callback.onError, [node, errors]);
-
- displayErrors();
-
- } else {
-
- _executeCallback(options.submit.callback.onBeforeSubmit, [node]);
-
- (options.submit.callback.onSubmit) ? _executeCallback(options.submit.callback.onSubmit, [node]) : submitForm();
-
- _executeCallback(options.submit.callback.onAfterSubmit, [node]);
-
- }
-
- return false;
-
- });
-
- }
-
- /**
- * For every "data-validation" & "data-pattern" attributes that are not disabled inside the jQuery "node" object
- * the "validateInput" function will be called.
- *
- * @returns {boolean} true if no error(s) were found (valid form)
- */
- function validateForm () {
-
- $.each(
- $(node).find('[' + _data.validation + '],[' + _data.regex + ']'),
- function (index, input) {
-
- if ($(this).is(':disabled')) {
- return false;
- }
-
- validateInput(input);
-
- }
- );
-
- return $.isEmptyObject(errors);
-
- }
-
- /**
- * Prepare the information from the data attributes
- * and call the "validateRule" function.
- *
- * @param {object} input Reference of the input element
- *
- * @returns {boolean} true if no error(s) were found (valid input)
- */
- function validateInput (input) {
-
- var inputName = $(input).attr('name');
-
- if (!inputName) {
- window.debug('Validation.validateInput - Invalid {string} inputName on ' + input.toString());
- return false;
- }
-
- var value = _getInputValue(input),
-
- matches = inputName.replace(/]$/, '').split(/]\[|[[\]]/g),
- inputShortName = matches[matches.length - 1],
-
- validationArray = $(input).attr(_data.validation),
- validationMessage = $(input).attr(_data.validationMessage),
- validationRegex = $(input).attr(_data.regex),
- validationRegexMessage = $(input).attr(_data.regexMessage),
-
- validateOnce = false;
-
- if (validationArray) {
- validationArray = _api._splitValidation(validationArray);
- }
-
- // Validates the "data-validation"
- if (!$.isEmptyObject(validationArray)) {
-
- // "OPTIONAL" input will not be validated if it's empty
- if (value === '' && $.inArray('OPTIONAL', validationArray) !== -1) {
- return true;
- }
-
- $.each(validationArray, function (i, rule) {
-
- if (validateOnce === true) {
- return true;
- }
-
- try {
-
- validateRule(value, rule);
-
- } catch (error) {
-
- if (validationMessage || !options.submit.settings.allErrors) {
- validateOnce = true;
- }
-
- error[0] = validationMessage || error[0];
-
- registerError(inputName, error[0].replace('$', inputShortName).replace('%', error[1]));
-
- }
-
- });
-
- }
-
- // Validates the "data-validation-regex"
- if (validationRegex) {
-
- var pattern = validationRegex.split('/');
-
- if (pattern.length > 1) {
-
- var tmpPattern = "";
-
- // Do not loop through the last item knowing its a potential modifier
- for (var k = 0; k < pattern.length - 1; k++) {
- if (pattern[k] !== "") {
- tmpPattern += pattern[k] + '/';
- }
- }
- // Remove last added "/"
- tmpPattern = tmpPattern.slice(0, -1);
-
- // Test the last item for modifier(s)
- if (/[gimsxeU]+/.test(pattern[pattern.length - 1])) {
- var patternModifier = pattern[pattern.length - 1];
- }
-
- pattern = tmpPattern;
- } else {
- pattern = pattern[0];
- }
-
- // Validate the regex
- try {
-
- var rule = new RegExp(pattern, patternModifier);
-
- } catch (error) {
-
- window.debug('Invalid data-validation-regex on ' + inputName);
- return true;
-
- }
-
- try {
-
- validateRule(value, rule);
-
- } catch (error) {
-
- error[0] = validationRegexMessage || error[0];
-
- registerError(inputName, error[0].replace('$', inputShortName));
-
- }
-
- }
-
- return $.isEmptyObject(errors[inputName]);
-
- }
-
- /**
- * Validate an input value against one rule.
- * If a "value-rule" mismatch occurs, an error is thrown to the caller function.
- *
- * @param {string} value
- * @param rule
- *
- * @returns {*} Error if a mismatch occurred.
- */
- function validateRule (value, rule) {
-
- // Validate for custom "data-validation-regex"
- if (rule instanceof RegExp) {
- if (rule.test(value)) {
- throw [options.messages['default'], ''];
- }
- return;
- }
-
- // Validate for predefined "data-validation" _rules
- if (_rules[rule]) {
- /*
- if (typeof _rules[rule] === "string") {
-
- try {
- var isLuhn = eval(_rules[rule] + '(' + value.toString() + ')');
- } catch (error) {
- }
-
- return;
- }
-*/
-
- if (!_rules[rule].test(value)) {
- throw [options.messages[rule], ''];
- }
- return;
- }
-
- // Validate for comparison "data-validation"
- var comparison = rule.match(_rules['COMPARISON']);
- if (!comparison || comparison.length !== 4) {
- window.debug('Validation.validateRule - Invalid validation rule: ' + rule);
- return;
- }
-
- var type = comparison[1],
- operator = comparison[2],
- compared = comparison[3],
- comparedValue;
-
- switch (type) {
-
- // Compare input "Length"
- case "L":
-
- if (isNaN(compared)) {
-
- comparedValue = $(node).find('[name="' + compared + '"]').val();
- if (!comparedValue) {
- window.debug('$.Validation.validateRule - Unable to find value of input[name="' + compared + '"] inside rule ' + rule)
- return false;
- }
-
- if (!value || !eval('"' + encodeURIComponent(value) + '"' + operator + '"' + encodeURIComponent(comparedValue) + '"')) {
- throw [options.messages[operator], compared];
- }
-
- } else {
-
- if (!value || eval(value.length + operator + parseFloat(compared)) == false) {
- throw [options.messages[operator], compared];
- }
-
- }
- break;
-
- // Compare input "Value"
- case "V":
- default:
-
- if (isNaN(compared)) {
-
- comparedValue = $(node).find('[name="' + compared + '"]').val();
- if (!comparedValue) {
- window.debug('$.Validation.validateRule - Unable to find value of input[name="' + compared + '"] inside rule ' + rule)
- return false;
- }
-
- if (!value || !eval('"' + encodeURIComponent(value) + '"' + operator + '"' + encodeURIComponent(comparedValue) + '"')) {
- throw [options.messages[operator].replace(' characters', ''), compared];
- }
-
- } else {
-
- if (!value || !eval(value + operator + parseFloat(compared))) {
- throw [options.messages[operator].replace(' characters', ''), compared];
- }
-
- }
- break;
-
- }
-
- }
-
- /**
- * Register an error into the global "error" variable.
- *
- * @param {string} inputName Input where the error occurred
- * @param {string} error Description of the error to be displayed
- */
- function registerError (inputName, error) {
-
- if (!errors[inputName]) {
- errors[inputName] = [];
- }
-
- errors[inputName].push(error.capitalize());
-
- }
-
- /**
- * Display a single error based on "inputName" key inside the "errors" global array.
- * The input, the label and the "inputContainer" will be given the "errorClass" and other
- * settings will be considered.
- *
- * @param {string} inputName Key used for search into "errors"
- *
- * @returns {boolean} false if an unwanted behavior occurs
- */
- function displayOneError (inputName) {
-
- var input,
- inputId,
- errorContainer,
- label,
- html = '',
- group,
- groupInput;
-
- if (!errors.hasOwnProperty(inputName)) {
- return false;
- }
-
- input = $(node).find('[name="' + inputName + '"]');
-
- label = null;
-
- if (!input[0]) {
- window.debug('Validation.displayOneError unable to find ' + inputName);
- return false;
- }
-
- group = input.attr(_data.validationGroup);
-
- if (group) {
-
- groupInput = $(node).find('[name="' + inputName + '"]');
- label = $(node).find('[id="' + group + '"]');
-
- if (label[0]) {
- label.addClass(options.submit.settings.errorClass);
- errorContainer = label;
- }
-
- //$(node).find('[' + _data.validationGroup + '="' + group + '"]').addClass(options.submit.settings.errorClass)
-
- } else {
-
- input.addClass(options.submit.settings.errorClass);
-
- if (options.submit.settings.inputContainer) {
- input.parentsUntil(node, options.submit.settings.inputContainer).addClass(options.submit.settings.errorClass)
- }
-
- inputId = input.attr('id');
-
- if (inputId) {
- label = $(node).find('label[for="' + inputId + '"]')[0];
- }
-
- if (!label) {
- label = input.parentsUntil(node, 'label')[0];
- }
-
- if (label) {
- label = $(label);
- label.addClass(options.submit.settings.errorClass);
- }
- }
-
- if (options.submit.settings.display === 'inline') {
- errorContainer = errorContainer || input.parent();
- } else if (options.submit.settings.display === 'block') {
- errorContainer = $(node);
- }
-
- if (options.submit.settings.display === "inline" ||
- (options.submit.settings.display === "block" && !errorContainer.find('[' + _data.errorList + ']')[0])
- ) {
- if (options.submit.settings.insertion === 'append') {
- errorContainer.append(html);
- } else if (options.submit.settings.insertion === 'prepend') {
- errorContainer.prepend(html);
- }
- }
-
- for (var i = 0; i < errors[inputName].length; i++) {
- errorContainer.find('ul').append('' + errors[inputName][i] + '');
- }
-
- if (options.submit.settings.clear || options.dynamic.settings.trigger) {
-
- if (group && groupInput) {
- input = groupInput;
- }
-
- input.unbind(options.submit.settings.clear)
- .on(options.submit.settings.clear + " " + options.dynamic.settings.trigger, function (a,b,c,d,e) {
-
- return function () {
-
- if (e) {
-
- if ($(c).hasClass(options.submit.settings.errorClass)) {
- resetOneError(a,b,c,d,e);
- }
-
- } else if ($(b).hasClass(options.submit.settings.errorClass)) {
- resetOneError(a,b,c,d);
- }
- };
-
- }(inputName, input, label, errorContainer, group))
- }
-
- if (options.submit.settings.scrollToError && !hasScrolled) {
-
- hasScrolled = true;
-
- if (typeof $.scrollTo !== 'function') {
- window.debug('Missing jQuery.scrollTo, scrolling will not happen.');
- return false;
- }
-
- $.scrollTo(
- (options.submit.settings.display === 'block') ?
- errorContainer :
- input
- , 500, { offset: -100 });
-
- }
-
- }
-
- /**
- * Display all of the errors
- */
- function displayErrors () {
-
- for (var inputName in errors) {
- displayOneError(inputName);
- }
-
- }
-
- /**
- * Remove an input error.
- *
- * @param {string} inputName Key reference to delete the error from "errors" global variable
- * @param {object} input jQuery object of the input
- * @param {object} label jQuery object of the input's label
- * @param {object} container jQuery object of the "errorList"
- * @param {string} [group] Name of the group if any (ex: used on input radio)
- */
- function resetOneError(inputName, input, label, container, group) {
-
- try {
- delete errors[inputName];
- hasScrolled = false;
- } catch(error) {
- window.debug('Validation.resetOneError unable to find and delete ' + inputName + ' inside {object} errors.');
- return false;
- }
-
- if (options.submit.settings.inputContainer) {
- (group ? label : input).parentsUntil(node, options.submit.settings.inputContainer).removeClass(options.submit.settings.errorClass)
- }
-
- label && label.removeClass(options.submit.settings.errorClass);
-
- input.removeClass(options.submit.settings.errorClass);
-
- if (options.submit.settings.display === 'inline') {
- container.find('[' + _data.errorList + ']').remove();
- }
-
- }
-
- /**
- * Remove all of the input error(s) display.
- */
- function resetErrors () {
-
- errors = [],
- hasScrolled = false;;
-
- $(node).find('[' + _data.errorList + ']').remove();
- $(node).find('.' + options.submit.settings.errorClass).removeClass(options.submit.settings.errorClass);
-
- }
-
- /**
- * Submits the form once it succeeded the validation process.
- * Note: This function will be overridden if "options.submit.settings.onSubmit" is defined
- */
- function submitForm () {
-
- node.submit();
-
- }
-
- /**
- * @private
- * Helper to get the value of an regular, radio or chackbox input
- *
- * @param input
- *
- * @returns {string} value
- */
- var _getInputValue = function (input) {
-
- var value;
-
- // Get the value or state of the input based on its type
- switch ($(input).attr('type')) {
- case 'checkbox':
- value = ($(input).is(':checked')) ? 1 : '';
- break;
- case 'radio':
- value = $(node).find('input[name="' + $(input).attr('name') + '"]:checked').val() || '';
- break;
- default:
- value = $(input).val();
- break;
- }
-
- return value;
-
- };
-
- /**
- * @private
- * Executes an anonymous function or a string reached from the window scope.
- *
- * @example
- * Note: These examples works with every callbacks (onInit, onError, onSubmit, onBeforeSubmit & onAfterSubmit)
- *
- * // An anonymous function inside the "onInit" option
- * onInit: function() { console.log(':D'); };
- *
- * * // myFunction() located on window.coucou scope
- * onInit: 'window.coucou.myFunction'
- *
- * // myFunction(a,b) located on window.coucou scope passing 2 parameters
- * onInit: ['window.coucou.myFunction', [':D', ':)']];
- *
- * // Anonymous function to execute a local function
- * onInit: function () { myFunction(':D'); }
- *
- * @param {string|array} callback The function to be called
- * @param {array} [extraParams] In some cases the function can be called with Extra parameters (onError)
- *
- * @returns {boolean}
- */
- var _executeCallback = function (callback, extraParams) {
-
- if (!callback) {
- return false;
- }
-
- var _callback;
-
- if (typeof callback === "function") {
-
- _callback = callback;
-
- } else if (typeof callback === "string" || callback instanceof Array) {
-
- _callback = window;
-
- if (typeof callback === "string") {
- callback = [callback, []];
- }
-
- var _exploded = callback[0].split('.'),
- _params = callback[1],
- _isValid = true,
- _splitIndex = 0;
-
- while (_splitIndex < _exploded.length) {
-
- if (typeof _callback !== 'undefined') {
- _callback = _callback[_exploded[_splitIndex++]];
- } else {
- _isValid = false;
- break;
- }
- }
-
- if (!_isValid || typeof _callback !== "function") {
- window.debug('Validation._executeFunction - Invalid function - ' + callback[0].toString());
- return false;
- }
-
- }
-
- _callback.apply(this, $.merge(_params || [], (extraParams) ? extraParams : []));
- return true;
-
- };
-
- var _validateLuhn = function (luhn) {
-
- var len = luhn.length,
- mul = 0,
- prodArr = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]],
- sum = 0;
-
- while (len--) {
- sum += prodArr[mul][parseInt(luhn.charAt(len), 10)];
- mul ^= 1;
- }
-
- return sum % 10 === 0 && sum > 0;
-
- };
-
-
-
- /**
- * @private
- * Constructs Validation
- */
- this.__construct = function () {
-
- extendMessages();
- extendOptions();
-
- delegateDynamicValidation();
- delegateValidation();
-
- }();
-
- return {
-
- /**
- * @public
- * Register error
- *
- * @param inputName
- * @param error
- */
- registerError: registerError,
-
- /**
- * @public
- * Display one error
- *
- * @param inputName
- */
- displayOneError: displayOneError,
-
- /**
- * @public
- * Display all errors
- */
- displayErrors: displayErrors
-
- };
-
- };
-
- // =================================================================================================================
-
- /**
- * @public
- * jQuery public function to implement the Validation on the selected node(s).
- *
- * @param {object} options To configure the Validation class.
- *
- * @return {object} Modified DOM element
- */
- $.fn.validate = $.validate = function (options) {
-
- return _api.validate(this, options);
-
- };
-
- /**
- * @public
- * jQuery public function to add one or multiple "data-validation" argument.
- *
- * @param {string|array} validation Arguments to add in the node's data-validation
- *
- * @return {object} Modified DOM element
- */
- $.fn.addValidation = function (validation) {
-
- return _api.addValidation(this, validation);
-
- };
-
- /**
- * @public
- * jQuery public function to remove one or multiple "data-validation" argument.
- *
- * @param {string|array} validation Arguments to remove in the node's data-validation
- *
- * @return {object} Modified DOM element
- */
- $.fn.removeValidation = function (validation) {
-
- return _api.removeValidation(this, validation);
-
- };
-
- /**
- * @public
- * jQuery public function to add one or multiple errors.
- *
- * @param {object} error Object of errors where the keys are the input names
- * @example
- * $('form#myForm').addError({
- * 'username': 'Invalid username, please choose another one.'
- * });
- *
- * @return {object} Modified DOM element
- */
- $.fn.addError = function (error) {
-
- return _api.addError(this, error);
-
- };
-
- // =================================================================================================================
-
- /**
- * @private
- * API to handles "addValidation" and "removeValidation" on attribute "data-validation".
- * Note: Contains fail-safe operations to unify the validation parameter.
- *
- * @example
- * $.addValidation('NOTEMPTY, L>=6')
- * $.addValidation('[notempty, v>=6]')
- * $.removeValidation(['OPTIONAL', 'V>=6'])
- *
- * @returns {object} Updated DOM object
- */
- var _api = {
-
- /**
- * @private
- * This function unifies the data through the validation process.
- * String, Uppercase and spaceless.
- *
- * @param {string|array} validation
- *
- * @returns {string}
- */
- _formatValidation: function (validation) {
-
- validation = validation.toString().replace(/\s/g, '');
-
- if (validation.charAt(0) === "[" && validation.charAt(validation.length - 1) === "]") {
- validation = validation.replace(/^\[|\]$/g, '');
- }
-
- return validation;
-
- },
-
- /**
- * @private
- * Splits the validation into an array, Uppercase the rules if they are not comparisons
- *
- * @param {string|array} validation
- *
- * @returns {array} Formatted validation keys
- */
- _splitValidation: function (validation) {
-
- var validationArray = this._formatValidation(validation).split(','),
- oneValidation;
-
- for (var i = 0; i < validationArray.length; i++) {
- oneValidation = validationArray[i];
- if (/^[a-z]+$/i.test(oneValidation)) {
- validationArray[i] = oneValidation.toUpperCase();
- }
- }
-
- return validationArray;
- },
-
- /**
- * @private
- * Joins the validation array to create the "data-validation" value
- *
- * @param {array} validation
- *
- * @returns {string}
- */
- _joinValidation: function (validation) {
-
- return '[' + validation.join(', ') + ']';
-
- },
-
- /**
- * API method to attach the submit event type on the specified node.
- * Note: Clears the previous event regardless to avoid double submits or unwanted behaviors.
- *
- * @param {object} node jQuery object(s)
- * @param {object} options To configure the Validation class.
- *
- * @returns {*}
- */
- validate: function (node, options) {
-
- if (typeof node === "function") {
-
- if (!options.submit.settings.form) {
- window.debug('$.validate - Undefined property "options.submit.settings.form" - Validation dropped.');
- return;
- }
-
- node = $(options.submit.settings.form);
-
- if (!node[0]) {
- window.debug('$.validate - Unable to find jQuery form element "options.submit.settings.form" - ' + options.submit.settings.form + ' - Validation dropped.');
- return;
- }
-
- } else if (typeof node[0] === 'undefined') {
-
- window.debug('$("' + node['selector'] + '").validate() - Unable to find jQuery element - Validation dropped.');
- return;
-
- }
-
- return node.each(function () {
-
- window.Validation[node.selector] = new Validation(this, options);
-
- });
-
- },
-
- /**
- * API method to handle the addition of "data-validation" arguments.
- * Note: ONLY the predefined validation arguments are allowed to be added
- * inside the "data-validation" attribute (see configuration).
- *
- * @param {object} node jQuery objects
- * @param {string|array} validation arguments to add in the node(s) "data-validation"
- *
- * @returns {*}
- */
- addValidation: function (node, validation) {
-
- var self = this;
-
- validation = self._splitValidation(validation);
-
- if (!validation) {
- return false;
- }
-
- return node.each( function () {
-
- var $this = $(this),
- validationData = $this.attr(_data.validation),
- validationArray = (validationData && validationData.length) ? self._splitValidation(validationData) : [],
- oneValidation;
-
- for (var i = 0; i < validation.length; i++) {
-
- oneValidation = self._formatValidation(validation[i]);
-
- if ($.inArray(oneValidation, validationArray) === -1) {
- validationArray.push(oneValidation);
- }
- }
-
- if (validationArray.length) {
- $this.attr(_data.validation, self._joinValidation(validationArray));
- }
-
- });
-
- },
-
- /**
- * API method to handle the removal of "data-validation" arguments.
- *
- * @param {object} node jQuery objects
- * @param {string|array} validation arguments to remove in the node(s) "data-validation"
- *
- * @returns {*}
- */
- removeValidation: function (node, validation) {
-
- var self = this;
-
- validation = self._splitValidation(validation);
- if (!validation) {
- return false;
- }
-
- return node.each( function () {
-
- var $this = $(this),
- validationData = $this.attr(_data.validation),
- validationArray = (validationData && validationData.length) ? self._splitValidation(validationData) : [],
- oneValidation,
- validationIndex;
-
- if (!validationArray.length) {
- $this.removeAttr(_data.validation);
- return true;
- }
-
- for (var i = 0; i < validation.length; i++) {
- oneValidation = self._formatValidation(validation[i]);
- validationIndex = $.inArray(oneValidation, validationArray);
- if (validationIndex !== -1) {
- validationArray.splice(validationIndex, 1);
- }
-
- }
-
- if (!validationArray.length) {
- $this.removeAttr(_data.validation);
- return true;
- }
-
- $this.attr(_data.validation, self._joinValidation(validationArray));
-
- });
-
- },
-
- /**
- * API method to manually trigger a form error.
- * Note: The same form jQuery selector MUST be used to recuperate the Validation configuration.
- *
- * @example
- * $('#form-signup_v3').addError(errorMessage)
- *
- * @param {object} node jQuery object
- * @param {object} error Object of errors to add on the node
- *
- * @returns {*}
- */
- addError: function (node, error) {
-
- if (!window.Validation[node.selector]) {
- window.debug('$.addError - Invalid node selector - Make sure you are using the same one you initialize the Validation with.');
- return false;
- }
-
- if (typeof error !== "object" || $.isEmptyObject(error)) {
- window.debug('$.addError - Invalid error object.');
- return false;
- }
-
- for (var inputName in error) {
-
- if (!error.hasOwnProperty(inputName)) {
- continue;
- }
-
- if (typeof error[inputName] === "string") {
- window.Validation[node.selector].registerError(inputName, error[inputName])
- } else if (error[inputName] instanceof Array) {
-
- for (var i = 0; i < error[inputName].length; i++) {
- window.Validation[node.selector].registerError(inputName, error[inputName][i])
- }
-
- } else {
- window.debug('$.addError - Invalid error object property - Accepted format: {"inputName": "errorString"} or {"inputName": ["errorString", "errorString"]}.');
- continue;
- }
-
- window.Validation[node.selector].displayOneError(inputName);
-
- }
-
- }
-
- };
-
- /**
- * Creates a fail-safe debugging system inside the console
- */
- window.debug = function () {
- if (this.console && this.console.debug) {
- this.console.debug('DEBUG: ' + Array.prototype.slice.call(arguments));
- } else {
- window.log('DEBUG: ' + Array.prototype.slice.call(arguments));
- }
- };
-
- String.prototype.capitalize = function() {
- return this.charAt(0).toUpperCase() + this.slice(1);
- }
-
-}(window, document, window.jQuery));
\ No newline at end of file
diff --git a/jquery.validation-1.1.0.min.js b/jquery.validation-1.1.0.min.js
deleted file mode 100644
index eb9a890..0000000
--- a/jquery.validation-1.1.0.min.js
+++ /dev/null
@@ -1,16 +0,0 @@
-/**
- * jQuery Form Validation
- *
- * @author Tom Bertrand
- * @version 1.1.0 (2014-05-26)
- *
- * @copyright
- * Copyright (C) 2014 Tom Bertrand.
- *
- * @link
- * http://www.runningcoder.org/jqueryvalidation/
- *
- * @license
- * Licensed under the MIT license.
- */
-(function(window,document,$,undefined){window.Validation={};if(typeof Object.preventExtensions!=="function"){Object.preventExtensions=function(e){return e}}var _rules={NOTEMPTY:/./,NUMERIC:/^[0-9]+$/,MIXED:/^[\w\s-]+$/,NOSPACE:/^[^\s]+$/,TRIM:/^[^\s].*[^\s]$/,DATE:/^\d{4}-\d{2}-\d{2}(\s\d{2}:\d{2}(:\d{2})?)?$/,EMAIL:/^([^@]+?)@(([a-z0-9]-*)*[a-z0-9]+\.)+([a-z0-9]+)$/i,URL:/^(https?:\/\/)?((([a-z0-9]-*)*[a-z0-9]+\.?)*([a-z0-9]+))(\/[\w?=\.-]*)*$/,PHONE:/^(\()?\d{3}(\))?(-|\s)?\d{3}(-|\s)\d{4}$/,OPTIONAL:/^.*$/,COMPARISON:/^\s*([LV])\s*([<>]=?|==|!=)\s*([^<>=!]+?)\s*$/,LUHN:"_validateLuhn"};var _messages=Object.preventExtensions({"default":"$ contain error(s).",NOTEMPTY:"$ must not be empty.",NUMERIC:"$ must be numeric.",STRING:"$ must be a string.",NOSPACE:"$ must not contain spaces.",TRIM:"$ must not start or end with space character.",MIXED:"$ must be letters or numbers (no special characters).",DATE:"$ is not a valid with format YYYY-MM-DD.",EMAIL:"$ is not valid.",URL:"$ is not valid.",PHONE:"$ is not a valid phone number.","<":"$ must be less than % characters.","<=":"$ must be less or equal to % characters.",">":"$ must be greater than % characters.",">=":"$ must be greater or equal to % characters.","==":"$ must be equal to %","!=":"$ must be different than %"}),_extendedMessages=false;var _data={validation:"data-validation",validationMessage:"data-validation-message",regex:"data-validation-regex",regexMessage:"data-validation-regex-message",validationGroup:"data-validation-group",errorList:"data-error-list"};var _options={submit:{settings:{form:null,display:"inline",insertion:"append",allErrors:false,trigger:"click",button:"input[type='submit']",errorClass:"error",errorListClass:"error-list",inputContainer:null,clear:"focusin",scrollToError:false},callback:{onInit:null,onValidate:null,onError:null,onBeforeSubmit:null,onSubmit:null,onAfterSubmit:null}},dynamic:{settings:{trigger:null,delay:300},callback:{onSuccess:null,onError:null,onComplete:null}},messages:{}};var _supported={submit:{settings:{display:["inline","block"],insertion:["append","prepend"],allErrors:[true,false],clear:["focusin","keypress",false],trigger:["click","dblclick","focusout","hover","mousedown","mouseenter","mouseleave","mousemove","mouseout","mouseover","mouseup","toggle"],scrollToError:[true,false]}},dynamic:{settings:{trigger:["focusout","keydown","keypress","keyup"]}}};var Validation=function(node,options){function extendMessages(){if(!window.Validation.messages||_extendedMessages){return false}_messages=$.extend(_messages,window.Validation.messages);_extendedMessages=true}function extendOptions(){if(!(options instanceof Object)){options={}}var e=Object.preventExtensions($.extend(true,{},_options)),t=Object.preventExtensions($.extend(true,{},_messages));e.messages=$.extend(t,options.messages||{});for(var n in options){if(!options.hasOwnProperty(n)||!(options[n]instanceof Object)){continue}for(var r in options[n]){if(!options[n].hasOwnProperty(r)||!(options[n][r]instanceof Object)){continue}for(var i in options[n][r]){if(!options[n][r].hasOwnProperty(i)){continue}if(_supported[n]&&_supported[n][r]&&_supported[n][r][i]&&$.inArray(options[n][r][i],_supported[n][r][i])===-1){window.debug("Validation.extendOptions - Delete unsupported property - "+r+": {"+i.toString()+": "+options[n][r][i].toString()+"}");delete options[n][r][i]}}if(e[n]&&e[n][r]){e[n][r]=$.extend(Object.preventExtensions(e[n][r]),options[n][r])}}}if(e.dynamic.settings.trigger){if(e.dynamic.settings.trigger==="keypress"&&e.submit.settings.clear==="keypress"){e.dynamic.settings.trigger="keydown"}}options=e}function delegateDynamicValidation(){if(!options.dynamic.settings.trigger){return false}$.each($(node).find("["+_data.validation+"],["+_data.regex+"]"),function(e,t){$(t).unbind(options.dynamic.settings.trigger).on(options.dynamic.settings.trigger,function(e){if($(this).is(":disabled")){return false}var t=this;_typeWatch(function(){if(!validateInput(t)){displayOneError(t.name);_executeCallback(options.dynamic.callback.onError,[node,t,errors[t.name]])}else{_executeCallback(options.dynamic.callback.onSuccess,[node,t])}_executeCallback(options.dynamic.callback.onComplete,[node,t])},options.dynamic.settings.delay)})})}function delegateValidation(){_executeCallback(options.submit.callback.onInit,[node]);$(node).on("submit",false);$(node).find(options.submit.settings.button).unbind(options.submit.settings.trigger).on(options.submit.settings.trigger,function(e){e.preventDefault();resetErrors();_executeCallback(options.submit.callback.onValidate,[node]);if(!validateForm()){_executeCallback(options.submit.callback.onError,[node,errors]);displayErrors()}else{_executeCallback(options.submit.callback.onBeforeSubmit,[node]);options.submit.callback.onSubmit?_executeCallback(options.submit.callback.onSubmit,[node]):submitForm();_executeCallback(options.submit.callback.onAfterSubmit,[node])}return false})}function validateForm(){$.each($(node).find("["+_data.validation+"],["+_data.regex+"]"),function(e,t){if($(this).is(":disabled")){return false}validateInput(t)});return $.isEmptyObject(errors)}function validateInput(e){var t=$(e).attr("name");if(!t){window.debug("Validation.validateInput - Invalid {string} inputName on "+e.toString());return false}var n=_getInputValue(e),r=t.replace(/]$/,"").split(/]\[|[[\]]/g),i=r[r.length-1],s=$(e).attr(_data.validation),o=$(e).attr(_data.validationMessage),u=$(e).attr(_data.regex),a=$(e).attr(_data.regexMessage),f=false;if(s){s=_api._splitValidation(s)}if(!$.isEmptyObject(s)){if(n===""&&$.inArray("OPTIONAL",s)!==-1){return true}$.each(s,function(e,r){if(f===true){return true}try{validateRule(n,r)}catch(s){if(o||!options.submit.settings.allErrors){f=true}s[0]=o||s[0];registerError(t,s[0].replace("$",i).replace("%",s[1]))}})}if(u){var l=u.split("/");if(l.length>1){var c="";for(var h=0;h",o,u;if(!errors.hasOwnProperty(e)){return false}t=$(node).find('[name="'+e+'"]');i=null;if(!t[0]){window.debug("Validation.displayOneError unable to find "+e);return false}o=t.attr(_data.validationGroup);if(o){u=$(node).find('[name="'+e+'"]');i=$(node).find('[id="'+o+'"]');if(i[0]){i.addClass(options.submit.settings.errorClass);r=i}}else{t.addClass(options.submit.settings.errorClass);if(options.submit.settings.inputContainer){t.parentsUntil(node,options.submit.settings.inputContainer).addClass(options.submit.settings.errorClass)}n=t.attr("id");if(n){i=$(node).find('label[for="'+n+'"]')[0]}if(!i){i=t.parentsUntil(node,"label")[0]}if(i){i=$(i);i.addClass(options.submit.settings.errorClass)}}if(options.submit.settings.display==="inline"){r=r||t.parent()}else if(options.submit.settings.display==="block"){r=$(node)}if(options.submit.settings.display==="inline"||options.submit.settings.display==="block"&&!r.find("["+_data.errorList+"]")[0]){if(options.submit.settings.insertion==="append"){r.append(s)}else if(options.submit.settings.insertion==="prepend"){r.prepend(s)}}for(var a=0;a"+errors[e][a]+"")}if(options.submit.settings.clear||options.dynamic.settings.trigger){if(o&&u){t=u}t.unbind(options.submit.settings.clear).on(options.submit.settings.clear+" "+options.dynamic.settings.trigger,function(e,t,n,r,i){return function(){if(i){if($(n).hasClass(options.submit.settings.errorClass)){resetOneError(e,t,n,r,i)}}else if($(t).hasClass(options.submit.settings.errorClass)){resetOneError(e,t,n,r)}}}(e,t,i,r,o))}if(options.submit.settings.scrollToError&&!hasScrolled){hasScrolled=true;if(typeof $.scrollTo!=="function"){window.debug("Missing jQuery.scrollTo, scrolling will not happen.");return false}$.scrollTo(options.submit.settings.display==="block"?r:t,500,{offset:-100})}}function displayErrors(){for(var e in errors){displayOneError(e)}}function resetOneError(e,t,n,r,i){try{delete errors[e];hasScrolled=false}catch(s){window.debug("Validation.resetOneError unable to find and delete "+e+" inside {object} errors.");return false}if(options.submit.settings.inputContainer){(i?n:t).parentsUntil(node,options.submit.settings.inputContainer).removeClass(options.submit.settings.errorClass)}n&&n.removeClass(options.submit.settings.errorClass);t.removeClass(options.submit.settings.errorClass);if(options.submit.settings.display==="inline"){r.find("["+_data.errorList+"]").remove()}}function resetErrors(){errors=[],hasScrolled=false;$(node).find("["+_data.errorList+"]").remove();$(node).find("."+options.submit.settings.errorClass).removeClass(options.submit.settings.errorClass)}function submitForm(){node.submit()}var errors=[],hasScrolled=false;var _typeWatch=function(){var e=0;return function(t,n){clearTimeout(e);e=setTimeout(t,n)}}();var _getInputValue=function(e){var t;switch($(e).attr("type")){case"checkbox":t=$(e).is(":checked")?1:"";break;case"radio":t=$(node).find('input[name="'+$(e).attr("name")+'"]:checked').val()||"";break;default:t=$(e).val();break}return t};var _executeCallback=function(e,t){if(!e){return false}var n;if(typeof e==="function"){n=e}else if(typeof e==="string"||e instanceof Array){n=window;if(typeof e==="string"){e=[e,[]]}var r=e[0].split("."),i=e[1],s=true,o=0;while(o0};this.__construct=function(){extendMessages();extendOptions();delegateDynamicValidation();delegateValidation()}();return{registerError:registerError,displayOneError:displayOneError,displayErrors:displayErrors}};$.fn.validate=$.validate=function(e){return _api.validate(this,e)};$.fn.addValidation=function(e){return _api.addValidation(this,e)};$.fn.removeValidation=function(e){return _api.removeValidation(this,e)};$.fn.addError=function(e){return _api.addError(this,e)};var _api={_formatValidation:function(e){e=e.toString().replace(/\s/g,"");if(e.charAt(0)==="["&&e.charAt(e.length-1)==="]"){e=e.replace(/^\[|\]$/g,"")}return e},_splitValidation:function(e){var t=this._formatValidation(e).split(","),n;for(var r=0;r]=?|==|!=)\s*([^<>=!]+?)\s*$/
+ };
+
+ /**
+ * @private
+ * Error messages
+ */
+ var _messages = {
+ 'default': '$ contain error(s).',
+ 'NOTEMPTY': '$ must not be empty.',
+ 'INTEGER': '$ must be an integer.',
+ 'NUMERIC': '$ must be numeric.',
+ 'MIXED': '$ must be letters or numbers (no special characters).',
+ 'NAME': '$ must not contain special characters.',
+ 'NOSPACE': '$ must not contain spaces.',
+ 'TRIM': '$ must not start or end with space character.',
+ 'DATE': '$ is not a valid with format YYYY-MM-DD.',
+ 'EMAIL': '$ is not valid.',
+ 'URL': '$ is not valid.',
+ 'PHONE': '$ is not a valid phone number.',
+ '<': '$ must be less than % characters.',
+ '<=': '$ must be less or equal to % characters.',
+ '>': '$ must be greater than % characters.',
+ '>=': '$ must be greater or equal to % characters.',
+ '==': '$ must be equal to %',
+ '!=': '$ must be different than %'
+ };
+
+ /**
+ * @private
+ * HTML5 data attributes
+ */
+ var _data = {
+ validation: 'data-validation',
+ validationMessage: 'data-validation-message',
+ regex: 'data-validation-regex',
+ regexReverse: 'data-validation-regex-reverse',
+ regexMessage: 'data-validation-regex-message',
+ group: 'data-validation-group',
+ label: 'data-validation-label',
+ errorList: 'data-error-list'
+ }
+
+ /**
+ * @private
+ * Default options
+ *
+ * @link http://www.runningcoder.org/jqueryvalidation/documentation/
+ */
+ var _options = {
+ submit: {
+ settings: {
+ form: null,
+ display: "inline",
+ insertion: "append",
+ allErrors: false,
+ trigger: "click",
+ button: "[type='submit']",
+ errorClass: "error",
+ errorListClass: "error-list",
+ errorListContainer: null,
+ errorTemplate: null,
+ inputContainer: null,
+ clear: "focusin",
+ scrollToError: false
+ },
+ callback: {
+ onInit: null,
+ onValidate: null,
+ onError: null,
+ onBeforeSubmit: null,
+ onSubmit: null,
+ onAfterSubmit: null
+ }
+ },
+ dynamic: {
+ settings: {
+ trigger: null,
+ delay: 300
+ },
+ callback: {
+ onSuccess: null,
+ onError: null,
+ onComplete: null
+ }
+ },
+ rules: {},
+ messages: {},
+ labels: {},
+ debug: false
+ };
+
+ /**
+ * @private
+ * Limit the supported options on matching keys
+ */
+ var _supported = {
+ submit: {
+ settings: {
+ display: ["inline", "block"],
+ insertion: ["append", "prepend"], //"before", "insertBefore", "after", "insertAfter"
+ allErrors: [true, false],
+ clear: ["focusin", "keypress", false],
+ trigger: [
+ "click", "dblclick", "focusout",
+ "hover", "mousedown", "mouseenter",
+ "mouseleave", "mousemove", "mouseout",
+ "mouseover", "mouseup", "toggle"
+ ]
+ }
+ },
+ dynamic: {
+ settings: {
+ trigger: ["focusout", "keydown", "keypress", "keyup"]
+ }
+ },
+ debug: [true, false]
+ };
+
+ // =================================================================================================================
+
+ /**
+ * @constructor
+ * Validation Class
+ *
+ * @param {Object} node jQuery form object
+ * @param {Object} options User defined options
+ */
+ var Validation = function (node, options) {
+
+ var errors = [],
+ messages = {},
+ formData = {},
+ delegateSuffix = ".vd", // validation.delegate
+ resetSuffix = ".vr"; // validation.resetError
+
+ window.Validation.hasScrolled = false;
+
+ /**
+ * Extends user-defined "options.message" into the default Validation "_message".
+ */
+ function extendRules() {
+ options.rules = $.extend(
+ true,
+ {},
+ _rules,
+ options.rules
+ );
+ }
+
+ /**
+ * Extends user-defined "options.message" into the default Validation "_message".
+ */
+ function extendMessages() {
+ options.messages = $.extend(
+ true,
+ {},
+ _messages,
+ options.messages
+ );
+ }
+
+ /**
+ * Extends user-defined "options" into the default Validation "_options".
+ * Notes:
+ * - preventExtensions prevents from modifying the Validation "_options" object structure
+ * - filter through the "_supported" to delete unsupported "options"
+ */
+ function extendOptions() {
+
+ if (!(options instanceof Object)) {
+ options = {};
+ }
+
+ var tpmOptions = Object.preventExtensions($.extend(true, {}, _options));
+
+ for (var method in options) {
+
+ if (!options.hasOwnProperty(method) || method === "debug") {
+ continue;
+ }
+
+ if (~["labels", "messages", "rules"].indexOf(method) && options[method] instanceof Object) {
+ tpmOptions[method] = options[method];
+ continue;
+ }
+
+ if (!_options[method] || !(options[method] instanceof Object)) {
+
+ // {debug}
+ options.debug && window.Debug.log({
+ 'node': node,
+ 'function': 'extendOptions()',
+ 'arguments': '{' + method + ': ' + JSON.stringify(options[method]) + '}',
+ 'message': 'WARNING - ' + method + ' - invalid option'
+ });
+ // {/debug}
+
+ continue;
+ }
+
+ for (var type in options[method]) {
+ if (!options[method].hasOwnProperty(type)) {
+ continue;
+ }
+
+ if (!_options[method][type] || !(options[method][type] instanceof Object)) {
+
+ // {debug}
+ options.debug && window.Debug.log({
+ 'node': node,
+ 'function': 'extendOptions()',
+ 'arguments': '{' + type + ': ' + JSON.stringify(options[method][type]) + '}',
+ 'message': 'WARNING - ' + type + ' - invalid option'
+ });
+ // {/debug}
+
+ continue;
+ }
+
+ for (var option in options[method][type]) {
+ if (!options[method][type].hasOwnProperty(option)) {
+ continue;
+ }
+
+ if (_supported[method] &&
+ _supported[method][type] &&
+ _supported[method][type][option] &&
+ $.inArray(options[method][type][option], _supported[method][type][option]) === -1) {
+
+ // {debug}
+ options.debug && window.Debug.log({
+ 'node': node,
+ 'function': 'extendOptions()',
+ 'arguments': '{' + option + ': ' + JSON.stringify(options[method][type][option]) + '}',
+ 'message': 'WARNING - ' + option.toString() + ': ' + JSON.stringify(options[method][type][option]) + ' - unsupported option'
+ });
+ // {/debug}
+
+ delete options[method][type][option];
+ }
+
+ }
+ if (tpmOptions[method] && tpmOptions[method][type]) {
+ tpmOptions[method][type] = $.extend(Object.preventExtensions(tpmOptions[method][type]), options[method][type]);
+ }
+ }
+ }
+
+ // {debug}
+ if (options.debug && $.inArray(options.debug, _supported.debug !== -1)) {
+ tpmOptions.debug = options.debug;
+ }
+ // {/debug}
+
+ // @TODO Would there be a better fix to solve event conflict?
+ if (tpmOptions.dynamic.settings.trigger) {
+ if (tpmOptions.dynamic.settings.trigger === "keypress" && tpmOptions.submit.settings.clear === "keypress") {
+ tpmOptions.dynamic.settings.trigger = "keydown";
+ }
+ }
+
+ options = tpmOptions;
+
+ }
+
+ /**
+ * Delegates the dynamic validation on data-validation and data-validation-regex attributes based on trigger.
+ *
+ * @returns {Boolean} false if the option is not set
+ */
+ function delegateDynamicValidation() {
+
+ if (!options.dynamic.settings.trigger) {
+ return false;
+ }
+
+ // {debug}
+ options.debug && window.Debug.log({
+ 'node': node,
+ 'function': 'delegateDynamicValidation()',
+ 'message': 'OK - Dynamic Validation activated on ' + node.length + ' form(s)'
+ });
+ // {/debug}
+
+ if (!node.find('[' + _data.validation + '],[' + _data.regex + ']')[0]) {
+
+ // {debug}
+ options.debug && window.Debug.log({
+ 'node': node,
+ 'function': 'delegateDynamicValidation()',
+ 'arguments': 'node.find([' + _data.validation + '],[' + _data.regex + '])',
+ 'message': 'ERROR - [' + _data.validation + '] not found'
+ });
+ // {/debug}
+
+ return false;
+ }
+
+ var event = options.dynamic.settings.trigger + delegateSuffix;
+ if (options.dynamic.settings.trigger !== "focusout") {
+ event += " change" + delegateSuffix + " paste" + delegateSuffix;
+ }
+
+ $.each(
+ node.find('[' + _data.validation + '],[' + _data.regex + ']'),
+ function (index, input) {
+
+ $(input).unbind(event).on(event, function (e) {
+
+ if ($(this).is(':disabled')) {
+ return false;
+ }
+
+ //e.preventDefault();
+
+ var input = this,
+ keyCode = e.keyCode || null;
+
+ _typeWatch(function () {
+
+ if (!validateInput(input)) {
+
+ displayOneError(input.name);
+ _executeCallback(options.dynamic.callback.onError, [node, input, keyCode, errors[input.name]]);
+
+ } else {
+
+ _executeCallback(options.dynamic.callback.onSuccess, [node, input, keyCode]);
+
+ }
+
+ _executeCallback(options.dynamic.callback.onComplete, [node, input, keyCode]);
+
+ }, options.dynamic.settings.delay);
+
+ });
+ }
+ );
+ }
+
+ /**
+ * Delegates the submit validation on data-validation and data-validation-regex attributes based on trigger.
+ * Note: Disable the form submit function so the callbacks are not by-passed
+ */
+ function delegateValidation() {
+
+ _executeCallback(options.submit.callback.onInit, [node]);
+
+ var event = options.submit.settings.trigger + '.vd';
+
+ // {debug}
+ options.debug && window.Debug.log({
+ 'node': node,
+ 'function': 'delegateValidation()',
+ 'message': 'OK - Validation activated on ' + node.length + ' form(s)'
+ });
+ // {/debug}
+
+ if (!node.find(options.submit.settings.button)[0]) {
+
+ // {debug}
+ options.debug && window.Debug.log({
+ 'node': node,
+ 'function': 'delegateValidation()',
+ 'arguments': '{button: ' + options.submit.settings.button + '}',
+ 'message': 'ERROR - node.find("' + options.submit.settings.button + '") not found'
+ });
+ // {/debug}
+
+ return false;
+
+ }
+
+ node.on("submit", false);
+ node.find(options.submit.settings.button).off('.vd').on(event, function (e) {
+
+ e.preventDefault();
+
+ resetErrors();
+
+ _executeCallback(options.submit.callback.onValidate, [node]);
+
+ if (!validateForm()) {
+
+ displayErrors();
+ _executeCallback(options.submit.callback.onError, [node, errors, formData]);
+
+ } else {
+
+ _executeCallback(options.submit.callback.onBeforeSubmit, [node]);
+
+ if (typeof options.submit.callback.onSubmit === "function") {
+ if (_executeCallback(options.submit.callback.onSubmit, [node, formData]) === true) {
+ submitForm();
+ }
+ } else {
+ submitForm();
+ }
+
+ _executeCallback(options.submit.callback.onAfterSubmit, [node]);
+
+ }
+
+ // {debug}
+ options.debug && window.Debug.print();
+ // {/debug}
+
+ return false;
+
+ });
+
+ }
+
+ /**
+ * For every "data-validation" & "data-pattern" attributes that are not disabled inside the jQuery "node" object
+ * the "validateInput" function will be called.
+ *
+ * @returns {Boolean} true if no error(s) were found (valid form)
+ */
+ function validateForm() {
+
+ var isValid = isEmpty(errors);
+
+ formData = {};
+
+ $.each(
+ node.find('input:not([type="submit"]), select, textarea').not(':disabled'),
+ function(index, input) {
+
+ input = $(input);
+
+ var value = _getInputValue(input[0]),
+ inputName = input.attr('name');
+
+ if (inputName) {
+ if (/\[]$/.test(inputName)) {
+ inputName = inputName.replace(/\[]$/, '');
+ if (!(formData[inputName] instanceof Array)) {
+ formData[inputName] = [];
+ }
+ formData[inputName].push(value)
+ } else {
+ formData[inputName] = value;
+ }
+ }
+
+ if (!!input.attr(_data.validation) || !!input.attr(_data.regex)) {
+ if (!validateInput(input[0], value)) {
+ isValid = false;
+ }
+ }
+ }
+ );
+
+ prepareFormData();
+
+ return isValid;
+
+ }
+
+ /**
+ * Loop through formData and build an object
+ *
+ * @returns {Object} data
+ */
+ function prepareFormData() {
+
+ var data = {},
+ matches,
+ index;
+
+ for (var i in formData) {
+ if (!formData.hasOwnProperty(i)) continue;
+
+ index = 0;
+ matches = i.split(/\[(.+?)]/g);
+
+ var tmpObject = {},
+ tmpArray = [];
+
+ for (var k = matches.length - 1; k >= 0; k--) {
+ if (matches[k] === "") {
+ matches.splice(k, 1);
+ continue;
+ }
+
+ if (tmpArray.length < 1) {
+ tmpObject[matches[k]] = Number(formData[i]) || formData[i];
+ } else {
+ tmpObject = {};
+ tmpObject[matches[k]] = tmpArray[tmpArray.length - 1];
+ }
+ tmpArray.push(tmpObject)
+
+ }
+
+ data = $.extend(true, data, tmpObject);
+
+ }
+
+ formData = data;
+
+ }
+
+ /**
+ * Prepare the information from the data attributes
+ * and call the "validateRule" function.
+ *
+ * @param {Object} input Reference of the input element
+ *
+ * @returns {Boolean} true if no error(s) were found (valid input)
+ */
+ function validateInput(input, value) {
+
+ var inputName = $(input).attr('name'),
+ value = value || _getInputValue(input);
+
+ if (!inputName) {
+
+ // {debug}
+ options.debug && window.Debug.log({
+ 'node': node,
+ 'function': 'validateInput()',
+ 'arguments': '$(input).attr("name")',
+ 'message': 'ERROR - Missing input [name]'
+ });
+ // {/debug}
+
+ return false;
+ }
+
+ var matches = inputName.replace(/]$/, '').split(/]\[|[[\]]/g),
+ inputShortName = window.Validation.labels[inputName] ||
+ options.labels[inputName] ||
+ $(input).attr(_data.label) ||
+ matches[matches.length - 1],
+
+ validationArray = $(input).attr(_data.validation),
+ validationMessage = $(input).attr(_data.validationMessage),
+ validationRegex = $(input).attr(_data.regex),
+ validationRegexReverse = !($(input).attr(_data.regexReverse) === undefined),
+ validationRegexMessage = $(input).attr(_data.regexMessage),
+
+ validateOnce = false;
+
+ if (validationArray) {
+ validationArray = _api._splitValidation(validationArray);
+ }
+
+ // Validates the "data-validation"
+ if (validationArray instanceof Array && validationArray.length > 0) {
+
+ // "OPTIONAL" input will not be validated if it's empty
+ if ($.trim(value) === '' && ~validationArray.indexOf('OPTIONAL')) {
+ return true;
+ }
+
+ $.each(validationArray, function (i, rule) {
+
+ if (validateOnce === true) {
+ return true;
+ }
+
+ try {
+
+ validateRule(value, rule);
+
+ } catch (error) {
+
+ if (validationMessage || !options.submit.settings.allErrors) {
+ validateOnce = true;
+ }
+
+ error[0] = validationMessage || error[0];
+
+ registerError(inputName, error[0].replace('$', inputShortName).replace('%', error[1]));
+
+ }
+
+ });
+
+ }
+
+ // Validates the "data-validation-regex"
+ if (validationRegex) {
+
+ var rule = _buildRegexFromString(validationRegex);
+
+ // Do not block validation if a regexp is bad, only skip it
+ if (!(rule instanceof RegExp)) {
+ return true;
+ }
+
+ try {
+
+ validateRule(value, rule, validationRegexReverse);
+
+ } catch (error) {
+
+ error[0] = validationRegexMessage || error[0];
+
+ registerError(inputName, error[0].replace('$', inputShortName));
+
+ }
+
+ }
+
+ return !errors[inputName] || errors[inputName] instanceof Array && errors[inputName].length === 0;
+
+ }
+
+ /**
+ * Validate an input value against one rule.
+ * If a "value-rule" mismatch occurs, an error is thrown to the caller function.
+ *
+ * @param {String} value
+ * @param {*} rule
+ * @param {Boolean} [reversed]
+ *
+ * @returns {*} Error if a mismatch occurred.
+ */
+ function validateRule(value, rule, reversed) {
+
+ // Validate for "data-validation-regex" and "data-validation-regex-reverse"
+ if (rule instanceof RegExp) {
+ var isValid = rule.test(value);
+
+ if (reversed) {
+ isValid = !isValid;
+ }
+
+ if (!isValid) {
+ throw [options.messages['default'], ''];
+ }
+ return;
+ }
+
+ if (options.rules[rule]) {
+ if (!options.rules[rule].test(value)) {
+ throw [options.messages[rule], ''];
+ }
+ return;
+ }
+
+ // Validate for comparison "data-validation"
+ var comparison = rule.match(options.rules.COMPARISON);
+
+ if (!comparison || comparison.length !== 4) {
+
+ // {debug}
+ options.debug && window.Debug.log({
+ 'node': node,
+ 'function': 'validateRule()',
+ 'arguments': 'value: ' + value + ' rule: ' + rule,
+ 'message': 'WARNING - Invalid comparison'
+ });
+ // {/debug}
+
+ return;
+ }
+
+ var type = comparison[1],
+ operator = comparison[2],
+ compared = comparison[3],
+ comparedValue;
+
+ switch (type) {
+
+ // Compare input "Length"
+ case "L":
+
+ // Only numeric value for "L" are allowed
+ if (isNaN(compared)) {
+
+ // {debug}
+ options.debug && window.Debug.log({
+ 'node': node,
+ 'function': 'validateRule()',
+ 'arguments': 'compare: ' + compared + ' rule: ' + rule,
+ 'message': 'WARNING - Invalid rule, "L" compare must be numeric'
+ });
+ // {/debug}
+
+ return false;
+
+ } else {
+
+ if (!value || eval(value.length + operator + parseFloat(compared)) === false) {
+ throw [options.messages[operator], compared];
+ }
+
+ }
+
+ break;
+
+ // Compare input "Value"
+ case "V":
+ default:
+
+ // Compare Field values
+ if (isNaN(compared)) {
+
+ comparedValue = node.find('[name="' + compared + '"]').val();
+ if (!comparedValue) {
+
+ // {debug}
+ options.debug && window.Debug.log({
+ 'node': node,
+ 'function': 'validateRule()',
+ 'arguments': 'compare: ' + compared + ' rule: ' + rule,
+ 'message': 'WARNING - Unable to find compared field [name="' + compared + '"]'
+ });
+ // {/debug}
+
+ return false;
+ }
+
+ if (!value || !eval('"' + encodeURIComponent(value) + '"' + operator + '"' + encodeURIComponent(comparedValue) + '"')) {
+ throw [options.messages[operator].replace(' characters', ''), compared];
+ }
+
+ } else {
+ // Compare numeric value
+ if (!value || isNaN(value) || !eval(value + operator + parseFloat(compared))) {
+ throw [options.messages[operator].replace(' characters', ''), compared];
+ }
+
+ }
+ break;
+
+ }
+
+ }
+
+ /**
+ * Register an error into the global "error" variable.
+ *
+ * @param {String} inputName Input where the error occurred
+ * @param {String} error Description of the error to be displayed
+ */
+ function registerError(inputName, error) {
+
+ if (!errors[inputName]) {
+ errors[inputName] = [];
+ }
+
+ error = error.capitalize();
+
+ var hasError = false;
+ for (var i = 0; i < errors[inputName].length; i++) {
+ if (errors[inputName][i] === error) {
+ hasError = true;
+ break;
+ }
+ }
+
+ if (!hasError) {
+ errors[inputName].push(error);
+ }
+
+ }
+
+ /**
+ * Display a single error based on "inputName" key inside the "errors" global array.
+ * The input, the label and the "inputContainer" will be given the "errorClass" and other
+ * settings will be considered.
+ *
+ * @param {String} inputName Key used for search into "errors"
+ *
+ * @returns {Boolean} false if an unwanted behavior occurs
+ */
+ function displayOneError(inputName) {
+
+ var input,
+ inputId,
+ errorContainer,
+ label,
+ html = '',
+ group,
+ groupInput;
+
+ if (!errors.hasOwnProperty(inputName)) {
+ return false;
+ }
+
+ input = node.find('[name="' + inputName + '"]');
+
+ label = null;
+
+ if (!input[0]) {
+
+ // {debug}
+ options.debug && window.Debug.log({
+ 'node': node,
+ 'function': 'displayOneError()',
+ 'arguments': '[name="' + inputName + '"]',
+ 'message': 'ERROR - Unable to find input by name "' + inputName + '"'
+ });
+ // {/debug}
+
+ return false;
+ }
+
+ group = input.attr(_data.group);
+
+ if (group) {
+
+ groupInput = node.find('[name="' + inputName + '"]');
+ label = node.find('[id="' + group + '"]');
+
+ if (label[0]) {
+ label.addClass(options.submit.settings.errorClass);
+ errorContainer = label;
+ }
+
+ //node.find('[' + _data.group + '="' + group + '"]').addClass(options.submit.settings.errorClass)
+
+ } else {
+
+ input.addClass(options.submit.settings.errorClass);
+
+ if (options.submit.settings.inputContainer) {
+ input.parentsUntil(node, options.submit.settings.inputContainer).addClass(options.submit.settings.errorClass);
+ }
+
+ inputId = input.attr('id');
+
+ if (inputId) {
+ label = node.find('label[for="' + inputId + '"]')[0];
+ }
+
+ if (!label) {
+ label = input.parentsUntil(node, 'label')[0];
+ }
+
+ if (label) {
+ label = $(label);
+ label.addClass(options.submit.settings.errorClass);
+ }
+ }
+
+ if (options.submit.settings.display === 'inline') {
+ if (options.submit.settings.errorListContainer) {
+ errorContainer = input.parentsUntil(node, options.submit.settings.errorListContainer);
+ } else {
+ errorContainer = errorContainer || input.parent();
+ }
+ } else if (options.submit.settings.display === 'block') {
+ errorContainer = node;
+ }
+
+ // Prevent double error list if the previous one has not been cleared.
+ if (options.submit.settings.display === 'inline' && errorContainer.find('[' + _data.errorList + ']')[0]) {
+ return false;
+ }
+
+ if (options.submit.settings.display === "inline" ||
+ (options.submit.settings.display === "block" && !errorContainer.find('[' + _data.errorList + ']')[0])
+ ) {
+ if (options.submit.settings.insertion === 'append') {
+ errorContainer.append(html);
+ } else if (options.submit.settings.insertion === 'prepend') {
+ errorContainer.prepend(html);
+ }
+ }
+
+ for (var i = 0; i < errors[inputName].length; i++) {
+ if (options.submit.settings.errorTemplate) {
+ errorContainer.find('[' + _data.errorList + '] ul')
+ .append('' + options.submit.settings.errorTemplate.replace('{{validation-message}}', errors[inputName][i]) + '');
+ } else {
+ errorContainer.find('[' + _data.errorList + '] ul').append('' + errors[inputName][i] + '');
+ }
+ }
+
+ if (options.submit.settings.clear || options.dynamic.settings.trigger) {
+
+ if (group && groupInput) {
+ input = groupInput;
+ }
+
+ var event = "coucou" + resetSuffix;
+ if (options.submit.settings.clear) {
+ event += " " + options.submit.settings.clear + resetSuffix;
+ if (~['radio', 'checkbox'].indexOf(input[0].type)) {
+ event += " change" + resetSuffix;
+ }
+ }
+ if (options.dynamic.settings.trigger) {
+ event += " " + options.dynamic.settings.trigger + resetSuffix;
+ if (options.dynamic.settings.trigger !== "focusout" && !~['radio', 'checkbox'].indexOf(input[0].type)) {
+ event += " change" + resetSuffix + " paste" + resetSuffix;
+ }
+ }
+
+ input.unbind(event).on(event, function (a, b, c, d, e) {
+
+ return function () {
+ if (e) {
+ if ($(c).hasClass(options.submit.settings.errorClass)) {
+ resetOneError(a, b, c, d, e);
+ }
+ } else if ($(b).hasClass(options.submit.settings.errorClass)) {
+ resetOneError(a, b, c, d);
+ }
+ };
+
+ }(inputName, input, label, errorContainer, group));
+ }
+
+ if (options.submit.settings.scrollToError && !window.Validation.hasScrolled) {
+
+ window.Validation.hasScrolled = true;
+
+ var offset = parseFloat(options.submit.settings.scrollToError.offset) || 0,
+ duration = parseFloat(options.submit.settings.scrollToError.duration) || 500,
+ handle = (options.submit.settings.display === 'block') ? errorContainer : input;
+
+ $('html, body').animate({
+ scrollTop: handle.offset().top + offset
+ }, duration);
+
+ }
+
+ }
+
+ /**
+ * Display all of the errors
+ */
+ function displayErrors() {
+
+ for (var inputName in errors) {
+ if (!errors.hasOwnProperty(inputName)) continue;
+ displayOneError(inputName);
+ }
+
+ }
+
+ /**
+ * Remove an input error.
+ *
+ * @param {String} inputName Key reference to delete the error from "errors" global variable
+ * @param {Object} input jQuery object of the input
+ * @param {Object} label jQuery object of the input's label
+ * @param {Object} container jQuery object of the "errorList"
+ * @param {String} [group] Name of the group if any (ex: used on input radio)
+ */
+ function resetOneError(inputName, input, label, container, group) {
+
+ delete errors[inputName];
+
+ if (container) {
+
+ //window.Validation.hasScrolled = false;
+
+ if (options.submit.settings.inputContainer) {
+ (group ? label : input).parentsUntil(node, options.submit.settings.inputContainer).removeClass(options.submit.settings.errorClass);
+ }
+
+ label && label.removeClass(options.submit.settings.errorClass);
+
+ input.removeClass(options.submit.settings.errorClass);
+
+ if (options.submit.settings.display === 'inline') {
+ container.find('[' + _data.errorList + ']').remove();
+ }
+
+ } else {
+
+ if (!input) {
+ input = node.find('[name="' + inputName + '"]');
+
+ if (!input[0]) {
+
+ // {debug}
+ options.debug && window.Debug.log({
+ 'node': node,
+ 'function': 'resetOneError()',
+ 'arguments': '[name="' + inputName + '"]',
+ 'message': 'ERROR - Unable to find input by name "' + inputName + '"'
+ });
+ // {/debug}
+
+ return false;
+ }
+ }
+
+ input.trigger('coucou' + resetSuffix);
+ }
+
+ }
+
+ /**
+ * Remove all of the input error(s) display.
+ */
+ function resetErrors() {
+
+ errors = [];
+ window.Validation.hasScrolled = false;
+
+ node.find('[' + _data.errorList + ']').remove();
+ node.find('.' + options.submit.settings.errorClass).removeClass(options.submit.settings.errorClass);
+
+ }
+
+ /**
+ * Submits the form once it succeeded the validation process.
+ * Note:
+ * - This function will be overridden if "options.submit.settings.onSubmit" is defined
+ * - The node can't be submitted by jQuery since it has been disabled, use the form native submit function instead
+ */
+ function submitForm() {
+
+ node[0].submit()
+
+ }
+
+ /**
+ * Destroy the Validation instance
+ *
+ * @returns {Boolean}
+ */
+ function destroy() {
+
+ resetErrors();
+ node.find('[' + _data.validation + '],[' + _data.regex + ']').off(delegateSuffix + ' ' + resetSuffix);
+
+ node.find(options.submit.settings.button).off(delegateSuffix).on('click' + delegateSuffix, function () {
+ $(this).closest('form')[0].submit();
+ });
+
+ //delete window.Validation.form[node.selector];
+
+ return true;
+
+ }
+
+ /**
+ * @private
+ * Helper to get the value of an regular, radio or chackbox input
+ *
+ * @param input
+ *
+ * @returns {String} value
+ */
+ var _getInputValue = function (input) {
+
+ var value;
+
+ // Get the value or state of the input based on its type
+ switch ($(input).attr('type')) {
+ case 'checkbox':
+ value = ($(input).is(':checked')) ? 1 : '';
+ break;
+ case 'radio':
+ value = node.find('input[name="' + $(input).attr('name') + '"]:checked').val() || '';
+ break;
+ default:
+ value = $(input).val();
+ break;
+ }
+
+ return value;
+
+ };
+
+ /**
+ * @private
+ * Execute function once the timer is reached.
+ * If the function is recalled before the timer ends, the first call will be canceled.
+ */
+ var _typeWatch = (function () {
+ var timer = 0;
+ return function (callback, ms) {
+ clearTimeout(timer);
+ timer = setTimeout(callback, ms);
+ };
+ })();
+
+ /**
+ * @private
+ * Executes an anonymous function or a string reached from the window scope.
+ *
+ * @example
+ * Note: These examples works with every callbacks (onInit, onError, onSubmit, onBeforeSubmit & onAfterSubmit)
+ *
+ * // An anonymous function inside the "onInit" option
+ * onInit: function() { console.log(':D'); };
+ *
+ * * // myFunction() located on window.coucou scope
+ * onInit: 'window.coucou.myFunction'
+ *
+ * // myFunction(a,b) located on window.coucou scope passing 2 parameters
+ * onInit: ['window.coucou.myFunction', [':D', ':)']];
+ *
+ * // Anonymous function to execute a local function
+ * onInit: function () { myFunction(':D'); }
+ *
+ * @param {String|Array} callback The function to be called
+ * @param {Array} [extraParams] In some cases the function can be called with Extra parameters (onError)
+ *
+ * @returns {Boolean}
+ */
+ var _executeCallback = function (callback, extraParams) {
+
+ if (!callback) {
+ return false;
+ }
+
+ var _callback;
+
+ if (typeof callback === "function") {
+
+ _callback = callback;
+
+ } else if (typeof callback === "string" || callback instanceof Array) {
+
+ _callback = window;
+
+ if (typeof callback === "string") {
+ callback = [callback, []];
+ }
+
+ var _exploded = callback[0].split('.'),
+ _params = callback[1],
+ _isValid = true,
+ _splitIndex = 0;
+
+ while (_splitIndex < _exploded.length) {
+
+ if (typeof _callback !== 'undefined') {
+ _callback = _callback[_exploded[_splitIndex++]];
+ } else {
+ _isValid = false;
+ break;
+ }
+ }
+
+ if (!_isValid || typeof _callback !== "function") {
+
+ // {debug}
+ options.debug && window.Debug.log({
+ 'node': node,
+ 'function': '_executeCallback()',
+ 'arguments': JSON.stringify(callback),
+ 'message': 'WARNING - Invalid callback function"'
+ });
+ // {/debug}
+
+ return false;
+ }
+
+ }
+
+ return _callback.apply(this, $.merge(_params || [], (extraParams) ? extraParams : []));
+
+ };
+
+ /**
+ * @private
+ * Constructs Validation
+ */
+ this.__construct = function () {
+
+ extendOptions();
+ extendRules();
+ extendMessages();
+
+ delegateDynamicValidation();
+ delegateValidation();
+
+ // {debug}
+ options.debug && window.Debug.print();
+ // {/debug}
+
+ }();
+
+ return {
+
+ /**
+ * @public
+ * Register error
+ *
+ * @param inputName
+ * @param error
+ */
+ registerError: registerError,
+
+ /**
+ * @public
+ * Display one error
+ *
+ * @param inputName
+ */
+ displayOneError: displayOneError,
+
+ /**
+ * @public
+ * Display all errors
+ */
+ displayErrors: displayErrors,
+
+ /**
+ * @public
+ * Remove one error
+ */
+ resetOneError: resetOneError,
+
+ /**
+ * @public
+ * Remove all errors
+ */
+ resetErrors: resetErrors,
+
+ /**
+ * @public
+ * Destroy the Validation instance
+ */
+ destroy: destroy
+
+ };
+
+ };
+
+ // =================================================================================================================
+
+ /**
+ * @public
+ * jQuery public function to implement the Validation on the selected node(s).
+ *
+ * @param {object} options To configure the Validation class.
+ *
+ * @return {object} Modified DOM element
+ */
+ $.fn.validate = $.validate = function (options) {
+
+ return _api.validate(this, options);
+
+ };
+
+ /**
+ * @public
+ * jQuery public function to add one or multiple "data-validation" argument.
+ *
+ * @param {string|array} validation Arguments to add in the node's data-validation
+ *
+ * @return {object} Modified DOM element
+ */
+ $.fn.addValidation = function (validation) {
+
+ return _api.addValidation(this, validation);
+
+ };
+
+ /**
+ * @public
+ * jQuery public function to remove one or multiple "data-validation" argument.
+ *
+ * @param {string|array} validation Arguments to remove in the node's data-validation
+ *
+ * @return {object} Modified DOM element
+ */
+ $.fn.removeValidation = function (validation) {
+
+ return _api.removeValidation(this, validation);
+
+ };
+
+ /**
+ * @public
+ * jQuery public function to add one or multiple errors.
+ *
+ * @param {object} error Object of errors where the keys are the input names
+ * @example
+ * $('form#myForm').addError({
+ * 'username': 'Invalid username, please choose another one.'
+ * });
+ *
+ * @return {object} Modified DOM element
+ */
+ $.fn.addError = function (error) {
+
+ return _api.addError(this, error);
+
+ };
+
+ /**
+ * @public
+ * jQuery public function to remove one or multiple errors.
+ *
+ * @param {array} error Array of errors where the keys are the input names
+ * @example
+ * $('form#myForm').removeError([
+ * 'username'
+ * ]);
+ *
+ * @return {object} Modified DOM element
+ */
+ $.fn.removeError = function (error) {
+
+ return _api.removeError(this, error);
+
+ };
+
+ /**
+ * @public
+ * jQuery public function to add a validation rule.
+ *
+ * @example
+ * $.alterValidationRules({
+ * rule: 'FILENAME',
+ * regex: /^[^\\/:\*\?<>\|\"\']*$/,
+ * message: '$ has an invalid filename.'
+ * })
+ *
+ * @param {Object|Array} name
+ */
+ $.fn.alterValidationRules = $.alterValidationRules = function (rules) {
+
+ if (!(rules instanceof Array)) {
+ rules = [rules];
+ }
+
+ for (var i = 0; i < rules.length; i++) {
+ _api.alterValidationRules(rules[i]);
+ }
+
+ };
+
+ // =================================================================================================================
+
+ /**
+ * @private
+ * API to handles "addValidation" and "removeValidation" on attribute "data-validation".
+ * Note: Contains fail-safe operations to unify the validation parameter.
+ *
+ * @example
+ * $.addValidation('NOTEMPTY, L>=6')
+ * $.addValidation('[notempty, v>=6]')
+ * $.removeValidation(['OPTIONAL', 'V>=6'])
+ *
+ * @returns {object} Updated DOM object
+ */
+ var _api = {
+
+ /**
+ * @private
+ * This function unifies the data through the validation process.
+ * String, Uppercase and spaceless.
+ *
+ * @param {string|array} validation
+ *
+ * @returns {string}
+ */
+ _formatValidation: function (validation) {
+
+ validation = validation.toString().replace(/\s/g, '');
+
+ if (validation.charAt(0) === "[" && validation.charAt(validation.length - 1) === "]") {
+ validation = validation.replace(/^\[|]$/g, '');
+ }
+
+ return validation;
+
+ },
+
+ /**
+ * @private
+ * Splits the validation into an array, Uppercase the rules if they are not comparisons
+ *
+ * @param {String|Array} validation
+ *
+ * @returns {Array} Formatted validation keys
+ */
+ _splitValidation: function (validation) {
+
+ var validationArray = this._formatValidation(validation).split(','),
+ oneValidation;
+
+ for (var i = 0; i < validationArray.length; i++) {
+ oneValidation = validationArray[i];
+ if (/^[a-z]+$/i.test(oneValidation)) {
+ validationArray[i] = oneValidation.toUpperCase();
+ }
+ }
+
+ return validationArray;
+ },
+
+ /**
+ * @private
+ * Joins the validation array to create the "data-validation" value
+ *
+ * @param {Array} validation
+ *
+ * @returns {String}
+ */
+ _joinValidation: function (validation) {
+
+ return '[' + validation.join(', ') + ']';
+
+ },
+
+ /**
+ * API method to attach the submit event type on the specified node.
+ * Note: Clears the previous event regardless to avoid double submits or unwanted behaviors.
+ *
+ * @param {Object} node jQuery object(s)
+ * @param {Object} options To configure the Validation class.
+ *
+ * @returns {*}
+ */
+ validate: function (node, options) {
+
+ if (typeof node === "function") {
+
+ if (!options.submit.settings.form) {
+
+ // {debug}
+ window.Debug.log({
+ 'node': node,
+ 'function': '$.validate()',
+ 'arguments': '',
+ 'message': 'Undefined property "options.submit.settings.form - Validation dropped'
+ });
+
+ window.Debug.print();
+ // {/debug}
+
+ return;
+ }
+
+ node = $(options.submit.settings.form);
+
+ if (!node[0] || node[0].nodeName.toLowerCase() !== "form") {
+
+ // {debug}
+ window.Debug.log({
+ 'function': '$.validate()',
+ 'arguments': options.submit.settings.form,
+ 'message': 'Unable to find jQuery form element - Validation dropped'
+ });
+
+ window.Debug.print();
+ // {/debug}
+
+ return;
+ }
+
+ } else if (typeof node[0] === 'undefined') {
+
+ // {debug}
+ window.Debug.log({
+ 'node': node,
+ 'function': '$.validate()',
+ 'arguments': '$("' + node.selector + '").validate()',
+ 'message': 'Unable to find jQuery form element - Validation dropped'
+ });
+
+ window.Debug.print();
+ // {/debug}
+
+ return;
+ }
+
+ if (options === "destroy") {
+
+ if (!window.Validation.form[node.selector]) {
+
+ // {debug}
+ window.Debug.log({
+ 'node': node,
+ 'function': '$.validate("destroy")',
+ 'arguments': '',
+ 'message': 'Unable to destroy "' + node.selector + '", perhaps it\'s already destroyed?'
+ });
+
+ window.Debug.print();
+ // {/debug}
+
+ return;
+ }
+
+ window.Validation.form[node.selector].destroy();
+
+ return;
+
+ }
+
+ return node.each(function () {
+ window.Validation.form[node.selector] = new Validation($(this), options);
+ });
+
+ },
+
+ /**
+ * API method to handle the addition of "data-validation" arguments.
+ * Note: ONLY the predefined validation arguments are allowed to be added
+ * inside the "data-validation" attribute (see configuration).
+ *
+ * @param {Object} node jQuery objects
+ * @param {String|Array} validation arguments to add in the node(s) "data-validation"
+ *
+ * @returns {*}
+ */
+ addValidation: function (node, validation) {
+
+ var self = this;
+
+ validation = self._splitValidation(validation);
+
+ if (!validation) {
+ return false;
+ }
+
+ return node.each(function () {
+
+ var $this = $(this),
+ validationData = $this.attr(_data.validation),
+ validationArray = (validationData && validationData.length) ? self._splitValidation(validationData) : [],
+ oneValidation;
+
+ for (var i = 0; i < validation.length; i++) {
+
+ oneValidation = self._formatValidation(validation[i]);
+
+ if ($.inArray(oneValidation, validationArray) === -1) {
+ validationArray.push(oneValidation);
+ }
+ }
+
+ if (validationArray.length) {
+ $this.attr(_data.validation, self._joinValidation(validationArray));
+ }
+
+ });
+
+ },
+
+ /**
+ * API method to handle the removal of "data-validation" arguments.
+ *
+ * @param {Object} node jQuery objects
+ * @param {String|Array} validation arguments to remove in the node(s) "data-validation"
+ *
+ * @returns {*}
+ */
+ removeValidation: function (node, validation) {
+
+ var self = this;
+
+ validation = self._splitValidation(validation);
+ if (!validation) {
+ return false;
+ }
+
+ return node.each(function () {
+
+ var $this = $(this),
+ validationData = $this.attr(_data.validation),
+ validationArray = (validationData && validationData.length) ? self._splitValidation(validationData) : [],
+ oneValidation,
+ validationIndex;
+
+ if (!validationArray.length) {
+ $this.removeAttr(_data.validation);
+ return true;
+ }
+
+ for (var i = 0; i < validation.length; i++) {
+ oneValidation = self._formatValidation(validation[i]);
+ validationIndex = $.inArray(oneValidation, validationArray);
+ if (validationIndex !== -1) {
+ validationArray.splice(validationIndex, 1);
+ }
+
+ }
+
+ if (!validationArray.length) {
+ $this.removeAttr(_data.validation);
+ return true;
+ }
+
+ $this.attr(_data.validation, self._joinValidation(validationArray));
+
+ });
+
+ },
+
+ /**
+ * API method to manually trigger a form error.
+ * Note: The same form jQuery selector MUST be used to recuperate the Validation configuration.
+ *
+ * @example
+ * $('#form-signup_v3').addError({
+ * 'inputName': 'my error message',
+ * 'inputName2': [
+ * 'first error message',
+ * 'second error message'
+ * ]
+ * })
+ *
+ * @param {Object} node jQuery object
+ * @param {Object} error Object of errors to add on the node
+ *
+ * @returns {*}
+ */
+ addError: function (node, error) {
+
+ if (!window.Validation.form[node.selector]) {
+
+ // {debug}
+ window.Debug.log({
+ 'node': node,
+ 'function': '$.addError()',
+ 'arguments': 'window.Validation.form[' + node.selector + ']',
+ 'message': 'ERROR - Invalid node selector'
+ });
+
+ window.Debug.print();
+ // {/debug}
+
+ return false;
+ }
+
+ if (typeof error !== "object" || Object.prototype.toString.call(error) !== "[object Object]") {
+
+ // {debug}
+ window.Debug.log({
+ 'node': node,
+ 'function': '$.addError()',
+ 'arguments': 'window.Validation.form[' + node.selector + ']',
+ 'message': 'ERROR - Invalid argument, must be type object'
+ });
+
+ window.Debug.print();
+ // {/debug}
+
+ return false;
+ }
+
+ var input,
+ onlyOnce = true;
+ for (var inputName in error) {
+
+ if (!error.hasOwnProperty(inputName)) {
+ continue;
+ }
+
+ if (!(error[inputName] instanceof Array)) {
+ error[inputName] = [error[inputName]];
+ }
+
+ input = $(node.selector).find('[name="' + inputName + '"]');
+ if (!input[0]) {
+
+ // {debug}
+ window.Debug.log({
+ 'node': node,
+ 'function': '$.addError()',
+ 'arguments': inputName,
+ 'message': 'ERROR - Unable to find ' + '$(' + node.selector + ').find("[name="' + inputName + '"]")'
+ });
+
+ window.Debug.print();
+ // {/debug}
+
+ continue;
+ }
+
+ if (onlyOnce) {
+ window.Validation.hasScrolled = false;
+ onlyOnce = false;
+ }
+
+ window.Validation.form[node.selector].resetOneError(inputName, input);
+
+ for (var i = 0; i < error[inputName].length; i++) {
+
+ if (typeof error[inputName][i] !== "string") {
+
+ // {debug}
+ window.Debug.log({
+ 'node': node,
+ 'function': '$.addError()',
+ 'arguments': error[inputName][i],
+ 'message': 'ERROR - Invalid error object property - Accepted format: {"inputName": "errorString"} or {"inputName": ["errorString", "errorString"]}'
+ });
+
+ window.Debug.print();
+ // {/debug}
+
+ continue;
+ }
+
+ window.Validation.form[node.selector].registerError(inputName, error[inputName][i]);
+
+ }
+
+ window.Validation.form[node.selector].displayOneError(inputName);
+
+ }
+
+ },
+
+ /**
+ * API method to manually remove a form error.
+ * Note: The same form jQuery selector MUST be used to recuperate the Validation configuration.
+ *
+ * @example
+ * $('#form-signin_v2').removeError([
+ * 'signin_v2[username]',
+ * 'signin_v2[password]'
+ * ])
+ *
+ * @param {Object} node jQuery object
+ * @param {Object} inputName Object of errors to remove on the node
+ *
+ * @returns {*}
+ */
+ removeError: function (node, inputName) {
+
+ if (!window.Validation.form[node.selector]) {
+
+ // {debug}
+ window.Debug.log({
+ 'node': node,
+ 'function': '$.removeError()',
+ 'arguments': 'window.Validation.form[' + node.selector + ']',
+ 'message': 'ERROR - Invalid node selector'
+ });
+
+ window.Debug.print();
+ // {/debug}
+
+ return false;
+ }
+
+ if (!inputName) {
+ window.Validation.form[node.selector].resetErrors();
+ return false;
+ }
+
+ if (typeof inputName === "object" && Object.prototype.toString.call(inputName) !== "[object Array]") {
+
+ // {debug}
+ window.Debug.log({
+ 'node': node,
+ 'function': '$.removeError()',
+ 'arguments': inputName,
+ 'message': 'ERROR - Invalid inputName, must be type String or Array'
+ });
+
+ window.Debug.print();
+ // {/debug}
+
+ return false;
+ }
+
+ if (!(inputName instanceof Array)) {
+ inputName = [inputName];
+ }
+
+ var input;
+ for (var i = 0; i < inputName.length; i++) {
+
+ input = $(node.selector).find('[name="' + inputName[i] + '"]');
+ if (!input[0]) {
+
+ // {debug}
+ window.Debug.log({
+ 'node': node,
+ 'function': '$.removeError()',
+ 'arguments': inputName[i],
+ 'message': 'ERROR - Unable to find ' + '$(' + node.selector + ').find("[name="' + inputName[i] + '"]")'
+ });
+
+ window.Debug.print();
+ // {/debug}
+
+ continue;
+ }
+
+ window.Validation.form[node.selector].resetOneError(inputName[i], input);
+
+ }
+
+ },
+
+ /**
+ * API method to add a validation rule.
+ *
+ * @example
+ * $.alterValidationRules({
+ * rule: 'FILENAME',
+ * regex: /^[^\\/:\*\?<>\|\"\']*$/,
+ * message: '$ has an invalid filename.'
+ * })
+ *
+ * @param {Object} ruleObj
+ */
+ alterValidationRules: function (ruleObj) {
+
+ if (!ruleObj.rule || (!ruleObj.regex && !ruleObj.message)) {
+ // {debug}
+ window.Debug.log({
+ 'function': '$.alterValidationRules()',
+ 'message': 'ERROR - Missing one or multiple parameter(s) {rule, regex, message}'
+ });
+ window.Debug.print();
+ // {/debug}
+ return false;
+ }
+
+ ruleObj.rule = ruleObj.rule.toUpperCase();
+
+ if (ruleObj.regex) {
+
+ var regex = _buildRegexFromString(ruleObj.regex);
+
+ if (!(regex instanceof RegExp)) {
+ // {debug}
+ window.Debug.log({
+ 'function': '$.alterValidationRules(rule)',
+ 'arguments': regex.toString(),
+ 'message': 'ERROR - Invalid rule'
+ });
+ window.Debug.print();
+ // {/debug}
+ return false;
+ }
+
+ _rules[ruleObj.rule] = regex;
+ }
+
+ if (ruleObj.message) {
+ _messages[ruleObj.rule] = ruleObj.message;
+ }
+
+ return true;
+ }
+
+ };
+
+ /**
+ * @private
+ * Converts string into a regex
+ *
+ * @param {String|Object} regex
+ * @returns {Object|Boolean} rule
+ */
+ function _buildRegexFromString(regex) {
+
+ if (!regex || (typeof regex !== "string" && !(regex instanceof RegExp))) {
+ _regexDebug();
+ return false;
+ }
+
+ if (typeof regex !== 'string') {
+ regex = regex.toString();
+ }
+
+ var separator = regex.charAt(0),
+ index = regex.length - 1,
+ pattern,
+ modifier,
+ rule;
+
+ while (index > 0) {
+ if (/[gimsxeU]/.test(regex.charAt(index))) {
+ index--;
+ } else {
+ break;
+ }
+ }
+
+ if (regex.charAt(index) !== separator) {
+ separator = null;
+ }
+
+ if (separator && index !== regex.length - 1) {
+ modifier = regex.substr(index + 1, regex.length - 1);
+ }
+
+ if (separator) {
+ pattern = regex.substr(1, index - 1);
+ } else {
+ pattern = regex;
+ }
+
+ try {
+ rule = new RegExp(pattern, modifier);
+ } catch (error) {
+ _regexDebug();
+ return false;
+ }
+
+ return rule;
+
+ function _regexDebug() {
+ // {debug}
+ window.Debug.log({
+ 'function': '_buildRegexFromString()',
+ 'arguments': '{pattern: {' + (pattern || '') + '}, modifier: {' + (modifier || '') + '}',
+ 'message': 'WARNING - Invalid regex given: ' + regex
+ });
+ window.Debug.print();
+ // {/debug}
+ }
+
+ }
+
+ // {debug}
+ window.Debug = {
+
+ table: {},
+ log: function (debugObject) {
+
+ if (!debugObject.message || typeof debugObject.message !== "string") {
+ return false;
+ }
+
+ this.table[debugObject.message] = $.extend(
+ Object.preventExtensions(
+ {
+ 'node': '',
+ 'function': '',
+ 'arguments': ''
+ }
+ ), debugObject
+ );
+
+ },
+ print: function () {
+
+ if (isEmpty(this.table)) {
+ return false;
+ }
+
+ if (console.group !== undefined || console.table !== undefined) {
+
+ console.groupCollapsed('--- jQuery Form Validation Debug ---');
+
+ if (console.table) {
+ console.table(this.table);
+ } else {
+ $.each(this.table, function (index, data) {
+ console.log(data['Name'] + ': ' + data['Execution Time'] + 'ms');
+ });
+ }
+
+ console.groupEnd();
+
+ } else {
+ console.log('Debug is not available on your current browser, try the most recent version of Chrome or Firefox.');
+ }
+
+ this.table = {};
+
+ }
+
+ };
+ // {/debug}
+
+ String.prototype.capitalize = function () {
+ return this.charAt(0).toUpperCase() + this.slice(1);
+ };
+
+ function isEmpty (obj) {
+ for (var prop in obj) {
+ if (obj.hasOwnProperty(prop))
+ return false;
+ }
+
+ return true;
+ }
+
+ if (!Array.prototype.indexOf) {
+ Array.prototype.indexOf = function (elt /*, from*/) {
+ var len = this.length >>> 0;
+
+ var from = Number(arguments[1]) || 0;
+ from = (from < 0)
+ ? Math.ceil(from)
+ : Math.floor(from);
+ if (from < 0)
+ from += len;
+
+ for (; from < len; from++) {
+ if (from in this &&
+ this[from] === elt)
+ return from;
+ }
+ return -1;
+ };
+ }
+
+ // {debug}
+ if (!JSON && !JSON.stringify) {
+ JSON.stringify = function (obj) {
+ var t = typeof (obj);
+ if (t !== "object" || obj === null) {
+ // simple data type
+ if (t === "string") {
+ obj = '"' + obj + '"';
+ }
+ return String(obj);
+ }
+ else {
+ var n, v, json = [], arr = (obj && obj.constructor === Array);
+ for (n in obj) {
+ if (true) {
+ v = obj[n];
+ t = typeof(v);
+ if (t === "string") {
+ v = '"' + v + '"';
+ }
+ else if (t === "object" && v !== null) {
+ v = JSON.stringify(v);
+ }
+ json.push((arr ? "" : '"' + n + '": ') + String(v));
+ }
+ }
+ return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}");
+ }
+ };
+ }
+ // {/debug}
+
+}(window, document, window.jQuery));
\ No newline at end of file