text: /*!
  * Parsley.js
  * Version 2.8.1 - built Mon, Sep 10th 2018, 9:14 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
  //
  
  (function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('jquery')) :
    typeof define === 'function' && define.amd ? define(['jquery'], factory) :
    (factory(global.jQuery));
  }(this, (function ($) { 'use strict';
  
    function setup_ew () {
      beforeEach(function () {
        sinon.spy(window.console, 'warn');
        window.console.warn.expectedCallCount = 0;
      });
      afterEach(function () {
        expect(window.console.warn.callCount).to.be(window.console.warn.expectedCallCount);
        window.console.warn.restore();
      });
  
      global.expectWarning = function (fn) {
        var w = window.console.warn;
        expect(w.callCount).to.be(w.expectedCallCount);
        var result = fn.call();
        w.expectedCallCount++;
        expect(w.callCount).to.be(w.expectedCallCount);
        return result;
      };
    }
  
    function setup_dlc () {
      afterEach(function () {
        expect($('form input').length).to.be(0);
      });
      window.$ = $; // Export for testing purposes...
    }
  
    function setup () {
      beforeEach(function () {
        this.sandbox = global.sinon.sandbox.create();
        global.stub = this.sandbox.stub.bind(this.sandbox);
        global.spy = this.sandbox.spy.bind(this.sandbox);
      });
      afterEach(function () {
        delete global.stub;
        delete global.spy;
        this.sandbox.restore();
      });
      setup_ew();
      setup_dlc();
    }
  
    var mocha = {
      globals: ["$", "stub", "spy", "expect", "expectWarning"]
    };
  
    window.global = window;
    global.travis = false;
    global.mocha.setup('bdd');
  
    global.onload = function () {
      global.mocha.checkLeaks();
      global.mocha.globals(mocha.globals);
      global.mocha.run();
      setup();
    };
  
    function _typeof(obj) {
      if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
        _typeof = function (obj) {
          return typeof obj;
        };
      } else {
        _typeof = function (obj) {
          return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
        };
      }
  
      return _typeof(obj);
    }
  
    function _extends() {
      _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;
      };
  
      return _extends.apply(this, arguments);
    }
  
    function _slicedToArray(arr, i) {
      return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
    }
  
    function _toConsumableArray(arr) {
      return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();
    }
  
    function _arrayWithoutHoles(arr) {
      if (Array.isArray(arr)) {
        for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
  
        return arr2;
      }
    }
  
    function _arrayWithHoles(arr) {
      if (Array.isArray(arr)) return arr;
    }
  
    function _iterableToArray(iter) {
      if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
    }
  
    function _iterableToArrayLimit(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"] != null) _i["return"]();
        } finally {
          if (_d) throw _e;
        }
      }
  
      return _arr;
    }
  
    function _nonIterableSpread() {
      throw new TypeError("Invalid attempt to spread non-iterable instance");
    }
  
    function _nonIterableRest() {
      throw new TypeError("Invalid attempt to destructure non-iterable instance");
    }
  
    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);
          }),
              _parsed$map2 = _slicedToArray(_parsed$map, 4),
              _ = _parsed$map2[0],
              year = _parsed$map2[1],
              month = _parsed$map2[2],
              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 ".concat(requirementType, ": \"").concat(string, "\"");
        return converted;
      },
      namespaceEvents: function namespaceEvents(events, namespace) {
        events = this.trimString(events || '').split(/\s+/);
        if (!events[0]) return '';
        return $.map(events, function (evt) {
          return "".concat(evt, ".").concat(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;
  
        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("[".concat(this.options.namespace, "multiple=\"").concat(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 parseArguments = function parseArguments(type, args) {
      return args.map(Utils.parse[type]);
    }; // operatorToValidator returns a validating function for an operator function, applied to the given type
  
  
    var operatorToValidator = function operatorToValidator(type, operator) {
      return function (value) {
        for (var _len = arguments.length, requirementsAndInput = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
          requirementsAndInput[_key - 1] = arguments[_key];
        }
  
        requirementsAndInput.pop(); // Get rid of `input` argument
  
        if (!value) return true; // Builtin validators all accept empty strings, except `required` of course
  
        return operator.apply(void 0, [value].concat(_toConsumableArray(parseArguments(type, requirementsAndInput))));
      };
    };
  
    var comparisonOperator = function comparisonOperator(operator) {
      return {
        validateDate: operatorToValidator('date', operator),
        validateNumber: 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] : {},
                _ref$step = _ref.step,
                step = _ref$step === void 0 ? 'any' : _ref$step,
                _ref$base = _ref.base,
                base = _ref$base === void 0 ? 0 : _ref$base;
  
            var tester = typeTesters[type];
  
            if (!tester) {
              throw new Error('validator type `' + type + '` is not supported');
            }
  
            if (!value) return true; // Builtin validators all accept empty strings, except `required` of course
  
            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) {
            if (!value) return true; // Builtin validators all accept empty strings, except `required` of course
  
            return regexp.test(value);
          },
          requirementType: 'regexp',
          priority: 64
        },
        minlength: {
          validateString: function validateString(value, requirement) {
            if (!value) return true; // Builtin validators all accept empty strings, except `required` of course
  
            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) {
            if (!value) return true; // Builtin validators all accept empty strings, except `required` of course
  
            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: comparisonOperator(function (value, requirement) {
          return value >= requirement;
        }),
        max: comparisonOperator(function (value, requirement) {
          return value <= requirement;
        }),
        range: comparisonOperator(function (value, min, max) {
          return value >= min && value <= max;
        }),
        equalto: {
          validateString: function validateString(value, refOrValue) {
            if (!value) return true; // Builtin validators all accept empty strings, except `required` of course
  
            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 _this = this;
  
        this.$element.on('submit.Parsley', function (evt) {
          _this.onSubmitValidate(evt);
        });
        this.$element.on('click.Parsley', Utils._SubmitSelector, function (evt) {
          _this.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 _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
            message = _ref.message,
            assert = _ref.assert,
            _ref$updateClass = _ref.updateClass,
            updateClass = _ref$updateClass === void 0 ? true : _ref$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 _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
            message = _ref2.message,
            assert = _ref2.assert,
            _ref2$updateClass = _ref2.updateClass,
            updateClass = _ref2$updateClass === void 0 ? true : _ref2$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 _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
            _ref3$updateClass = _ref3.updateClass,
            updateClass = _ref3$updateClass === void 0 ? true : _ref3$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, _ref4) {
        var message = _ref4.message,
            assert = _ref4.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, _ref5) {
        var message = _ref5.message,
            assert = _ref5.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 _this2 = 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 () {
          _this2._validateIfNeeded();
        });else if (trigger = Utils.namespaceEvents(this.options.trigger, 'Parsley')) {
          $toBind.on(trigger, function (event) {
            _this2._validateIfNeeded(event);
          });
        }
      },
      _validateIfNeeded: function _validateIfNeeded(event) {
        var _this3 = 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 _this3.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 statusMapping = {
      pending: null,
      resolved: true,
      rejected: false
    };
    Form.prototype = {
      onSubmitValidate: function onSubmitValidate(event) {
        var _this = 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')) ; else {
          // Rejected or pending: cancel this submit
          event.stopImmediatePropagation();
          event.preventDefault();
          if ('pending' === promise.state()) promise.done(function () {
            _this._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 = Array.prototype.slice.call(arguments),
              group = _arguments[0],
              force = _arguments[1],
              event = _arguments[2];
  
          options = {
            group: group,
            force: force,
            event: event
          };
        }
  
        return statusMapping[this.whenValidate(options).state()];
      },
      whenValidate: function whenValidate() {
        var _this2 = this,
            _Utils$all$done$fail$;
  
        var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
            group = _ref.group,
            force = _ref.force,
            event = _ref.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`");
              _this2.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(_this2.fields, function (field) {
            return field.whenValidate({
              force: force,
              group: group
            });
          });
        });
  
        return (_Utils$all$done$fail$ = Utils.all(promises).done(function () {
          _this2._trigger('success');
        }).fail(function () {
          _this2.validationResult = false;
  
          _this2.focus();
  
          _this2._trigger('error');
        }).always(function () {
          _this2._trigger('validated');
        })).pipe.apply(_Utils$all$done$fail$, _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 = Array.prototype.slice.call(arguments),
              group = _arguments2[0],
              force = _arguments2[1];
  
          options = {
            group: group,
            force: force
          };
        }
  
        return 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 _this3 = this;
  
        var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
            group = _ref2.group,
            force = _ref2.force;
  
        this._refreshFields();
  
        var promises = this._withoutReactualizingFormOptions(function () {
          return $.map(_this3.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 _this4 = this;
  
        var oldFields = this.fields;
        this.fields = [];
        this.fieldsMappedById = {};
  
        this._withoutReactualizingFormOptions(function () {
          _this4.$element.find(_this4.options.inputs).not(_this4.options.excluded).each(function (_, element) {
            var fieldInstance = new window.Parsley.Factory(element, {}, _this4); // 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 _this4.fieldsMappedById[uniqueId]) {
                _this4.fieldsMappedById[uniqueId] = fieldInstance;
  
                _this4.fields.push(fieldInstance);
              }
            }
          });
  
          $.each(Utils.difference(oldFields, _this4.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 _this$validator;
  
        return (_this$validator = this.validator).validate.apply(_this$validator, [value].concat(_toConsumableArray(this.requirementList), [instance]));
      },
      _parseRequirements: function _parseRequirements(options) {
        var _this = this;
  
        this.requirementList = this.validator.parseRequirements(this.requirements, function (key) {
          return options[_this.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 statusMapping$1 = {
      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 _this$whenValid$alway,
            _this = this;
  
        var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
            force = _ref.force,
            group = _ref.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 (_this$whenValid$alway = this.whenValid({
          force: force,
          value: this.value,
          _refreshed: true
        }).always(function () {
          _this._reflowUI();
        }).done(function () {
          _this._trigger('success');
        }).fail(function () {
          _this._trigger('error');
        }).always(function () {
          _this._trigger('validated');
        })).pipe.apply(_this$whenValid$alway, _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 _arguments = Array.prototype.slice.call(arguments),
              force = _arguments[0],
              value = _arguments[1];
  
          options = {
            force: force,
            value: value
          };
        }
  
        var promise = this.whenValid(options);
        if (!promise) // Excluded via `group`
          return true;
        return statusMapping$1[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 _this2 = this;
  
        var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
            _ref2$force = _ref2.force,
            force = _ref2$force === void 0 ? false : _ref2$force,
            value = _ref2.value,
            group = _ref2.group,
            _refreshed = _ref2._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 _this2._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 _this3 = 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 (!(_this3.validationResult instanceof Array)) _this3.validationResult = [];
  
          _this3.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 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__ = '@@version';
        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 _this = this;
  
        var name;
        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(_this.options.namespace + 'multiple', _this.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 Field(this.element, this.domOptions, this.options, this.parent), new Base(), window.ParsleyExtend);
            break;
  
          case 'parsleyFieldMultiple':
            parsleyInstance = $.extend(new 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: '@@version'
    }); // Supplement Field and Form with Base
    // This way, the constructors will have access to those methods
  
  
    _extends(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 '".concat(method, "' through Validator is deprecated. Simply call 'window.Parsley.").concat(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 '".concat(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 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 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 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));
    };
  
    $.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,
    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');
  
    function InputEvent() {
      var _this = 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 (_this.isNativeEvent(evt)) {
            $(evt.target).trigger('input');
          }
        },
        misbehaves: function misbehaves(evt) {
          if (_this.isNativeEvent(evt)) {
            _this.behavesOk(evt);
  
            $(document).on('change.inputevent', evt.data.selector, _this.fakeInputEvent);
  
            _this.fakeInputEvent(evt);
          }
        },
        behavesOk: function behavesOk(evt) {
          if (_this.isNativeEvent(evt)) {
            $(document) // Simply unbinds the testing handler
            .off('input.inputevent', evt.data.selector, _this.behavesOk).off('change.inputevent', evt.data.selector, _this.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
            }, _this.behavesOk).on('change.inputevent', selector, {
              selector: selector
            }, _this.misbehaves);
          }
        },
        uninstall: function uninstall() {
          delete globals.inputEventPatched;
          $(document).off('.inputevent');
        }
      });
    }
    var inputevent = new InputEvent();
  
    inputevent.install();
  
    describe('Base', () => {
      it('should provide a actualizeOptions() method', () => {
        $('body').append('<input type="email" data-parsley-pattern="[A-F][0-9]{5}" data-parsley-required id="element" />');
        var parsleyField = $('#element').parsley({
          foo: 'bar'
        });
        expect(parsleyField.options.pattern).to.eql('[A-F][0-9]{5}');
        expect(parsleyField.options.required).to.eql('');
        $('#element').removeAttr('data-parsley-pattern');
        parsleyField.actualizeOptions();
        expect(parsleyField.options.pattern).to.be(undefined);
        expect(parsleyField.options.required).to.eql('');
      });
      it('should use reset() on field', () => {
        $('body').append('<input type="email" data-parsley-pattern="[A-F][0-9]{5}" data-parsley-required id="element" />');
        var parsleyField = $('#element').parsley();
        parsleyField.validate();
        expect($('#parsley-id-' + parsleyField.__id__ + ' li').length).to.be(1);
        parsleyField.reset();
        expect($('#parsley-id-' + parsleyField.__id__ + ' li').length).to.be(0);
      });
      it('should use reset() on form', done => {
        $('body').append('<form id="element">' + '<input id="field1" type="text" required />' + '<div id="field2"></div>' + '<textarea id="field2"></textarea>' + '</form>');
        var parsleyForm = $('#element').parsley();
        parsleyForm.validate();
        expect($('#parsley-id-' + $('#field1').psly().__id__ + ' li').length).to.be(1);
        $('#element').parsley().on('form:reset', () => {
          done();
        });
        parsleyForm.reset();
        expect($('#parsley-id-' + $('#field1').psly().__id__ + ' li').length).to.be(0);
      });
      it('should use destroy() on field', done => {
        $('body').append('<input type="email" data-parsley-pattern="[A-F][0-9]{5}" data-parsley-required id="element" />');
        var parsleyField = $('#element').parsley();
        $('#element').parsley().on('field:destroy', () => {
          done();
        });
        expect($('#element').data('Parsley')).to.have.key('__class__');
        expect($('#element').data('Parsley').__class__).to.be('Field');
        parsleyField.destroy();
        expect($('#element').data('Parsley')).to.be(undefined);
      });
      it('should use destroy() on form', done => {
        $('body').append('<form id="element">' + '<input id="field1" type="text"/>' + '<div id="field2"></div>' + '<textarea id="field2"></textarea>' + '</form>');
        var parsleyForm = $('#element').parsley();
        var fieldEventsCount = 0;
        var formEventsCount = 0; // Test that a subscribed field event on parent form would be triggered by fields too
        // Here we only have field1 and field2 as valid parsley fields
  
        $('#element').parsley().on('field:destroy', () => {
          fieldEventsCount++;
        });
        $('#element').parsley().on('form:destroy', () => {
          formEventsCount++;
        });
        expect($('#element').data('Parsley')).to.have.key('__class__');
        expect($('#element').data('Parsley').__class__).to.be('Form');
        expect($('#field1').data('Parsley')).to.have.key('__class__');
        expect($('#field1').data('Parsley').__class__).to.be('Field');
        parsleyForm.destroy();
        expect(fieldEventsCount).to.be(2);
        expect(formEventsCount).to.be(1); // we should never enter here since parsley form instance is destroyed
  
        Parsley.on('form:validate', () => {
          expect(true).to.be(false);
        }); // test that a submit event does not trigger parsley validation anymore
  
        $('#element').on('submit', e => {
          e.preventDefault();
          expect($('#element').data('Parsley')).to.be(undefined);
          expect($('#field1').data('Parsley')).to.be(undefined);
          Parsley.off('form:validate');
          done();
        });
        $('#element').submit();
      });
      afterEach(() => {
        $('#element, .parsley-errors-list').remove();
      });
    });
  
    describe('Field', () => {
      it('should be a function', () => {
        expect(Field).to.be.a('function');
      });
      it('should throw an error if no parsleyInstance given', () => {
        expect(Field).to.throwException();
      });
      it('should properly bind DOM constraints', () => {
        $('body').append('<input type="text" id="element" data-parsley-required />');
        var parsleyField = $('#element').parsley();
        expect(parsleyField.constraints.length).to.be(1);
        expect(parsleyField.constraints[0].name).to.be('required');
        expect(parsleyField.constraints[0].isDomConstraint).to.be(true);
      });
      it('should properly bind HTML DOM supported constraints', () => {
        $('body').append('<input type="email" id="element" />');
        var parsleyField = $('#element').parsley();
        expect(parsleyField.constraints.length).to.be(1);
        expect(parsleyField.constraints[0].name).to.be('type');
        expect(parsleyField.constraints[0].isDomConstraint).to.be(true);
      });
      it('should ignore unknown types', () => {
        $('body').append('<input type="" id="element" />');
        var parsleyField = $('#element').parsley();
        expect(parsleyField.constraints.length).to.be(0);
      });
      it('should ignore mistyped types', () => {
        $('body').append('<input type="    email" id="element" />');
        var parsleyField = $('#element').parsley();
        expect(parsleyField.constraints.length).to.be(0);
      });
      it('should have a proper addConstraint() javascript method', () => {
        $('body').append('<input type="text" id="element" />');
        var parsleyField = $('#element').parsley().addConstraint('required', true);
        expect(parsleyField.constraints.length).to.be(1);
        expect(parsleyField.constraints[0].name).to.be('required');
        expect(parsleyField.constraints[0].requirements).to.be(true);
        expect(parsleyField.constraints[0].priority).to.be(512);
        expect(parsleyField.constraints[0].isDomConstraint).to.be(false); // trying to add an existing constraint result in an update
  
        parsleyField.addConstraint('required', false, 64);
        expect(parsleyField.constraints.length).to.be(1);
        expect(parsleyField.constraints[0].name).to.be('required');
        expect(parsleyField.constraints[0].requirements).to.be(false);
        expect(parsleyField.constraints[0].priority).to.be(64);
      });
      it('should have a proper updateConstraint() javascript method', () => {
        $('body').append('<input type="text" id="element" />');
        var parsleyField = $('#element').parsley().addConstraint('required', true); // same as above test where addConstraint resulted in an updateConstraint
  
        parsleyField.updateConstraint('required', false, 64);
        expect(parsleyField.constraints.length).to.be(1);
        expect(parsleyField.constraints[0].name).to.be('required');
        expect(parsleyField.constraints[0].requirements).to.be(false);
        expect(parsleyField.constraints[0].priority).to.be(64);
      });
      it('should have a proper removeConstraint() javascript method', () => {
        $('body').append('<input type="text" id="element" />');
        var parsleyField = $('#element').parsley().addConstraint('required', true).addConstraint('notblank', true).removeConstraint('required');
        expect(parsleyField.constraints.length).to.be(1);
        expect(parsleyField.constraints[0].name).to.be('notblank');
        expect(parsleyField._isRequired()).to.be(false);
      });
      it('should return true for fields without constraints', () => {
        $('body').append('<input type="text" id="element" value="hola" data-parsley-minlength="5" />');
        var parsleyField = $('#element').parsley(); // Start with some validation errors:
  
        expect(parsleyField.isValid()).to.eql(false); // The remove constraint and check result:
  
        $('#element').removeAttr('data-parsley-minlength');
        expect(parsleyField.isValid()).to.be(true);
      });
      it('should properly bind HTML5 supported constraints', () => {
        $('body').append('<input type="email" pattern="\\w+" id="element" required min="5" max="100" minlength="1" maxlength="3" />');
        var parsleyField = $('#element').parsley(); // 5 validators: type=email, pattern, required, (min+max => range) and (minlength+maxlength => length)
  
        expect(parsleyField.constraints.length).to.be(5);
        $('#element').removeAttr('min'); // still 5 validators, with max instead of range now
  
        expect(parsleyField.actualizeOptions().constraints.length).to.be(5);
        $('#element').removeAttr('minlength'); // still 5 validators, with maxlength instead of length now
  
        expect(parsleyField.actualizeOptions().constraints.length).to.be(5);
      });
      it('should properly bind HTML5 date inputs', () => {
        // Uses RFC 3339/ISO 8601 format YYYY-MM-DD
        $('body').append('<input type="date" id="element" max="2000-01-02" min="1999-01-02" value="1998-12-30" />');
        var parsleyField = $('#element').parsley();
        expect(parsleyField.constraints.length).to.be(2); // Type=Date, Range
  
        expect(parsleyField.isValid()).to.be(false);
        expect(parsleyField.isValid({
          value: '1999-02-03'
        })).to.be(true);
        $('#element').removeAttr('min');
        expect(parsleyField.isValid()).to.be(true);
        expect(parsleyField.constraints.length).to.be(2); // Type=Date, Max
  
        $('#element').val('2001-03-03');
        expect(parsleyField.isValid()).to.be(false);
        $('#element').val('2001/01/01').removeAttr('max');
        expect(parsleyField.isValid()).to.be(false); // Still invalid because of format
  
        expect(parsleyField.constraints.length).to.be(1); // Type=Date
      });
  
      var itShouldFollowSpecForNumber = (step, min, initial, value, valid) => {
        var attrs = [step ? `step="${step}" ` : '', min ? `min="${min}" ` : '', initial ? `value="${initial}" ` : ''].join('');
        it('should follow HTML5 spec to validate "number" type ' + (attrs ? `with attributes ${attrs}` : '') + `by ${valid ? 'accepting' : 'rejecting'} "${value}"`, () => {
          var $input = $(`<input type="number" ${attrs}>`);
          expect($input.parsley().isValid({
            value
          })).to.be(valid);
        });
      };
  
      var checks = [// step | min | initial | good        | bad values
      //      |     |  value  | values      |
      // ---- | --- | ------- | ----------- | ----------
      "       |     |         | 1, -2, 4.0  | 1.1, 4.       ", "  any  |     |         | -2, 4.3, .1 | 4., hi, ., 1. ", "  AnY  |     |         | .1e+2, -.2  | 4e, 4e-, .e+2 ", "  0.1  |     |         | -2, 4.3     | 4.03          ", "  0.01 |     |         | 1.11        | 1.111         ", // Bug #1056
      "       | 0.3 |         | 4.3         | -2, -2.7, 4.0 ", "       |     |   0.3   | -2.7, 4.3   | -2, -2.3, 4.0 ", "  0.4  | 0.3 |         | 1.1         | -2.9, 1.6, 1.8", "  0.4  |     |   0.3   | -2.9, 1.1   | 1.6, 1.8      ", "  0.4  | 0.3 |   0.5   | 1.1         | -2.9, 1.6, 1.8"];
      $.each(checks, (_, check) => {
        var trim = val => {
          return val.trim();
        };
  
        var [step, min, initial, ...goodAndBad] = check.split('|').map(trim);
        var xs = goodAndBad.map(values => {
          return values.split(',').map(trim);
        });
        var [good, bad] = xs;
        $.each(good, (_, val) => {
          itShouldFollowSpecForNumber(step, min, initial, val, true);
          itShouldFollowSpecForNumber(step, min, initial, val.trim() + '0', true);
        });
        $.each(bad, (_, val) => {
          itShouldFollowSpecForNumber(step, min, initial, val, false);
        });
      }); // 'any' must be exact match
  
      itShouldFollowSpecForNumber('   any    ', '', '', '4.2', false); // min / initial should be auto-trimmed
  
      itShouldFollowSpecForNumber('0.2', '   0.3    ', '', '0.3', true); // scientific notation
  
      itShouldFollowSpecForNumber('', '0.3', '', '43e-1', true); // commas are not accepted in the spec
  
      itShouldFollowSpecForNumber('any', '', '', '1,000', false);
  
      var itShouldFollowSpecForNumber = (step, min, initial, value, valid) => {
        var attrs = [step ? `step="${step}" ` : '', min ? `min="${min}" ` : '', initial ? `value="${initial}" ` : ''].join('');
        it('should follow HTML5 spec to validate "number" type ' + (attrs ? `with attributes ${attrs}` : '') + `by ${valid ? 'accepting' : 'rejecting'} "${value}"`, () => {
          var $input = $(`<input type="number" ${attrs}>`);
          expect($input.parsley().isValid({
            value
          })).to.be(valid);
        });
      };
  
      it('should have a default step of "any" for data-parsley-type="number"', () => {
        var $input = $(`<input data-parsley-type="number" value="4.444">`);
        expect($input.parsley().isValid()).to.be(true);
        $input.attr('data-parsley-type-step', '1');
        expect($input.parsley().isValid()).to.be(false);
      });
      it('should valid simple validator', () => {
        $('body').append('<input type="text" id="element" value="" />');
        var parsleyField = $('#element').parsley().addConstraint('required', true);
        expect(parsleyField.isValid()).to.be(false);
        $('#element').val('foo');
        expect(parsleyField.isValid()).to.be(true);
      });
      it('should valid more complex `type` validator', () => {
        $('body').append('<input type="text" id="element" value="foo" />');
        var parsleyField = $('#element').parsley().addConstraint('type', 'email');
        expect(parsleyField.isValid()).to.be(false);
        $('#element').val('foo');
        expect(parsleyField.isValid()).to.be(false);
        $('#element').val('foo@bar.baz');
        expect(parsleyField.isValid()).to.be(true);
      });
      it('should valid most complex Callback() validator', () => {
        $('body').append('<input type="text" id="element" value="" />');
        window.Parsley.addValidator('ismultiple', (value, multiple) => {
          if (!isNaN(parseFloat(value)) && isFinite(value)) return !(Number(value) % multiple);
          return false;
        }, 512);
        var parsleyField = $('#element').parsley().addConstraint('ismultiple', 2);
        expect(parsleyField.isValid()).to.eql(true);
        $('#element').val('1');
        expect(parsleyField.isValid()).to.be(false);
        $('#element').val('2');
        expect(parsleyField.isValid()).to.be(true);
        parsleyField.updateConstraint('ismultiple', 3);
        expect(parsleyField.isValid()).to.be(false);
        $('#element').val('9');
        expect(parsleyField.isValid()).to.be(true);
        window.Parsley.removeValidator('ismultiple');
      });
      it('should properly compute constraints on each validation', () => {
        $('body').append('<input type="email" data-parsley-required id="element" />');
        window.Parsley.addValidator('foobazer', value => {
          return 'foobar' === value;
        }, 2);
        window.Parsley.addValidator('ismultiple', (value, multiple) => {
          if (!isNaN(parseFloat(value)) && isFinite(value)) return !(Number(value) % multiple);
          return false;
        }, 512);
        var parsleyField = $('#element').parsley().addConstraint('ismultiple', 4).addConstraint('foobazer', true);
        parsleyField.refresh();
        expect(parsleyField.constraints.length).to.be(4);
        $('#element').removeAttr('data-parsley-required');
        parsleyField.refresh();
        expect(parsleyField.constraints.length).to.be(3);
        parsleyField.removeConstraint('ismultiple').refresh();
        expect(parsleyField.constraints.length).to.be(2);
        window.Parsley.removeValidator('foobazer');
        window.Parsley.removeValidator('ismultiple');
      });
      it('should handle constraints priorities on validation', () => {
        $('body').append('<input type="email" pattern="[A-F][0-9]{5}" required id="element" />');
        var parsleyField = $('#element').parsley();
        expect(parsleyField.isValid()).to.be(false);
        expect(parsleyField.validationResult.length).to.be(1);
        expect(parsleyField.validationResult[0].assert.name).to.be('required');
        $('#element').val('foo');
        expect(parsleyField.isValid()).to.be(false);
        expect(parsleyField.validationResult.length).to.be(1);
        expect(parsleyField.validationResult[0].assert.name).to.be('type');
        $('#element').val('foo@bar.baz');
        expect(parsleyField.isValid()).to.be(false);
        expect(parsleyField.validationResult.length).to.be(1);
        expect(parsleyField.validationResult[0].assert.name).to.be('pattern');
      });
      it('should handle all violations if `priorityEnabled` is set to false', () => {
        $('body').append('<input type="email" pattern="[A-F][0-9]{5}" value="x" id="element" />');
        var parsleyField = $('#element').parsley({
          priorityEnabled: false
        });
        expect(parsleyField.isValid()).to.be(false);
        expect(parsleyField.validationResult.length).to.be(2);
      });
      it('should trigger field:validate event', done => {
        $('body').append('<input type="email" pattern="[A-F][0-9]{5}" required id="element" />');
        $('#element').psly().on('field:validate', function () {
          // we are before validation!
          expect(this.validationResult).to.be(true);
          done();
        }).validate();
      });
      it('should trigger field:validated event', done => {
        $('body').append('<input type="email" pattern="[A-F][0-9]{5}" required id="element" />');
        $('#element').psly().on('field:validated', function () {
          // we are after validation!
          expect(this.validationResult.length).to.be(1);
          done();
        }).validate();
      });
      it('should trigger field:error event', done => {
        $('body').append('<input type="email" pattern="[A-F][0-9]{5}" required id="element" />');
        $('#element').psly().on('field:error', function () {
          expect(this.validationResult.length).to.be(1);
          done();
        }).validate();
      });
      it('should trigger parsley:field:success event', done => {
        $('body').append('<input type="email" required id="element" value="foo@bar.baz" />');
        $('#element').psly().on('field:success', function () {
          expect(this.validationResult).to.be(true);
          done();
        }).validate();
      });
      it('should have the validationResult be changeable', () => {
        var submitted = false;
        $('<form id="element"><input/></form>').appendTo('body').parsley().on('field:success', field => {
          field.validationResult = false;
        }).on('field:error', field => {
          field.validationResult = true;
        }).on('form:submit', form => {
          submitted = true;
          return false;
        });
        $('#element').submit();
        expect(submitted).to.be(false);
        $('#element input').attr('required', true);
        $('#element').submit();
        expect(submitted).to.be(true);
      });
      it('should allow `this.value` alteration with field:validate.parsley event', () => {
        $('body').append('<input type="email" required id="element" value="foo@bar.baz" />');
        expect($('#element').parsley().validate()).to.be(true);
        $('#element').parsley().on('field:validate', function () {
          this.value = '';
        });
        expect($('#element').parsley().validate()).not.to.be(true);
      });
      it('should have validateIfEmpty / force:true to validate empty fields', () => {
        var calls = [];
        window.Parsley.addValidator('checkevenwhenempty', value => {
          calls.push(value);
          return 'foo@bar.com' === value;
        }, 2);
        $('body').append('<input type="email" data-parsley-checkevenwhenempty id="element" />');
        expect($('#element').psly().isValid()).to.be.eql(true);
        expect(calls).to.be.eql([]);
        expect($('#element').psly().isValid({
          force: true
        })).to.be.eql(false);
        expect(calls).to.be.eql(['']);
        $('#element').attr('data-parsley-validate-if-empty', '');
        expect($('#element').psly().isValid()).to.be.eql(false);
        expect(calls).to.be.eql(['', '']);
        $('#element').val('foo@bar.com');
        expect($('#element').psly().isValid()).to.be.eql(true);
        expect(calls).to.be.eql(['', '', 'foo@bar.com']);
        window.Parsley.removeValidator('checkevenwhenempty');
      });
      it('should allow passing a specific value to `isValid` method', () => {
        expect($('<input type="email" value="foo">').parsley().isValid()).to.be(false);
        expect($('<input type="email" value="foo">').parsley().isValid({
          value: ''
        })).to.be(true);
        expectWarning(() => {
          expect($('<input type="email" value="foo">').parsley().isValid(true, '')).to.be(true);
        });
      });
      it('should have a whitespace="squish" option', () => {
        $('body').append('<input type="text" id="element" value=" foo    bar " />');
        expect($('#element').parsley().getValue()).to.be(' foo    bar ');
        $('#element').attr('data-parsley-whitespace', 'squish').parsley().actualizeOptions();
        expect($('#element').parsley().getValue()).to.be('foo bar');
      });
      it('should have a whitespace="trim" option', () => {
        $('body').append('<input type="text" id="element" value=" foo " />');
        expect($('#element').parsley().getValue()).to.be(' foo ');
        $('#element').attr('data-parsley-whitespace', 'trim').parsley().actualizeOptions();
        expect($('#element').parsley().getValue()).to.be('foo');
      });
      it('should have a trim-value option', () => {
        $('body').append('<input type="text" id="element" value=" foo " />');
        expect($('#element').parsley().getValue()).to.be(' foo ');
        $('#element').attr('data-parsley-trim-value', true).parsley().actualizeOptions();
        expectWarning(() => {
          expect($('#element').parsley().getValue()).to.be('foo');
        });
      });
      it('should delay validation if debounce option specified', done => {
        // Use a an initially valid input. Any success event will be a sign that validation completed
        // and treated as a failure. We will make the field invalid before delayed validation occurs,
        // so only error event will be a test success.
        $('body').append('<input data-parsley-debounce="100" value="x" required data-parsley-trigger="validatenow" id="element"/>');
        $('#element').parsley().on('field:error', () => done()).on('field:success', () => expect().fail('validation should not happen yet')); // Trigger validation immediately
  
        $('#element').trigger('validatenow'); // and a bit later, which should reset the counter
  
        setTimeout(() => $('#element').trigger('validatenow'), 50); // Set final condition after a delay longer than our debounce, less than 50+debounce
  
        setTimeout(() => $('#element').val(''), 140);
      });
      it('should inherit options from the form, even if the form is bound after', () => {
        $('body').append('<form id="element" data-parsley-required>' + '<input type="text"/></form>');
        var psly = $('#element input').parsley();
        expect(psly.isValid()).not.to.be(false);
        $('#element').parsley();
        expect(psly.isValid()).to.be(false);
      });
      it('should have options that can be set easily', () => {
        var psly = $('<input type="text"/>').parsley();
        psly.options.required = true;
        expect(psly.isValid()).to.be(false);
      });
      it('should have a value option', () => {
        $('body').append('<input type="text" id="element"/>');
        expect($('#element').parsley({
          value: 'foo'
        }).getValue()).to.be('foo');
      });
      it('should accept a function as value option', () => {
        $('body').append('<input type="text" id="element"/>');
        var str = 'fo';
        var parsley = $('#element').parsley({
          value: () => {
            return str = str + 'o';
          }
        });
        expect(parsley.getValue()).to.be('foo');
        expect(parsley.getValue()).to.be('fooo');
      });
      it('should properly handle null or undefined values', () => {
        $('body').append('<input type="text" id="element" required value/>');
        expect($('#element').parsley().isValid()).to.be(false);
      });
      afterEach(() => {
        $('#element, .parsley-errors-list').remove();
      });
    });
  
    describe('Form', () => {
      it('should be a function', () => {
        expect(Form).to.be.a('function');
      });
      it('should bind parsleyFields children', () => {
        $('body').append('<form id="element">' + '<input id="field1" type="text"/>' + '<div id="field2"></div>' + '<textarea id="field2"></textarea>' + '</form>');
        var parsleyForm = $('#element').parsley();
        expect(parsleyForm.fields.length).to.be(2);
      });
      it('should bind parsleyFields children, and not excluded ones', () => {
        $('body').append('<form id="element">' + '<input id="field1" type="text"/>' + '<div id="field2"></div>' + '<textarea id="field2"></textarea>' + '<div data-parsley-validate></div>' + // Form, not a valid child
        '<input id="field3" disabled />' + // Disabled, excluded by custom options below
        '<input id="field-excluded" data-parsley-excluded="true" />' + // Excluded with 'excluded' option
        '<input type="submit"/>' + // Excluded field, not valid
        '</form>');
        var parsleyForm = $('#element').parsley({
          excluded: '[disabled], input[type=button], input[type=submit], input[type=reset]'
        });
        expect(parsleyForm.fields.length).to.be(2);
      });
      it('should properly reset excluded', () => {
        $('body').append('<form id="element">' + '<input id="field" required class="foo"/>' + '</form>');
        var parsleyForm = $('#element').parsley();
        parsleyForm.validate();
        expect($('.parsley-error').length).to.be(1);
        parsleyForm.options.excluded = ".foo";
        parsleyForm.validate();
        expect($('.parsley-error').length).to.be(0);
      });
      it('should properly bind options for form and children fields', () => {
        $('body').append('<form id="element" data-parsley-trigger="change">' + '<input id="field1" type="text" data-parsley-required="true" />' + '<div id="field2"></div>' + '<textarea id="field3" data-parsley-notblank="true"></textarea>' + '</form>');
        var parsleyForm = $('#element').parsley();
        expect(parsleyForm.fields.length).to.be(2);
        expect($('#field1').parsley().options.trigger).to.be('change');
        expect($('#field1').parsley().options.required).to.eql(true);
        expect($('#field1').parsley().options.notblank).to.be(undefined);
        expect($('#field3').parsley().options.notblank).to.eql(true);
        expect($('#field3').parsley().options.required).to.be(undefined);
      });
      it('should properly store validation state after `validate()`', () => {
        $('body').append('<form id="element" data-parsley-trigger="change">' + '<input id="field1" type="text" data-parsley-required="true" />' + '<div id="field2"></div>' + '<textarea id="field3" data-parsley-notblank="true"></textarea>' + '</form>');
        var parsleyForm = $('#element').parsley();
        parsleyForm.validate();
        expect(parsleyForm.validationResult).to.be(false);
        $('#field1').val('foo');
        $('#field3').val('foo');
        expect(parsleyForm.validate()).to.be(true);
      });
      it('should handle group validation', () => {
        $('body').append('<form id="element">' + '<input id="field1" type="text" data-parsley-group="foo" data-parsley-required="true" />' + '<div id="field2"></div>' + '<textarea id="field3" data-parsley-required="true"></textarea>' + '</form>');
        var parsleyForm = $('#element').parsley();
        expect(parsleyForm.isValid()).to.be(false);
        $('#field1').val('value');
        expect(parsleyForm.isValid()).to.be(false);
        expect(parsleyForm.isValid({
          group: 'foo'
        })).to.be(true);
        $('#field3').attr('data-parsley-group', 'bar');
        expectWarning(() => {
          expect(parsleyForm.isValid('bar')).to.be(false);
        });
      });
      it('should handle group validation with controls with multiple group names', () => {
        $('body').append('<form id="element">' + '<input id="field1" type="text" data-parsley-group=\'["foo", "bar"]\' data-parsley-required="true" />' + '<input id="field2" type="text" data-parsley-group=\'["bar", "baz"]\' data-parsley-required="true" />' + '<textarea id="field3" data-parsley-group=\'["baz", "qux"]\' data-parsley-required="true"></textarea>' + '</form>');
        var parsleyForm = $('#element').parsley();
        expect(parsleyForm.isValid()).to.be(false);
        $('#field1').val('value');
        $('#field2').val('value');
        expect(parsleyForm.isValid()).to.be(false); // group name only on one required field, with value
  
        expect(parsleyForm.isValid('foo')).to.be(true); // group name on multiple required fields, all with values
  
        expect(parsleyForm.isValid('bar')).to.be(true); // group name on multiple required fields, one missing a value
  
        expect(parsleyForm.isValid('baz')).to.be(false); // group name on single required field, without value
  
        expect(parsleyForm.isValid('qux')).to.be(false);
      });
      it('should send submit button values, even for async validations', () => {
        var deferred = null;
        window.Parsley.addValidator('custom', () => {
          deferred = $.Deferred();
          return deferred.promise();
        });
        $('body').append('<form id="element">' + '<input id="field1" type="text" name="nick" data-parsley-custom data-parsley-required />' + '<div id="field2" name="comment"></div>' + '<input type="submit" name="foo" value="bar" />' + '<input type="submit" name="foo" value="other" />' + '<button name="foo" value="but">ok</button>' + '</form>');
        var parsleyForm = $('#element').parsley();
        $('#element input:last').click(); // Form should not be submitted at this point, coz field is required
  
        expect(deferred).to.be(null);
        $('#field1').val('something');
        var values = [];
        $('#element').on('submit', evt => {
          expect(evt.parsley).to.be(true);
          values.push($('form input[type!=submit][name="foo"]').val());
          evt.preventDefault();
        });
        $('#element button').click();
        expect(values).to.eql([]);
        deferred.resolve();
        expect(values).to.eql(['but']);
        $('#element input[value="other"]').click();
        deferred.resolve();
        expect(values).to.eql(['but', 'other']);
        $('#element').submit(); // Similar to pressing 'enter'
  
        deferred.resolve();
        expect(values).to.eql(['but', 'other', 'bar']);
        window.Parsley.removeValidator('custom');
      });
      it('should not validate when triggered by a button with formnovalidate', () => {
        var $form = $('<form id="element"><input type="string" required /><input type="submit" formnovalidate /><form>').appendTo($('body'));
        $form.on('submit', e => {
          e.preventDefault();
        });
        var callbacks = [];
        $.each(['validate', 'error', 'success', 'validated', 'submit'], (i, cb) => {
          $form.parsley().on('form:' + cb, () => {
            callbacks.push(cb);
          });
        });
        $form.parsley();
        $form.find('input[type=submit]').click();
        expect(callbacks.join()).to.be('');
      });
      it('should have a force option for validate and isValid methods', () => {
        $('body').append('<form id="element">' + '<input id="field1" type="email" />' + '<input id="field3" data-parsley-notblank="true" />' + '</form>');
        expect($('#element').parsley().isValid()).to.be(true);
        expect($('#element').parsley().isValid({
          force: true
        })).to.be(false);
        expect($('#element').parsley().validate()).to.be(true);
        expectWarning(() => {
          expect($('#element').parsley().validate(undefined, true)).to.be(false);
        });
      });
      it('should properly bind dynamically added fields', () => {
        $('body').append('<form id="element" data-parsley-trigger="change"></form>');
        $('#element').append('<input type="email" id="email" required />');
        var fieldInstance = $('#email').psly();
        expect(fieldInstance.__class__).to.be('Field');
        var formInstance = $('#element').psly(); // form corectly have its field, and field have finaly its parent form
  
        expect(formInstance.fields[0].$element.attr('id')).to.be('email');
        expect(fieldInstance.parent.__class__).to.be('Form');
      });
      it('should fire the right callbacks in the right order', () => {
        var $form = $('<form id="element"><input type="string" required /><form>').appendTo($('body'));
        $form.on('submit', e => {
          e.preventDefault();
        });
        var callbacks = [];
        $.each(['validate', 'error', 'success', 'validated', 'submit'], (i, cb) => {
          $form.parsley().on('form:' + cb, () => {
            callbacks.push(cb);
          });
        });
        $form.parsley();
        $form.submit();
        $form.find('input').val('Hello');
        $form.submit();
        expect(callbacks.join()).to.be('validate,error,validated,validate,success,validated,submit');
      });
      it('should fire "form:validate.parsley" to give the opportunity for changes before validation occurs', () => {
        var $form = $('<form id="element"><input type="string" required /><form>').appendTo($('body'));
        $form.parsley().on('form:validate', function () {
          this.$element.find('input').remove();
        });
        expect($form.parsley().validate()).to.be(true);
      });
      it('should stop event propagation on form submit', done => {
        $('body').append('<form id="element"><input type="text" required/></form>');
        var parsleyInstance = $('#element').parsley().on('form:validated', () => {
          done();
        });
        $('#element').on('submit', () => {
          // It sould never pass here!
          expect(true).to.be(false);
        }).submit();
      });
      it('should have the validationResult be changeable', () => {
        var submitted = false;
        $('<form id="element"></form>').appendTo('body').parsley().on('form:success', form => {
          form.validationResult = false;
        }).on('form:error', form => {
          form.validationResult = true;
        }).on('form:submit', form => {
          submitted = true;
          return false;
        });
        $('#element').submit();
        expect(submitted).to.be(false);
        $('#element').append('<input required>').submit();
        expect(submitted).to.be(true);
      });
      it('should fire form:submit.event and be interruptable when validated', done => {
        $('<form id="element"></form>').appendTo('body').parsley().on('form:submit', () => {
          done();
          return false;
        });
        $('#element').submit();
      });
      it('should deprecate interruptions with submitEvent.preventDefault()', () => {
        expectWarning(() => {
          $('<form id="element"></form>').appendTo('body').parsley().on('form:validate', form => {
            form.submitEvent.preventDefault();
          }).on('form:submit', form => {
            throw new Error('Form should not have been submitted');
          });
          $('#element').submit();
        });
      });
      it('should fire field:reset event if fields are removed or excluded', () => {
        var parsleyInstance;
        var steps = [];
        var step = 'init';
        var parsleyForm = $('<form id="element"><input type="text" required></form>').appendTo('body').parsley().on('field:reset', function () {
          steps.push('form: ' + step);
          expect(this).to.be(parsleyInstance);
        });
        parsleyInstance = $('#element input').parsley().on('field:reset', function () {
          steps.push('field: ' + step);
          expect(this).to.be(parsleyInstance);
        });
        parsleyForm.validate();
        parsleyForm.validate();
        parsleyForm.options.excluded = '[required]';
        step = 'excluded';
        parsleyForm.validate();
        parsleyForm.validate();
        parsleyForm.options.excluded = '';
        step = 'not excluded';
        parsleyForm.validate();
        parsleyForm.validate();
        var $i = $('#element input').detach();
        step = 'detached';
        parsleyForm.validate();
        parsleyForm.validate();
        $i.appendTo('form');
        step = 'reattached';
        parsleyForm.validate();
        parsleyForm.validate();
        $i.remove();
        step = 'removed';
        parsleyForm.validate();
        parsleyForm.validate();
        expect(steps).to.eql(['field: excluded', 'form: excluded', 'field: detached', 'form: detached', 'field: removed', 'form: removed']);
      });
      it('should handle validators returning promises', done => {
        var called = 0;
        var shouldSubmit = false;
        var form = $('<form id="element"><input data-parsley-custom value="x"/></form>').appendTo('body').parsley();
        var deferred;
        window.Parsley.addValidator('custom', () => {
          called++;
          deferred = $.Deferred();
          return deferred.promise();
        });
        $('#element').on('submit', evt => {
          evt.preventDefault();
          expect(evt.parsley).to.be(true); // Sanity check
  
          expect(shouldSubmit).to.be(true);
          window.Parsley.removeValidator('custom');
          done();
        });
        $('#element').submit();
        expect(called).to.eql(1);
        deferred.reject();
        var promise = form.whenValidate();
        expect(called).to.eql(2);
        expect(promise.state()).to.eql('pending');
        deferred.reject();
        expect(promise.state()).to.eql('rejected');
        $('#element').submit();
        expect(called).to.eql(3);
        shouldSubmit = true;
        deferred.resolve();
      });
      it('should handle priority correctly', () => {
        var calls = [];
        var form = $('<form id="element"><input value="0" data-parsley-custom1 data-parsley-custom2 data-parsley-custom3 data-parsley-custom4/></form>').appendTo('body').parsley().on('form:submit', evt => {
          return false;
        });
  
        for (const i of [1, 2, 3, 4]) window.Parsley.addValidator(`custom${i}`, {
          priority: i <= 2 ? 100 : 10 - i,
          validateNumber: function (value, requirement) {
            calls.push(i);
            return value > i;
          }
        });
  
        $('#element').submit();
        $('#element input').val('3');
        $('#element').submit();
        $('#element input').val('5');
        $('#element').submit();
        expect(calls).to.eql([2, 1, 2, 1, 3, 2, 1, 3, 4]);
  
        for (const i of [1, 2, 3, 4]) window.Parsley.removeValidator(`custom${i}`);
      });
      it('should refresh', () => {
        $('body').append('<form id="element">' + '<input required />' + '</form>');
        var parsleyForm = $('#element').parsley();
        parsleyForm.validate();
        expect($('.parsley-error').length).to.be(1);
        parsleyForm.options.excluded = "input";
        parsleyForm.refresh();
        expect($('.parsley-error').length).to.be(0);
      });
      afterEach(() => {
        $('#element').remove();
      });
    });
  
    describe('Multiple', () => {
      it('should not throw errors with multiple items with weird automated generated names', () => {
        $('body').append('<form id="element">' + '<input type="checkbox" name="hello[]" id="check1" value="1" />' + '<input type="checkbox" name="{{ hello }}" id="check2" value="2" />' + '<input type="checkbox" name="$hello$" id="check3" value="3" />' + '<input type="checkbox" name="hello world[ x ]" id="check4" value="4" />' + '<input type="checkbox" value="foo" />' + '</form>');
        expectWarning(() => {
          $('#element').parsley();
        });
      });
      it('should return same Multiple instance for each field in same multiple group, and it should count as one field in form', () => {
        $('body').append('<form id="element" >' + '<input type="checkbox" name="check" id="check1" value="1" />' + '<input type="checkbox" name="check" id="check2" value="2" />' + '<input type="checkbox" name="check" id="check3" value="3" />' + '</form>');
        var parsleyMultipleInstance = $('#check1').parsley();
        expect($('#check2').parsley().__id__).to.be(parsleyMultipleInstance.__id__);
        expect($('#check3').parsley().__id__).to.be(parsleyMultipleInstance.__id__);
        expect(parsleyMultipleInstance.$elements.length).to.be(3);
        expect($('#element').parsley().fields.length).to.be(1);
      });
      it('should auto add a data-parsley-multiple attribute to each correctly binded multiple input', () => {
        $('body').append('<form id="element" >' + '<input type="checkbox" name="check" id="check1" value="1" />' + '<input type="checkbox" name="check" id="check2" value="2" />' + '<input type="checkbox" name="check" id="check3" value="3" />' + '<input type="checkbox" value="foo" />' + '</form>');
        expectWarning(() => {
          $('#element').parsley();
        });
        expect($('#check1').attr('data-parsley-multiple')).to.be('check');
        expect($('#check2').attr('data-parsley-multiple')).to.be('check');
        expect($('#check3').attr('data-parsley-multiple')).to.be('check');
        expect($('#check4').eq(3).attr('data-parsley-multiple')).to.be(undefined);
      });
      it('should have a specific `getValue` method (checkbox)', () => {
        $('body').append('<form id="element" >' + '<input type="checkbox" name="check[]" id="check1" value="1" />' + '<input type="checkbox" name="check[]" id="check2" value="2" />' + '</form>');
        expect($('#check1').parsley().getValue()).to.be.eql([]);
        expect($('#check2').attr('checked', 'checked').parsley().getValue()).to.be.eql(['2']);
      });
      it('should have a specific `getValue` method (radio)', () => {
        $('body').append('<form id="element" >' + '<input type="radio" name="radio" id="radio1" value="3" />' + '<input type="radio" name="radio" id="radio2" value="4" />' + '</form>');
        expect($('#radio1').parsley().getValue()).to.be.eql('');
        expect($('#radio2').attr('checked', 'checked').parsley().getValue()).to.be.eql('4');
      });
      it('should handle required constraint (checkbox)', () => {
        $('body').append('<form id="element" >' + '<input type="checkbox" name="check[]" id="check1" value="1" />' + '<input type="checkbox" name="check[]" id="check2" value="2" required />' + '</form>');
        expect($('#element').parsley().isValid()).to.be(false);
        $('#check2').attr('checked', 'checked');
        expect($('#element').parsley().isValid()).to.be(true);
      });
      it('should handle required constraint (radio)', () => {
        $('body').append('<form id="element" >' + '<input type="radio" name="radio" id="radio1" value="3" required />' + '<input type="radio" name="radio" id="radio2" value="4" />' + '</form>');
        expect($('#element').parsley().isValid()).to.be(false);
        $('#radio1').attr('checked', 'checked');
        expect($('#element').parsley().isValid()).to.be(true);
      });
      it('should handle check constraint', () => {
        $('body').append('<form id="element" >' + '<input type="checkbox" name="check[]" id="check1" value="1" data-parsley-check="[1, 2]" />' + '<input type="checkbox" name="check[]" id="check2" value="2" />' + '<input type="checkbox" name="check[]" id="check3" value="3" />' + '<input type="checkbox" name="check[]" id="check4" value="4" />' + '</form>'); // if not required, field is optional and do not fail
  
        expect($('#check1').parsley().isValid()).to.be.eql(true);
        expect($('#element').parsley().isValid()).to.be(true); // once required, it fails if not rightly checked
  
        $('#check1').attr('required', 'true');
        expect($('#element').parsley().isValid()).to.be(false);
        $('#check2').attr('checked', 'checked');
        expect($('#element').parsley().isValid()).to.be(true);
        $('#check1').attr('checked', 'checked');
        $('#check3').attr('checked', 'checked');
        expect($('#element').parsley().isValid()).to.be(false);
      });
      it('should support select multiple', () => {
        $('body').append('<select multiple name="foo" id="element" required data-parsley-mincheck="2">' + '<option value="1">1</option>' + '<option value="2">2</option>' + '<option value="3">3</option>' + '</select>');
        var parsleyField = $('#element').parsley();
        expect(parsleyField.__class__).to.be('FieldMultiple');
        expect(parsleyField.options.multiple).to.be('foo');
        expect(parsleyField.getValue()).to.be.eql([]);
        expect(parsleyField.isValid()).to.be(false);
        $('#element option[value="1"]').attr('selected', 'selected');
        expect(parsleyField.getValue()).to.be.eql(['1']);
        expect(parsleyField.isValid()).to.be(false);
        $('#element option[value="2"]').attr('selected', 'selected');
        expect(parsleyField.getValue()).to.be.eql(['1', '2']);
        expect(parsleyField.isValid()).to.be(true);
      });
      it('should support select with default without a value', () => {
        $('body').append('<select id="element" required>' + '<option selected="selected" value>default</option>' + '<option value="2">2</option>' + '</select>');
        expect($('#element').parsley().isValid()).to.be(false);
      });
      it('should not bind radio or checkboxes without a name or and id or a multiple option', () => {
        $('body').append('<input type="radio" value="foo" />');
        var parsleyInstance = expectWarning(() => {
          return $('input[type=radio]').psly();
        });
        expect(parsleyInstance.__class__).to.be('Parsley');
        $('input[type=radio]').attr('id', 'element');
        parsleyInstance = $('#element').parsley();
        expect(parsleyInstance.__class__).to.be('FieldMultiple');
        expect(parsleyInstance.options.multiple).to.be('element');
        $('#element').attr('name', 'element');
        parsleyInstance = $('input[name=element]').parsley();
        expect(parsleyInstance.__class__).to.be('FieldMultiple');
        expect(parsleyInstance.options.multiple).to.be('element');
        parsleyInstance.destroy();
        $('#element').attr('data-parsley-multiple', 'elementfoo');
        parsleyInstance = $('input[name=element]').parsley();
        expect(parsleyInstance.__class__).to.be('FieldMultiple');
        expect(parsleyInstance.options.multiple).to.be('elementfoo');
      });
      it('should bind select multiple input without a name or a multiple option', () => {
        $('body').append('<select multiple id="element"></select>');
        expect($('#element').parsley().__class__).to.be('FieldMultiple');
        expect($('#element').attr('data-parsley-multiple')).to.be('element');
      });
      it('should remove errors on change, whatever field is changed', () => {
        $('body').append('<form id="element" >' + '<input type="checkbox" name="check[]" id="check1" value="1" required />' + '<input type="checkbox" name="check[]" id="check2" value="2" />' + '<input type="checkbox" name="check[]" id="check3" value="3" />' + '<input type="checkbox" name="check[]" id="check4" value="4" />' + '</form>');
        $('#element').parsley().validate();
        expect($('.parsley-errors-list.filled').length).to.be(1);
        $('#check2').attr('checked', 'checked').trigger('input');
        expect($('.parsley-errors-list.filled').length).to.be(0);
      });
      it('should add errors on change if trigger enabled, whatever field is changed', () => {
        $('body').append('<form id="element" >' + '<input type="checkbox" name="check[]" id="check1" value="1" required data-parsley-mincheck="2" data-parsley-trigger="change" />' + '<input type="checkbox" name="check[]" id="check2" value="2" />' + '<input type="checkbox" name="check[]" id="check3" value="3" />' + '<input type="checkbox" name="check[]" id="check4" value="4" />' + '</form>');
        var parsleyInstance = $('#check1').parsley();
        expect(parsleyInstance.validationResult).to.be(true);
        $('#check3').trigger($.Event('change'));
        expect(parsleyInstance.validationResult.length).to.be(1);
      });
      it('should bind only valid multiple siblings sharing the same name', () => {
        $('body').append('<form id="element">' + '<input name="foo" type="hidden" value="0"/>' + '<input name="foo" id="check" type="checkbox" value="1"/>' + '<input name="foo" id="check-2" type="checkbox" value="2"/>' + '</form>' + '<form id="element-2">' + '<input name="foo" id="other-check" type="checkbox" value="3"/>' + '</form>');
        $('#element, #element-2').parsley();
        expect($('#check').parsley().$elements.length).to.be(2);
      });
      it('should handle form namespace configuration inheritance and click events while multiple binding through Form', () => {
        $('body').append('<form id="element" >' + '<input type="radio" name="radio" id="radio1" value="3" foo-bar-required />' + '<input type="radio" name="radio" id="radio2" value="4" />' + '</form>'); // set specific namespace here for form
  
        var parsleyInstance = $('#element').parsley({
          namespace: 'foo-bar-'
        });
        parsleyInstance.validate();
        expect($('ul.parsley-errors-list li').length).to.be(1);
        $('#radio2').trigger('click').trigger('input');
        expect($('ul.parsley-errors-list li').length).to.be(0);
      });
      it('should handle dynamic multiple items removal', () => {
        $('body').append('<form id="element" >' + '<input type="checkbox" name="check[]" id="check1" value="1" data-parsley-check="[1, 2]" />' + '<input type="checkbox" name="check[]" id="check2" value="2" />' + '<input type="checkbox" name="check[]" id="check3" value="3" />' + '<input type="checkbox" name="check[]" id="check4" value="4" />' + '</form>'); // bind all multiple checkbox inputs. TODO refacto multiple binding
  
        $('#element').parsley();
        var parsleyInstance = $('[type=checkbox]:first').parsley();
        expect(parsleyInstance.$elements.length).to.be(4);
        $('[type=checkbox]:last').remove(); // validate form to go through all multiple inputs. TODO refacto multiple binding
  
        $('#element').parsley().validate();
        expect(parsleyInstance.$elements.length).to.be(3);
      });
      it('value option can be a function', () => {
        $('body').append('<form id="element" >' + '<input type="checkbox" name="check[]" id="check1" value="1" required />' + '<input type="checkbox" name="check[]" id="check2" value="2" />' + '<input type="checkbox" name="check[]" id="check3" value="3" />' + '<input type="checkbox" name="check[]" id="check4" value="4" />' + '</form>');
        var called = false;
        var valid = $('#element').parsley({
          value: function () {
            called = true;
            return ['x'];
          }
        }).isValid();
        expect(valid).to.be(true);
        expect(called).to.be(true);
      });
      afterEach(() => {
        $('#element, #element-2, .parsley-errors-list').remove();
      });
    });
  
    var Factory$1 = Parsley.Factory;
    describe('Factory', () => {
      it('should be a function', () => {
        expect(Factory$1).to.be.a('function');
      });
      it('should register some window globals', () => {
        expect(window.ParsleyUtils).not.to.be(undefined);
        expect(window.ParsleyValidator).not.to.be(undefined);
      });
      it('should throw an error if no element given', () => {
        expect(Factory$1).to.throwException();
      });
      it('should return Form instance if instantiated on a form', () => {
        $('body').append('<form id="element"></form>');
        var parsleyInstance = new Factory$1($('#element')[0]);
        expect(parsleyInstance).to.be.an('object');
        expect(parsleyInstance.__class__).to.be('Form');
      });
      it('should return Field instance if instantiated on a field', () => {
        $('body').append('<input id="element" />');
        var parsleyInstance = new Factory$1($('#element')[0]);
        expect(parsleyInstance).to.be.an('object');
        expect(parsleyInstance.__class__).to.be('Field');
      });
      it('should return Field even if instantiated on an unsupported element', () => {
        $('body').append('<div id="element"></div>');
        var parsleyInstance = new Factory$1($('#element')[0]);
        expect(parsleyInstance).to.be.an('object');
        expect(parsleyInstance.__class__).to.be('Field');
      });
      it('should return Field instance even if instantiated on an excluded field type, and do not have an errors container', () => {
        $('body').append('<input type="submit" id="element" />');
        var parsleyInstance = new Factory$1($('#element')[0]);
        expect(parsleyInstance).to.be.an('object');
        expect(parsleyInstance.__class__).to.be('Field');
      });
      it('should have excluded fields by default', () => {
        $('body').append('<form id="element" >' + '<input type="submit" />' + '<input type="reset" />' + '<input type="hidden" />' + '<input type="button" />' + '</form>');
        var parsleyInstance = $('#element').parsley();
        expect(parsleyInstance.fields.length).to.be(0);
      });
      it('should return Form if instantiated on an unsupported element with data-parsley-validate attribute', () => {
        $('body').append('<div id="element" data-parsley-validate></div>');
        var parsleyInstance = new Factory$1($('#element')[0]);
        expect(parsleyInstance).to.be.an('object');
        expect(parsleyInstance.__class__).to.be('Form');
      });
      it('should handle namespace configuration', () => {
        $('body').append('<div id="element"></div>'); // default ParsleyOptions.namespace
  
        expect(new Factory$1($('#element')[0]).options.namespace).to.be('data-parsley-'); // global JS config
  
        $('#element').parsley().destroy();
        window.ParsleyConfig.namespace = 'data-foo-';
        expect(new Factory$1($('#element')[0]).options.namespace).to.be('data-foo-'); // option on the go
  
        $('#element').parsley().destroy();
        expect(new Factory$1($('#element')[0], {
          namespace: 'data-bar-'
        }).options.namespace).to.be('data-bar-'); // data- DOM-API
  
        $('#element').parsley().destroy();
        $('#element').attr('data-parsley-namespace', 'data-baz-');
        expect(new Factory$1($('#element')[0], {
          namespace: 'data-bar-'
        }).options.namespace).to.be('data-bar-');
        delete window.ParsleyConfig.namespace;
      });
      it('should handle proper options management', () => {
        $('body').append('<form id="element" data-parsley-foo="bar" data-parsley-baz="baz"></form>');
        window.ParsleyConfig = Object.assign(window.ParsleyConfig, {
          bar: 'baz',
          baz: 'qux'
        });
        var parsleyInstance = new Factory$1($('#element')[0], {
          qux: 'bux'
        });
        expect(parsleyInstance.options.foo).to.be('bar');
        expect(parsleyInstance.options.baz).to.be('baz');
        expect(parsleyInstance.options.bar).to.be('baz');
        expect(parsleyInstance.options.qux).to.be('bux');
        delete window.ParsleyConfig.bar;
        delete window.ParsleyConfig.baz;
      });
      it('should have a jquery plugin API', () => {
        $('body').append('<input type="text" id="element" data-parsley-namespace="baz-"></div>');
        var parsleyInstance = $('#element').parsley({
          foo: 'bar'
        });
        expect(parsleyInstance.__class__).to.be('Field');
        expect(parsleyInstance.options.namespace).to.be('baz-');
        expect(parsleyInstance.options.foo).to.be('bar');
      });
      it('should have a jquery API returning undefined if done on a empty set', () => {
        expect($('#foo').parsley()).to.be(undefined);
      });
      it('should have a jquery API that binds multiple selectors', () => {
        $('body').append('<div id="element">' + '<input type="text" id="foo" required />' + '<input type="text" id="bar" required />' + '</div>');
        expect($('input').parsley().length).to.be(2);
      });
      it('should set options with $.fn.parsley', () => {
        $('body').append('<form id="element" data-parsley-foo="bar"></form>');
        var parsleyInstance = $('#element').parsley({
          foo: 42
        });
        expect(parsleyInstance.options.foo).to.be(42);
        $('#element').parsley({
          foo: 'updated'
        });
        expect(parsleyInstance.options.foo).to.be('updated');
        delete parsleyInstance.options.foo;
        expect(parsleyInstance.options.foo).to.be('bar');
      });
      afterEach(() => {
        $('#element').remove();
      });
    });
  
    describe('PubSub', () => {
      it('listen() without context', done => {
        expectWarning(() => {
          $.listen('foo', (instance, arg) => {
            expect(arg).to.be('bar');
            done();
          });
        });
        $.emit('foo', 'bar');
      });
      it('listen() with context', done => {
        var obj = {
          foo: bar => {
            return 'foo' + bar;
          }
        };
        $.listen('foo', obj, function (instance, arg) {
          expect(this.foo(arg)).to.be('foobar');
          done();
        });
        $.emit('foo', 'bar');
      });
      it('listenTo() Field', done => {
        $('body').append('<input type="text" id="element" />');
        $('body').append('<input type="text" id="element2" />');
        var instance = $('#element').psly();
        $.listenTo(instance, 'foo', parsleyInstance => {
          expect(parsleyInstance.__id__).to.be(instance.__id__);
          done();
        });
        $.emit('foo', 'bar');
        $.emit('foo', $('#element2').psly());
        $.emit('foo', instance);
      });
      it('listenTo() Form will listen to Form', done => {
        $('body').append('<form id="element" data-parsley-trigger="change">' + '<input id="field1" type="text" data-parsley-required="true" />' + '<div id="field2"></div>' + '<textarea id="field3" data-parsley-notblank="true"></textarea>' + '</form>');
        $.listenTo($('#element').psly(), 'foo', parsleyInstance => {
          expect($('#element').psly().__id__ === parsleyInstance.__id__);
          done();
        });
        $.emit('foo', $('#element').psly());
      });
      it('listenTo() Form will listen to its fields too', done => {
        $('body').append('<form id="element" data-parsley-trigger="change">' + '<input id="field1" type="text" data-parsley-required="true" />' + '<div id="field2"></div>' + '<textarea id="field3" data-parsley-notblank="true"></textarea>' + '</form>');
        $.listenTo($('#element').psly(), 'foo', instance => {
          done();
        });
        $.emit('foo', $('#field1').psly());
      });
      it('unsubscribeTo()', done => {
        $('body').append('<input type="text" id="element" />');
        $.listen('foo', () => {
          done();
        });
        $.listenTo($('#element').psly(), 'foo', () => {
          expect(true).to.be(false);
        });
        $.unsubscribeTo($('#element').psly(), 'foo');
        $.emit('foo', $('#element').psly());
      });
      it('unsubscribe()', () => {
        var fn = () => {
          expect(true).to.be(false);
        };
  
        $.listen('foo', fn);
        $.unsubscribe('foo', fn);
        $.emit('foo');
      });
      afterEach(() => {
        $('#element, #element2').remove();
        $.unsubscribeAll('foo');
      });
    });
  
    describe('ParsleyRemote', () => {
      var stubbed = false;
  
      var stubAjax = status => {
        restoreAjax();
        var deferred = $.Deferred();
        var xhr = Object.assign(deferred.promise(), {
          status: status
        });
  
        if (status === 200) {
          deferred.resolve({}, 'success', 'xhr');
        } else {
          deferred.reject(xhr, 'error', 'error');
        }
  
        sinon.stub($, 'ajax').returns(xhr);
        stubbed = true;
      };
  
      var restoreAjax = () => {
        if (stubbed) $.ajax.restore();
        stubbed = false;
      };
  
      afterEach(restoreAjax);
      beforeEach(() => {
        delete window.Parsley._remoteCache;
      });
      it('should have window.ParsleyExtend defined', () => {
        expect(window.ParsleyExtend).not.to.be(undefined);
      });
      it('should handle properly validation with remote validator', done => {
        $('body').append('<input type="text" data-parsley-remote="http://foo.bar" id="element" required name="element" value="foo" />');
        var parsleyInstance = $('#element').parsley();
        stubAjax(400);
        parsleyInstance.whenValid().fail(() => {
          stubAjax(200);
          $('#element').val('bar');
          parsleyInstance.whenValid().done(() => {
            done();
          });
        });
      });
      it('should handle remote reverse option', done => {
        $('body').append('<input type="text" data-parsley-remote="http://foo.bar" id="element" data-parsley-remote-reverse="true" required name="element" value="baz" />');
        var parsleyInstance = $('#element').parsley();
        stubAjax(200);
        parsleyInstance.whenValid().fail(() => {
          stubAjax(400);
          $('#element').val('bux');
          parsleyInstance.whenValid().done(() => {
            done();
          });
        });
      });
      it('should handle remote options', done => {
        $('body').append('<input type="text" data-parsley-remote="http://foo.bar" id="element" data-parsley-remote-options=\'{ "type": "POST", "data": {"foo": "bar"} }\' required name="element" value="baz" />');
        var parsleyInstance = $('#element').parsley();
        stubAjax(200);
        parsleyInstance.whenValid().done(() => {
          expect($.ajax.calledWithMatch({
            type: 'POST'
          })).to.be(true);
          expect($.ajax.calledWithMatch({
            url: 'http://foo.bar'
          })).to.be(true);
          expect($.ajax.calledWithMatch({
            data: {
              foo: 'bar',
              element: 'baz'
            }
          })).to.be(true);
          done();
        });
      });
      it('should save some calls for queries already done', done => {
        $('body').append('<input type="text" data-parsley-remote="http://foo.bar" id="element" required name="element" value="foo" />');
        var parsleyInstance = $('#element').parsley();
        stubAjax(200);
        parsleyInstance.whenValid().done(() => {
          expect($.ajax.calledOnce).to.be(true);
          expect($.ajax.calledWithMatch({
            data: {
              element: 'foo'
            }
          })).to.be(true);
          stubAjax(400);
          $('#element').val('bar');
          parsleyInstance.whenValid().fail(() => {
            expect($.ajax.calledOnce).to.be(true);
            expect($.ajax.calledWithMatch({
              data: {
                element: 'bar'
              }
            })).to.be(true);
            stubAjax(200);
            $('#element').val('foo');
            parsleyInstance.whenValid().done(() => {
              expect($.ajax.callCount).to.be(0);
              expect($.ajax.calledOnce).to.be(false);
              done();
            });
          });
        });
      });
      it('should handle remote validator option', done => {
        window.Parsley.addAsyncValidator('custom', xhr => {
          return xhr.status === 404;
        });
        $('body').append('<input type="text" data-parsley-remote="http://foo.bar" id="element" data-parsley-remote-validator="custom" required name="element" value="foobar" />');
        var parsleyInstance = $('#element').parsley();
        stubAjax(200);
        parsleyInstance.whenValid().fail(() => {
          stubAjax(400);
          $('#element').val('foobaz');
          parsleyInstance.whenValid().fail(() => {
            stubAjax(404);
            $('#element').val('fooquux');
            parsleyInstance.whenValid().done(() => {
              done();
            });
          });
        });
      });
      it('should handle remote validator option with custom url', done => {
        $('body').append('<input type="text" data-parsley-remote id="element" data-parsley-remote-validator="mycustom" required name="element" value="foobar" />');
        var parsleyInstance = $('#element').parsley();
        window.Parsley.addAsyncValidator('mycustom', xhr => {
          return xhr.status === 404;
        }, 'http://foobar.baz');
        stubAjax(200);
        parsleyInstance.whenValid().fail(() => {
          expect($.ajax.calledWithMatch({
            url: 'http://foobar.baz'
          })).to.be(true);
          done();
        });
      });
      it('should have PluginField as the `this` context of the AJAX callback', done => {
        $('body').append('<input type="text" data-parsley-remote id="element" data-parsley-remote-validator="mycustom" required name="element" value="foobar" />');
        var parsleyInstance = $('#element').parsley();
        window.Parsley.addAsyncValidator('mycustom', function (xhr) {
          expect(this.__class__).to.be('Field');
        }, 'http://foobar.baz');
        stubAjax(200);
        parsleyInstance.whenValid().fail(() => {
          expect($.ajax.calledWithMatch({
            url: 'http://foobar.baz'
          })).to.be(true);
          done();
        });
      });
      it('should handle deprecated call to addAsyncValidator using an instance', () => {
        $('body').append('<input type="text" data-parsley-remote id="element" data-parsley-remote-validator="mycustom" required name="element" value="foobar" />');
        expectWarning(() => {
          $('input').parsley().addAsyncValidator('instancetest', $.noop, '');
        });
        expect(Parsley.asyncValidators.instancetest).not.to.be(undefined);
      });
      it('should clear the cache before and after submitting a form', () => {
        var parsleyInstance = $('<form id="element"><input type="text" required></form>').appendTo('body').on('submit', evt => {
          evt.preventDefault();
        }).parsley();
        window.Parsley._remoteCache = {
          dummy: 42
        };
        $('#element').submit();
        expect(window.Parsley._remoteCache.dummy).to.be(undefined);
        window.Parsley._remoteCache = {
          dummy: 42
        };
        $('#element input').val('hello');
        parsleyInstance.validate();
        expect(window.Parsley._remoteCache.dummy).to.be(42);
        $('#element').submit();
        expect(window.Parsley._remoteCache.dummy).to.be(undefined);
      });
      it('should allow the change of XHR options', done => {
        var parsleyInstance = $('<input id="element" data-parsley-remote="http://parsleyjs.org" name="element" value="foobar"/>').appendTo('body').parsley().on('field:ajaxoptions', (field, options) => {
          options.url = options.url + '/test/' + options.data.element;
        });
        stubAjax(200);
        parsleyInstance.whenValid().done(() => {
          expect($.ajax.calledWithMatch({
            url: 'http://parsleyjs.org/test/foobar'
          })).to.be(true);
          expect($.ajax.calledWithMatch({
            data: {
              element: 'foobar'
            }
          })).to.be(true);
          done();
        });
      });
      it('should allow RESTful URLs', done => {
        var parsleyInstance = $('<input id="element" data-parsley-remote="http://parsleyjs.org/thisisrest/{value}" name="element" value="foo bar"/>').appendTo('body').parsley();
        stubAjax(200);
        parsleyInstance.whenValid().done(() => {
          expect($.ajax.calledWithMatch({
            url: 'http://parsleyjs.org/thisisrest/foo%20bar'
          })).to.be(true);
          expect($.ajax.calledWithMatch({
            data: {
              element: 'foo bar'
            }
          })).to.be(false);
          done();
        });
      });
      it.skip('should abort successives querries and do not handle their return');
      afterEach(() => {
        $('#element, .parsley-errors-list').remove();
      });
    });
  
    describe('UI', () => {
      before(() => {
        Parsley.setLocale('en');
      });
      it('should create proper errors container when needed', () => {
        $('body').append('<input type="text" id="element" data-parsley-required />');
        var parsleyField = $('#element').psly();
        expect($('ul#parsley-id-' + parsleyField.__id__).length).to.be(0);
        parsleyField.validate();
        expect($('#element').attr('data-parsley-id')).to.be(parsleyField.__id__);
        expect($('ul#parsley-id-' + parsleyField.__id__).length).to.be(1);
        expect($('ul#parsley-id-' + parsleyField.__id__).hasClass('parsley-errors-list')).to.be(true);
      });
      it('should handle errors-container option', () => {
        $('body').append('<form id="element">' + '<input id="field1" type="text" required data-parsley-errors-container="#container" />' + '<div id="container"></div>' + '<div id="container2"></div>' + '</form>');
        $('#element').psly().validate();
        expect($('#container .parsley-errors-list').length).to.be(1);
        $('#element').psly().destroy();
        $('#field1').removeAttr('data-parsley-errors-container');
        $('#element').psly({
          errorsContainer: function (ins) {
            expect(ins).to.be($('#field1').psly());
            expect(this).to.be($('#field1').psly());
            return $('#container2');
          }
        }).validate();
        expect($('#container2 .parsley-errors-list').length).to.be(1);
      });
      it('should handle errors-container option with function', () => {
        $('body').append('<form id="element">' + '<input id="field1" type="text" required data-parsley-errors-container="parsleyContainerFunction" />' + '<div id="container"></div>' + '<div id="container2"></div>' + '</form>');
  
        window.parsleyContainerFunction = function (ins) {
          expect(ins).to.be($('#field1').psly());
          expect(this).to.be($('#field1').psly());
          return $('#container2');
        };
  
        $('#element').psly().validate();
        expect($('#container2 .parsley-errors-list').length).to.be(1);
        delete window.parsleyContainerFunction;
      });
      it('should handle wrong errors-container option', () => {
        $('body').append('<input type="text" id="element" data-parsley-errors-container="#donotexist" required/>');
        var parsley = $('#element').psly();
        expectWarning(() => {
          parsley.validate();
        });
      });
      it('should not add success class on a field without constraints', () => {
        $('body').append('<input type="text" id="element" />');
        var parsleyField = $('#element').psly();
        parsleyField.validate();
        expect($('#element').hasClass('parsley-error')).to.be(false);
        expect($('#element').hasClass('parsley-success')).to.be(false);
      });
      it('should not add success class on an empty optional field', () => {
        $('body').append('<input type="number" id="element" />');
        var parsleyField = $('#element').psly();
        parsleyField.validate();
        expect($('#element').hasClass('parsley-error')).to.be(false);
        expect($('#element').hasClass('parsley-success')).to.be(false);
      });
  
      var checkType = (type, html, fillValue) => {
        it(`should add proper parsley class on success or failure (${type})`, () => {
          $('body').append(`<form id="element"><section>${html}</section></form>`);
          let form = $('#element').parsley();
          let $inputHolder = $('#element section').children().first();
          form.validate();
          expect($inputHolder.attr('class')).to.be('parsley-error');
          expect($('.parsley-errors-list').parent().prop("tagName")).to.be('SECTION'); // Fill and revalidate:
  
          fillValue($inputHolder);
          form.validate();
          expect($inputHolder.attr('class')).to.be('parsley-success');
        });
      };
  
      let callVal = $input => $input.val('foo');
  
      checkType('text', '<input type="text" required/>', callVal);
      checkType('select', '<select multiple required><option value="foo">foo</option>', callVal);
  
      let callProp = $fieldset => $fieldset.find('input').prop('checked', true);
  
      checkType('radio', '<fieldset><input type="radio" name="foo" required /></fieldset>', callProp);
      checkType('checkbox', '<fieldset><input type="checkbox" name="foo" required /></fieldset>', callProp);
      it('should handle class-handler option', () => {
        $('body').append('<form id="element">' + '<input id="field1" type="email" data-parsley-class-handler="#field2" required />' + '<div id="field2"></div>' + '<div id="field3"></div>' + '</form>');
        $('#element').psly().validate();
        expect($('#field2').hasClass('parsley-error')).to.be(true);
        $('#element').psly().destroy();
        $('#field1').removeAttr('data-parsley-class-handler');
        $('#element').psly({
          classHandler: function (ins) {
            expect(ins).to.be($('#field1').parsley());
            expect(this).to.be($('#field1').parsley());
            return $('#field3');
          }
        }).validate();
        expect($('#field3').hasClass('parsley-error')).to.be(true);
      });
      it('should handle class-handler option with a function', () => {
        $('body').append('<form id="element">' + '<input id="field1" type="email" data-parsley-class-handler="#field2" required />' + '<div id="field4"></div>' + '</form>');
        $('#field1').attr('data-parsley-class-handler', 'parsleyClassHandler');
  
        window.parsleyClassHandler = function (ins) {
          expect(ins).to.be($('#field1').parsley());
          expect(this).to.be($('#field1').parsley());
          return $('#field4');
        };
  
        $('#element').psly().validate();
        expect($('#field4').hasClass('parsley-error')).to.be(true);
        $('#element').psly().destroy();
        $('#field1').attr('data-parsley-class-handler', 'someUndefinedFunctionName');
        expectWarning(() => {
          $('#element').psly().validate();
        });
        expect($('#field1').hasClass('parsley-error')).to.be(true);
        delete window.parsleyClassHandler;
      });
      it('should show higher priority error message by default', () => {
        $('body').append('<input type="email" id="element" required />');
        var parsleyField = $('#element').psly();
        parsleyField.validate();
        expect($('#element').hasClass('parsley-error')).to.be(true);
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').length).to.be(1);
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').hasClass('parsley-required')).to.be(true);
        $('#element').val('foo').psly().validate();
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').length).to.be(1);
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').hasClass('parsley-type')).to.be(true);
      });
      it('should show custom error message by validator', () => {
        $('body').append('<input type="email" id="element" required data-parsley-required-message="foo" data-parsley-type-message="bar"/>');
        var parsleyField = $('#element').psly();
        parsleyField.validate();
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').text()).to.be('foo');
        $('#element').val('foo').psly().validate();
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').text()).to.be('bar');
      });
      it('should show custom error message with variabilized parameters', () => {
        $('body').append('<input type="text" id="element" value="bar" data-parsley-minlength="7" data-parsley-minlength-message="foo %s bar"/>');
        var parsleyField = $('#element').psly();
        parsleyField.validate();
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').text()).to.be('foo 7 bar');
      });
      it('should show custom error message for whole field', () => {
        $('body').append('<input type="email" id="element" required data-parsley-error-message="baz"/>');
        var parsleyField = $('#element').psly();
        parsleyField.validate();
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').text()).to.be('baz');
        $('#element').val('foo').psly().validate();
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').text()).to.be('baz');
        $('#element').val('foo@bar.baz').psly().validate();
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').length).to.be(0);
      });
      it('should display no error message if diabled', () => {
        $('body').append('<input type="email" id="element" required data-parsley-errors-messages-disabled />');
        var parsleyField = $('#element').psly();
        parsleyField.validate();
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').length).to.be(0);
        expect($('#element').hasClass('parsley-error')).to.be(true);
      });
      it('should handle simple triggers (change, focus...)', () => {
        $('body').append('<input type="email" id="element" required data-parsley-trigger="change" />');
        var parsleyField = $('#element').psly();
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').length).to.be(0);
        $('#element').trigger($.Event('change'));
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').length).to.be(1);
      });
      it('should allow customization of triggers after first error', () => {
        $('body').append('<input type="email" id="element" required data-parsley-trigger-after-failure="focusout" />');
        var parsleyField = $('#element').psly();
        parsleyField.validate();
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').length).to.be(1);
        $('#element').val('a@example.com');
        $('#element').trigger('input');
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').length).to.be(1);
        $('#element').trigger('focusout');
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').length).to.be(0);
      });
      it('should auto bind error trigger on select field error (input=text)', () => {
        $('body').append('<input type="email" id="element" required />');
        var parsleyField = $('#element').psly();
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').length).to.be(0);
        parsleyField.validate();
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').length).to.be(1);
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').hasClass('parsley-required')).to.be(true);
        $('#element').val('foo').trigger('input');
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').hasClass('parsley-type')).to.be(true);
      });
      it('should auto bind error trigger on select field error (select)', () => {
        $('body').append('<select id="element" required>' + '<option value="">Choose</option>' + '<option value="foo">foo</option>' + '<option value="bar">bar</option>' + '</select>');
        var parsleyField = $('#element').psly();
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').length).to.be(0);
        parsleyField.validate();
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').length).to.be(1);
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').hasClass('parsley-required')).to.be(true);
        $('#element [option="foo"]').attr('selected', 'selected');
        $('#element').trigger($.Event('change'));
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').hasClass('parsley-type')).to.be(false);
      });
      it('should handle complex triggers (keyup, keypress...)', () => {
        $('body').append('<input type="email" id="element" required data-parsley-trigger="keyup" />');
        var parsleyField = $('#element').psly();
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').length).to.be(0);
        $('#element').val('foo').trigger($.Event('keyup'));
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').length).to.be(0);
        $('#element').val('foob').trigger($.Event('keyup'));
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').length).to.be(1);
      });
      it('should handle trigger keyup threshold validation', () => {
        $('body').append('<input type="email" id="element" data-parsley-validation-threshold="7" required data-parsley-trigger="keyup" />');
        var parsleyField = $('#element').psly();
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').length).to.be(0);
        $('#element').val('a@b.com').trigger('keyup');
        expect($('#element').hasClass('success')).to.be(false);
        $('#element').val('aa@b.com').trigger('keyup');
        expect($('#element').hasClass('parsley-success')).to.be(true);
        $('#element').val('@b.com').trigger('keyup');
        expect($('#element').hasClass('parsley-success')).to.be(false);
      });
      it('should handle UI disabling', () => {
        $('body').append('<input type="email" id="element" data-parsley-ui-enabled="false" required data-parsley-trigger="keyup" />');
        var parsleyField = $('#element').psly();
        expect($('ul#parsley-id-' + parsleyField.__id__).length).to.be(0);
        parsleyField.validate();
        expect($('ul#parsley-id-' + parsleyField.__id__).length).to.be(0);
      });
      it('should add novalidate on form elem', () => {
        $('body').append('<form id="element" data-parsley-trigger="change">' + '<input id="field1" type="text" data-parsley-required="true" />' + '<div id="field2"></div>' + '<textarea id="field3" data-parsley-notblank="true"></textarea>' + '</form>');
        var parsleyForm = $('#element').parsley();
        expect($('#element').attr('novalidate')).not.to.be(undefined);
      });
      it('should test the no-focus option', () => {
        $('body').append('<form id="element" data-parsley-focus="first">' + '<input id="field1" type="text" data-parsley-required="true" data-parsley-no-focus />' + '<input id="field2" data-parsley-required />' + '</form>');
        $('#element').parsley().validate();
        expect($('#element').parsley()._focusedField.attr('id')).to.be('field2');
        $('#field2').val('foo');
        $('#element').psly().validate();
        expect($('#element').parsley()._focusedField).to.be(null);
        $('#field1').removeAttr('data-parsley-no-focus');
        $('#element').psly().validate();
        expect($('#element').parsley()._focusedField.attr('id')).to.be('field1');
        $('#element').attr('data-parsley-focus', 'last');
        $('#element').psly().validate();
        expect($('#element').parsley()._focusedField.attr('id')).to.be('field1');
        $('#field2').val('');
        $('#element').psly().validate();
        expect($('#element').parsley()._focusedField.attr('id')).to.be('field2');
      });
      it('should test the manual add / update / remove error', () => {
        $('body').append('<input type="text" id="element" />');
        var parsleyField = $('#element').parsley();
        parsleyField.removeError('non-existent');
        parsleyField.validate();
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').length).to.be(0);
        expect($('#element').hasClass('parsley-error')).to.be(false);
        expectWarning(() => {
          window.ParsleyUI.addError(parsleyField, 'foo', 'bar');
        });
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').length).to.be(1);
        expect($('#element').hasClass('parsley-error')).to.be(true);
        expect($('li.parsley-foo').length).to.be(1);
        expect($('li.parsley-foo').text()).to.be('bar');
        expectWarning(() => {
          window.ParsleyUI.updateError(parsleyField, 'foo', 'baz');
        });
        expect($('li.parsley-foo').text()).to.be('baz');
        expectWarning(() => {
          window.ParsleyUI.removeError(parsleyField, 'foo');
        });
        expect($('#element').hasClass('parsley-error')).to.be(false);
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').length).to.be(0);
      });
      it('should have a getErrorsMessage() method', () => {
        $('body').append('<input type="email" id="element" value="foo" data-parsley-minlength="5" />');
        var parsleyInstance = $('#element').parsley();
        parsleyInstance.validate();
        expectWarning(() => {
          window.ParsleyUI.getErrorsMessages(parsleyInstance);
        });
        expect(window.ParsleyUI.getErrorsMessages(parsleyInstance).length).to.be(1);
        expect(window.ParsleyUI.getErrorsMessages(parsleyInstance)[0]).to.be('This value should be a valid email.');
        $('#element').attr('data-parsley-priority-enabled', false);
        parsleyInstance.validate();
        expect(window.ParsleyUI.getErrorsMessages(parsleyInstance).length).to.be(2);
        expect(window.ParsleyUI.getErrorsMessages(parsleyInstance)[0]).to.be('This value is too short. It should have 5 characters or more.');
      });
      it('should not have errors ul created for excluded fields', () => {
        $('body').append('<div id="hidden"><input type="hidden" id="element" value="foo" data-parsley-minlength="5" /></div>');
        var parsleyInstance = $('#element').parsley();
        expect($('#hidden ul').length).to.be(0);
        $('#hidden').remove();
      });
      it('should remove filled class from errors container when reseting', () => {
        $('body').append('<input type="email" id="element" value="foo" data-parsley-minlength="5" />');
        var parsleyInstance = $('#element').parsley();
        parsleyInstance.validate();
        parsleyInstance.reset();
        expect($('ul#parsley-id-' + parsleyInstance.__id__).hasClass('filled')).to.be(false);
      });
      it('should re-bind error triggers after a reset (input=text)', () => {
        $('body').append('<input type="text" id="element" required />');
        var parsleyInstance = $('#element').parsley();
        parsleyInstance.validate();
        parsleyInstance.reset();
        parsleyInstance.validate();
        expect($('ul#parsley-id-' + parsleyInstance.__id__ + ' li').length).to.be(1);
        $('#element').val('foo').trigger('input');
        expect($('ul#parsley-id-' + parsleyInstance.__id__ + ' li').length).to.be(0);
      });
      it('should re-bind error triggers after a reset (select)', () => {
        $('body').append('<select id="element" required>' + '<option value="">Choose</option>' + '<option value="foo">foo</option>' + '<option value="bar">bar</option>' + '</select>');
        var parsleyInstance = $('#element').parsley();
        parsleyInstance.validate();
        parsleyInstance.reset();
        parsleyInstance.validate();
        expect($('ul#parsley-id-' + parsleyInstance.__id__ + ' li').length).to.be(1);
        $('#element option[value="foo"]').prop('selected', true);
        $('#element').trigger('input');
        expect($('ul#parsley-id-' + parsleyInstance.__id__ + ' li').length).to.be(0);
      });
      it('should re-bind custom triggers after a reset', () => {
        $('body').append('<input type="text" id="element" required data-parsley-trigger="focusout" />');
        var parsleyInstance = $('#element').parsley();
        parsleyInstance.validate();
        parsleyInstance.reset();
        $('#element').trigger('focusout');
        expect($('ul#parsley-id-' + parsleyInstance.__id__ + ' li').length).to.be(1);
      });
      it('should handle custom error message for validators with compound names', () => {
        $('body').append('<input type="text" value="1" id="element" data-parsley-custom-validator="2" data-parsley-custom-validator-message="custom-validator error"/>');
        window.Parsley.addValidator('customValidator', (value, requirement) => {
          return requirement === value;
        }, 32);
        var parsleyField = $('#element').psly();
        parsleyField.validate();
        expect($('ul#parsley-id-' + parsleyField.__id__ + ' li').text()).to.be('custom-validator error');
        window.Parsley.removeValidator('customValidator');
      });
      it('should handle custom error messages returned from custom validators', () => {
        $('body').append('<input type="text" value="1" id="element" data-parsley-custom-validator="2" data-parsley-custom-validator-message="custom-validator error"/>');
        window.Parsley.addValidator('customValidator', (value, requirement) => {
          return $.Deferred().reject("Hey, this ain't good at all").promise();
        }, 32);
        var parsleyField = $('#element').psly();
        parsleyField.validate();
        expect($(`ul#parsley-id-${parsleyField.__id__} li`).text()).to.be("Hey, this ain't good at all");
        window.Parsley.removeValidator('customValidator');
      });
      it('should run before events are fired', () => {
        $('body').append('<input type="text" id="element" required/>');
        var parsley = $('#element').parsley().on('field:validated', () => {
          expect($('.parsley-errors-list')).to.have.length(1);
        });
        parsley.validate();
      });
      afterEach(() => {
        $('#element, .parsley-errors-list').remove();
      });
    });
  
    describe('Utils', () => {
      it('should warn when using the old global ParsleyUtils', () => {
        expectWarning(() => {
          expect(window.ParsleyUtils.deserializeValue('true')).to.be(true);
        });
      });
      it('should have a proper deserializeValue() function', () => {
        expect(Utils.deserializeValue('true')).to.be(true);
        expect(Utils.deserializeValue('1')).to.be(1);
        expect(Utils.deserializeValue('["foo", "bar"]')).to.be.an('array');
        expect(Utils.deserializeValue('{"foo": "bar"}')).to.be.an('object');
      });
      it('should have a proper camelize() function', () => {
        expect(Utils.camelize('foo-bar')).to.be('fooBar');
        expect(Utils.camelize('foo-bar-baz')).to.be('fooBarBaz');
        expect(Utils.camelize('foo-bAr-baz')).to.be('fooBArBaz');
      });
      it('should have a proper dasherize() function', () => {
        expect(Utils.dasherize('fooBar')).to.be('foo-bar');
        expect(Utils.dasherize('fooBarBaz')).to.be('foo-bar-baz');
        expect(Utils.dasherize('fooBArBaz')).to.be('foo-b-ar-baz');
      });
      it('should have a proper attr() function', () => {
        var element = {
          attributes: [{
            specified: true,
            name: "data-parsley-foo",
            value: "bar"
          }, {
            specified: true,
            name: "parsley-foo",
            value: "baz"
          }, {
            specified: true,
            name: "data-parsley-bar",
            value: "[0, 42]"
          }, {
            specified: false,
            name: "data-parsley-foo",
            value: "bar"
          }, {
            foo: "bar"
          }]
        };
        var attr = Utils.attr(element, 'data-parsley-');
        expect(attr).to.eql({
          'foo': 'bar',
          'bar': [0, 42]
        });
      });
      it('should have a proper attr() function that rewrites a given object', () => {
        var obj = Utils.objectCreate({
          foo: 'x',
          fox: 'trot'
        });
        obj.deleteMe = 'please';
        var $element = $('<b data-parsley-foo="a" data-parsley-bar="[0, 42]" parsley-baz="baz">');
        Utils.attr($element[0], 'data-parsley-', obj);
        expect(obj).to.eql({
          foo: "a",
          bar: [0, 42]
        });
        expect(obj.fox).to.eql('trot');
      });
      it('should have a checkAttr feature', () => {
        var element = $('<span data-parsley-required-message="foo" data-parsley-validate="true">')[0];
        expect(Utils.checkAttr(element, 'data-parsley-', 'required')).to.be(false);
        expect(Utils.checkAttr(element, 'data-parsley-', 'required-message')).to.be(true);
        expect(Utils.checkAttr(element, 'data-parsley-', 'validate')).to.be(true);
      });
      describe('namespaceEvents', () => {
        var itMaps = (what, toWhat) => {
          it(`maps '${what}' to ''`, () => {
            expect(Utils.namespaceEvents(what, 'ns')).to.eql(toWhat);
          });
        };
  
        itMaps('foo', 'foo.ns');
  
        for (var evt of ['', '  ', false, null, undefined]) {
          itMaps(evt, '');
        }
  
        itMaps('   foo    bar   ', 'foo.ns bar.ns');
      });
      describe('parse', () => {
        describe('date', () => {
          it('returns null for invalid dates', () => {
            expect(Utils.parse.date('1972-13-02')).to.be.null;
            expect(Utils.parse.date('1973-02-29')).to.be.null;
            expect(Utils.parse.date('72-01-06')).to.be.null;
            expect(Utils.parse.date('1972/01/06')).to.be.null;
            expect(Utils.parse.date('1972-01-6')).to.be.null;
            expect(Utils.parse.date('1972-1-06')).to.be.null;
            expect(Utils.parse.date('  1972-01 -06  ')).to.be.null;
          });
          it('returns a date for strings of form YYYY/MM/DD', () => {
            expect(Utils.parse.date('1972-01-06')).to.eql(new Date('1972/01/06'));
          });
        });
      });
    });
  
    describe('Validator', () => {
      var testParsing = function (type, input, output, extraOptions) {
        it(`parses ${type} requirements`, () => {
          var c = new Validator({
            requirementType: type
          });
          expect(c.parseRequirements(input, extraOptions)).to.eql(output);
        });
      };
  
      testParsing('integer', '42', [42]);
      testParsing('number', '4.2', [4.2]);
      testParsing('string', '42', ['42']);
      testParsing(['number', 'string'], '[4.2, 4.2]', [4.2, '4.2']);
      testParsing({
        '': 'number',
        'foo': 'string',
        'bar': 'string'
      }, '4.2', [4.2, {
        foo: 'FOO',
        bar: 'BAR'
      }], value => {
        return value.toUpperCase();
      });
    });
  
    // Validation errors messages for Parsley
    Parsley.addMessages('fr', {
      defaultMessage: "Cette valeur semble non valide.",
      type: {
        email: "Cette valeur n'est pas une adresse email valide.",
        url: "Cette valeur n'est pas une URL valide.",
        number: "Cette valeur doit être un nombre.",
        integer: "Cette valeur doit être un entier.",
        digits: "Cette valeur doit être numérique.",
        alphanum: "Cette valeur doit être alphanumérique."
      },
      notblank: "Cette valeur ne peut pas être vide.",
      required: "Ce champ est requis.",
      pattern: "Cette valeur semble non valide.",
      min: "Cette valeur ne doit pas être inférieure à %s.",
      max: "Cette valeur ne doit pas excéder %s.",
      range: "Cette valeur doit être comprise entre %s et %s.",
      minlength: "Cette chaîne est trop courte. Elle doit avoir au minimum %s caractères.",
      maxlength: "Cette chaîne est trop longue. Elle doit avoir au maximum %s caractères.",
      length: "Cette valeur doit contenir entre %s et %s caractères.",
      mincheck: "Vous devez sélectionner au moins %s choix.",
      maxcheck: "Vous devez sélectionner %s choix maximum.",
      check: "Vous devez sélectionner entre %s et %s choix.",
      equalto: "Cette valeur devrait être identique."
    });
    Parsley.setLocale('fr');
  
    describe('ValidatorRegistry', () => {
      var validatorRegistry = Parsley._validatorRegistry;
      let instance = $('<input>').parsley();
  
      var expectValidation = function (value, name, requirements, extra = {}) {
        var validatorSpec = validatorRegistry.validators[name];
        var validator = new Validator(validatorSpec);
        var argList = validator.parseRequirements(requirements, key => {
          return extra[key];
        });
        return expect(validator.validate(value, ...argList, instance));
      };
  
      afterEach(() => {
        Parsley.setLocale('en');
      });
      it('should be a function', () => {
        expect(ValidatorRegistry).to.be.a('function');
      });
      it('should bind global config validators if given in constructor', () => {
        $.extend(true, Parsley.options, {
          validators: {
            foo: {
              fn: () => {},
              priority: 42
            },
            bar: {
              fn: () => {},
              priority: 12
            }
          }
        });
        var validator = new ValidatorRegistry(Parsley.options.validators);
        expect(validator.validators).to.have.key('foo');
        expect(validator.validators).to.have.key('bar');
        expect(validatorRegistry.validators).not.to.have.key('foo');
        delete Parsley.options.validators.foo;
        delete Parsley.options.validators.bar;
      });
      it('should have a required validator', () => {
        expectValidation('', 'required').not.to.be(true);
        expectValidation('foo', 'required').to.be(true);
      });
      it('should have a notblank validator', () => {
        expectValidation(' ', 'notblank').not.to.be(true);
        expectValidation('foo', 'notblank').to.be(true);
      });
      it('should have a type="email" validator', () => {
        expectValidation('', 'type', 'email').to.be(true);
        expectValidation('foo', 'type', 'email').not.to.be(true);
        expectValidation('foo@bar.baz', 'type', 'email').to.be(true);
        expectValidation('foo+bar@bar.baz', 'type', 'email').to.be(true);
        expectValidation('foo.bar@bar.baz', 'type', 'email').to.be(true);
        expectValidation('foo.bar@bar.com.ext', 'type', 'email').to.be(true);
        expectValidation('foo@bar..tt.com', 'type', 'email').to.be(false);
      });
      it('should have a type="date" validator', () => {
        expectValidation('', 'type', 'date').to.be(true);
        expectValidation('foo', 'type', 'date').not.to.be(true);
        expectValidation('12', 'type', 'date').not.to.be(true);
        expectValidation('2001-01-30', 'type', 'date').to.be(true);
        expectValidation('2001-02-30', 'type', 'date').not.to.be(true);
        expectValidation('2001-30-01', 'type', 'date').not.to.be(true);
      });
      it('should have a min validator', () => {
        expectValidation('', 'min', 6).to.be(true);
        expectValidation('foo', 'min', 6).not.to.be(true);
        expectValidation('1', 'min', 6).not.to.be(true);
        expectValidation('6', 'min', 6).to.be(true);
        expectValidation('10', 'min', 6).to.be(true);
        $('body').append('<input type="text" id="element" value="7" min="2" />');
        expect($('#element').parsley().isValid()).to.be(true);
      });
      it('should have a max validator', () => {
        expectValidation('', 'max', 10).to.be(true);
        expectValidation('foo', 'max', 10).not.to.be(true);
        expectValidation('1', 'max', 10).to.be(true);
        expectValidation('1', 'max', '10').to.be(true);
        expectValidation('10', 'max', 10).to.be(true);
        expectValidation('17', 'max', 10).not.to.be(true);
        $('body').append('<input type="text" id="element" value="7" max="20" />');
        expect($('#element').parsley().isValid()).to.be(true);
      });
      it('should have a range validator', () => {
        expectValidation('1', 'range', [5, 10]).not.to.be(true);
        expectValidation('7', 'range', [5, 10]).to.be(true);
        expectValidation('17', 'range', [5, 10]).not.to.be(true);
        $('body').append('<input type="text" id="element" value="7" max="20" min="2" />');
        expect($('#element').parsley().isValid()).to.be(true);
        $('#element').remove();
        $('body').append('<input type="range" id="element" value="7" max="20" min="2" />');
        expect($('#element').parsley().isValid()).to.be(true);
      });
      it('should have a type="number" validator', () => {
        expectValidation('foo', 'type', 'number').not.to.be(true);
        expectValidation('-', 'type', 'number').not.to.be(true);
        expectValidation('1', 'type', 'number').to.be(true);
        expectValidation('1.5', 'type', 'number', {
          step: 'any'
        }).to.be(true);
        expectValidation('-1.5', 'type', 'number', {
          step: 'any'
        }).to.be(true);
        expectValidation('1500.642', 'type', 'number', {
          step: 'any'
        }).to.be(true);
        expectValidation('0.5', 'type', 'number', {
          step: 'any'
        }).to.be(true);
        expectValidation('.5', 'type', 'number', {
          step: 'any'
        }).to.be(true);
      });
      it('should have a type="digits" validator', () => {
        expectValidation('foo', 'type', 'digits').not.to.be(true);
        expectValidation('1', 'type', 'digits').to.be(true);
        expectValidation('-1', 'type', 'digits').not.to.be(true);
        expectValidation('1.5', 'type', 'digits').not.to.be(true);
        expectValidation('-1.5', 'type', 'digits').not.to.be(true);
        expectValidation('1,500.642', 'type', 'digits').not.to.be(true);
      });
      it('should have a type="integer" validator', () => {
        expectValidation('foo', 'type', 'integer').not.to.be(true);
        expectValidation('1', 'type', 'integer').to.be(true);
        expectValidation('-1', 'type', 'integer').to.be(true);
        expectValidation('1.5', 'type', 'integer').not.to.be(true);
        expectValidation('-1.5', 'type', 'integer').not.to.be(true);
      });
      it('should have a type="alphanum" validator', () => {
        expectValidation('foo', 'type', 'alphanum').to.be(true);
        expectValidation('foo bar', 'type', 'alphanum').not.to.be(true);
        expectValidation('foo$', 'type', 'alphanum').not.to.be(true);
        $('body').append('<input data-parsley-type="alphanum" id="element" value="v4kRRyhYvo0P" />');
        expect($('#element').parsley().isValid()).to.be(true);
      });
      it('should have a type="url" validator', () => {
        expectValidation('foo', 'type', 'url').not.to.be(true);
        expectValidation('foo bar', 'type', 'url').not.to.be(true);
        expectValidation('http://', 'type', 'url').not.to.be(true);
        expectValidation('foo.bar', 'type', 'url').to.be(true);
        expectValidation('www.foo.bar', 'type', 'url').to.be(true);
        expectValidation('http://www.foo.bar', 'type', 'url').to.be(true);
        expectValidation('https://www.foo.bar', 'type', 'url').to.be(true);
        expectValidation('http://192.168.1.1/foo/bar', 'type', 'url').to.be(true);
      });
      it('should have a pattern validator', () => {
        expectValidation('a', 'pattern', '[a-z]+').to.be(true);
        expectValidation('A', 'pattern', '[a-z]+').not.to.be(true);
        expectValidation('a', 'pattern', '/[a-z]+/').to.be(true);
        expectValidation('A', 'pattern', '/[a-z]+/').not.to.be(true);
        expectValidation('a', 'pattern', '/[a-z]+/i').to.be(true);
        expectValidation('A', 'pattern', '/[a-z]+/i').to.be(true);
      });
      it('should have a pattern validator that behaves as the standard when not of the form /pattern/flag', () => {
        expectValidation('aa', 'pattern', '[a-z]{1,2}').to.be(true);
        expectValidation('aaa', 'pattern', '[a-z]{1,2}').not.to.be(true);
        expectValidation('aa', 'pattern', '^[a-z]{2}$').to.be(true);
      });
      it('should have a pattern validator that extends the standard for form /pattern/flag', () => {
        expectValidation('zAz', 'pattern', '/a/i').to.be(true);
      });
      it('should have a length validator', () => {
        expectValidation('foobar', 'length', [3, 9]).to.be(true);
        expectValidation('foo', 'length', [4, 9]).not.to.be(true);
        expectValidation('foobarbaz', 'length', [3, 8]).not.to.be(true);
      });
      it('should have a minlength validator', () => {
        expectValidation('foo', 'minlength', 3).to.be(true);
        expectValidation('fo', 'minlength', 3).not.to.be(true);
        $('body').append('<input type="text" id="element" value="foo" data-parsley-minlength="2" />');
        expect($('#element').parsley().isValid()).to.be(true);
      });
      it('should have a maxlength validator', () => {
        expectValidation('foo', 'maxlength', 3).to.be(true);
        expectValidation('foobar', 'maxlength', 3).not.to.be(true);
        $('body').append('<input type="text" id="element" value="foo" data-parsley-maxlength="10" />');
        expect($('#element').parsley().isValid()).to.be(true);
      });
      it('should have a check validator', () => {
        expectValidation(['foo', 'bar', 'baz'], 'check', [3, 5]).to.be(true);
        expectValidation(['foo', 'bar', 'baz', 'qux', 'bux'], 'check', [3, 4]).not.to.be(true);
        expectValidation(['foo', 'bar'], 'check', [3, 5]).not.to.be(true);
      });
      it('should have a mincheck validator', () => {
        expectValidation(['foo', 'bar', 'baz'], 'mincheck', 3).to.be(true);
        expectValidation(['foo', 'bar'], 'mincheck', 3).not.to.be(true);
      });
      it('should have a maxcheck validator', () => {
        expectValidation(['foo', 'bar', 'baz'], 'maxcheck', 3).to.be(true);
        expectValidation(['foo', 'bar', 'baz', 'qux'], 'maxcheck', 3).not.to.be(true);
      });
      it('should have an equalto validator', () => {
        expectValidation('', 'equalto', 'foo').to.be(true);
        expectValidation('bar', 'equalto', 'foo').not.to.be(true);
        expectValidation('foo', 'equalto', 'foo').to.be(true);
        $('body').append('<input type="text" id="element" data-parsley-equalto="#equalto" required /><input type="text" id="equalto" value="foo" />');
        expect($('#element').psly().isValid()).to.be(false);
        $('#element').val('fo');
        expect($('#element').psly().isValid()).to.be(false);
        $('#element').val('foo');
        expect($('#element').psly().isValid()).to.be(true);
        $('#equalto').remove();
      });
      it('should handle proper error message for validators', () => {
        expect(validatorRegistry.getErrorMessage({
          name: 'length',
          requirements: [3, 6]
        })).to.be('This value length is invalid. It should be between 3 and 6 characters long.');
        expect(validatorRegistry.getErrorMessage({
          name: 'notexisting'
        })).to.be('This value seems to be invalid.');
      });
      it('should handle proper error message for validators in various languages', () => {
        validatorRegistry.setLocale('fr');
        expect(validatorRegistry.getErrorMessage({
          name: 'length',
          requirements: [3, 6]
        })).to.be('Cette valeur doit contenir entre 3 et 6 caractères.');
        expect(validatorRegistry.getErrorMessage({
          name: 'notexisting'
        })).to.be('Cette valeur semble non valide.');
      });
      it('should not break for an incomplete language', () => {
        validatorRegistry.addCatalog('klingon', {}, true);
        expect(validatorRegistry.getErrorMessage({
          name: 'type',
          requirements: 'email'
        })).to.be('This value seems to be invalid.');
        expect(validatorRegistry.getErrorMessage({
          name: 'length',
          requirements: [3, 6]
        })).to.be('This value seems to be invalid.');
      });
      afterEach(() => {
        $('#element').remove();
      });
      it('should warn if a custom validator has a reserved name', () => {
        $.extend(true, Parsley.options, {
          validators: {
            excluded: {
              fn: () => {},
              priority: 42
            }
          }
        });
        expectWarning(() => {
          var validatorRegistry = new ValidatorRegistry(Parsley.options.validators);
        });
        delete Parsley.options.validators.excluded;
      });
      it('should warn when adding an already defined validator', () => {
        validatorRegistry.addValidator('foo', $.noop);
        expectWarning(() => {
          validatorRegistry.addValidator('foo', $.noop);
        });
        validatorRegistry.removeValidator('foo');
      });
      it('should warn when updating or deleting a custom validator not already defined', () => {
        expectWarning(() => {
          validatorRegistry.updateValidator('foo', () => {});
        });
        validatorRegistry.removeValidator('foo');
      });
      it('should warn when updating or deleting a custom validator not already defined', () => {
        expectWarning(() => {
          validatorRegistry.removeValidator('foo');
        });
      });
      it('should provide deprecated access through Validator for compatibility', () => {
        window.Parsley.formatMessage('foo', 'bar');
        expectWarning(() => {
          window.ParsleyValidator.formatMessage('foo', 'bar');
        });
      });
      it('should provide two ways to add error messages', () => {
        window.Parsley.addValidator('testMessage', {
          validateString: $.noop,
          messages: {
            en: 'Not good at all',
            fr: 'Très nul'
          }
        });
        window.Parsley.addMessage('es', 'testMessage', 'Muy malo');
        expect(window.Parsley.getErrorMessage({
          name: 'testMessage'
        })).to.eql('Not good at all');
        window.Parsley.setLocale('fr');
        expect(window.Parsley.getErrorMessage({
          name: 'testMessage'
        })).to.eql('Très nul');
        window.Parsley.setLocale('es');
        expect(window.Parsley.getErrorMessage({
          name: 'testMessage'
        })).to.eql('Muy malo');
        window.Parsley.setLocale('en');
      });
      it('can return the existence of a validator', () => {
        expect(window.Parsley.hasValidator('required')).to.be(true);
        expect(window.Parsley.hasValidator('deriuqer')).to.be(false);
      });
    });
  
  })));
  //# sourceMappingURL=__spec-build.js.map
  