/*! * Parsley.js * Version 2.3.5 - built Sun, Feb 28th 2016, 6:25 am * http://parsleyjs.org * Guillaume Potier - * Marc-Andre Lafortune - * MIT Licensed */ // The source code below is generated by babel as // Parsley is written in ECMAScript 6 // var _slice = Array.prototype.slice; 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 ParsleyUtils__ParsleyUtils = { // 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 ('undefined' === typeof $element || 'undefined' === typeof $element[0]) return obj; attributes = $element[0].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.is('[' + namespace + _checkAttr + ']'); }, setAttr: function setAttr($element, namespace, attr, value) { $element[0].setAttribute(this.dasherize(namespace + attr), String(value)); }, generateID: function generateID() { return '' + globalID++; }, /** Third party functions **/ // Zepto deserialize function deserializeValue: function deserializeValue(value) { var num; try { return value ? value == "true" || (value == "false" ? false : value == "null" ? null : !isNaN(num = Number(value)) ? num : /^[\[\{]/.test(value) ? $.parseJSON(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, ''); }, namespaceEvents: function namespaceEvents(events, namespace) { events = this.trimString(events || '').split(/\s+/); if (!events[0]) return ''; return $.map(events, function(evt) { return evt + '.' + namespace; }).join(' '); }, // 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; }; })() }; var ParsleyUtils__default = ParsleyUtils__ParsleyUtils; // 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 ParsleyDefaults = { // ### 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(ParsleyField) {}, // Return the `$element` where errors will be appended // Could also be (and given directly from DOM) a valid selector like `'#div'` errorsContainer: function errorsContainer(ParsleyField) {}, // ul elem that would receive errors' list errorsWrapper: '', // li elem that would receive error message errorTemplate: '
  • ' }; var ParsleyAbstract = function ParsleyAbstract() {}; ParsleyAbstract.prototype = { asyncSupport: true, // Deprecated actualizeOptions: function actualizeOptions() { ParsleyUtils__default.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 = ParsleyUtils__default.objectCreate(this.parent.options); this.options = ParsleyUtils__default.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; }, // Reset UI reset: function reset() { // Field case: just emit a reset event for UI if ('ParsleyForm' !== this.__class__) { this._resetUI(); return this._trigger('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(); if ('ParsleyForm' !== this.__class__) { this.$element.removeData('Parsley'); this.$element.removeData('ParsleyFieldMultiple'); this._trigger('destroy'); return; } // 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'); }, asyncIsValid: function asyncIsValid(group, force) { ParsleyUtils__default.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.find('[' + this.options.namespace + 'multiple="' + this.options.multiple + '"]') : this.$element; } }; var requirementConverters = { string: function string(_string) { return _string; }, integer: function integer(string) { if (isNaN(string)) throw 'Requirement is not an integer: "' + string + '"'; return parseInt(string, 10); }, number: function number(string) { if (isNaN(string)) throw 'Requirement is not a number: "' + string + '"'; return parseFloat(string); }, reference: function reference(string) { // Unused for now var result = $(string); if (result.length === 0) throw 'No such reference: "' + string + '"'; return result; }, boolean: function boolean(string) { return string !== 'false'; }, object: function object(string) { return ParsleyUtils__default.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); } }; 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(ParsleyUtils__default.trimString); if (values.length !== length) throw 'Requirement has ' + values.length + ' values when ' + length + ' are needed'; return values; }; var convertRequirement = function convertRequirement(requirementType, string) { var converter = requirementConverters[requirementType || 'string']; if (!converter) throw 'Unknown requirement specification: "' + requirementType + '"'; return converter(string); }; 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 = convertRequirement(requirementSpec[key], value); extra[key] = value; } else { main = convertRequirement(requirementSpec[key], string); } } return [main, extra]; }; // A Validator needs to implement the methods `validate` and `parseRequirements` var ParsleyValidator = function ParsleyValidator(spec) { $.extend(true, this, spec); }; ParsleyValidator.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.call(this, value, requirementFirstArg); } if ($.isArray(value)) { if (!this.validateMultiple) throw 'Validator `' + this.name + '` does not handle multiple values'; return this.validateMultiple.apply(this, arguments); } else { 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 $.isArray(requirements) ? requirements : [requirements]; } var type = this.requirementType; if ($.isArray(type)) { var values = convertArrayRequirement(requirements, type.length); for (var i = 0; i < values.length; i++) values[i] = convertRequirement(type[i], values[i]); return values; } else if ($.isPlainObject(type)) { return convertExtraOptionRequirement(type, requirements, extraOptionReader); } else { return [convertRequirement(type, requirements)]; } }, // Defaults: requirementType: 'string', priority: 2 }; var ParsleyValidatorRegistry = function ParsleyValidatorRegistry(validators, catalog) { this.__class__ = 'ParsleyValidatorRegistry'; // Default Parsley locale is en this.locale = 'en'; this.init(validators || {}, catalog || {}); }; var typeRegexes = { email: /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-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-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i, // 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, 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-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)' + // domain name '(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*' + // TLD identifier '(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))' + ")" + // port number "(?::\\d{2,5})?" + // resource path "(?:/\\S*)?" + "$", 'i') }; typeRegexes.range = typeRegexes.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)); }; ParsleyValidatorRegistry.prototype = { init: function init(validators, catalog) { this.catalog = catalog; // Copy prototype's validators: this.validators = $.extend({}, 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]) ParsleyUtils__default.warn('Validator "' + name + '" is already defined.'); else if (ParsleyDefaults.hasOwnProperty(name)) { ParsleyUtils__default.warn('"' + name + '" is a restricted keyword and is not a valid validator name.'); return; } return this._setValidator.apply(this, arguments); }, updateValidator: function updateValidator(name, arg1, arg2) { if (!this.validators[name]) { ParsleyUtils__default.warn('Validator "' + name + '" is not already defined.'); return this.addValidator.apply(this, arguments); } return this._setValidator(this, arguments); }, removeValidator: function removeValidator(name) { if (!this.validators[name]) ParsleyUtils__default.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 ParsleyValidator(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 ? '1' : _ref$step; var _ref$base = _ref.base; var base = _ref$base === undefined ? 0 : _ref$base; var regex = typeRegexes[type]; if (!regex) { throw new Error('validator type `' + type + '` is not supported'); } if (!regex.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: { validateNumber: function validateNumber(value, requirement) { return value >= requirement; }, requirementType: 'number', priority: 30 }, max: { validateNumber: function validateNumber(value, requirement) { return value <= requirement; }, requirementType: 'number', priority: 30 }, range: { validateNumber: function validateNumber(value, min, max) { return value >= min && value <= max; }, requirementType: ['number', 'number'], priority: 30 }, equalto: { validateString: function validateString(value, refOrValue) { var $reference = $(refOrValue); if ($reference.length) return value === $reference.val(); else return value === refOrValue; }, priority: 256 } } }; var ParsleyUI = {}; 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 : [] }; }; ParsleyUI.Form = { _actualizeTriggers: function _actualizeTriggers() { var _this = this; this.$element.on('submit.Parsley', function(evt) { _this.onSubmitValidate(evt); }); this.$element.on('click.Parsley', 'input[type="submit"], button[type="submit"]', function(evt) { _this.onSubmitButton(evt); }); // UI could be disabled if (false === this.options.uiEnabled) return; this.$element.attr('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'); } }; ParsleyUI.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.$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.$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.attr(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() { // An element selector could be passed through DOM with `data-parsley-class-handler=#foo` 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 $handler = this.options.classHandler.call(this, this); // If this function returned a valid existing DOM element, go for it if ('undefined' !== typeof $handler && $handler.length) return $handler; // Otherwise, if simple element (input, texatrea, select...) it will perfectly host the classes if (!this.options.multiple || this.$element.is('select')) return this.$element; // But if multiple element (radio, checkbox), that would be their parent return this.$element.parent(); }, _insertErrorWrapper: function _insertErrorWrapper() { var $errorsContainer; // Nothing to do if already inserted if (0 !== this._ui.$errorsWrapper.parent().length) return this._ui.$errorsWrapper.parent(); if ('string' === typeof this.options.errorsContainer) { if ($(this.options.errorsContainer).length) return $(this.options.errorsContainer).append(this._ui.$errorsWrapper); else ParsleyUtils__default.warn('The errors container `' + this.options.errorsContainer + '` does not exist in DOM'); } else if ('function' === typeof this.options.errorsContainer) $errorsContainer = this.options.errorsContainer.call(this, this); if ('undefined' !== typeof $errorsContainer && $errorsContainer.length) return $errorsContainer.append(this._ui.$errorsWrapper); var $from = this.$element; if (this.options.multiple) $from = $from.parent(); return $from.after(this._ui.$errorsWrapper); }, _actualizeTriggers: function _actualizeTriggers() { var _this2 = this; var $toBind = this._findRelated(); // Remove Parsley events already bound on this field $toBind.off('.Parsley'); if (this._failedOnce) $toBind.on(ParsleyUtils__default.namespaceEvents(this.options.triggerAfterFailure, 'Parsley'), function() { _this2.validate(); }); else { $toBind.on(ParsleyUtils__default.namespaceEvents(this.options.trigger, 'Parsley'), function(event) { _this2._eventValidate(event); }); } }, _eventValidate: function _eventValidate(event) { // 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 (/key|input/.test(event.type)) if (!(this._ui && this._ui.validationInformationVisible) && this.getValue().length <= this.options.validationThreshold) return; 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 ParsleyForm = function ParsleyForm(element, domOptions, options) { this.__class__ = 'ParsleyForm'; this.__id__ = ParsleyUtils__default.generateID(); this.$element = $(element); this.domOptions = domOptions; this.options = options; this.parent = window.Parsley; this.fields = []; this.validationResult = null; }; var ParsleyForm__statusMapping = { pending: null, resolved: true, rejected: false }; ParsleyForm.prototype = { onSubmitValidate: function onSubmitValidate(event) { var _this3 = 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('input[type="submit"], button[type="submit"]').first(); this._$submitSource = null; this.$element.find('.parsley-synthetic-submit-button').prop('disabled', true); if ($submitSource.is('[formnovalidate]')) return; 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() { _this3._submit($submitSource); }); } }, onSubmitButton: function onSubmitButton(event) { this._$submitSource = $(event.target); }, // 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 = $('').appendTo(this.$element); $synthetic.attr({ name: $submitSource.attr('name'), value: $submitSource.attr('value') }); } this.$element.trigger($.extend($.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)) { ParsleyUtils__default.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 ParsleyForm__statusMapping[this.whenValidate(options).state()]; }, whenValidate: function whenValidate() { var _this4 = 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 = $.extend({}, event, { preventDefault: function preventDefault() { ParsleyUtils__default.warnOnce("Using `this.submitEvent.preventDefault()` is deprecated; instead, call `this.validationResult = false`"); _this4.validationResult = false; } }); } this.validationResult = true; // fire validate event to eventually modify things before very 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(_this4.fields, function(field) { return field.whenValidate({ force: force, group: group }); }); }); var promiseBasedOnValidationResult = function promiseBasedOnValidationResult() { var r = $.Deferred(); if (false === _this4.validationResult) r.reject(); return r.resolve().promise(); }; return $.when.apply($, _toConsumableArray(promises)).done(function() { _this4._trigger('success'); }).fail(function() { _this4.validationResult = false; _this4.focus(); _this4._trigger('error'); }).always(function() { _this4._trigger('validated'); }).pipe(promiseBasedOnValidationResult, promiseBasedOnValidationResult); }, // 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)) { ParsleyUtils__default.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 ParsleyForm__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 _this5 = 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(_this5.fields, function(field) { return field.whenValid({ group: group, force: force }); }); }); return $.when.apply($, _toConsumableArray(promises)); }, _refreshFields: function _refreshFields() { return this.actualizeOptions()._bindFields(); }, _bindFields: function _bindFields() { var _this6 = this; var oldFields = this.fields; this.fields = []; this.fieldsMappedById = {}; this._withoutReactualizingFormOptions(function() { _this6.$element.find(_this6.options.inputs).not(_this6.options.excluded).each(function(_, element) { var fieldInstance = new window.Parsley.Factory(element, {}, _this6); // Only add valid and not excluded `ParsleyField` and `ParsleyFieldMultiple` children if (('ParsleyField' === fieldInstance.__class__ || 'ParsleyFieldMultiple' === fieldInstance.__class__) && true !== fieldInstance.options.excluded) if ('undefined' === typeof _this6.fieldsMappedById[fieldInstance.__class__ + '-' + fieldInstance.__id__]) { _this6.fieldsMappedById[fieldInstance.__class__ + '-' + fieldInstance.__id__] = fieldInstance; _this6.fields.push(fieldInstance); } }); $(oldFields).not(_this6.fields).each(function(_, field) { field._trigger('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 ConstraintFactory = function ConstraintFactory(parsleyField, name, requirements, priority, isDomConstraint) { if (!/ParsleyField/.test(parsleyField.__class__)) throw new Error('ParsleyField or ParsleyFieldMultiple instance expected'); var validatorSpec = window.Parsley._validatorRegistry.validators[name]; var validator = new ParsleyValidator(validatorSpec); $.extend(this, { validator: validator, name: name, requirements: requirements, priority: priority || parsleyField.options[name + 'Priority'] || validator.priority, isDomConstraint: true === isDomConstraint }); this._parseRequirements(parsleyField.options); }; var capitalize = function capitalize(str) { var cap = str[0].toUpperCase(); return cap + str.slice(1); }; ConstraintFactory.prototype = { validate: function validate(value, instance) { var args = this.requirementList.slice(0); // Make copy args.unshift(value); args.push(instance); return this.validator.validate.apply(this.validator, args); }, _parseRequirements: function _parseRequirements(options) { var _this7 = this; this.requirementList = this.validator.parseRequirements(this.requirements, function(key) { return options[_this7.name + capitalize(key)]; }); } }; var ParsleyField = function ParsleyField(field, domOptions, options, parsleyFormInstance) { this.__class__ = 'ParsleyField'; this.__id__ = ParsleyUtils__default.generateID(); 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 = []; // Bind constraints this._bindConstraints(); }; var parsley_field__statusMapping = { pending: null, resolved: true, rejected: false }; ParsleyField.prototype = { // # Public API // Validate field and trigger some events for mainly `ParsleyUI` // @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)) { ParsleyUtils__default.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 `ParsleyUI` // @returns a promise that succeeds only when all validations do // or `undefined` if field is not in the given `group`. whenValidate: function whenValidate() { var _this8 = 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.refreshConstraints(); 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 this.whenValid({ force: force, value: this.value, _refreshed: true }).always(function() { _this8._reflowUI(); }).done(function() { _this8._trigger('success'); }).fail(function() { _this8._trigger('error'); }).always(function() { _this8._trigger('validated'); }); }, 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 ($.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)) { ParsleyUtils__default.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 _this9 = 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.refreshConstraints(); // 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 'parsley: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 = $.when.apply($, _toConsumableArray($.map(constraints, function(constraint) { return _this9._validateConstraint(value, constraint); }))); promises.push(promise); if (promise.state() === 'rejected') return false; // Interrupt processing if a group has already failed }); return $.when.apply($, promises); }, // @returns a promise _validateConstraint: function _validateConstraint(value, constraint) { var _this10 = 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 $.when(result).fail(function(errorMessage) { if (true === _this10.validationResult) _this10.validationResult = []; _this10.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); }, // Actualize options that could have change since previous validation // Re-bind accordingly constraints (could be some new, removed or updated) refreshConstraints: function refreshConstraints() { return this.actualizeOptions()._bindConstraints(); }, /** * 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 ConstraintFactory(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 (this.$element.hasClass('required') || this.$element.attr('required')) this.addConstraint('required', true, undefined, true); // html5 pattern if ('string' === typeof this.$element.attr('pattern')) this.addConstraint('pattern', this.$element.attr('pattern'), undefined, true); // range if ('undefined' !== typeof this.$element.attr('min') && 'undefined' !== typeof this.$element.attr('max')) this.addConstraint('range', [this.$element.attr('min'), this.$element.attr('max')], undefined, true); // HTML5 min else if ('undefined' !== typeof this.$element.attr('min')) this.addConstraint('min', this.$element.attr('min'), undefined, true); // HTML5 max else if ('undefined' !== typeof this.$element.attr('max')) this.addConstraint('max', this.$element.attr('max'), undefined, true); // length if ('undefined' !== typeof this.$element.attr('minlength') && 'undefined' !== typeof this.$element.attr('maxlength')) this.addConstraint('length', [this.$element.attr('minlength'), this.$element.attr('maxlength')], undefined, true); // HTML5 minlength else if ('undefined' !== typeof this.$element.attr('minlength')) this.addConstraint('minlength', this.$element.attr('minlength'), undefined, true); // HTML5 maxlength else if ('undefined' !== typeof this.$element.attr('maxlength')) this.addConstraint('maxlength', this.$element.attr('maxlength'), undefined, true); // html5 types var type = this.$element.attr('type'); if ('undefined' === typeof type) return this; // 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.attr('step'), base: this.$element.attr('min') || this.$element.attr('value') }], undefined, true); // Regular other HTML5 supported types } else if (/^(email|url|range)$/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) ParsleyUtils__default.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 = ParsleyUtils__default.trimString(value); return value; }, // 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 = ParsleyField; var ParsleyMultiple = function ParsleyMultiple() { this.__class__ = 'ParsleyFieldMultiple'; }; ParsleyMultiple.prototype = { // Add new `$element` sibling for multiple field addElement: function addElement($element) { this.$elements.push($element); return this; }, // See `ParsleyField.refreshConstraints()` refreshConstraints: function refreshConstraints() { var fieldConstraints; this.constraints = []; // Select multiple special treatment if (this.$element.is('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('ParsleyFieldMultiple').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 `ParsleyField.getValue()` getValue: function getValue() { // Value could be overriden in DOM if ('function' === typeof this.options.value) value = this.options.value(this); else if ('undefined' !== typeof this.options.value) return this.options.value; // Radio input case if (this.$element.is('input[type=radio]')) return this._findRelated().filter(':checked').val() || ''; // checkbox input case if (this.$element.is('input[type=checkbox]')) { var values = []; this._findRelated().filter(':checked').each(function() { values.push($(this).val()); }); return values; } // Select multiple case if (this.$element.is('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 ParsleyFactory = function ParsleyFactory(element, options, parsleyFormInstance) { 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 ParsleyForm 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); } 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 && 'ParsleyForm' !== parsleyFormInstance.__class__) throw new Error('Parent instance must be a ParsleyForm instance'); this.parent = parsleyFormInstance || window.Parsley; return this.init(options); }; ParsleyFactory.prototype = { init: function init(options) { this.__class__ = 'Parsley'; this.__version__ = '2.3.5'; this.__id__ = ParsleyUtils__default.generateID(); // Pre-compute options this._resetOptions(options); // A ParsleyForm instance is obviously a `
    ` element but also every node that is not an input and has the `data-parsley-validate` attribute if (this.$element.is('form') || ParsleyUtils__default.checkAttr(this.$element, this.options.namespace, 'validate') && !this.$element.is(this.options.inputs)) return this.bind('parsleyForm'); // Every other element is bound as a `ParsleyField` or `ParsleyFieldMultiple` return this.isMultiple() ? this.handleMultiple() : this.bind('parsleyField'); }, isMultiple: function isMultiple() { return this.$element.is('input[type=radio], input[type=checkbox]') || this.$element.is('select') && 'undefined' !== typeof this.$element.attr('multiple'); }, // Multiples fields are a real nightmare :( // Maybe some refactoring would be appreciated here... handleMultiple: function handleMultiple() { var _this11 = this; var name; var multiple; var parsleyMultipleInstance; // Handle multiple name if (this.options.multiple); // We already have our 'multiple' identifier else if ('undefined' !== typeof this.$element.attr('name') && this.$element.attr('name').length) this.options.multiple = name = this.$element.attr('name'); else if ('undefined' !== typeof this.$element.attr('id') && this.$element.attr('id').length) this.options.multiple = this.$element.attr('id'); // Special select multiple input if (this.$element.is('select') && 'undefined' !== typeof this.$element.attr('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) { ParsleyUtils__default.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 ('undefined' !== typeof name) { $('input[name="' + name + '"]').each(function(i, input) { if ($(input).is('input[type=radio], input[type=checkbox]')) $(input).attr(_this11.options.namespace + 'multiple', _this11.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('ParsleyFieldMultiple')) { parsleyMultipleInstance.addElement(this.$element); } break; } } // Create a secret ParsleyField instance for every multiple field. It will be stored in `data('ParsleyFieldMultiple')` // And will be useful later to access classic `ParsleyField` stuff while being in a `ParsleyFieldMultiple` instance this.bind('parsleyField', true); return parsleyMultipleInstance || this.bind('parsleyFieldMultiple'); }, // Return proper `ParsleyForm`, `ParsleyField` or `ParsleyFieldMultiple` bind: function bind(type, doNotStore) { var parsleyInstance; switch (type) { case 'parsleyForm': parsleyInstance = $.extend(new ParsleyForm(this.$element, this.domOptions, this.options), window.ParsleyExtend)._bindFields(); break; case 'parsleyField': parsleyInstance = $.extend(new parsley_field(this.$element, this.domOptions, this.options, this.parent), window.ParsleyExtend); break; case 'parsleyFieldMultiple': parsleyInstance = $.extend(new parsley_field(this.$element, this.domOptions, this.options, this.parent), new ParsleyMultiple(), window.ParsleyExtend)._init(); break; default: throw new Error(type + 'is not a supported Parsley type'); } if (this.options.multiple) ParsleyUtils__default.setAttr(this.$element, this.options.namespace, 'multiple', this.options.multiple); if ('undefined' !== typeof doNotStore) { this.$element.data('ParsleyFieldMultiple', 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 ParsleyForm or ParsleyField 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) { ParsleyUtils__default.warn('Parsley requires ES5 to run properly. Please include https://github.com/es-shims/es5-shim'); } // Inherit `on`, `off` & `trigger` to Parsley: var Parsley = $.extend(new ParsleyAbstract(), { $element: $(document), actualizeOptions: null, _resetOptions: null, Factory: ParsleyFactory, version: '2.3.5' }); // Supplement ParsleyField and Form with ParsleyAbstract // This way, the constructors will have access to those methods $.extend(parsley_field.prototype, ParsleyUI.Field, ParsleyAbstract.prototype); $.extend(ParsleyForm.prototype, ParsleyUI.Form, ParsleyAbstract.prototype); // Inherit actualizeOptions and _resetOptions: $.extend(ParsleyFactory.prototype, ParsleyAbstract.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) { ParsleyUtils__default.warn('You must bind Parsley on an existing element.'); return; } return new ParsleyFactory(this, options); }; // ### ParsleyField and ParsleyForm 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 = $.extend(ParsleyUtils__default.objectCreate(ParsleyDefaults), window.ParsleyConfig); window.ParsleyConfig = Parsley.options; // Old way of accessing global options // ### Globals window.Parsley = window.psly = Parsley; window.ParsleyUtils = ParsleyUtils__default; // ### Define methods that forward to the registry, and deprecate all access except through window.Parsley var registry = window.Parsley._validatorRegistry = new ParsleyValidatorRegistry(window.ParsleyConfig.validators, window.ParsleyConfig.i18n); window.ParsleyValidator = {}; $.each('setLocale addCatalog addMessage addMessages getErrorMessage formatMessage addValidator updateValidator removeValidator'.split(' '), function(i, method) { window.Parsley[method] = $.proxy(registry, method); window.ParsleyValidator[method] = function() { var _window$Parsley; ParsleyUtils__default.warnOnce('Accessing the method \'' + method + '\' through ParsleyValidator is deprecated. Simply call \'window.Parsley.' + method + '(...)\''); return (_window$Parsley = window.Parsley)[method].apply(_window$Parsley, arguments); }; }); // ### ParsleyUI // Deprecated global object window.Parsley.UI = ParsleyUI; window.ParsleyUI = { removeError: function removeError(instance, name, doNotUpdateClass) { var updateClass = true !== doNotUpdateClass; ParsleyUtils__default.warnOnce('Accessing ParsleyUI 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) { ParsleyUtils__default.warnOnce('Accessing ParsleyUI 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; ParsleyUtils__default.warnOnce('Accessing ParsleyUI 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 }); }; }); // Alleviate glaring Firefox bug https://bugzilla.mozilla.org/show_bug.cgi?id=1250521 // See also https://github.com/guillaumepotier/Parsley.js/issues/1068 if (/firefox/i.test(navigator.userAgent)) { $(document).on('change', 'select', function(evt) { $(evt.target).trigger('input'); }); } // ### 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() { ParsleyUtils__default.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 ParsleyForm)) 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 ParsleyForm)) 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 ParsleyForm; 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.attr('name') || instance.$element.attr('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 = {}; }); window.ParsleyExtend.addAsyncValidator = function() { ParsleyUtils.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'); var parsley = Parsley; return parsley; }); //# sourceMappingURL=parsley.js.map