2495 lines
91 KiB
JavaScript
2495 lines
91 KiB
JavaScript
|
/*!
|
||
|
* Parsley.js
|
||
|
* Version 2.8.1 - built Sat, Feb 3rd 2018, 2:27 pm
|
||
|
* http://parsleyjs.org
|
||
|
* Guillaume Potier - <guillaume@wisembly.com>
|
||
|
* Marc-Andre Lafortune - <petroselinum@marc-andre.ca>
|
||
|
* MIT Licensed
|
||
|
*/
|
||
|
|
||
|
// The source code below is generated by babel as
|
||
|
// Parsley is written in ECMAScript 6
|
||
|
//
|
||
|
var _slice = Array.prototype.slice;
|
||
|
|
||
|
var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })();
|
||
|
|
||
|
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
||
|
|
||
|
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } }
|
||
|
|
||
|
(function (global, factory) {
|
||
|
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('jquery')) : typeof define === 'function' && define.amd ? define(['jquery'], factory) : global.parsley = factory(global.jQuery);
|
||
|
})(this, function ($) {
|
||
|
'use strict';
|
||
|
|
||
|
var globalID = 1;
|
||
|
var pastWarnings = {};
|
||
|
|
||
|
var Utils = {
|
||
|
// Parsley DOM-API
|
||
|
// returns object from dom attributes and values
|
||
|
attr: function attr(element, namespace, obj) {
|
||
|
var i;
|
||
|
var attribute;
|
||
|
var attributes;
|
||
|
var regex = new RegExp('^' + namespace, 'i');
|
||
|
|
||
|
if ('undefined' === typeof obj) obj = {};else {
|
||
|
// Clear all own properties. This won't affect prototype's values
|
||
|
for (i in obj) {
|
||
|
if (obj.hasOwnProperty(i)) delete obj[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!element) return obj;
|
||
|
|
||
|
attributes = element.attributes;
|
||
|
for (i = attributes.length; i--;) {
|
||
|
attribute = attributes[i];
|
||
|
|
||
|
if (attribute && attribute.specified && regex.test(attribute.name)) {
|
||
|
obj[this.camelize(attribute.name.slice(namespace.length))] = this.deserializeValue(attribute.value);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return obj;
|
||
|
},
|
||
|
|
||
|
checkAttr: function checkAttr(element, namespace, _checkAttr) {
|
||
|
return element.hasAttribute(namespace + _checkAttr);
|
||
|
},
|
||
|
|
||
|
setAttr: function setAttr(element, namespace, attr, value) {
|
||
|
element.setAttribute(this.dasherize(namespace + attr), String(value));
|
||
|
},
|
||
|
|
||
|
getType: function getType(element) {
|
||
|
return element.getAttribute('type') || 'text';
|
||
|
},
|
||
|
|
||
|
generateID: function generateID() {
|
||
|
return '' + globalID++;
|
||
|
},
|
||
|
|
||
|
/** Third party functions **/
|
||
|
deserializeValue: function deserializeValue(value) {
|
||
|
var num;
|
||
|
|
||
|
try {
|
||
|
return value ? value == "true" || (value == "false" ? false : value == "null" ? null : !isNaN(num = Number(value)) ? num : /^[\[\{]/.test(value) ? JSON.parse(value) : value) : value;
|
||
|
} catch (e) {
|
||
|
return value;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
// Zepto camelize function
|
||
|
camelize: function camelize(str) {
|
||
|
return str.replace(/-+(.)?/g, function (match, chr) {
|
||
|
return chr ? chr.toUpperCase() : '';
|
||
|
});
|
||
|
},
|
||
|
|
||
|
// Zepto dasherize function
|
||
|
dasherize: function dasherize(str) {
|
||
|
return str.replace(/::/g, '/').replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2').replace(/([a-z\d])([A-Z])/g, '$1_$2').replace(/_/g, '-').toLowerCase();
|
||
|
},
|
||
|
|
||
|
warn: function warn() {
|
||
|
var _window$console;
|
||
|
|
||
|
if (window.console && 'function' === typeof window.console.warn) (_window$console = window.console).warn.apply(_window$console, arguments);
|
||
|
},
|
||
|
|
||
|
warnOnce: function warnOnce(msg) {
|
||
|
if (!pastWarnings[msg]) {
|
||
|
pastWarnings[msg] = true;
|
||
|
this.warn.apply(this, arguments);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_resetWarnings: function _resetWarnings() {
|
||
|
pastWarnings = {};
|
||
|
},
|
||
|
|
||
|
trimString: function trimString(string) {
|
||
|
return string.replace(/^\s+|\s+$/g, '');
|
||
|
},
|
||
|
|
||
|
parse: {
|
||
|
date: function date(string) {
|
||
|
var parsed = string.match(/^(\d{4,})-(\d\d)-(\d\d)$/);
|
||
|
if (!parsed) return null;
|
||
|
|
||
|
var _parsed$map = parsed.map(function (x) {
|
||
|
return parseInt(x, 10);
|
||
|
});
|
||
|
|
||
|
var _parsed$map2 = _slicedToArray(_parsed$map, 4);
|
||
|
|
||
|
var _ = _parsed$map2[0];
|
||
|
var year = _parsed$map2[1];
|
||
|
var month = _parsed$map2[2];
|
||
|
var day = _parsed$map2[3];
|
||
|
|
||
|
var date = new Date(year, month - 1, day);
|
||
|
if (date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day) return null;
|
||
|
return date;
|
||
|
},
|
||
|
string: function string(_string) {
|
||
|
return _string;
|
||
|
},
|
||
|
integer: function integer(string) {
|
||
|
if (isNaN(string)) return null;
|
||
|
return parseInt(string, 10);
|
||
|
},
|
||
|
number: function number(string) {
|
||
|
if (isNaN(string)) throw null;
|
||
|
return parseFloat(string);
|
||
|
},
|
||
|
'boolean': function _boolean(string) {
|
||
|
return !/^\s*false\s*$/i.test(string);
|
||
|
},
|
||
|
object: function object(string) {
|
||
|
return Utils.deserializeValue(string);
|
||
|
},
|
||
|
regexp: function regexp(_regexp) {
|
||
|
var flags = '';
|
||
|
|
||
|
// Test if RegExp is literal, if not, nothing to be done, otherwise, we need to isolate flags and pattern
|
||
|
if (/^\/.*\/(?:[gimy]*)$/.test(_regexp)) {
|
||
|
// Replace the regexp literal string with the first match group: ([gimy]*)
|
||
|
// If no flag is present, this will be a blank string
|
||
|
flags = _regexp.replace(/.*\/([gimy]*)$/, '$1');
|
||
|
// Again, replace the regexp literal string with the first match group:
|
||
|
// everything excluding the opening and closing slashes and the flags
|
||
|
_regexp = _regexp.replace(new RegExp('^/(.*?)/' + flags + '$'), '$1');
|
||
|
} else {
|
||
|
// Anchor regexp:
|
||
|
_regexp = '^' + _regexp + '$';
|
||
|
}
|
||
|
return new RegExp(_regexp, flags);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
parseRequirement: function parseRequirement(requirementType, string) {
|
||
|
var converter = this.parse[requirementType || 'string'];
|
||
|
if (!converter) throw 'Unknown requirement specification: "' + requirementType + '"';
|
||
|
var converted = converter(string);
|
||
|
if (converted === null) throw 'Requirement is not a ' + requirementType + ': "' + string + '"';
|
||
|
return converted;
|
||
|
},
|
||
|
|
||
|
namespaceEvents: function namespaceEvents(events, namespace) {
|
||
|
events = this.trimString(events || '').split(/\s+/);
|
||
|
if (!events[0]) return '';
|
||
|
return $.map(events, function (evt) {
|
||
|
return evt + '.' + namespace;
|
||
|
}).join(' ');
|
||
|
},
|
||
|
|
||
|
difference: function difference(array, remove) {
|
||
|
// This is O(N^2), should be optimized
|
||
|
var result = [];
|
||
|
$.each(array, function (_, elem) {
|
||
|
if (remove.indexOf(elem) == -1) result.push(elem);
|
||
|
});
|
||
|
return result;
|
||
|
},
|
||
|
|
||
|
// Alter-ego to native Promise.all, but for jQuery
|
||
|
all: function all(promises) {
|
||
|
// jQuery treats $.when() and $.when(singlePromise) differently; let's avoid that and add spurious elements
|
||
|
return $.when.apply($, _toConsumableArray(promises).concat([42, 42]));
|
||
|
},
|
||
|
|
||
|
// Object.create polyfill, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Polyfill
|
||
|
objectCreate: Object.create || (function () {
|
||
|
var Object = function Object() {};
|
||
|
return function (prototype) {
|
||
|
if (arguments.length > 1) {
|
||
|
throw Error('Second argument not supported');
|
||
|
}
|
||
|
if (typeof prototype != 'object') {
|
||
|
throw TypeError('Argument must be an object');
|
||
|
}
|
||
|
Object.prototype = prototype;
|
||
|
var result = new Object();
|
||
|
Object.prototype = null;
|
||
|
return result;
|
||
|
};
|
||
|
})(),
|
||
|
|
||
|
_SubmitSelector: 'input[type="submit"], button:submit'
|
||
|
};
|
||
|
|
||
|
// All these options could be overriden and specified directly in DOM using
|
||
|
// `data-parsley-` default DOM-API
|
||
|
// eg: `inputs` can be set in DOM using `data-parsley-inputs="input, textarea"`
|
||
|
// eg: `data-parsley-stop-on-first-failing-constraint="false"`
|
||
|
|
||
|
var Defaults = {
|
||
|
// ### General
|
||
|
|
||
|
// Default data-namespace for DOM API
|
||
|
namespace: 'data-parsley-',
|
||
|
|
||
|
// Supported inputs by default
|
||
|
inputs: 'input, textarea, select',
|
||
|
|
||
|
// Excluded inputs by default
|
||
|
excluded: 'input[type=button], input[type=submit], input[type=reset], input[type=hidden]',
|
||
|
|
||
|
// Stop validating field on highest priority failing constraint
|
||
|
priorityEnabled: true,
|
||
|
|
||
|
// ### Field only
|
||
|
|
||
|
// identifier used to group together inputs (e.g. radio buttons...)
|
||
|
multiple: null,
|
||
|
|
||
|
// identifier (or array of identifiers) used to validate only a select group of inputs
|
||
|
group: null,
|
||
|
|
||
|
// ### UI
|
||
|
// Enable\Disable error messages
|
||
|
uiEnabled: true,
|
||
|
|
||
|
// Key events threshold before validation
|
||
|
validationThreshold: 3,
|
||
|
|
||
|
// Focused field on form validation error. 'first'|'last'|'none'
|
||
|
focus: 'first',
|
||
|
|
||
|
// event(s) that will trigger validation before first failure. eg: `input`...
|
||
|
trigger: false,
|
||
|
|
||
|
// event(s) that will trigger validation after first failure.
|
||
|
triggerAfterFailure: 'input',
|
||
|
|
||
|
// Class that would be added on every failing validation Parsley field
|
||
|
errorClass: 'parsley-error',
|
||
|
|
||
|
// Same for success validation
|
||
|
successClass: 'parsley-success',
|
||
|
|
||
|
// Return the `$element` that will receive these above success or error classes
|
||
|
// Could also be (and given directly from DOM) a valid selector like `'#div'`
|
||
|
classHandler: function classHandler(Field) {},
|
||
|
|
||
|
// Return the `$element` where errors will be appended
|
||
|
// Could also be (and given directly from DOM) a valid selector like `'#div'`
|
||
|
errorsContainer: function errorsContainer(Field) {},
|
||
|
|
||
|
// ul elem that would receive errors' list
|
||
|
errorsWrapper: '<ul class="parsley-errors-list"></ul>',
|
||
|
|
||
|
// li elem that would receive error message
|
||
|
errorTemplate: '<li></li>'
|
||
|
};
|
||
|
|
||
|
var Base = function Base() {
|
||
|
this.__id__ = Utils.generateID();
|
||
|
};
|
||
|
|
||
|
Base.prototype = {
|
||
|
asyncSupport: true, // Deprecated
|
||
|
|
||
|
_pipeAccordingToValidationResult: function _pipeAccordingToValidationResult() {
|
||
|
var _this = this;
|
||
|
|
||
|
var pipe = function pipe() {
|
||
|
var r = $.Deferred();
|
||
|
if (true !== _this.validationResult) r.reject();
|
||
|
return r.resolve().promise();
|
||
|
};
|
||
|
return [pipe, pipe];
|
||
|
},
|
||
|
|
||
|
actualizeOptions: function actualizeOptions() {
|
||
|
Utils.attr(this.element, this.options.namespace, this.domOptions);
|
||
|
if (this.parent && this.parent.actualizeOptions) this.parent.actualizeOptions();
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
_resetOptions: function _resetOptions(initOptions) {
|
||
|
this.domOptions = Utils.objectCreate(this.parent.options);
|
||
|
this.options = Utils.objectCreate(this.domOptions);
|
||
|
// Shallow copy of ownProperties of initOptions:
|
||
|
for (var i in initOptions) {
|
||
|
if (initOptions.hasOwnProperty(i)) this.options[i] = initOptions[i];
|
||
|
}
|
||
|
this.actualizeOptions();
|
||
|
},
|
||
|
|
||
|
_listeners: null,
|
||
|
|
||
|
// Register a callback for the given event name
|
||
|
// Callback is called with context as the first argument and the `this`
|
||
|
// The context is the current parsley instance, or window.Parsley if global
|
||
|
// A return value of `false` will interrupt the calls
|
||
|
on: function on(name, fn) {
|
||
|
this._listeners = this._listeners || {};
|
||
|
var queue = this._listeners[name] = this._listeners[name] || [];
|
||
|
queue.push(fn);
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
// Deprecated. Use `on` instead
|
||
|
subscribe: function subscribe(name, fn) {
|
||
|
$.listenTo(this, name.toLowerCase(), fn);
|
||
|
},
|
||
|
|
||
|
// Unregister a callback (or all if none is given) for the given event name
|
||
|
off: function off(name, fn) {
|
||
|
var queue = this._listeners && this._listeners[name];
|
||
|
if (queue) {
|
||
|
if (!fn) {
|
||
|
delete this._listeners[name];
|
||
|
} else {
|
||
|
for (var i = queue.length; i--;) if (queue[i] === fn) queue.splice(i, 1);
|
||
|
}
|
||
|
}
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
// Deprecated. Use `off`
|
||
|
unsubscribe: function unsubscribe(name, fn) {
|
||
|
$.unsubscribeTo(this, name.toLowerCase());
|
||
|
},
|
||
|
|
||
|
// Trigger an event of the given name
|
||
|
// A return value of `false` interrupts the callback chain
|
||
|
// Returns false if execution was interrupted
|
||
|
trigger: function trigger(name, target, extraArg) {
|
||
|
target = target || this;
|
||
|
var queue = this._listeners && this._listeners[name];
|
||
|
var result;
|
||
|
var parentResult;
|
||
|
if (queue) {
|
||
|
for (var i = queue.length; i--;) {
|
||
|
result = queue[i].call(target, target, extraArg);
|
||
|
if (result === false) return result;
|
||
|
}
|
||
|
}
|
||
|
if (this.parent) {
|
||
|
return this.parent.trigger(name, target, extraArg);
|
||
|
}
|
||
|
return true;
|
||
|
},
|
||
|
|
||
|
asyncIsValid: function asyncIsValid(group, force) {
|
||
|
Utils.warnOnce("asyncIsValid is deprecated; please use whenValid instead");
|
||
|
return this.whenValid({ group: group, force: force });
|
||
|
},
|
||
|
|
||
|
_findRelated: function _findRelated() {
|
||
|
return this.options.multiple ? $(this.parent.element.querySelectorAll('[' + this.options.namespace + 'multiple="' + this.options.multiple + '"]')) : this.$element;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var convertArrayRequirement = function convertArrayRequirement(string, length) {
|
||
|
var m = string.match(/^\s*\[(.*)\]\s*$/);
|
||
|
if (!m) throw 'Requirement is not an array: "' + string + '"';
|
||
|
var values = m[1].split(',').map(Utils.trimString);
|
||
|
if (values.length !== length) throw 'Requirement has ' + values.length + ' values when ' + length + ' are needed';
|
||
|
return values;
|
||
|
};
|
||
|
|
||
|
var convertExtraOptionRequirement = function convertExtraOptionRequirement(requirementSpec, string, extraOptionReader) {
|
||
|
var main = null;
|
||
|
var extra = {};
|
||
|
for (var key in requirementSpec) {
|
||
|
if (key) {
|
||
|
var value = extraOptionReader(key);
|
||
|
if ('string' === typeof value) value = Utils.parseRequirement(requirementSpec[key], value);
|
||
|
extra[key] = value;
|
||
|
} else {
|
||
|
main = Utils.parseRequirement(requirementSpec[key], string);
|
||
|
}
|
||
|
}
|
||
|
return [main, extra];
|
||
|
};
|
||
|
|
||
|
// A Validator needs to implement the methods `validate` and `parseRequirements`
|
||
|
|
||
|
var Validator = function Validator(spec) {
|
||
|
$.extend(true, this, spec);
|
||
|
};
|
||
|
|
||
|
Validator.prototype = {
|
||
|
// Returns `true` iff the given `value` is valid according the given requirements.
|
||
|
validate: function validate(value, requirementFirstArg) {
|
||
|
if (this.fn) {
|
||
|
// Legacy style validator
|
||
|
|
||
|
if (arguments.length > 3) // If more args then value, requirement, instance...
|
||
|
requirementFirstArg = [].slice.call(arguments, 1, -1); // Skip first arg (value) and last (instance), combining the rest
|
||
|
return this.fn(value, requirementFirstArg);
|
||
|
}
|
||
|
|
||
|
if (Array.isArray(value)) {
|
||
|
if (!this.validateMultiple) throw 'Validator `' + this.name + '` does not handle multiple values';
|
||
|
return this.validateMultiple.apply(this, arguments);
|
||
|
} else {
|
||
|
var instance = arguments[arguments.length - 1];
|
||
|
if (this.validateDate && instance._isDateInput()) {
|
||
|
arguments[0] = Utils.parse.date(arguments[0]);
|
||
|
if (arguments[0] === null) return false;
|
||
|
return this.validateDate.apply(this, arguments);
|
||
|
}
|
||
|
if (this.validateNumber) {
|
||
|
if (isNaN(value)) return false;
|
||
|
arguments[0] = parseFloat(arguments[0]);
|
||
|
return this.validateNumber.apply(this, arguments);
|
||
|
}
|
||
|
if (this.validateString) {
|
||
|
return this.validateString.apply(this, arguments);
|
||
|
}
|
||
|
throw 'Validator `' + this.name + '` only handles multiple values';
|
||
|
}
|
||
|
},
|
||
|
|
||
|
// Parses `requirements` into an array of arguments,
|
||
|
// according to `this.requirementType`
|
||
|
parseRequirements: function parseRequirements(requirements, extraOptionReader) {
|
||
|
if ('string' !== typeof requirements) {
|
||
|
// Assume requirement already parsed
|
||
|
// but make sure we return an array
|
||
|
return Array.isArray(requirements) ? requirements : [requirements];
|
||
|
}
|
||
|
var type = this.requirementType;
|
||
|
if (Array.isArray(type)) {
|
||
|
var values = convertArrayRequirement(requirements, type.length);
|
||
|
for (var i = 0; i < values.length; i++) values[i] = Utils.parseRequirement(type[i], values[i]);
|
||
|
return values;
|
||
|
} else if ($.isPlainObject(type)) {
|
||
|
return convertExtraOptionRequirement(type, requirements, extraOptionReader);
|
||
|
} else {
|
||
|
return [Utils.parseRequirement(type, requirements)];
|
||
|
}
|
||
|
},
|
||
|
// Defaults:
|
||
|
requirementType: 'string',
|
||
|
|
||
|
priority: 2
|
||
|
|
||
|
};
|
||
|
|
||
|
var ValidatorRegistry = function ValidatorRegistry(validators, catalog) {
|
||
|
this.__class__ = 'ValidatorRegistry';
|
||
|
|
||
|
// Default Parsley locale is en
|
||
|
this.locale = 'en';
|
||
|
|
||
|
this.init(validators || {}, catalog || {});
|
||
|
};
|
||
|
|
||
|
var typeTesters = {
|
||
|
email: /^((([a-zA-Z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-zA-Z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-zA-Z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-zA-Z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-zA-Z]|\d|-|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-zA-Z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-zA-Z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-zA-Z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-zA-Z]|\d|-|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-zA-Z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/,
|
||
|
|
||
|
// Follow https://www.w3.org/TR/html5/infrastructure.html#floating-point-numbers
|
||
|
number: /^-?(\d*\.)?\d+(e[-+]?\d+)?$/i,
|
||
|
|
||
|
integer: /^-?\d+$/,
|
||
|
|
||
|
digits: /^\d+$/,
|
||
|
|
||
|
alphanum: /^\w+$/i,
|
||
|
|
||
|
date: {
|
||
|
test: function test(value) {
|
||
|
return Utils.parse.date(value) !== null;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
url: new RegExp("^" +
|
||
|
// protocol identifier
|
||
|
"(?:(?:https?|ftp)://)?" + // ** mod: make scheme optional
|
||
|
// user:pass authentication
|
||
|
"(?:\\S+(?::\\S*)?@)?" + "(?:" +
|
||
|
// IP address exclusion
|
||
|
// private & local networks
|
||
|
// "(?!(?:10|127)(?:\\.\\d{1,3}){3})" + // ** mod: allow local networks
|
||
|
// "(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})" + // ** mod: allow local networks
|
||
|
// "(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})" + // ** mod: allow local networks
|
||
|
// IP address dotted notation octets
|
||
|
// excludes loopback network 0.0.0.0
|
||
|
// excludes reserved space >= 224.0.0.0
|
||
|
// excludes network & broacast addresses
|
||
|
// (first & last IP address of each class)
|
||
|
"(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" + "(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" + "(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" + "|" +
|
||
|
// host name
|
||
|
'(?:(?:[a-zA-Z\\u00a1-\\uffff0-9]-*)*[a-zA-Z\\u00a1-\\uffff0-9]+)' +
|
||
|
// domain name
|
||
|
'(?:\\.(?:[a-zA-Z\\u00a1-\\uffff0-9]-*)*[a-zA-Z\\u00a1-\\uffff0-9]+)*' +
|
||
|
// TLD identifier
|
||
|
'(?:\\.(?:[a-zA-Z\\u00a1-\\uffff]{2,}))' + ")" +
|
||
|
// port number
|
||
|
"(?::\\d{2,5})?" +
|
||
|
// resource path
|
||
|
"(?:/\\S*)?" + "$")
|
||
|
};
|
||
|
typeTesters.range = typeTesters.number;
|
||
|
|
||
|
// See http://stackoverflow.com/a/10454560/8279
|
||
|
var decimalPlaces = function decimalPlaces(num) {
|
||
|
var match = ('' + num).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);
|
||
|
if (!match) {
|
||
|
return 0;
|
||
|
}
|
||
|
return Math.max(0,
|
||
|
// Number of digits right of decimal point.
|
||
|
(match[1] ? match[1].length : 0) - (
|
||
|
// Adjust for scientific notation.
|
||
|
match[2] ? +match[2] : 0));
|
||
|
};
|
||
|
|
||
|
// parseArguments('number', ['1', '2']) => [1, 2]
|
||
|
var ValidatorRegistry__parseArguments = function ValidatorRegistry__parseArguments(type, args) {
|
||
|
return args.map(Utils.parse[type]);
|
||
|
};
|
||
|
// operatorToValidator returns a validating function for an operator function, applied to the given type
|
||
|
var ValidatorRegistry__operatorToValidator = function ValidatorRegistry__operatorToValidator(type, operator) {
|
||
|
return function (value) {
|
||
|
for (var _len = arguments.length, requirementsAndInput = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
||
|
requirementsAndInput[_key - 1] = arguments[_key];
|
||
|
}
|
||
|
|
||
|
requirementsAndInput.pop(); // Get rid of `input` argument
|
||
|
return operator.apply(undefined, [value].concat(_toConsumableArray(ValidatorRegistry__parseArguments(type, requirementsAndInput))));
|
||
|
};
|
||
|
};
|
||
|
|
||
|
var ValidatorRegistry__comparisonOperator = function ValidatorRegistry__comparisonOperator(operator) {
|
||
|
return {
|
||
|
validateDate: ValidatorRegistry__operatorToValidator('date', operator),
|
||
|
validateNumber: ValidatorRegistry__operatorToValidator('number', operator),
|
||
|
requirementType: operator.length <= 2 ? 'string' : ['string', 'string'], // Support operators with a 1 or 2 requirement(s)
|
||
|
priority: 30
|
||
|
};
|
||
|
};
|
||
|
|
||
|
ValidatorRegistry.prototype = {
|
||
|
init: function init(validators, catalog) {
|
||
|
this.catalog = catalog;
|
||
|
// Copy prototype's validators:
|
||
|
this.validators = _extends({}, this.validators);
|
||
|
|
||
|
for (var name in validators) this.addValidator(name, validators[name].fn, validators[name].priority);
|
||
|
|
||
|
window.Parsley.trigger('parsley:validator:init');
|
||
|
},
|
||
|
|
||
|
// Set new messages locale if we have dictionary loaded in ParsleyConfig.i18n
|
||
|
setLocale: function setLocale(locale) {
|
||
|
if ('undefined' === typeof this.catalog[locale]) throw new Error(locale + ' is not available in the catalog');
|
||
|
|
||
|
this.locale = locale;
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
// Add a new messages catalog for a given locale. Set locale for this catalog if set === `true`
|
||
|
addCatalog: function addCatalog(locale, messages, set) {
|
||
|
if ('object' === typeof messages) this.catalog[locale] = messages;
|
||
|
|
||
|
if (true === set) return this.setLocale(locale);
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
// Add a specific message for a given constraint in a given locale
|
||
|
addMessage: function addMessage(locale, name, message) {
|
||
|
if ('undefined' === typeof this.catalog[locale]) this.catalog[locale] = {};
|
||
|
|
||
|
this.catalog[locale][name] = message;
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
// Add messages for a given locale
|
||
|
addMessages: function addMessages(locale, nameMessageObject) {
|
||
|
for (var name in nameMessageObject) this.addMessage(locale, name, nameMessageObject[name]);
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
// Add a new validator
|
||
|
//
|
||
|
// addValidator('custom', {
|
||
|
// requirementType: ['integer', 'integer'],
|
||
|
// validateString: function(value, from, to) {},
|
||
|
// priority: 22,
|
||
|
// messages: {
|
||
|
// en: "Hey, that's no good",
|
||
|
// fr: "Aye aye, pas bon du tout",
|
||
|
// }
|
||
|
// })
|
||
|
//
|
||
|
// Old API was addValidator(name, function, priority)
|
||
|
//
|
||
|
addValidator: function addValidator(name, arg1, arg2) {
|
||
|
if (this.validators[name]) Utils.warn('Validator "' + name + '" is already defined.');else if (Defaults.hasOwnProperty(name)) {
|
||
|
Utils.warn('"' + name + '" is a restricted keyword and is not a valid validator name.');
|
||
|
return;
|
||
|
}
|
||
|
return this._setValidator.apply(this, arguments);
|
||
|
},
|
||
|
|
||
|
hasValidator: function hasValidator(name) {
|
||
|
return !!this.validators[name];
|
||
|
},
|
||
|
|
||
|
updateValidator: function updateValidator(name, arg1, arg2) {
|
||
|
if (!this.validators[name]) {
|
||
|
Utils.warn('Validator "' + name + '" is not already defined.');
|
||
|
return this.addValidator.apply(this, arguments);
|
||
|
}
|
||
|
return this._setValidator.apply(this, arguments);
|
||
|
},
|
||
|
|
||
|
removeValidator: function removeValidator(name) {
|
||
|
if (!this.validators[name]) Utils.warn('Validator "' + name + '" is not defined.');
|
||
|
|
||
|
delete this.validators[name];
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
_setValidator: function _setValidator(name, validator, priority) {
|
||
|
if ('object' !== typeof validator) {
|
||
|
// Old style validator, with `fn` and `priority`
|
||
|
validator = {
|
||
|
fn: validator,
|
||
|
priority: priority
|
||
|
};
|
||
|
}
|
||
|
if (!validator.validate) {
|
||
|
validator = new Validator(validator);
|
||
|
}
|
||
|
this.validators[name] = validator;
|
||
|
|
||
|
for (var locale in validator.messages || {}) this.addMessage(locale, name, validator.messages[locale]);
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
getErrorMessage: function getErrorMessage(constraint) {
|
||
|
var message;
|
||
|
|
||
|
// Type constraints are a bit different, we have to match their requirements too to find right error message
|
||
|
if ('type' === constraint.name) {
|
||
|
var typeMessages = this.catalog[this.locale][constraint.name] || {};
|
||
|
message = typeMessages[constraint.requirements];
|
||
|
} else message = this.formatMessage(this.catalog[this.locale][constraint.name], constraint.requirements);
|
||
|
|
||
|
return message || this.catalog[this.locale].defaultMessage || this.catalog.en.defaultMessage;
|
||
|
},
|
||
|
|
||
|
// Kind of light `sprintf()` implementation
|
||
|
formatMessage: function formatMessage(string, parameters) {
|
||
|
if ('object' === typeof parameters) {
|
||
|
for (var i in parameters) string = this.formatMessage(string, parameters[i]);
|
||
|
|
||
|
return string;
|
||
|
}
|
||
|
|
||
|
return 'string' === typeof string ? string.replace(/%s/i, parameters) : '';
|
||
|
},
|
||
|
|
||
|
// Here is the Parsley default validators list.
|
||
|
// A validator is an object with the following key values:
|
||
|
// - priority: an integer
|
||
|
// - requirement: 'string' (default), 'integer', 'number', 'regexp' or an Array of these
|
||
|
// - validateString, validateMultiple, validateNumber: functions returning `true`, `false` or a promise
|
||
|
// Alternatively, a validator can be a function that returns such an object
|
||
|
//
|
||
|
validators: {
|
||
|
notblank: {
|
||
|
validateString: function validateString(value) {
|
||
|
return (/\S/.test(value)
|
||
|
);
|
||
|
},
|
||
|
priority: 2
|
||
|
},
|
||
|
required: {
|
||
|
validateMultiple: function validateMultiple(values) {
|
||
|
return values.length > 0;
|
||
|
},
|
||
|
validateString: function validateString(value) {
|
||
|
return (/\S/.test(value)
|
||
|
);
|
||
|
},
|
||
|
priority: 512
|
||
|
},
|
||
|
type: {
|
||
|
validateString: function validateString(value, type) {
|
||
|
var _ref = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
|
||
|
|
||
|
var _ref$step = _ref.step;
|
||
|
var step = _ref$step === undefined ? 'any' : _ref$step;
|
||
|
var _ref$base = _ref.base;
|
||
|
var base = _ref$base === undefined ? 0 : _ref$base;
|
||
|
|
||
|
var tester = typeTesters[type];
|
||
|
if (!tester) {
|
||
|
throw new Error('validator type `' + type + '` is not supported');
|
||
|
}
|
||
|
if (!tester.test(value)) return false;
|
||
|
if ('number' === type) {
|
||
|
if (!/^any$/i.test(step || '')) {
|
||
|
var nb = Number(value);
|
||
|
var decimals = Math.max(decimalPlaces(step), decimalPlaces(base));
|
||
|
if (decimalPlaces(nb) > decimals) // Value can't have too many decimals
|
||
|
return false;
|
||
|
// Be careful of rounding errors by using integers.
|
||
|
var toInt = function toInt(f) {
|
||
|
return Math.round(f * Math.pow(10, decimals));
|
||
|
};
|
||
|
if ((toInt(nb) - toInt(base)) % toInt(step) != 0) return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
},
|
||
|
requirementType: {
|
||
|
'': 'string',
|
||
|
step: 'string',
|
||
|
base: 'number'
|
||
|
},
|
||
|
priority: 256
|
||
|
},
|
||
|
pattern: {
|
||
|
validateString: function validateString(value, regexp) {
|
||
|
return regexp.test(value);
|
||
|
},
|
||
|
requirementType: 'regexp',
|
||
|
priority: 64
|
||
|
},
|
||
|
minlength: {
|
||
|
validateString: function validateString(value, requirement) {
|
||
|
return value.length >= requirement;
|
||
|
},
|
||
|
requirementType: 'integer',
|
||
|
priority: 30
|
||
|
},
|
||
|
maxlength: {
|
||
|
validateString: function validateString(value, requirement) {
|
||
|
return value.length <= requirement;
|
||
|
},
|
||
|
requirementType: 'integer',
|
||
|
priority: 30
|
||
|
},
|
||
|
length: {
|
||
|
validateString: function validateString(value, min, max) {
|
||
|
return value.length >= min && value.length <= max;
|
||
|
},
|
||
|
requirementType: ['integer', 'integer'],
|
||
|
priority: 30
|
||
|
},
|
||
|
mincheck: {
|
||
|
validateMultiple: function validateMultiple(values, requirement) {
|
||
|
return values.length >= requirement;
|
||
|
},
|
||
|
requirementType: 'integer',
|
||
|
priority: 30
|
||
|
},
|
||
|
maxcheck: {
|
||
|
validateMultiple: function validateMultiple(values, requirement) {
|
||
|
return values.length <= requirement;
|
||
|
},
|
||
|
requirementType: 'integer',
|
||
|
priority: 30
|
||
|
},
|
||
|
check: {
|
||
|
validateMultiple: function validateMultiple(values, min, max) {
|
||
|
return values.length >= min && values.length <= max;
|
||
|
},
|
||
|
requirementType: ['integer', 'integer'],
|
||
|
priority: 30
|
||
|
},
|
||
|
min: ValidatorRegistry__comparisonOperator(function (value, requirement) {
|
||
|
return value >= requirement;
|
||
|
}),
|
||
|
max: ValidatorRegistry__comparisonOperator(function (value, requirement) {
|
||
|
return value <= requirement;
|
||
|
}),
|
||
|
range: ValidatorRegistry__comparisonOperator(function (value, min, max) {
|
||
|
return value >= min && value <= max;
|
||
|
}),
|
||
|
equalto: {
|
||
|
validateString: function validateString(value, refOrValue) {
|
||
|
var $reference = $(refOrValue);
|
||
|
if ($reference.length) return value === $reference.val();else return value === refOrValue;
|
||
|
},
|
||
|
priority: 256
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var UI = {};
|
||
|
|
||
|
var diffResults = function diffResults(newResult, oldResult, deep) {
|
||
|
var added = [];
|
||
|
var kept = [];
|
||
|
|
||
|
for (var i = 0; i < newResult.length; i++) {
|
||
|
var found = false;
|
||
|
|
||
|
for (var j = 0; j < oldResult.length; j++) if (newResult[i].assert.name === oldResult[j].assert.name) {
|
||
|
found = true;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (found) kept.push(newResult[i]);else added.push(newResult[i]);
|
||
|
}
|
||
|
|
||
|
return {
|
||
|
kept: kept,
|
||
|
added: added,
|
||
|
removed: !deep ? diffResults(oldResult, newResult, true).added : []
|
||
|
};
|
||
|
};
|
||
|
|
||
|
UI.Form = {
|
||
|
|
||
|
_actualizeTriggers: function _actualizeTriggers() {
|
||
|
var _this2 = this;
|
||
|
|
||
|
this.$element.on('submit.Parsley', function (evt) {
|
||
|
_this2.onSubmitValidate(evt);
|
||
|
});
|
||
|
this.$element.on('click.Parsley', Utils._SubmitSelector, function (evt) {
|
||
|
_this2.onSubmitButton(evt);
|
||
|
});
|
||
|
|
||
|
// UI could be disabled
|
||
|
if (false === this.options.uiEnabled) return;
|
||
|
|
||
|
this.element.setAttribute('novalidate', '');
|
||
|
},
|
||
|
|
||
|
focus: function focus() {
|
||
|
this._focusedField = null;
|
||
|
|
||
|
if (true === this.validationResult || 'none' === this.options.focus) return null;
|
||
|
|
||
|
for (var i = 0; i < this.fields.length; i++) {
|
||
|
var field = this.fields[i];
|
||
|
if (true !== field.validationResult && field.validationResult.length > 0 && 'undefined' === typeof field.options.noFocus) {
|
||
|
this._focusedField = field.$element;
|
||
|
if ('first' === this.options.focus) break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (null === this._focusedField) return null;
|
||
|
|
||
|
return this._focusedField.focus();
|
||
|
},
|
||
|
|
||
|
_destroyUI: function _destroyUI() {
|
||
|
// Reset all event listeners
|
||
|
this.$element.off('.Parsley');
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
UI.Field = {
|
||
|
|
||
|
_reflowUI: function _reflowUI() {
|
||
|
this._buildUI();
|
||
|
|
||
|
// If this field doesn't have an active UI don't bother doing something
|
||
|
if (!this._ui) return;
|
||
|
|
||
|
// Diff between two validation results
|
||
|
var diff = diffResults(this.validationResult, this._ui.lastValidationResult);
|
||
|
|
||
|
// Then store current validation result for next reflow
|
||
|
this._ui.lastValidationResult = this.validationResult;
|
||
|
|
||
|
// Handle valid / invalid / none field class
|
||
|
this._manageStatusClass();
|
||
|
|
||
|
// Add, remove, updated errors messages
|
||
|
this._manageErrorsMessages(diff);
|
||
|
|
||
|
// Triggers impl
|
||
|
this._actualizeTriggers();
|
||
|
|
||
|
// If field is not valid for the first time, bind keyup trigger to ease UX and quickly inform user
|
||
|
if ((diff.kept.length || diff.added.length) && !this._failedOnce) {
|
||
|
this._failedOnce = true;
|
||
|
this._actualizeTriggers();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
// Returns an array of field's error message(s)
|
||
|
getErrorsMessages: function getErrorsMessages() {
|
||
|
// No error message, field is valid
|
||
|
if (true === this.validationResult) return [];
|
||
|
|
||
|
var messages = [];
|
||
|
|
||
|
for (var i = 0; i < this.validationResult.length; i++) messages.push(this.validationResult[i].errorMessage || this._getErrorMessage(this.validationResult[i].assert));
|
||
|
|
||
|
return messages;
|
||
|
},
|
||
|
|
||
|
// It's a goal of Parsley that this method is no longer required [#1073]
|
||
|
addError: function addError(name) {
|
||
|
var _ref2 = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
|
||
|
|
||
|
var message = _ref2.message;
|
||
|
var assert = _ref2.assert;
|
||
|
var _ref2$updateClass = _ref2.updateClass;
|
||
|
var updateClass = _ref2$updateClass === undefined ? true : _ref2$updateClass;
|
||
|
|
||
|
this._buildUI();
|
||
|
this._addError(name, { message: message, assert: assert });
|
||
|
|
||
|
if (updateClass) this._errorClass();
|
||
|
},
|
||
|
|
||
|
// It's a goal of Parsley that this method is no longer required [#1073]
|
||
|
updateError: function updateError(name) {
|
||
|
var _ref3 = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
|
||
|
|
||
|
var message = _ref3.message;
|
||
|
var assert = _ref3.assert;
|
||
|
var _ref3$updateClass = _ref3.updateClass;
|
||
|
var updateClass = _ref3$updateClass === undefined ? true : _ref3$updateClass;
|
||
|
|
||
|
this._buildUI();
|
||
|
this._updateError(name, { message: message, assert: assert });
|
||
|
|
||
|
if (updateClass) this._errorClass();
|
||
|
},
|
||
|
|
||
|
// It's a goal of Parsley that this method is no longer required [#1073]
|
||
|
removeError: function removeError(name) {
|
||
|
var _ref4 = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
|
||
|
|
||
|
var _ref4$updateClass = _ref4.updateClass;
|
||
|
var updateClass = _ref4$updateClass === undefined ? true : _ref4$updateClass;
|
||
|
|
||
|
this._buildUI();
|
||
|
this._removeError(name);
|
||
|
|
||
|
// edge case possible here: remove a standard Parsley error that is still failing in this.validationResult
|
||
|
// but highly improbable cuz' manually removing a well Parsley handled error makes no sense.
|
||
|
if (updateClass) this._manageStatusClass();
|
||
|
},
|
||
|
|
||
|
_manageStatusClass: function _manageStatusClass() {
|
||
|
if (this.hasConstraints() && this.needsValidation() && true === this.validationResult) this._successClass();else if (this.validationResult.length > 0) this._errorClass();else this._resetClass();
|
||
|
},
|
||
|
|
||
|
_manageErrorsMessages: function _manageErrorsMessages(diff) {
|
||
|
if ('undefined' !== typeof this.options.errorsMessagesDisabled) return;
|
||
|
|
||
|
// Case where we have errorMessage option that configure an unique field error message, regardless failing validators
|
||
|
if ('undefined' !== typeof this.options.errorMessage) {
|
||
|
if (diff.added.length || diff.kept.length) {
|
||
|
this._insertErrorWrapper();
|
||
|
|
||
|
if (0 === this._ui.$errorsWrapper.find('.parsley-custom-error-message').length) this._ui.$errorsWrapper.append($(this.options.errorTemplate).addClass('parsley-custom-error-message'));
|
||
|
|
||
|
return this._ui.$errorsWrapper.addClass('filled').find('.parsley-custom-error-message').html(this.options.errorMessage);
|
||
|
}
|
||
|
|
||
|
return this._ui.$errorsWrapper.removeClass('filled').find('.parsley-custom-error-message').remove();
|
||
|
}
|
||
|
|
||
|
// Show, hide, update failing constraints messages
|
||
|
for (var i = 0; i < diff.removed.length; i++) this._removeError(diff.removed[i].assert.name);
|
||
|
|
||
|
for (i = 0; i < diff.added.length; i++) this._addError(diff.added[i].assert.name, { message: diff.added[i].errorMessage, assert: diff.added[i].assert });
|
||
|
|
||
|
for (i = 0; i < diff.kept.length; i++) this._updateError(diff.kept[i].assert.name, { message: diff.kept[i].errorMessage, assert: diff.kept[i].assert });
|
||
|
},
|
||
|
|
||
|
_addError: function _addError(name, _ref5) {
|
||
|
var message = _ref5.message;
|
||
|
var assert = _ref5.assert;
|
||
|
|
||
|
this._insertErrorWrapper();
|
||
|
this._ui.$errorClassHandler.attr('aria-describedby', this._ui.errorsWrapperId);
|
||
|
this._ui.$errorsWrapper.addClass('filled').append($(this.options.errorTemplate).addClass('parsley-' + name).html(message || this._getErrorMessage(assert)));
|
||
|
},
|
||
|
|
||
|
_updateError: function _updateError(name, _ref6) {
|
||
|
var message = _ref6.message;
|
||
|
var assert = _ref6.assert;
|
||
|
|
||
|
this._ui.$errorsWrapper.addClass('filled').find('.parsley-' + name).html(message || this._getErrorMessage(assert));
|
||
|
},
|
||
|
|
||
|
_removeError: function _removeError(name) {
|
||
|
this._ui.$errorClassHandler.removeAttr('aria-describedby');
|
||
|
this._ui.$errorsWrapper.removeClass('filled').find('.parsley-' + name).remove();
|
||
|
},
|
||
|
|
||
|
_getErrorMessage: function _getErrorMessage(constraint) {
|
||
|
var customConstraintErrorMessage = constraint.name + 'Message';
|
||
|
|
||
|
if ('undefined' !== typeof this.options[customConstraintErrorMessage]) return window.Parsley.formatMessage(this.options[customConstraintErrorMessage], constraint.requirements);
|
||
|
|
||
|
return window.Parsley.getErrorMessage(constraint);
|
||
|
},
|
||
|
|
||
|
_buildUI: function _buildUI() {
|
||
|
// UI could be already built or disabled
|
||
|
if (this._ui || false === this.options.uiEnabled) return;
|
||
|
|
||
|
var _ui = {};
|
||
|
|
||
|
// Give field its Parsley id in DOM
|
||
|
this.element.setAttribute(this.options.namespace + 'id', this.__id__);
|
||
|
|
||
|
/** Generate important UI elements and store them in this **/
|
||
|
// $errorClassHandler is the $element that woul have parsley-error and parsley-success classes
|
||
|
_ui.$errorClassHandler = this._manageClassHandler();
|
||
|
|
||
|
// $errorsWrapper is a div that would contain the various field errors, it will be appended into $errorsContainer
|
||
|
_ui.errorsWrapperId = 'parsley-id-' + (this.options.multiple ? 'multiple-' + this.options.multiple : this.__id__);
|
||
|
_ui.$errorsWrapper = $(this.options.errorsWrapper).attr('id', _ui.errorsWrapperId);
|
||
|
|
||
|
// ValidationResult UI storage to detect what have changed bwt two validations, and update DOM accordingly
|
||
|
_ui.lastValidationResult = [];
|
||
|
_ui.validationInformationVisible = false;
|
||
|
|
||
|
// Store it in this for later
|
||
|
this._ui = _ui;
|
||
|
},
|
||
|
|
||
|
// Determine which element will have `parsley-error` and `parsley-success` classes
|
||
|
_manageClassHandler: function _manageClassHandler() {
|
||
|
// Class handled could also be determined by function given in Parsley options
|
||
|
if ('string' === typeof this.options.classHandler && $(this.options.classHandler).length) return $(this.options.classHandler);
|
||
|
|
||
|
// Class handled could also be determined by function given in Parsley options
|
||
|
var $handlerFunction = this.options.classHandler;
|
||
|
|
||
|
// It might also be the function name of a global function
|
||
|
if ('string' === typeof this.options.classHandler && 'function' === typeof window[this.options.classHandler]) $handlerFunction = window[this.options.classHandler];
|
||
|
|
||
|
if ('function' === typeof $handlerFunction) {
|
||
|
var $handler = $handlerFunction.call(this, this);
|
||
|
|
||
|
// If this function returned a valid existing DOM element, go for it
|
||
|
if ('undefined' !== typeof $handler && $handler.length) return $handler;
|
||
|
} else if ('object' === typeof $handlerFunction && $handlerFunction instanceof jQuery && $handlerFunction.length) {
|
||
|
return $handlerFunction;
|
||
|
} else if ($handlerFunction) {
|
||
|
Utils.warn('The class handler `' + $handlerFunction + '` does not exist in DOM nor as a global JS function');
|
||
|
}
|
||
|
|
||
|
return this._inputHolder();
|
||
|
},
|
||
|
|
||
|
_inputHolder: function _inputHolder() {
|
||
|
// if simple element (input, texatrea, select...) it will perfectly host the classes and precede the error container
|
||
|
if (!this.options.multiple || this.element.nodeName === 'SELECT') return this.$element;
|
||
|
|
||
|
// But if multiple element (radio, checkbox), that would be their parent
|
||
|
return this.$element.parent();
|
||
|
},
|
||
|
|
||
|
_insertErrorWrapper: function _insertErrorWrapper() {
|
||
|
var $errorsContainer = this.options.errorsContainer;
|
||
|
|
||
|
// Nothing to do if already inserted
|
||
|
if (0 !== this._ui.$errorsWrapper.parent().length) return this._ui.$errorsWrapper.parent();
|
||
|
|
||
|
if ('string' === typeof $errorsContainer) {
|
||
|
if ($($errorsContainer).length) return $($errorsContainer).append(this._ui.$errorsWrapper);else if ('function' === typeof window[$errorsContainer]) $errorsContainer = window[$errorsContainer];else Utils.warn('The errors container `' + $errorsContainer + '` does not exist in DOM nor as a global JS function');
|
||
|
}
|
||
|
|
||
|
if ('function' === typeof $errorsContainer) $errorsContainer = $errorsContainer.call(this, this);
|
||
|
|
||
|
if ('object' === typeof $errorsContainer && $errorsContainer.length) return $errorsContainer.append(this._ui.$errorsWrapper);
|
||
|
|
||
|
return this._inputHolder().after(this._ui.$errorsWrapper);
|
||
|
},
|
||
|
|
||
|
_actualizeTriggers: function _actualizeTriggers() {
|
||
|
var _this3 = this;
|
||
|
|
||
|
var $toBind = this._findRelated();
|
||
|
var trigger;
|
||
|
|
||
|
// Remove Parsley events already bound on this field
|
||
|
$toBind.off('.Parsley');
|
||
|
if (this._failedOnce) $toBind.on(Utils.namespaceEvents(this.options.triggerAfterFailure, 'Parsley'), function () {
|
||
|
_this3._validateIfNeeded();
|
||
|
});else if (trigger = Utils.namespaceEvents(this.options.trigger, 'Parsley')) {
|
||
|
$toBind.on(trigger, function (event) {
|
||
|
_this3._validateIfNeeded(event);
|
||
|
});
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_validateIfNeeded: function _validateIfNeeded(event) {
|
||
|
var _this4 = this;
|
||
|
|
||
|
// For keyup, keypress, keydown, input... events that could be a little bit obstrusive
|
||
|
// do not validate if val length < min threshold on first validation. Once field have been validated once and info
|
||
|
// about success or failure have been displayed, always validate with this trigger to reflect every yalidation change.
|
||
|
if (event && /key|input/.test(event.type)) if (!(this._ui && this._ui.validationInformationVisible) && this.getValue().length <= this.options.validationThreshold) return;
|
||
|
|
||
|
if (this.options.debounce) {
|
||
|
window.clearTimeout(this._debounced);
|
||
|
this._debounced = window.setTimeout(function () {
|
||
|
return _this4.validate();
|
||
|
}, this.options.debounce);
|
||
|
} else this.validate();
|
||
|
},
|
||
|
|
||
|
_resetUI: function _resetUI() {
|
||
|
// Reset all event listeners
|
||
|
this._failedOnce = false;
|
||
|
this._actualizeTriggers();
|
||
|
|
||
|
// Nothing to do if UI never initialized for this field
|
||
|
if ('undefined' === typeof this._ui) return;
|
||
|
|
||
|
// Reset all errors' li
|
||
|
this._ui.$errorsWrapper.removeClass('filled').children().remove();
|
||
|
|
||
|
// Reset validation class
|
||
|
this._resetClass();
|
||
|
|
||
|
// Reset validation flags and last validation result
|
||
|
this._ui.lastValidationResult = [];
|
||
|
this._ui.validationInformationVisible = false;
|
||
|
},
|
||
|
|
||
|
_destroyUI: function _destroyUI() {
|
||
|
this._resetUI();
|
||
|
|
||
|
if ('undefined' !== typeof this._ui) this._ui.$errorsWrapper.remove();
|
||
|
|
||
|
delete this._ui;
|
||
|
},
|
||
|
|
||
|
_successClass: function _successClass() {
|
||
|
this._ui.validationInformationVisible = true;
|
||
|
this._ui.$errorClassHandler.removeClass(this.options.errorClass).addClass(this.options.successClass);
|
||
|
},
|
||
|
_errorClass: function _errorClass() {
|
||
|
this._ui.validationInformationVisible = true;
|
||
|
this._ui.$errorClassHandler.removeClass(this.options.successClass).addClass(this.options.errorClass);
|
||
|
},
|
||
|
_resetClass: function _resetClass() {
|
||
|
this._ui.$errorClassHandler.removeClass(this.options.successClass).removeClass(this.options.errorClass);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var Form = function Form(element, domOptions, options) {
|
||
|
this.__class__ = 'Form';
|
||
|
|
||
|
this.element = element;
|
||
|
this.$element = $(element);
|
||
|
this.domOptions = domOptions;
|
||
|
this.options = options;
|
||
|
this.parent = window.Parsley;
|
||
|
|
||
|
this.fields = [];
|
||
|
this.validationResult = null;
|
||
|
};
|
||
|
|
||
|
var Form__statusMapping = { pending: null, resolved: true, rejected: false };
|
||
|
|
||
|
Form.prototype = {
|
||
|
onSubmitValidate: function onSubmitValidate(event) {
|
||
|
var _this5 = this;
|
||
|
|
||
|
// This is a Parsley generated submit event, do not validate, do not prevent, simply exit and keep normal behavior
|
||
|
if (true === event.parsley) return;
|
||
|
|
||
|
// If we didn't come here through a submit button, use the first one in the form
|
||
|
var submitSource = this._submitSource || this.$element.find(Utils._SubmitSelector)[0];
|
||
|
this._submitSource = null;
|
||
|
this.$element.find('.parsley-synthetic-submit-button').prop('disabled', true);
|
||
|
if (submitSource && null !== submitSource.getAttribute('formnovalidate')) return;
|
||
|
|
||
|
window.Parsley._remoteCache = {};
|
||
|
|
||
|
var promise = this.whenValidate({ event: event });
|
||
|
|
||
|
if ('resolved' === promise.state() && false !== this._trigger('submit')) {
|
||
|
// All good, let event go through. We make this distinction because browsers
|
||
|
// differ in their handling of `submit` being called from inside a submit event [#1047]
|
||
|
} else {
|
||
|
// Rejected or pending: cancel this submit
|
||
|
event.stopImmediatePropagation();
|
||
|
event.preventDefault();
|
||
|
if ('pending' === promise.state()) promise.done(function () {
|
||
|
_this5._submit(submitSource);
|
||
|
});
|
||
|
}
|
||
|
},
|
||
|
|
||
|
onSubmitButton: function onSubmitButton(event) {
|
||
|
this._submitSource = event.currentTarget;
|
||
|
},
|
||
|
// internal
|
||
|
// _submit submits the form, this time without going through the validations.
|
||
|
// Care must be taken to "fake" the actual submit button being clicked.
|
||
|
_submit: function _submit(submitSource) {
|
||
|
if (false === this._trigger('submit')) return;
|
||
|
// Add submit button's data
|
||
|
if (submitSource) {
|
||
|
var $synthetic = this.$element.find('.parsley-synthetic-submit-button').prop('disabled', false);
|
||
|
if (0 === $synthetic.length) $synthetic = $('<input class="parsley-synthetic-submit-button" type="hidden">').appendTo(this.$element);
|
||
|
$synthetic.attr({
|
||
|
name: submitSource.getAttribute('name'),
|
||
|
value: submitSource.getAttribute('value')
|
||
|
});
|
||
|
}
|
||
|
|
||
|
this.$element.trigger(_extends($.Event('submit'), { parsley: true }));
|
||
|
},
|
||
|
|
||
|
// Performs validation on fields while triggering events.
|
||
|
// @returns `true` if all validations succeeds, `false`
|
||
|
// if a failure is immediately detected, or `null`
|
||
|
// if dependant on a promise.
|
||
|
// Consider using `whenValidate` instead.
|
||
|
validate: function validate(options) {
|
||
|
if (arguments.length >= 1 && !$.isPlainObject(options)) {
|
||
|
Utils.warnOnce('Calling validate on a parsley form without passing arguments as an object is deprecated.');
|
||
|
|
||
|
var _arguments = _slice.call(arguments);
|
||
|
|
||
|
var group = _arguments[0];
|
||
|
var force = _arguments[1];
|
||
|
var event = _arguments[2];
|
||
|
|
||
|
options = { group: group, force: force, event: event };
|
||
|
}
|
||
|
return Form__statusMapping[this.whenValidate(options).state()];
|
||
|
},
|
||
|
|
||
|
whenValidate: function whenValidate() {
|
||
|
var _Utils$all$done$fail$always,
|
||
|
_this6 = this;
|
||
|
|
||
|
var _ref7 = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
|
||
|
|
||
|
var group = _ref7.group;
|
||
|
var force = _ref7.force;
|
||
|
var event = _ref7.event;
|
||
|
|
||
|
this.submitEvent = event;
|
||
|
if (event) {
|
||
|
this.submitEvent = _extends({}, event, { preventDefault: function preventDefault() {
|
||
|
Utils.warnOnce("Using `this.submitEvent.preventDefault()` is deprecated; instead, call `this.validationResult = false`");
|
||
|
_this6.validationResult = false;
|
||
|
} });
|
||
|
}
|
||
|
this.validationResult = true;
|
||
|
|
||
|
// fire validate event to eventually modify things before every validation
|
||
|
this._trigger('validate');
|
||
|
|
||
|
// Refresh form DOM options and form's fields that could have changed
|
||
|
this._refreshFields();
|
||
|
|
||
|
var promises = this._withoutReactualizingFormOptions(function () {
|
||
|
return $.map(_this6.fields, function (field) {
|
||
|
return field.whenValidate({ force: force, group: group });
|
||
|
});
|
||
|
});
|
||
|
|
||
|
return (_Utils$all$done$fail$always = Utils.all(promises).done(function () {
|
||
|
_this6._trigger('success');
|
||
|
}).fail(function () {
|
||
|
_this6.validationResult = false;
|
||
|
_this6.focus();
|
||
|
_this6._trigger('error');
|
||
|
}).always(function () {
|
||
|
_this6._trigger('validated');
|
||
|
})).pipe.apply(_Utils$all$done$fail$always, _toConsumableArray(this._pipeAccordingToValidationResult()));
|
||
|
},
|
||
|
|
||
|
// Iterate over refreshed fields, and stop on first failure.
|
||
|
// Returns `true` if all fields are valid, `false` if a failure is detected
|
||
|
// or `null` if the result depends on an unresolved promise.
|
||
|
// Prefer using `whenValid` instead.
|
||
|
isValid: function isValid(options) {
|
||
|
if (arguments.length >= 1 && !$.isPlainObject(options)) {
|
||
|
Utils.warnOnce('Calling isValid on a parsley form without passing arguments as an object is deprecated.');
|
||
|
|
||
|
var _arguments2 = _slice.call(arguments);
|
||
|
|
||
|
var group = _arguments2[0];
|
||
|
var force = _arguments2[1];
|
||
|
|
||
|
options = { group: group, force: force };
|
||
|
}
|
||
|
return Form__statusMapping[this.whenValid(options).state()];
|
||
|
},
|
||
|
|
||
|
// Iterate over refreshed fields and validate them.
|
||
|
// Returns a promise.
|
||
|
// A validation that immediately fails will interrupt the validations.
|
||
|
whenValid: function whenValid() {
|
||
|
var _this7 = this;
|
||
|
|
||
|
var _ref8 = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
|
||
|
|
||
|
var group = _ref8.group;
|
||
|
var force = _ref8.force;
|
||
|
|
||
|
this._refreshFields();
|
||
|
|
||
|
var promises = this._withoutReactualizingFormOptions(function () {
|
||
|
return $.map(_this7.fields, function (field) {
|
||
|
return field.whenValid({ group: group, force: force });
|
||
|
});
|
||
|
});
|
||
|
return Utils.all(promises);
|
||
|
},
|
||
|
|
||
|
refresh: function refresh() {
|
||
|
this._refreshFields();
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
// Reset UI
|
||
|
reset: function reset() {
|
||
|
// Form case: emit a reset event for each field
|
||
|
for (var i = 0; i < this.fields.length; i++) this.fields[i].reset();
|
||
|
|
||
|
this._trigger('reset');
|
||
|
},
|
||
|
|
||
|
// Destroy Parsley instance (+ UI)
|
||
|
destroy: function destroy() {
|
||
|
// Field case: emit destroy event to clean UI and then destroy stored instance
|
||
|
this._destroyUI();
|
||
|
|
||
|
// Form case: destroy all its fields and then destroy stored instance
|
||
|
for (var i = 0; i < this.fields.length; i++) this.fields[i].destroy();
|
||
|
|
||
|
this.$element.removeData('Parsley');
|
||
|
this._trigger('destroy');
|
||
|
},
|
||
|
|
||
|
_refreshFields: function _refreshFields() {
|
||
|
return this.actualizeOptions()._bindFields();
|
||
|
},
|
||
|
|
||
|
_bindFields: function _bindFields() {
|
||
|
var _this8 = this;
|
||
|
|
||
|
var oldFields = this.fields;
|
||
|
|
||
|
this.fields = [];
|
||
|
this.fieldsMappedById = {};
|
||
|
|
||
|
this._withoutReactualizingFormOptions(function () {
|
||
|
_this8.$element.find(_this8.options.inputs).not(_this8.options.excluded).each(function (_, element) {
|
||
|
var fieldInstance = new window.Parsley.Factory(element, {}, _this8);
|
||
|
|
||
|
// Only add valid and not excluded `Field` and `FieldMultiple` children
|
||
|
if (('Field' === fieldInstance.__class__ || 'FieldMultiple' === fieldInstance.__class__) && true !== fieldInstance.options.excluded) {
|
||
|
var uniqueId = fieldInstance.__class__ + '-' + fieldInstance.__id__;
|
||
|
if ('undefined' === typeof _this8.fieldsMappedById[uniqueId]) {
|
||
|
_this8.fieldsMappedById[uniqueId] = fieldInstance;
|
||
|
_this8.fields.push(fieldInstance);
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
$.each(Utils.difference(oldFields, _this8.fields), function (_, field) {
|
||
|
field.reset();
|
||
|
});
|
||
|
});
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
// Internal only.
|
||
|
// Looping on a form's fields to do validation or similar
|
||
|
// will trigger reactualizing options on all of them, which
|
||
|
// in turn will reactualize the form's options.
|
||
|
// To avoid calling actualizeOptions so many times on the form
|
||
|
// for nothing, _withoutReactualizingFormOptions temporarily disables
|
||
|
// the method actualizeOptions on this form while `fn` is called.
|
||
|
_withoutReactualizingFormOptions: function _withoutReactualizingFormOptions(fn) {
|
||
|
var oldActualizeOptions = this.actualizeOptions;
|
||
|
this.actualizeOptions = function () {
|
||
|
return this;
|
||
|
};
|
||
|
var result = fn();
|
||
|
this.actualizeOptions = oldActualizeOptions;
|
||
|
return result;
|
||
|
},
|
||
|
|
||
|
// Internal only.
|
||
|
// Shortcut to trigger an event
|
||
|
// Returns true iff event is not interrupted and default not prevented.
|
||
|
_trigger: function _trigger(eventName) {
|
||
|
return this.trigger('form:' + eventName);
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
var Constraint = function Constraint(parsleyField, name, requirements, priority, isDomConstraint) {
|
||
|
var validatorSpec = window.Parsley._validatorRegistry.validators[name];
|
||
|
var validator = new Validator(validatorSpec);
|
||
|
priority = priority || parsleyField.options[name + 'Priority'] || validator.priority;
|
||
|
isDomConstraint = true === isDomConstraint;
|
||
|
|
||
|
_extends(this, {
|
||
|
validator: validator,
|
||
|
name: name,
|
||
|
requirements: requirements,
|
||
|
priority: priority,
|
||
|
isDomConstraint: isDomConstraint
|
||
|
});
|
||
|
this._parseRequirements(parsleyField.options);
|
||
|
};
|
||
|
|
||
|
var capitalize = function capitalize(str) {
|
||
|
var cap = str[0].toUpperCase();
|
||
|
return cap + str.slice(1);
|
||
|
};
|
||
|
|
||
|
Constraint.prototype = {
|
||
|
validate: function validate(value, instance) {
|
||
|
var _validator;
|
||
|
|
||
|
return (_validator = this.validator).validate.apply(_validator, [value].concat(_toConsumableArray(this.requirementList), [instance]));
|
||
|
},
|
||
|
|
||
|
_parseRequirements: function _parseRequirements(options) {
|
||
|
var _this9 = this;
|
||
|
|
||
|
this.requirementList = this.validator.parseRequirements(this.requirements, function (key) {
|
||
|
return options[_this9.name + capitalize(key)];
|
||
|
});
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var Field = function Field(field, domOptions, options, parsleyFormInstance) {
|
||
|
this.__class__ = 'Field';
|
||
|
|
||
|
this.element = field;
|
||
|
this.$element = $(field);
|
||
|
|
||
|
// Set parent if we have one
|
||
|
if ('undefined' !== typeof parsleyFormInstance) {
|
||
|
this.parent = parsleyFormInstance;
|
||
|
}
|
||
|
|
||
|
this.options = options;
|
||
|
this.domOptions = domOptions;
|
||
|
|
||
|
// Initialize some properties
|
||
|
this.constraints = [];
|
||
|
this.constraintsByName = {};
|
||
|
this.validationResult = true;
|
||
|
|
||
|
// Bind constraints
|
||
|
this._bindConstraints();
|
||
|
};
|
||
|
|
||
|
var parsley_field__statusMapping = { pending: null, resolved: true, rejected: false };
|
||
|
|
||
|
Field.prototype = {
|
||
|
// # Public API
|
||
|
// Validate field and trigger some events for mainly `UI`
|
||
|
// @returns `true`, an array of the validators that failed, or
|
||
|
// `null` if validation is not finished. Prefer using whenValidate
|
||
|
validate: function validate(options) {
|
||
|
if (arguments.length >= 1 && !$.isPlainObject(options)) {
|
||
|
Utils.warnOnce('Calling validate on a parsley field without passing arguments as an object is deprecated.');
|
||
|
options = { options: options };
|
||
|
}
|
||
|
var promise = this.whenValidate(options);
|
||
|
if (!promise) // If excluded with `group` option
|
||
|
return true;
|
||
|
switch (promise.state()) {
|
||
|
case 'pending':
|
||
|
return null;
|
||
|
case 'resolved':
|
||
|
return true;
|
||
|
case 'rejected':
|
||
|
return this.validationResult;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
// Validate field and trigger some events for mainly `UI`
|
||
|
// @returns a promise that succeeds only when all validations do
|
||
|
// or `undefined` if field is not in the given `group`.
|
||
|
whenValidate: function whenValidate() {
|
||
|
var _whenValid$always$done$fail$always,
|
||
|
_this10 = this;
|
||
|
|
||
|
var _ref9 = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
|
||
|
|
||
|
var force = _ref9.force;
|
||
|
var group = _ref9.group;
|
||
|
|
||
|
// do not validate a field if not the same as given validation group
|
||
|
this.refresh();
|
||
|
if (group && !this._isInGroup(group)) return;
|
||
|
|
||
|
this.value = this.getValue();
|
||
|
|
||
|
// Field Validate event. `this.value` could be altered for custom needs
|
||
|
this._trigger('validate');
|
||
|
|
||
|
return (_whenValid$always$done$fail$always = this.whenValid({ force: force, value: this.value, _refreshed: true }).always(function () {
|
||
|
_this10._reflowUI();
|
||
|
}).done(function () {
|
||
|
_this10._trigger('success');
|
||
|
}).fail(function () {
|
||
|
_this10._trigger('error');
|
||
|
}).always(function () {
|
||
|
_this10._trigger('validated');
|
||
|
})).pipe.apply(_whenValid$always$done$fail$always, _toConsumableArray(this._pipeAccordingToValidationResult()));
|
||
|
},
|
||
|
|
||
|
hasConstraints: function hasConstraints() {
|
||
|
return 0 !== this.constraints.length;
|
||
|
},
|
||
|
|
||
|
// An empty optional field does not need validation
|
||
|
needsValidation: function needsValidation(value) {
|
||
|
if ('undefined' === typeof value) value = this.getValue();
|
||
|
|
||
|
// If a field is empty and not required, it is valid
|
||
|
// Except if `data-parsley-validate-if-empty` explicitely added, useful for some custom validators
|
||
|
if (!value.length && !this._isRequired() && 'undefined' === typeof this.options.validateIfEmpty) return false;
|
||
|
|
||
|
return true;
|
||
|
},
|
||
|
|
||
|
_isInGroup: function _isInGroup(group) {
|
||
|
if (Array.isArray(this.options.group)) return -1 !== $.inArray(group, this.options.group);
|
||
|
return this.options.group === group;
|
||
|
},
|
||
|
|
||
|
// Just validate field. Do not trigger any event.
|
||
|
// Returns `true` iff all constraints pass, `false` if there are failures,
|
||
|
// or `null` if the result can not be determined yet (depends on a promise)
|
||
|
// See also `whenValid`.
|
||
|
isValid: function isValid(options) {
|
||
|
if (arguments.length >= 1 && !$.isPlainObject(options)) {
|
||
|
Utils.warnOnce('Calling isValid on a parsley field without passing arguments as an object is deprecated.');
|
||
|
|
||
|
var _arguments3 = _slice.call(arguments);
|
||
|
|
||
|
var force = _arguments3[0];
|
||
|
var value = _arguments3[1];
|
||
|
|
||
|
options = { force: force, value: value };
|
||
|
}
|
||
|
var promise = this.whenValid(options);
|
||
|
if (!promise) // Excluded via `group`
|
||
|
return true;
|
||
|
return parsley_field__statusMapping[promise.state()];
|
||
|
},
|
||
|
|
||
|
// Just validate field. Do not trigger any event.
|
||
|
// @returns a promise that succeeds only when all validations do
|
||
|
// or `undefined` if the field is not in the given `group`.
|
||
|
// The argument `force` will force validation of empty fields.
|
||
|
// If a `value` is given, it will be validated instead of the value of the input.
|
||
|
whenValid: function whenValid() {
|
||
|
var _this11 = this;
|
||
|
|
||
|
var _ref10 = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
|
||
|
|
||
|
var _ref10$force = _ref10.force;
|
||
|
var force = _ref10$force === undefined ? false : _ref10$force;
|
||
|
var value = _ref10.value;
|
||
|
var group = _ref10.group;
|
||
|
var _refreshed = _ref10._refreshed;
|
||
|
|
||
|
// Recompute options and rebind constraints to have latest changes
|
||
|
if (!_refreshed) this.refresh();
|
||
|
// do not validate a field if not the same as given validation group
|
||
|
if (group && !this._isInGroup(group)) return;
|
||
|
|
||
|
this.validationResult = true;
|
||
|
|
||
|
// A field without constraint is valid
|
||
|
if (!this.hasConstraints()) return $.when();
|
||
|
|
||
|
// Value could be passed as argument, needed to add more power to 'field:validate'
|
||
|
if ('undefined' === typeof value || null === value) value = this.getValue();
|
||
|
|
||
|
if (!this.needsValidation(value) && true !== force) return $.when();
|
||
|
|
||
|
var groupedConstraints = this._getGroupedConstraints();
|
||
|
var promises = [];
|
||
|
$.each(groupedConstraints, function (_, constraints) {
|
||
|
// Process one group of constraints at a time, we validate the constraints
|
||
|
// and combine the promises together.
|
||
|
var promise = Utils.all($.map(constraints, function (constraint) {
|
||
|
return _this11._validateConstraint(value, constraint);
|
||
|
}));
|
||
|
promises.push(promise);
|
||
|
if (promise.state() === 'rejected') return false; // Interrupt processing if a group has already failed
|
||
|
});
|
||
|
return Utils.all(promises);
|
||
|
},
|
||
|
|
||
|
// @returns a promise
|
||
|
_validateConstraint: function _validateConstraint(value, constraint) {
|
||
|
var _this12 = this;
|
||
|
|
||
|
var result = constraint.validate(value, this);
|
||
|
// Map false to a failed promise
|
||
|
if (false === result) result = $.Deferred().reject();
|
||
|
// Make sure we return a promise and that we record failures
|
||
|
return Utils.all([result]).fail(function (errorMessage) {
|
||
|
if (!(_this12.validationResult instanceof Array)) _this12.validationResult = [];
|
||
|
_this12.validationResult.push({
|
||
|
assert: constraint,
|
||
|
errorMessage: 'string' === typeof errorMessage && errorMessage
|
||
|
});
|
||
|
});
|
||
|
},
|
||
|
|
||
|
// @returns Parsley field computed value that could be overrided or configured in DOM
|
||
|
getValue: function getValue() {
|
||
|
var value;
|
||
|
|
||
|
// Value could be overriden in DOM or with explicit options
|
||
|
if ('function' === typeof this.options.value) value = this.options.value(this);else if ('undefined' !== typeof this.options.value) value = this.options.value;else value = this.$element.val();
|
||
|
|
||
|
// Handle wrong DOM or configurations
|
||
|
if ('undefined' === typeof value || null === value) return '';
|
||
|
|
||
|
return this._handleWhitespace(value);
|
||
|
},
|
||
|
|
||
|
// Reset UI
|
||
|
reset: function reset() {
|
||
|
this._resetUI();
|
||
|
return this._trigger('reset');
|
||
|
},
|
||
|
|
||
|
// Destroy Parsley instance (+ UI)
|
||
|
destroy: function destroy() {
|
||
|
// Field case: emit destroy event to clean UI and then destroy stored instance
|
||
|
this._destroyUI();
|
||
|
this.$element.removeData('Parsley');
|
||
|
this.$element.removeData('FieldMultiple');
|
||
|
this._trigger('destroy');
|
||
|
},
|
||
|
|
||
|
// Actualize options and rebind constraints
|
||
|
refresh: function refresh() {
|
||
|
this._refreshConstraints();
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
_refreshConstraints: function _refreshConstraints() {
|
||
|
return this.actualizeOptions()._bindConstraints();
|
||
|
},
|
||
|
|
||
|
refreshConstraints: function refreshConstraints() {
|
||
|
Utils.warnOnce("Parsley's refreshConstraints is deprecated. Please use refresh");
|
||
|
return this.refresh();
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Add a new constraint to a field
|
||
|
*
|
||
|
* @param {String} name
|
||
|
* @param {Mixed} requirements optional
|
||
|
* @param {Number} priority optional
|
||
|
* @param {Boolean} isDomConstraint optional
|
||
|
*/
|
||
|
addConstraint: function addConstraint(name, requirements, priority, isDomConstraint) {
|
||
|
|
||
|
if (window.Parsley._validatorRegistry.validators[name]) {
|
||
|
var constraint = new Constraint(this, name, requirements, priority, isDomConstraint);
|
||
|
|
||
|
// if constraint already exist, delete it and push new version
|
||
|
if ('undefined' !== this.constraintsByName[constraint.name]) this.removeConstraint(constraint.name);
|
||
|
|
||
|
this.constraints.push(constraint);
|
||
|
this.constraintsByName[constraint.name] = constraint;
|
||
|
}
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
// Remove a constraint
|
||
|
removeConstraint: function removeConstraint(name) {
|
||
|
for (var i = 0; i < this.constraints.length; i++) if (name === this.constraints[i].name) {
|
||
|
this.constraints.splice(i, 1);
|
||
|
break;
|
||
|
}
|
||
|
delete this.constraintsByName[name];
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
// Update a constraint (Remove + re-add)
|
||
|
updateConstraint: function updateConstraint(name, parameters, priority) {
|
||
|
return this.removeConstraint(name).addConstraint(name, parameters, priority);
|
||
|
},
|
||
|
|
||
|
// # Internals
|
||
|
|
||
|
// Internal only.
|
||
|
// Bind constraints from config + options + DOM
|
||
|
_bindConstraints: function _bindConstraints() {
|
||
|
var constraints = [];
|
||
|
var constraintsByName = {};
|
||
|
|
||
|
// clean all existing DOM constraints to only keep javascript user constraints
|
||
|
for (var i = 0; i < this.constraints.length; i++) if (false === this.constraints[i].isDomConstraint) {
|
||
|
constraints.push(this.constraints[i]);
|
||
|
constraintsByName[this.constraints[i].name] = this.constraints[i];
|
||
|
}
|
||
|
|
||
|
this.constraints = constraints;
|
||
|
this.constraintsByName = constraintsByName;
|
||
|
|
||
|
// then re-add Parsley DOM-API constraints
|
||
|
for (var name in this.options) this.addConstraint(name, this.options[name], undefined, true);
|
||
|
|
||
|
// finally, bind special HTML5 constraints
|
||
|
return this._bindHtml5Constraints();
|
||
|
},
|
||
|
|
||
|
// Internal only.
|
||
|
// Bind specific HTML5 constraints to be HTML5 compliant
|
||
|
_bindHtml5Constraints: function _bindHtml5Constraints() {
|
||
|
// html5 required
|
||
|
if (null !== this.element.getAttribute('required')) this.addConstraint('required', true, undefined, true);
|
||
|
|
||
|
// html5 pattern
|
||
|
if (null !== this.element.getAttribute('pattern')) this.addConstraint('pattern', this.element.getAttribute('pattern'), undefined, true);
|
||
|
|
||
|
// range
|
||
|
var min = this.element.getAttribute('min');
|
||
|
var max = this.element.getAttribute('max');
|
||
|
if (null !== min && null !== max) this.addConstraint('range', [min, max], undefined, true);
|
||
|
|
||
|
// HTML5 min
|
||
|
else if (null !== min) this.addConstraint('min', min, undefined, true);
|
||
|
|
||
|
// HTML5 max
|
||
|
else if (null !== max) this.addConstraint('max', max, undefined, true);
|
||
|
|
||
|
// length
|
||
|
if (null !== this.element.getAttribute('minlength') && null !== this.element.getAttribute('maxlength')) this.addConstraint('length', [this.element.getAttribute('minlength'), this.element.getAttribute('maxlength')], undefined, true);
|
||
|
|
||
|
// HTML5 minlength
|
||
|
else if (null !== this.element.getAttribute('minlength')) this.addConstraint('minlength', this.element.getAttribute('minlength'), undefined, true);
|
||
|
|
||
|
// HTML5 maxlength
|
||
|
else if (null !== this.element.getAttribute('maxlength')) this.addConstraint('maxlength', this.element.getAttribute('maxlength'), undefined, true);
|
||
|
|
||
|
// html5 types
|
||
|
var type = Utils.getType(this.element);
|
||
|
|
||
|
// Small special case here for HTML5 number: integer validator if step attribute is undefined or an integer value, number otherwise
|
||
|
if ('number' === type) {
|
||
|
return this.addConstraint('type', ['number', {
|
||
|
step: this.element.getAttribute('step') || '1',
|
||
|
base: min || this.element.getAttribute('value')
|
||
|
}], undefined, true);
|
||
|
// Regular other HTML5 supported types
|
||
|
} else if (/^(email|url|range|date)$/i.test(type)) {
|
||
|
return this.addConstraint('type', type, undefined, true);
|
||
|
}
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
// Internal only.
|
||
|
// Field is required if have required constraint without `false` value
|
||
|
_isRequired: function _isRequired() {
|
||
|
if ('undefined' === typeof this.constraintsByName.required) return false;
|
||
|
|
||
|
return false !== this.constraintsByName.required.requirements;
|
||
|
},
|
||
|
|
||
|
// Internal only.
|
||
|
// Shortcut to trigger an event
|
||
|
_trigger: function _trigger(eventName) {
|
||
|
return this.trigger('field:' + eventName);
|
||
|
},
|
||
|
|
||
|
// Internal only
|
||
|
// Handles whitespace in a value
|
||
|
// Use `data-parsley-whitespace="squish"` to auto squish input value
|
||
|
// Use `data-parsley-whitespace="trim"` to auto trim input value
|
||
|
_handleWhitespace: function _handleWhitespace(value) {
|
||
|
if (true === this.options.trimValue) Utils.warnOnce('data-parsley-trim-value="true" is deprecated, please use data-parsley-whitespace="trim"');
|
||
|
|
||
|
if ('squish' === this.options.whitespace) value = value.replace(/\s{2,}/g, ' ');
|
||
|
|
||
|
if ('trim' === this.options.whitespace || 'squish' === this.options.whitespace || true === this.options.trimValue) value = Utils.trimString(value);
|
||
|
|
||
|
return value;
|
||
|
},
|
||
|
|
||
|
_isDateInput: function _isDateInput() {
|
||
|
var c = this.constraintsByName.type;
|
||
|
return c && c.requirements === 'date';
|
||
|
},
|
||
|
|
||
|
// Internal only.
|
||
|
// Returns the constraints, grouped by descending priority.
|
||
|
// The result is thus an array of arrays of constraints.
|
||
|
_getGroupedConstraints: function _getGroupedConstraints() {
|
||
|
if (false === this.options.priorityEnabled) return [this.constraints];
|
||
|
|
||
|
var groupedConstraints = [];
|
||
|
var index = {};
|
||
|
|
||
|
// Create array unique of priorities
|
||
|
for (var i = 0; i < this.constraints.length; i++) {
|
||
|
var p = this.constraints[i].priority;
|
||
|
if (!index[p]) groupedConstraints.push(index[p] = []);
|
||
|
index[p].push(this.constraints[i]);
|
||
|
}
|
||
|
// Sort them by priority DESC
|
||
|
groupedConstraints.sort(function (a, b) {
|
||
|
return b[0].priority - a[0].priority;
|
||
|
});
|
||
|
|
||
|
return groupedConstraints;
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
var parsley_field = Field;
|
||
|
|
||
|
var Multiple = function Multiple() {
|
||
|
this.__class__ = 'FieldMultiple';
|
||
|
};
|
||
|
|
||
|
Multiple.prototype = {
|
||
|
// Add new `$element` sibling for multiple field
|
||
|
addElement: function addElement($element) {
|
||
|
this.$elements.push($element);
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
// See `Field._refreshConstraints()`
|
||
|
_refreshConstraints: function _refreshConstraints() {
|
||
|
var fieldConstraints;
|
||
|
|
||
|
this.constraints = [];
|
||
|
|
||
|
// Select multiple special treatment
|
||
|
if (this.element.nodeName === 'SELECT') {
|
||
|
this.actualizeOptions()._bindConstraints();
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
// Gather all constraints for each input in the multiple group
|
||
|
for (var i = 0; i < this.$elements.length; i++) {
|
||
|
|
||
|
// Check if element have not been dynamically removed since last binding
|
||
|
if (!$('html').has(this.$elements[i]).length) {
|
||
|
this.$elements.splice(i, 1);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
fieldConstraints = this.$elements[i].data('FieldMultiple')._refreshConstraints().constraints;
|
||
|
|
||
|
for (var j = 0; j < fieldConstraints.length; j++) this.addConstraint(fieldConstraints[j].name, fieldConstraints[j].requirements, fieldConstraints[j].priority, fieldConstraints[j].isDomConstraint);
|
||
|
}
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
// See `Field.getValue()`
|
||
|
getValue: function getValue() {
|
||
|
// Value could be overriden in DOM
|
||
|
if ('function' === typeof this.options.value) return this.options.value(this);else if ('undefined' !== typeof this.options.value) return this.options.value;
|
||
|
|
||
|
// Radio input case
|
||
|
if (this.element.nodeName === 'INPUT') {
|
||
|
var type = Utils.getType(this.element);
|
||
|
if (type === 'radio') return this._findRelated().filter(':checked').val() || '';
|
||
|
|
||
|
// checkbox input case
|
||
|
if (type === 'checkbox') {
|
||
|
var values = [];
|
||
|
|
||
|
this._findRelated().filter(':checked').each(function () {
|
||
|
values.push($(this).val());
|
||
|
});
|
||
|
|
||
|
return values;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Select multiple case
|
||
|
if (this.element.nodeName === 'SELECT' && null === this.$element.val()) return [];
|
||
|
|
||
|
// Default case that should never happen
|
||
|
return this.$element.val();
|
||
|
},
|
||
|
|
||
|
_init: function _init() {
|
||
|
this.$elements = [this.$element];
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var Factory = function Factory(element, options, parsleyFormInstance) {
|
||
|
this.element = element;
|
||
|
this.$element = $(element);
|
||
|
|
||
|
// If the element has already been bound, returns its saved Parsley instance
|
||
|
var savedparsleyFormInstance = this.$element.data('Parsley');
|
||
|
if (savedparsleyFormInstance) {
|
||
|
|
||
|
// If the saved instance has been bound without a Form parent and there is one given in this call, add it
|
||
|
if ('undefined' !== typeof parsleyFormInstance && savedparsleyFormInstance.parent === window.Parsley) {
|
||
|
savedparsleyFormInstance.parent = parsleyFormInstance;
|
||
|
savedparsleyFormInstance._resetOptions(savedparsleyFormInstance.options);
|
||
|
}
|
||
|
|
||
|
if ('object' === typeof options) {
|
||
|
_extends(savedparsleyFormInstance.options, options);
|
||
|
}
|
||
|
|
||
|
return savedparsleyFormInstance;
|
||
|
}
|
||
|
|
||
|
// Parsley must be instantiated with a DOM element or jQuery $element
|
||
|
if (!this.$element.length) throw new Error('You must bind Parsley on an existing element.');
|
||
|
|
||
|
if ('undefined' !== typeof parsleyFormInstance && 'Form' !== parsleyFormInstance.__class__) throw new Error('Parent instance must be a Form instance');
|
||
|
|
||
|
this.parent = parsleyFormInstance || window.Parsley;
|
||
|
return this.init(options);
|
||
|
};
|
||
|
|
||
|
Factory.prototype = {
|
||
|
init: function init(options) {
|
||
|
this.__class__ = 'Parsley';
|
||
|
this.__version__ = '2.8.1';
|
||
|
this.__id__ = Utils.generateID();
|
||
|
|
||
|
// Pre-compute options
|
||
|
this._resetOptions(options);
|
||
|
|
||
|
// A Form instance is obviously a `<form>` element but also every node that is not an input and has the `data-parsley-validate` attribute
|
||
|
if (this.element.nodeName === 'FORM' || Utils.checkAttr(this.element, this.options.namespace, 'validate') && !this.$element.is(this.options.inputs)) return this.bind('parsleyForm');
|
||
|
|
||
|
// Every other element is bound as a `Field` or `FieldMultiple`
|
||
|
return this.isMultiple() ? this.handleMultiple() : this.bind('parsleyField');
|
||
|
},
|
||
|
|
||
|
isMultiple: function isMultiple() {
|
||
|
var type = Utils.getType(this.element);
|
||
|
return type === 'radio' || type === 'checkbox' || this.element.nodeName === 'SELECT' && null !== this.element.getAttribute('multiple');
|
||
|
},
|
||
|
|
||
|
// Multiples fields are a real nightmare :(
|
||
|
// Maybe some refactoring would be appreciated here...
|
||
|
handleMultiple: function handleMultiple() {
|
||
|
var _this13 = this;
|
||
|
|
||
|
var name;
|
||
|
var multiple;
|
||
|
var parsleyMultipleInstance;
|
||
|
|
||
|
// Handle multiple name
|
||
|
this.options.multiple = this.options.multiple || (name = this.element.getAttribute('name')) || this.element.getAttribute('id');
|
||
|
|
||
|
// Special select multiple input
|
||
|
if (this.element.nodeName === 'SELECT' && null !== this.element.getAttribute('multiple')) {
|
||
|
this.options.multiple = this.options.multiple || this.__id__;
|
||
|
return this.bind('parsleyFieldMultiple');
|
||
|
|
||
|
// Else for radio / checkboxes, we need a `name` or `data-parsley-multiple` to properly bind it
|
||
|
} else if (!this.options.multiple) {
|
||
|
Utils.warn('To be bound by Parsley, a radio, a checkbox and a multiple select input must have either a name or a multiple option.', this.$element);
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
// Remove special chars
|
||
|
this.options.multiple = this.options.multiple.replace(/(:|\.|\[|\]|\{|\}|\$)/g, '');
|
||
|
|
||
|
// Add proper `data-parsley-multiple` to siblings if we have a valid multiple name
|
||
|
if (name) {
|
||
|
$('input[name="' + name + '"]').each(function (i, input) {
|
||
|
var type = Utils.getType(input);
|
||
|
if (type === 'radio' || type === 'checkbox') input.setAttribute(_this13.options.namespace + 'multiple', _this13.options.multiple);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// Check here if we don't already have a related multiple instance saved
|
||
|
var $previouslyRelated = this._findRelated();
|
||
|
for (var i = 0; i < $previouslyRelated.length; i++) {
|
||
|
parsleyMultipleInstance = $($previouslyRelated.get(i)).data('Parsley');
|
||
|
if ('undefined' !== typeof parsleyMultipleInstance) {
|
||
|
|
||
|
if (!this.$element.data('FieldMultiple')) {
|
||
|
parsleyMultipleInstance.addElement(this.$element);
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Create a secret Field instance for every multiple field. It will be stored in `data('FieldMultiple')`
|
||
|
// And will be useful later to access classic `Field` stuff while being in a `FieldMultiple` instance
|
||
|
this.bind('parsleyField', true);
|
||
|
|
||
|
return parsleyMultipleInstance || this.bind('parsleyFieldMultiple');
|
||
|
},
|
||
|
|
||
|
// Return proper `Form`, `Field` or `FieldMultiple`
|
||
|
bind: function bind(type, doNotStore) {
|
||
|
var parsleyInstance;
|
||
|
|
||
|
switch (type) {
|
||
|
case 'parsleyForm':
|
||
|
parsleyInstance = $.extend(new Form(this.element, this.domOptions, this.options), new Base(), window.ParsleyExtend)._bindFields();
|
||
|
break;
|
||
|
case 'parsleyField':
|
||
|
parsleyInstance = $.extend(new parsley_field(this.element, this.domOptions, this.options, this.parent), new Base(), window.ParsleyExtend);
|
||
|
break;
|
||
|
case 'parsleyFieldMultiple':
|
||
|
parsleyInstance = $.extend(new parsley_field(this.element, this.domOptions, this.options, this.parent), new Multiple(), new Base(), window.ParsleyExtend)._init();
|
||
|
break;
|
||
|
default:
|
||
|
throw new Error(type + 'is not a supported Parsley type');
|
||
|
}
|
||
|
|
||
|
if (this.options.multiple) Utils.setAttr(this.element, this.options.namespace, 'multiple', this.options.multiple);
|
||
|
|
||
|
if ('undefined' !== typeof doNotStore) {
|
||
|
this.$element.data('FieldMultiple', parsleyInstance);
|
||
|
|
||
|
return parsleyInstance;
|
||
|
}
|
||
|
|
||
|
// Store the freshly bound instance in a DOM element for later access using jQuery `data()`
|
||
|
this.$element.data('Parsley', parsleyInstance);
|
||
|
|
||
|
// Tell the world we have a new Form or Field instance!
|
||
|
parsleyInstance._actualizeTriggers();
|
||
|
parsleyInstance._trigger('init');
|
||
|
|
||
|
return parsleyInstance;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var vernums = $.fn.jquery.split('.');
|
||
|
if (parseInt(vernums[0]) <= 1 && parseInt(vernums[1]) < 8) {
|
||
|
throw "The loaded version of jQuery is too old. Please upgrade to 1.8.x or better.";
|
||
|
}
|
||
|
if (!vernums.forEach) {
|
||
|
Utils.warn('Parsley requires ES5 to run properly. Please include https://github.com/es-shims/es5-shim');
|
||
|
}
|
||
|
// Inherit `on`, `off` & `trigger` to Parsley:
|
||
|
var Parsley = _extends(new Base(), {
|
||
|
element: document,
|
||
|
$element: $(document),
|
||
|
actualizeOptions: null,
|
||
|
_resetOptions: null,
|
||
|
Factory: Factory,
|
||
|
version: '2.8.1'
|
||
|
});
|
||
|
|
||
|
// Supplement Field and Form with Base
|
||
|
// This way, the constructors will have access to those methods
|
||
|
_extends(parsley_field.prototype, UI.Field, Base.prototype);
|
||
|
_extends(Form.prototype, UI.Form, Base.prototype);
|
||
|
// Inherit actualizeOptions and _resetOptions:
|
||
|
_extends(Factory.prototype, Base.prototype);
|
||
|
|
||
|
// ### jQuery API
|
||
|
// `$('.elem').parsley(options)` or `$('.elem').psly(options)`
|
||
|
$.fn.parsley = $.fn.psly = function (options) {
|
||
|
if (this.length > 1) {
|
||
|
var instances = [];
|
||
|
|
||
|
this.each(function () {
|
||
|
instances.push($(this).parsley(options));
|
||
|
});
|
||
|
|
||
|
return instances;
|
||
|
}
|
||
|
|
||
|
// Return undefined if applied to non existing DOM element
|
||
|
if (this.length == 0) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
return new Factory(this[0], options);
|
||
|
};
|
||
|
|
||
|
// ### Field and Form extension
|
||
|
// Ensure the extension is now defined if it wasn't previously
|
||
|
if ('undefined' === typeof window.ParsleyExtend) window.ParsleyExtend = {};
|
||
|
|
||
|
// ### Parsley config
|
||
|
// Inherit from ParsleyDefault, and copy over any existing values
|
||
|
Parsley.options = _extends(Utils.objectCreate(Defaults), window.ParsleyConfig);
|
||
|
window.ParsleyConfig = Parsley.options; // Old way of accessing global options
|
||
|
|
||
|
// ### Globals
|
||
|
window.Parsley = window.psly = Parsley;
|
||
|
Parsley.Utils = Utils;
|
||
|
window.ParsleyUtils = {};
|
||
|
$.each(Utils, function (key, value) {
|
||
|
if ('function' === typeof value) {
|
||
|
window.ParsleyUtils[key] = function () {
|
||
|
Utils.warnOnce('Accessing `window.ParsleyUtils` is deprecated. Use `window.Parsley.Utils` instead.');
|
||
|
return Utils[key].apply(Utils, arguments);
|
||
|
};
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// ### Define methods that forward to the registry, and deprecate all access except through window.Parsley
|
||
|
var registry = window.Parsley._validatorRegistry = new ValidatorRegistry(window.ParsleyConfig.validators, window.ParsleyConfig.i18n);
|
||
|
window.ParsleyValidator = {};
|
||
|
$.each('setLocale addCatalog addMessage addMessages getErrorMessage formatMessage addValidator updateValidator removeValidator hasValidator'.split(' '), function (i, method) {
|
||
|
window.Parsley[method] = function () {
|
||
|
return registry[method].apply(registry, arguments);
|
||
|
};
|
||
|
window.ParsleyValidator[method] = function () {
|
||
|
var _window$Parsley;
|
||
|
|
||
|
Utils.warnOnce('Accessing the method \'' + method + '\' through Validator is deprecated. Simply call \'window.Parsley.' + method + '(...)\'');
|
||
|
return (_window$Parsley = window.Parsley)[method].apply(_window$Parsley, arguments);
|
||
|
};
|
||
|
});
|
||
|
|
||
|
// ### UI
|
||
|
// Deprecated global object
|
||
|
window.Parsley.UI = UI;
|
||
|
window.ParsleyUI = {
|
||
|
removeError: function removeError(instance, name, doNotUpdateClass) {
|
||
|
var updateClass = true !== doNotUpdateClass;
|
||
|
Utils.warnOnce('Accessing UI is deprecated. Call \'removeError\' on the instance directly. Please comment in issue 1073 as to your need to call this method.');
|
||
|
return instance.removeError(name, { updateClass: updateClass });
|
||
|
},
|
||
|
getErrorsMessages: function getErrorsMessages(instance) {
|
||
|
Utils.warnOnce('Accessing UI is deprecated. Call \'getErrorsMessages\' on the instance directly.');
|
||
|
return instance.getErrorsMessages();
|
||
|
}
|
||
|
};
|
||
|
$.each('addError updateError'.split(' '), function (i, method) {
|
||
|
window.ParsleyUI[method] = function (instance, name, message, assert, doNotUpdateClass) {
|
||
|
var updateClass = true !== doNotUpdateClass;
|
||
|
Utils.warnOnce('Accessing UI is deprecated. Call \'' + method + '\' on the instance directly. Please comment in issue 1073 as to your need to call this method.');
|
||
|
return instance[method](name, { message: message, assert: assert, updateClass: updateClass });
|
||
|
};
|
||
|
});
|
||
|
|
||
|
// ### PARSLEY auto-binding
|
||
|
// Prevent it by setting `ParsleyConfig.autoBind` to `false`
|
||
|
if (false !== window.ParsleyConfig.autoBind) {
|
||
|
$(function () {
|
||
|
// Works only on `data-parsley-validate`.
|
||
|
if ($('[data-parsley-validate]').length) $('[data-parsley-validate]').parsley();
|
||
|
});
|
||
|
}
|
||
|
|
||
|
var o = $({});
|
||
|
var deprecated = function deprecated() {
|
||
|
Utils.warnOnce("Parsley's pubsub module is deprecated; use the 'on' and 'off' methods on parsley instances or window.Parsley");
|
||
|
};
|
||
|
|
||
|
// Returns an event handler that calls `fn` with the arguments it expects
|
||
|
function adapt(fn, context) {
|
||
|
// Store to allow unbinding
|
||
|
if (!fn.parsleyAdaptedCallback) {
|
||
|
fn.parsleyAdaptedCallback = function () {
|
||
|
var args = Array.prototype.slice.call(arguments, 0);
|
||
|
args.unshift(this);
|
||
|
fn.apply(context || o, args);
|
||
|
};
|
||
|
}
|
||
|
return fn.parsleyAdaptedCallback;
|
||
|
}
|
||
|
|
||
|
var eventPrefix = 'parsley:';
|
||
|
// Converts 'parsley:form:validate' into 'form:validate'
|
||
|
function eventName(name) {
|
||
|
if (name.lastIndexOf(eventPrefix, 0) === 0) return name.substr(eventPrefix.length);
|
||
|
return name;
|
||
|
}
|
||
|
|
||
|
// $.listen is deprecated. Use Parsley.on instead.
|
||
|
$.listen = function (name, callback) {
|
||
|
var context;
|
||
|
deprecated();
|
||
|
if ('object' === typeof arguments[1] && 'function' === typeof arguments[2]) {
|
||
|
context = arguments[1];
|
||
|
callback = arguments[2];
|
||
|
}
|
||
|
|
||
|
if ('function' !== typeof callback) throw new Error('Wrong parameters');
|
||
|
|
||
|
window.Parsley.on(eventName(name), adapt(callback, context));
|
||
|
};
|
||
|
|
||
|
$.listenTo = function (instance, name, fn) {
|
||
|
deprecated();
|
||
|
if (!(instance instanceof parsley_field) && !(instance instanceof Form)) throw new Error('Must give Parsley instance');
|
||
|
|
||
|
if ('string' !== typeof name || 'function' !== typeof fn) throw new Error('Wrong parameters');
|
||
|
|
||
|
instance.on(eventName(name), adapt(fn));
|
||
|
};
|
||
|
|
||
|
$.unsubscribe = function (name, fn) {
|
||
|
deprecated();
|
||
|
if ('string' !== typeof name || 'function' !== typeof fn) throw new Error('Wrong arguments');
|
||
|
window.Parsley.off(eventName(name), fn.parsleyAdaptedCallback);
|
||
|
};
|
||
|
|
||
|
$.unsubscribeTo = function (instance, name) {
|
||
|
deprecated();
|
||
|
if (!(instance instanceof parsley_field) && !(instance instanceof Form)) throw new Error('Must give Parsley instance');
|
||
|
instance.off(eventName(name));
|
||
|
};
|
||
|
|
||
|
$.unsubscribeAll = function (name) {
|
||
|
deprecated();
|
||
|
window.Parsley.off(eventName(name));
|
||
|
$('form,input,textarea,select').each(function () {
|
||
|
var instance = $(this).data('Parsley');
|
||
|
if (instance) {
|
||
|
instance.off(eventName(name));
|
||
|
}
|
||
|
});
|
||
|
};
|
||
|
|
||
|
// $.emit is deprecated. Use jQuery events instead.
|
||
|
$.emit = function (name, instance) {
|
||
|
var _instance;
|
||
|
|
||
|
deprecated();
|
||
|
var instanceGiven = instance instanceof parsley_field || instance instanceof Form;
|
||
|
var args = Array.prototype.slice.call(arguments, instanceGiven ? 2 : 1);
|
||
|
args.unshift(eventName(name));
|
||
|
if (!instanceGiven) {
|
||
|
instance = window.Parsley;
|
||
|
}
|
||
|
(_instance = instance).trigger.apply(_instance, _toConsumableArray(args));
|
||
|
};
|
||
|
|
||
|
var pubsub = {};
|
||
|
|
||
|
$.extend(true, Parsley, {
|
||
|
asyncValidators: {
|
||
|
'default': {
|
||
|
fn: function fn(xhr) {
|
||
|
// By default, only status 2xx are deemed successful.
|
||
|
// Note: we use status instead of state() because responses with status 200
|
||
|
// but invalid messages (e.g. an empty body for content type set to JSON) will
|
||
|
// result in state() === 'rejected'.
|
||
|
return xhr.status >= 200 && xhr.status < 300;
|
||
|
},
|
||
|
url: false
|
||
|
},
|
||
|
reverse: {
|
||
|
fn: function fn(xhr) {
|
||
|
// If reverse option is set, a failing ajax request is considered successful
|
||
|
return xhr.status < 200 || xhr.status >= 300;
|
||
|
},
|
||
|
url: false
|
||
|
}
|
||
|
},
|
||
|
|
||
|
addAsyncValidator: function addAsyncValidator(name, fn, url, options) {
|
||
|
Parsley.asyncValidators[name] = {
|
||
|
fn: fn,
|
||
|
url: url || false,
|
||
|
options: options || {}
|
||
|
};
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
});
|
||
|
|
||
|
Parsley.addValidator('remote', {
|
||
|
requirementType: {
|
||
|
'': 'string',
|
||
|
'validator': 'string',
|
||
|
'reverse': 'boolean',
|
||
|
'options': 'object'
|
||
|
},
|
||
|
|
||
|
validateString: function validateString(value, url, options, instance) {
|
||
|
var data = {};
|
||
|
var ajaxOptions;
|
||
|
var csr;
|
||
|
var validator = options.validator || (true === options.reverse ? 'reverse' : 'default');
|
||
|
|
||
|
if ('undefined' === typeof Parsley.asyncValidators[validator]) throw new Error('Calling an undefined async validator: `' + validator + '`');
|
||
|
|
||
|
url = Parsley.asyncValidators[validator].url || url;
|
||
|
|
||
|
// Fill current value
|
||
|
if (url.indexOf('{value}') > -1) {
|
||
|
url = url.replace('{value}', encodeURIComponent(value));
|
||
|
} else {
|
||
|
data[instance.element.getAttribute('name') || instance.element.getAttribute('id')] = value;
|
||
|
}
|
||
|
|
||
|
// Merge options passed in from the function with the ones in the attribute
|
||
|
var remoteOptions = $.extend(true, options.options || {}, Parsley.asyncValidators[validator].options);
|
||
|
|
||
|
// All `$.ajax(options)` could be overridden or extended directly from DOM in `data-parsley-remote-options`
|
||
|
ajaxOptions = $.extend(true, {}, {
|
||
|
url: url,
|
||
|
data: data,
|
||
|
type: 'GET'
|
||
|
}, remoteOptions);
|
||
|
|
||
|
// Generate store key based on ajax options
|
||
|
instance.trigger('field:ajaxoptions', instance, ajaxOptions);
|
||
|
|
||
|
csr = $.param(ajaxOptions);
|
||
|
|
||
|
// Initialise querry cache
|
||
|
if ('undefined' === typeof Parsley._remoteCache) Parsley._remoteCache = {};
|
||
|
|
||
|
// Try to retrieve stored xhr
|
||
|
var xhr = Parsley._remoteCache[csr] = Parsley._remoteCache[csr] || $.ajax(ajaxOptions);
|
||
|
|
||
|
var handleXhr = function handleXhr() {
|
||
|
var result = Parsley.asyncValidators[validator].fn.call(instance, xhr, url, options);
|
||
|
if (!result) // Map falsy results to rejected promise
|
||
|
result = $.Deferred().reject();
|
||
|
return $.when(result);
|
||
|
};
|
||
|
|
||
|
return xhr.then(handleXhr, handleXhr);
|
||
|
},
|
||
|
|
||
|
priority: -1
|
||
|
});
|
||
|
|
||
|
Parsley.on('form:submit', function () {
|
||
|
Parsley._remoteCache = {};
|
||
|
});
|
||
|
|
||
|
Base.prototype.addAsyncValidator = function () {
|
||
|
Utils.warnOnce('Accessing the method `addAsyncValidator` through an instance is deprecated. Simply call `Parsley.addAsyncValidator(...)`');
|
||
|
return Parsley.addAsyncValidator.apply(Parsley, arguments);
|
||
|
};
|
||
|
|
||
|
// This is included with the Parsley library itself,
|
||
|
// thus there is no use in adding it to your project.
|
||
|
Parsley.addMessages('en', {
|
||
|
defaultMessage: "This value seems to be invalid.",
|
||
|
type: {
|
||
|
email: "This value should be a valid email.",
|
||
|
url: "This value should be a valid url.",
|
||
|
number: "This value should be a valid number.",
|
||
|
integer: "This value should be a valid integer.",
|
||
|
digits: "This value should be digits.",
|
||
|
alphanum: "This value should be alphanumeric."
|
||
|
},
|
||
|
notblank: "This value should not be blank.",
|
||
|
required: "This value is required.",
|
||
|
pattern: "This value seems to be invalid.",
|
||
|
min: "This value should be greater than or equal to %s.",
|
||
|
max: "This value should be lower than or equal to %s.",
|
||
|
range: "This value should be between %s and %s.",
|
||
|
minlength: "This value is too short. It should have %s characters or more.",
|
||
|
maxlength: "This value is too long. It should have %s characters or fewer.",
|
||
|
length: "This value length is invalid. It should be between %s and %s characters long.",
|
||
|
mincheck: "You must select at least %s choices.",
|
||
|
maxcheck: "You must select %s choices or fewer.",
|
||
|
check: "You must select between %s and %s choices.",
|
||
|
equalto: "This value should be the same."
|
||
|
});
|
||
|
|
||
|
Parsley.setLocale('en');
|
||
|
|
||
|
/**
|
||
|
* inputevent - Alleviate browser bugs for input events
|
||
|
* https://github.com/marcandre/inputevent
|
||
|
* @version v0.0.3 - (built Thu, Apr 14th 2016, 5:58 pm)
|
||
|
* @author Marc-Andre Lafortune <github@marc-andre.ca>
|
||
|
* @license MIT
|
||
|
*/
|
||
|
|
||
|
function InputEvent() {
|
||
|
var _this14 = this;
|
||
|
|
||
|
var globals = window || global;
|
||
|
|
||
|
// Slightly odd way construct our object. This way methods are force bound.
|
||
|
// Used to test for duplicate library.
|
||
|
_extends(this, {
|
||
|
|
||
|
// For browsers that do not support isTrusted, assumes event is native.
|
||
|
isNativeEvent: function isNativeEvent(evt) {
|
||
|
return evt.originalEvent && evt.originalEvent.isTrusted !== false;
|
||
|
},
|
||
|
|
||
|
fakeInputEvent: function fakeInputEvent(evt) {
|
||
|
if (_this14.isNativeEvent(evt)) {
|
||
|
$(evt.target).trigger('input');
|
||
|
}
|
||
|
},
|
||
|
|
||
|
misbehaves: function misbehaves(evt) {
|
||
|
if (_this14.isNativeEvent(evt)) {
|
||
|
_this14.behavesOk(evt);
|
||
|
$(document).on('change.inputevent', evt.data.selector, _this14.fakeInputEvent);
|
||
|
_this14.fakeInputEvent(evt);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
behavesOk: function behavesOk(evt) {
|
||
|
if (_this14.isNativeEvent(evt)) {
|
||
|
$(document) // Simply unbinds the testing handler
|
||
|
.off('input.inputevent', evt.data.selector, _this14.behavesOk).off('change.inputevent', evt.data.selector, _this14.misbehaves);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
// Bind the testing handlers
|
||
|
install: function install() {
|
||
|
if (globals.inputEventPatched) {
|
||
|
return;
|
||
|
}
|
||
|
globals.inputEventPatched = '0.0.3';
|
||
|
var _arr = ['select', 'input[type="checkbox"]', 'input[type="radio"]', 'input[type="file"]'];
|
||
|
for (var _i = 0; _i < _arr.length; _i++) {
|
||
|
var selector = _arr[_i];
|
||
|
$(document).on('input.inputevent', selector, { selector: selector }, _this14.behavesOk).on('change.inputevent', selector, { selector: selector }, _this14.misbehaves);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
uninstall: function uninstall() {
|
||
|
delete globals.inputEventPatched;
|
||
|
$(document).off('.inputevent');
|
||
|
}
|
||
|
|
||
|
});
|
||
|
};
|
||
|
|
||
|
var inputevent = new InputEvent();
|
||
|
|
||
|
inputevent.install();
|
||
|
|
||
|
var parsley = Parsley;
|
||
|
|
||
|
return parsley;
|
||
|
});
|
||
|
//# sourceMappingURL=parsley.js.map
|