node-red-contrib-knx-ultimate
Version:
Control your KNX and KNX Secure intallation via Node-Red! A bunch of KNX nodes, with integrated Philips HUE control and ETS group address importer. Easy to use and highly configurable.
1,315 lines (1,067 loc) • 93.3 kB
JavaScript
/*!
* Parsley.js
* Version 2.8.1 - built Sat, Feb 3rd 2018, 2:27 pm
* http://parsleyjs.org
* Guillaume Potier - <guillaume@wisembly.com>
* Marc-Andre Lafortune - <petroselinum@marc-andre.ca>
* MIT Licensed
*/
// The source code below is generated by babel as
// Parsley is written in ECMAScript 6
//
var _slice = Array.prototype.slice;
var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })();
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } }
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('jquery')) : typeof define === 'function' && define.amd ? define(['jquery'], factory) : global.parsley = factory(global.jQuery);
})(this, function ($) {
'use strict';
var globalID = 1;
var pastWarnings = {};
var Utils = {
// Parsley DOM-API
// returns object from dom attributes and values
attr: function attr(element, namespace, obj) {
var i;
var attribute;
var attributes;
var regex = new RegExp('^' + namespace, 'i');
if ('undefined' === typeof obj) obj = {};else {
// Clear all own properties. This won't affect prototype's values
for (i in obj) {
if (obj.hasOwnProperty(i)) delete obj[i];
}
}
if (!element) return obj;
attributes = element.attributes;
for (i = attributes.length; i--;) {
attribute = attributes[i];
if (attribute && attribute.specified && regex.test(attribute.name)) {
obj[this.camelize(attribute.name.slice(namespace.length))] = this.deserializeValue(attribute.value);
}
}
return obj;
},
checkAttr: function checkAttr(element, namespace, _checkAttr) {
return element.hasAttribute(namespace + _checkAttr);
},
setAttr: function setAttr(element, namespace, attr, value) {
element.setAttribute(this.dasherize(namespace + attr), String(value));
},
getType: function getType(element) {
return element.getAttribute('type') || 'text';
},
generateID: function generateID() {
return '' + globalID++;
},
/** Third party functions **/
deserializeValue: function deserializeValue(value) {
var num;
try {
return value ? value == "true" || (value == "false" ? false : value == "null" ? null : !isNaN(num = Number(value)) ? num : /^[\[\{]/.test(value) ? JSON.parse(value) : value) : value;
} catch (e) {
return value;
}
},
// Zepto camelize function
camelize: function camelize(str) {
return str.replace(/-+(.)?/g, function (match, chr) {
return chr ? chr.toUpperCase() : '';
});
},
// Zepto dasherize function
dasherize: function dasherize(str) {
return str.replace(/::/g, '/').replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2').replace(/([a-z\d])([A-Z])/g, '$1_$2').replace(/_/g, '-').toLowerCase();
},
warn: function warn() {
var _window$console;
if (window.console && 'function' === typeof window.console.warn) (_window$console = window.console).warn.apply(_window$console, arguments);
},
warnOnce: function warnOnce(msg) {
if (!pastWarnings[msg]) {
pastWarnings[msg] = true;
this.warn.apply(this, arguments);
}
},
_resetWarnings: function _resetWarnings() {
pastWarnings = {};
},
trimString: function trimString(string) {
return string.replace(/^\s+|\s+$/g, '');
},
parse: {
date: function date(string) {
var parsed = string.match(/^(\d{4,})-(\d\d)-(\d\d)$/);
if (!parsed) return null;
var _parsed$map = parsed.map(function (x) {
return parseInt(x, 10);
});
var _parsed$map2 = _slicedToArray(_parsed$map, 4);
var _ = _parsed$map2[0];
var year = _parsed$map2[1];
var month = _parsed$map2[2];
var day = _parsed$map2[3];
var date = new Date(year, month - 1, day);
if (date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day) return null;
return date;
},
string: function string(_string) {
return _string;
},
integer: function integer(string) {
if (isNaN(string)) return null;
return parseInt(string, 10);
},
number: function number(string) {
if (isNaN(string)) throw null;
return parseFloat(string);
},
'boolean': function _boolean(string) {
return !/^\s*false\s*$/i.test(string);
},
object: function object(string) {
return Utils.deserializeValue(string);
},
regexp: function regexp(_regexp) {
var flags = '';
// Test if RegExp is literal, if not, nothing to be done, otherwise, we need to isolate flags and pattern
if (/^\/.*\/(?:[gimy]*)$/.test(_regexp)) {
// Replace the regexp literal string with the first match group: ([gimy]*)
// If no flag is present, this will be a blank string
flags = _regexp.replace(/.*\/([gimy]*)$/, '$1');
// Again, replace the regexp literal string with the first match group:
// everything excluding the opening and closing slashes and the flags
_regexp = _regexp.replace(new RegExp('^/(.*?)/' + flags + '$'), '$1');
} else {
// Anchor regexp:
_regexp = '^' + _regexp + '$';
}
return new RegExp(_regexp, flags);
}
},
parseRequirement: function parseRequirement(requirementType, string) {
var converter = this.parse[requirementType || 'string'];
if (!converter) throw 'Unknown requirement specification: "' + requirementType + '"';
var converted = converter(string);
if (converted === null) throw 'Requirement is not a ' + requirementType + ': "' + string + '"';
return converted;
},
namespaceEvents: function namespaceEvents(events, namespace) {
events = this.trimString(events || '').split(/\s+/);
if (!events[0]) return '';
return $.map(events, function (evt) {
return evt + '.' + namespace;
}).join(' ');
},
difference: function difference(array, remove) {
// This is O(N^2), should be optimized
var result = [];
$.each(array, function (_, elem) {
if (remove.indexOf(elem) == -1) result.push(elem);
});
return result;
},
// Alter-ego to native Promise.all, but for jQuery
all: function all(promises) {
// jQuery treats $.when() and $.when(singlePromise) differently; let's avoid that and add spurious elements
return $.when.apply($, _toConsumableArray(promises).concat([42, 42]));
},
// Object.create polyfill, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Polyfill
objectCreate: Object.create || (function () {
var Object = function Object() {};
return function (prototype) {
if (arguments.length > 1) {
throw Error('Second argument not supported');
}
if (typeof prototype != 'object') {
throw TypeError('Argument must be an object');
}
Object.prototype = prototype;
var result = new Object();
Object.prototype = null;
return result;
};
})(),
_SubmitSelector: 'input[type="submit"], button:submit'
};
// All these options could be overriden and specified directly in DOM using
// `data-parsley-` default DOM-API
// eg: `inputs` can be set in DOM using `data-parsley-inputs="input, textarea"`
// eg: `data-parsley-stop-on-first-failing-constraint="false"`
var Defaults = {
// ### General
// Default data-namespace for DOM API
namespace: 'data-parsley-',
// Supported inputs by default
inputs: 'input, textarea, select',
// Excluded inputs by default
excluded: 'input[type=button], input[type=submit], input[type=reset], input[type=hidden]',
// Stop validating field on highest priority failing constraint
priorityEnabled: true,
// ### Field only
// identifier used to group together inputs (e.g. radio buttons...)
multiple: null,
// identifier (or array of identifiers) used to validate only a select group of inputs
group: null,
// ### UI
// Enable\Disable error messages
uiEnabled: true,
// Key events threshold before validation
validationThreshold: 3,
// Focused field on form validation error. 'first'|'last'|'none'
focus: 'first',
// event(s) that will trigger validation before first failure. eg: `input`...
trigger: false,
// event(s) that will trigger validation after first failure.
triggerAfterFailure: 'input',
// Class that would be added on every failing validation Parsley field
errorClass: 'parsley-error',
// Same for success validation
successClass: 'parsley-success',
// Return the `$element` that will receive these above success or error classes
// Could also be (and given directly from DOM) a valid selector like `'#div'`
classHandler: function classHandler(Field) {},
// Return the `$element` where errors will be appended
// Could also be (and given directly from DOM) a valid selector like `'#div'`
errorsContainer: function errorsContainer(Field) {},
// ul elem that would receive errors' list
errorsWrapper: '<ul class="parsley-errors-list"></ul>',
// li elem that would receive error message
errorTemplate: '<li></li>'
};
var Base = function Base() {
this.__id__ = Utils.generateID();
};
Base.prototype = {
asyncSupport: true, // Deprecated
_pipeAccordingToValidationResult: function _pipeAccordingToValidationResult() {
var _this = this;
var pipe = function pipe() {
var r = $.Deferred();
if (true !== _this.validationResult) r.reject();
return r.resolve().promise();
};
return [pipe, pipe];
},
actualizeOptions: function actualizeOptions() {
Utils.attr(this.element, this.options.namespace, this.domOptions);
if (this.parent && this.parent.actualizeOptions) this.parent.actualizeOptions();
return this;
},
_resetOptions: function _resetOptions(initOptions) {
this.domOptions = Utils.objectCreate(this.parent.options);
this.options = Utils.objectCreate(this.domOptions);
// Shallow copy of ownProperties of initOptions:
for (var i in initOptions) {
if (initOptions.hasOwnProperty(i)) this.options[i] = initOptions[i];
}
this.actualizeOptions();
},
_listeners: null,
// Register a callback for the given event name
// Callback is called with context as the first argument and the `this`
// The context is the current parsley instance, or window.Parsley if global
// A return value of `false` will interrupt the calls
on: function on(name, fn) {
this._listeners = this._listeners || {};
var queue = this._listeners[name] = this._listeners[name] || [];
queue.push(fn);
return this;
},
// Deprecated. Use `on` instead
subscribe: function subscribe(name, fn) {
$.listenTo(this, name.toLowerCase(), fn);
},
// Unregister a callback (or all if none is given) for the given event name
off: function off(name, fn) {
var queue = this._listeners && this._listeners[name];
if (queue) {
if (!fn) {
delete this._listeners[name];
} else {
for (var i = queue.length; i--;) if (queue[i] === fn) queue.splice(i, 1);
}
}
return this;
},
// Deprecated. Use `off`
unsubscribe: function unsubscribe(name, fn) {
$.unsubscribeTo(this, name.toLowerCase());
},
// Trigger an event of the given name
// A return value of `false` interrupts the callback chain
// Returns false if execution was interrupted
trigger: function trigger(name, target, extraArg) {
target = target || this;
var queue = this._listeners && this._listeners[name];
var result;
var parentResult;
if (queue) {
for (var i = queue.length; i--;) {
result = queue[i].call(target, target, extraArg);
if (result === false) return result;
}
}
if (this.parent) {
return this.parent.trigger(name, target, extraArg);
}
return true;
},
asyncIsValid: function asyncIsValid(group, force) {
Utils.warnOnce("asyncIsValid is deprecated; please use whenValid instead");
return this.whenValid({ group: group, force: force });
},
_findRelated: function _findRelated() {
return this.options.multiple ? $(this.parent.element.querySelectorAll('[' + this.options.namespace + 'multiple="' + this.options.multiple + '"]')) : this.$element;
}
};
var convertArrayRequirement = function convertArrayRequirement(string, length) {
var m = string.match(/^\s*\[(.*)\]\s*$/);
if (!m) throw 'Requirement is not an array: "' + string + '"';
var values = m[1].split(',').map(Utils.trimString);
if (values.length !== length) throw 'Requirement has ' + values.length + ' values when ' + length + ' are needed';
return values;
};
var convertExtraOptionRequirement = function convertExtraOptionRequirement(requirementSpec, string, extraOptionReader) {
var main = null;
var extra = {};
for (var key in requirementSpec) {
if (key) {
var value = extraOptionReader(key);
if ('string' === typeof value) value = Utils.parseRequirement(requirementSpec[key], value);
extra[key] = value;
} else {
main = Utils.parseRequirement(requirementSpec[key], string);
}
}
return [main, extra];
};
// A Validator needs to implement the methods `validate` and `parseRequirements`
var Validator = function Validator(spec) {
$.extend(true, this, spec);
};
Validator.prototype = {
// Returns `true` iff the given `value` is valid according the given requirements.
validate: function validate(value, requirementFirstArg) {
if (this.fn) {
// Legacy style validator
if (arguments.length > 3) // If more args then value, requirement, instance...
requirementFirstArg = [].slice.call(arguments, 1, -1); // Skip first arg (value) and last (instance), combining the rest
return this.fn(value, requirementFirstArg);
}
if (Array.isArray(value)) {
if (!this.validateMultiple) throw 'Validator `' + this.name + '` does not handle multiple values';
return this.validateMultiple.apply(this, arguments);
} else {
var instance = arguments[arguments.length - 1];
if (this.validateDate && instance._isDateInput()) {
arguments[0] = Utils.parse.date(arguments[0]);
if (arguments[0] === null) return false;
return this.validateDate.apply(this, arguments);
}
if (this.validateNumber) {
if (isNaN(value)) return false;
arguments[0] = parseFloat(arguments[0]);
return this.validateNumber.apply(this, arguments);
}
if (this.validateString) {
return this.validateString.apply(this, arguments);
}
throw 'Validator `' + this.name + '` only handles multiple values';
}
},
// Parses `requirements` into an array of arguments,
// according to `this.requirementType`
parseRequirements: function parseRequirements(requirements, extraOptionReader) {
if ('string' !== typeof requirements) {
// Assume requirement already parsed
// but make sure we return an array
return Array.isArray(requirements) ? requirements : [requirements];
}
var type = this.requirementType;
if (Array.isArray(type)) {
var values = convertArrayRequirement(requirements, type.length);
for (var i = 0; i < values.length; i++) values[i] = Utils.parseRequirement(type[i], values[i]);
return values;
} else if ($.isPlainObject(type)) {
return convertExtraOptionRequirement(type, requirements, extraOptionReader);
} else {
return [Utils.parseRequirement(type, requirements)];
}
},
// Defaults:
requirementType: 'string',
priority: 2
};
var ValidatorRegistry = function ValidatorRegistry(validators, catalog) {
this.__class__ = 'ValidatorRegistry';
// Default Parsley locale is en
this.locale = 'en';
this.init(validators || {}, catalog || {});
};
var typeTesters = {
email: /^((([a-zA-Z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-zA-Z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-zA-Z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-zA-Z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-zA-Z]|\d|-|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-zA-Z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-zA-Z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-zA-Z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-zA-Z]|\d|-|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-zA-Z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/,
// Follow https://www.w3.org/TR/html5/infrastructure.html#floating-point-numbers
number: /^-?(\d*\.)?\d+(e[-+]?\d+)?$/i,
integer: /^-?\d+$/,
digits: /^\d+$/,
alphanum: /^\w+$/i,
date: {
test: function test(value) {
return Utils.parse.date(value) !== null;
}
},
url: new RegExp("^" +
// protocol identifier
"(?:(?:https?|ftp)://)?" + // ** mod: make scheme optional
// user:pass authentication
"(?:\\S+(?::\\S*)?@)?" + "(?:" +
// IP address exclusion
// private & local networks
// "(?!(?:10|127)(?:\\.\\d{1,3}){3})" + // ** mod: allow local networks
// "(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})" + // ** mod: allow local networks
// "(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})" + // ** mod: allow local networks
// IP address dotted notation octets
// excludes loopback network 0.0.0.0
// excludes reserved space >= 224.0.0.0
// excludes network & broacast addresses
// (first & last IP address of each class)
"(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" + "(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" + "(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" + "|" +
// host name
'(?:(?:[a-zA-Z\\u00a1-\\uffff0-9]-*)*[a-zA-Z\\u00a1-\\uffff0-9]+)' +
// domain name
'(?:\\.(?:[a-zA-Z\\u00a1-\\uffff0-9]-*)*[a-zA-Z\\u00a1-\\uffff0-9]+)*' +
// TLD identifier
'(?:\\.(?:[a-zA-Z\\u00a1-\\uffff]{2,}))' + ")" +
// port number
"(?::\\d{2,5})?" +
// resource path
"(?:/\\S*)?" + "$")
};
typeTesters.range = typeTesters.number;
// See http://stackoverflow.com/a/10454560/8279
var decimalPlaces = function decimalPlaces(num) {
var match = ('' + num).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);
if (!match) {
return 0;
}
return Math.max(0,
// Number of digits right of decimal point.
(match[1] ? match[1].length : 0) - (
// Adjust for scientific notation.
match[2] ? +match[2] : 0));
};
// parseArguments('number', ['1', '2']) => [1, 2]
var ValidatorRegistry__parseArguments = function ValidatorRegistry__parseArguments(type, args) {
return args.map(Utils.parse[type]);
};
// operatorToValidator returns a validating function for an operator function, applied to the given type
var ValidatorRegistry__operatorToValidator = function ValidatorRegistry__operatorToValidator(type, operator) {
return function (value) {
for (var _len = arguments.length, requirementsAndInput = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
requirementsAndInput[_key - 1] = arguments[_key];
}
requirementsAndInput.pop(); // Get rid of `input` argument
return operator.apply(undefined, [value].concat(_toConsumableArray(ValidatorRegistry__parseArguments(type, requirementsAndInput))));
};
};
var ValidatorRegistry__comparisonOperator = function ValidatorRegistry__comparisonOperator(operator) {
return {
validateDate: ValidatorRegistry__operatorToValidator('date', operator),
validateNumber: ValidatorRegistry__operatorToValidator('number', operator),
requirementType: operator.length <= 2 ? 'string' : ['string', 'string'], // Support operators with a 1 or 2 requirement(s)
priority: 30
};
};
ValidatorRegistry.prototype = {
init: function init(validators, catalog) {
this.catalog = catalog;
// Copy prototype's validators:
this.validators = _extends({}, this.validators);
for (var name in validators) this.addValidator(name, validators[name].fn, validators[name].priority);
window.Parsley.trigger('parsley:validator:init');
},
// Set new messages locale if we have dictionary loaded in ParsleyConfig.i18n
setLocale: function setLocale(locale) {
if ('undefined' === typeof this.catalog[locale]) throw new Error(locale + ' is not available in the catalog');
this.locale = locale;
return this;
},
// Add a new messages catalog for a given locale. Set locale for this catalog if set === `true`
addCatalog: function addCatalog(locale, messages, set) {
if ('object' === typeof messages) this.catalog[locale] = messages;
if (true === set) return this.setLocale(locale);
return this;
},
// Add a specific message for a given constraint in a given locale
addMessage: function addMessage(locale, name, message) {
if ('undefined' === typeof this.catalog[locale]) this.catalog[locale] = {};
this.catalog[locale][name] = message;
return this;
},
// Add messages for a given locale
addMessages: function addMessages(locale, nameMessageObject) {
for (var name in nameMessageObject) this.addMessage(locale, name, nameMessageObject[name]);
return this;
},
// Add a new validator
//
// addValidator('custom', {
// requirementType: ['integer', 'integer'],
// validateString: function(value, from, to) {},
// priority: 22,
// messages: {
// en: "Hey, that's no good",
// fr: "Aye aye, pas bon du tout",
// }
// })
//
// Old API was addValidator(name, function, priority)
//
addValidator: function addValidator(name, arg1, arg2) {
if (this.validators[name]) Utils.warn('Validator "' + name + '" is already defined.');else if (Defaults.hasOwnProperty(name)) {
Utils.warn('"' + name + '" is a restricted keyword and is not a valid validator name.');
return;
}
return this._setValidator.apply(this, arguments);
},
hasValidator: function hasValidator(name) {
return !!this.validators[name];
},
updateValidator: function updateValidator(name, arg1, arg2) {
if (!this.validators[name]) {
Utils.warn('Validator "' + name + '" is not already defined.');
return this.addValidator.apply(this, arguments);
}
return this._setValidator.apply(this, arguments);
},
removeValidator: function removeValidator(name) {
if (!this.validators[name]) Utils.warn('Validator "' + name + '" is not defined.');
delete this.validators[name];
return this;
},
_setValidator: function _setValidator(name, validator, priority) {
if ('object' !== typeof validator) {
// Old style validator, with `fn` and `priority`
validator = {
fn: validator,
priority: priority
};
}
if (!validator.validate) {
validator = new Validator(validator);
}
this.validators[name] = validator;
for (var locale in validator.messages || {}) this.addMessage(locale, name, validator.messages[locale]);
return this;
},
getErrorMessage: function getErrorMessage(constraint) {
var message;
// Type constraints are a bit different, we have to match their requirements too to find right error message
if ('type' === constraint.name) {
var typeMessages = this.catalog[this.locale][constraint.name] || {};
message = typeMessages[constraint.requirements];
} else message = this.formatMessage(this.catalog[this.locale][constraint.name], constraint.requirements);
return message || this.catalog[this.locale].defaultMessage || this.catalog.en.defaultMessage;
},
// Kind of light `sprintf()` implementation
formatMessage: function formatMessage(string, parameters) {
if ('object' === typeof parameters) {
for (var i in parameters) string = this.formatMessage(string, parameters[i]);
return string;
}
return 'string' === typeof string ? string.replace(/%s/i, parameters) : '';
},
// Here is the Parsley default validators list.
// A validator is an object with the following key values:
// - priority: an integer
// - requirement: 'string' (default), 'integer', 'number', 'regexp' or an Array of these
// - validateString, validateMultiple, validateNumber: functions returning `true`, `false` or a promise
// Alternatively, a validator can be a function that returns such an object
//
validators: {
notblank: {
validateString: function validateString(value) {
return (/\S/.test(value)
);
},
priority: 2
},
required: {
validateMultiple: function validateMultiple(values) {
return values.length > 0;
},
validateString: function validateString(value) {
return (/\S/.test(value)
);
},
priority: 512
},
type: {
validateString: function validateString(value, type) {
var _ref = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
var _ref$step = _ref.step;
var step = _ref$step === undefined ? 'any' : _ref$step;
var _ref$base = _ref.base;
var base = _ref$base === undefined ? 0 : _ref$base;
var tester = typeTesters[type];
if (!tester) {
throw new Error('validator type `' + type + '` is not supported');
}
if (!tester.test(value)) return false;
if ('number' === type) {
if (!/^any$/i.test(step || '')) {
var nb = Number(value);
var decimals = Math.max(decimalPlaces(step), decimalPlaces(base));
if (decimalPlaces(nb) > decimals) // Value can't have too many decimals
return false;
// Be careful of rounding errors by using integers.
var toInt = function toInt(f) {
return Math.round(f * Math.pow(10, decimals));
};
if ((toInt(nb) - toInt(base)) % toInt(step) != 0) return false;
}
}
return true;
},
requirementType: {
'': 'string',
step: 'string',
base: 'number'
},
priority: 256
},
pattern: {
validateString: function validateString(value, regexp) {
return regexp.test(value);
},
requirementType: 'regexp',
priority: 64
},
minlength: {
validateString: function validateString(value, requirement) {
return value.length >= requirement;
},
requirementType: 'integer',
priority: 30
},
maxlength: {
validateString: function validateString(value, requirement) {
return value.length <= requirement;
},
requirementType: 'integer',
priority: 30
},
length: {
validateString: function validateString(value, min, max) {
return value.length >= min && value.length <= max;
},
requirementType: ['integer', 'integer'],
priority: 30
},
mincheck: {
validateMultiple: function validateMultiple(values, requirement) {
return values.length >= requirement;
},
requirementType: 'integer',
priority: 30
},
maxcheck: {
validateMultiple: function validateMultiple(values, requirement) {
return values.length <= requirement;
},
requirementType: 'integer',
priority: 30
},
check: {
validateMultiple: function validateMultiple(values, min, max) {
return values.length >= min && values.length <= max;
},
requirementType: ['integer', 'integer'],
priority: 30
},
min: ValidatorRegistry__comparisonOperator(function (value, requirement) {
return value >= requirement;
}),
max: ValidatorRegistry__comparisonOperator(function (value, requirement) {
return value <= requirement;
}),
range: ValidatorRegistry__comparisonOperator(function (value, min, max) {
return value >= min && value <= max;
}),
equalto: {
validateString: function validateString(value, refOrValue) {
var $reference = $(refOrValue);
if ($reference.length) return value === $reference.val();else return value === refOrValue;
},
priority: 256
}
}
};
var UI = {};
var diffResults = function diffResults(newResult, oldResult, deep) {
var added = [];
var kept = [];
for (var i = 0; i < newResult.length; i++) {
var found = false;
for (var j = 0; j < oldResult.length; j++) if (newResult[i].assert.name === oldResult[j].assert.name) {
found = true;
break;
}
if (found) kept.push(newResult[i]);else added.push(newResult[i]);
}
return {
kept: kept,
added: added,
removed: !deep ? diffResults(oldResult, newResult, true).added : []
};
};
UI.Form = {
_actualizeTriggers: function _actualizeTriggers() {
var _this2 = this;
this.$element.on('submit.Parsley', function (evt) {
_this2.onSubmitValidate(evt);
});
this.$element.on('click.Parsley', Utils._SubmitSelector, function (evt) {
_this2.onSubmitButton(evt);
});
// UI could be disabled
if (false === this.options.uiEnabled) return;
this.element.setAttribute('novalidate', '');
},
focus: function focus() {
this._focusedField = null;
if (true === this.validationResult || 'none' === this.options.focus) return null;
for (var i = 0; i < this.fields.length; i++) {
var field = this.fields[i];
if (true !== field.validationResult && field.validationResult.length > 0 && 'undefined' === typeof field.options.noFocus) {
this._focusedField = field.$element;
if ('first' === this.options.focus) break;
}
}
if (null === this._focusedField) return null;
return this._focusedField.focus();
},
_destroyUI: function _destroyUI() {
// Reset all event listeners
this.$element.off('.Parsley');
}
};
UI.Field = {
_reflowUI: function _reflowUI() {
this._buildUI();
// If this field doesn't have an active UI don't bother doing something
if (!this._ui) return;
// Diff between two validation results
var diff = diffResults(this.validationResult, this._ui.lastValidationResult);
// Then store current validation result for next reflow
this._ui.lastValidationResult = this.validationResult;
// Handle valid / invalid / none field class
this._manageStatusClass();
// Add, remove, updated errors messages
this._manageErrorsMessages(diff);
// Triggers impl
this._actualizeTriggers();
// If field is not valid for the first time, bind keyup trigger to ease UX and quickly inform user
if ((diff.kept.length || diff.added.length) && !this._failedOnce) {
this._failedOnce = true;
this._actualizeTriggers();
}
},
// Returns an array of field's error message(s)
getErrorsMessages: function getErrorsMessages() {
// No error message, field is valid
if (true === this.validationResult) return [];
var messages = [];
for (var i = 0; i < this.validationResult.length; i++) messages.push(this.validationResult[i].errorMessage || this._getErrorMessage(this.validationResult[i].assert));
return messages;
},
// It's a goal of Parsley that this method is no longer required [#1073]
addError: function addError(name) {
var _ref2 = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
var message = _ref2.message;
var assert = _ref2.assert;
var _ref2$updateClass = _ref2.updateClass;
var updateClass = _ref2$updateClass === undefined ? true : _ref2$updateClass;
this._buildUI();
this._addError(name, { message: message, assert: assert });
if (updateClass) this._errorClass();
},
// It's a goal of Parsley that this method is no longer required [#1073]
updateError: function updateError(name) {
var _ref3 = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
var message = _ref3.message;
var assert = _ref3.assert;
var _ref3$updateClass = _ref3.updateClass;
var updateClass = _ref3$updateClass === undefined ? true : _ref3$updateClass;
this._buildUI();
this._updateError(name, { message: message, assert: assert });
if (updateClass) this._errorClass();
},
// It's a goal of Parsley that this method is no longer required [#1073]
removeError: function removeError(name) {
var _ref4 = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
var _ref4$updateClass = _ref4.updateClass;
var updateClass = _ref4$updateClass === undefined ? true : _ref4$updateClass;
this._buildUI();
this._removeError(name);
// edge case possible here: remove a standard Parsley error that is still failing in this.validationResult
// but highly improbable cuz' manually removing a well Parsley handled error makes no sense.
if (updateClass) this._manageStatusClass();
},
_manageStatusClass: function _manageStatusClass() {
if (this.hasConstraints() && this.needsValidation() && true === this.validationResult) this._successClass();else if (this.validationResult.length > 0) this._errorClass();else this._resetClass();
},
_manageErrorsMessages: function _manageErrorsMessages(diff) {
if ('undefined' !== typeof this.options.errorsMessagesDisabled) return;
// Case where we have errorMessage option that configure an unique field error message, regardless failing validators
if ('undefined' !== typeof this.options.errorMessage) {
if (diff.added.length || diff.kept.length) {
this._insertErrorWrapper();
if (0 === this._ui.$errorsWrapper.find('.parsley-custom-error-message').length) this._ui.$errorsWrapper.append($(this.options.errorTemplate).addClass('parsley-custom-error-message'));
return this._ui.$errorsWrapper.addClass('filled').find('.parsley-custom-error-message').html(this.options.errorMessage);
}
return this._ui.$errorsWrapper.removeClass('filled').find('.parsley-custom-error-message').remove();
}
// Show, hide, update failing constraints messages
for (var i = 0; i < diff.removed.length; i++) this._removeError(diff.removed[i].assert.name);
for (i = 0; i < diff.added.length; i++) this._addError(diff.added[i].assert.name, { message: diff.added[i].errorMessage, assert: diff.added[i].assert });
for (i = 0; i < diff.kept.length; i++) this._updateError(diff.kept[i].assert.name, { message: diff.kept[i].errorMessage, assert: diff.kept[i].assert });
},
_addError: function _addError(name, _ref5) {
var message = _ref5.message;
var assert = _ref5.assert;
this._insertErrorWrapper();
this._ui.$errorClassHandler.attr('aria-describedby', this._ui.errorsWrapperId);
this._ui.$errorsWrapper.addClass('filled').append($(this.options.errorTemplate).addClass('parsley-' + name).html(message || this._getErrorMessage(assert)));
},
_updateError: function _updateError(name, _ref6) {
var message = _ref6.message;
var assert = _ref6.assert;
this._ui.$errorsWrapper.addClass('filled').find('.parsley-' + name).html(message || this._getErrorMessage(assert));
},
_removeError: function _removeError(name) {
this._ui.$errorClassHandler.removeAttr('aria-describedby');
this._ui.$errorsWrapper.removeClass('filled').find('.parsley-' + name).remove();
},
_getErrorMessage: function _getErrorMessage(constraint) {
var customConstraintErrorMessage = constraint.name + 'Message';
if ('undefined' !== typeof this.options[customConstraintErrorMessage]) return window.Parsley.formatMessage(this.options[customConstraintErrorMessage], constraint.requirements);
return window.Parsley.getErrorMessage(constraint);
},
_buildUI: function _buildUI() {
// UI could be already built or disabled
if (this._ui || false === this.options.uiEnabled) return;
var _ui = {};
// Give field its Parsley id in DOM
this.element.setAttribute(this.options.namespace + 'id', this.__id__);
/** Generate important UI elements and store them in this **/
// $errorClassHandler is the $element that woul have parsley-error and parsley-success classes
_ui.$errorClassHandler = this._manageClassHandler();
// $errorsWrapper is a div that would contain the various field errors, it will be appended into $errorsContainer
_ui.errorsWrapperId = 'parsley-id-' + (this.options.multiple ? 'multiple-' + this.options.multiple : this.__id__);
_ui.$errorsWrapper = $(this.options.errorsWrapper).attr('id', _ui.errorsWrapperId);
// ValidationResult UI storage to detect what have changed bwt two validations, and update DOM accordingly
_ui.lastValidationResult = [];
_ui.validationInformationVisible = false;
// Store it in this for later
this._ui = _ui;
},
// Determine which element will have `parsley-error` and `parsley-success` classes
_manageClassHandler: function _manageClassHandler() {
// Class handled could also be determined by function given in Parsley options
if ('string' === typeof this.options.classHandler && $(this.options.classHandler).length) return $(this.options.classHandler);
// Class handled could also be determined by function given in Parsley options
var $handlerFunction = this.options.classHandler;
// It might also be the function name of a global function
if ('string' === typeof this.options.classHandler && 'function' === typeof window[this.options.classHandler]) $handlerFunction = window[this.options.classHandler];
if ('function' === typeof $handlerFunction) {
var $handler = $handlerFunction.call(this, this);
// If this function returned a valid existing DOM element, go for it
if ('undefined' !== typeof $handler && $handler.length) return $handler;
} else if ('object' === typeof $handlerFunction && $handlerFunction instanceof jQuery && $handlerFunction.length) {
return $handlerFunction;
} else if ($handlerFunction) {
Utils.warn('The class handler `' + $handlerFunction + '` does not exist in DOM nor as a global JS function');
}
return this._inputHolder();
},
_inputHolder: function _inputHolder() {
// if simple element (input, texatrea, select...) it will perfectly host the classes and precede the error container
if (!this.options.multiple || this.element.nodeName === 'SELECT') return this.$element;
// But if multiple element (radio, checkbox), that would be their parent
return this.$element.parent();
},
_insertErrorWrapper: function _insertErrorWrapper() {
var $errorsContainer = this.options.errorsContainer;
// Nothing to do if already inserted
if (0 !== this._ui.$errorsWrapper.parent().length) return this._ui.$errorsWrapper.parent();
if ('string' === typeof $errorsContainer) {
if ($($errorsContainer).length) return $($errorsContainer).append(this._ui.$errorsWrapper);else if ('function' === typeof window[$errorsContainer]) $errorsContainer = window[$errorsContainer];else Utils.warn('The errors container `' + $errorsContainer + '` does not exist in DOM nor as a global JS function');
}
if ('function' === typeof $errorsContainer) $errorsContainer = $errorsContainer.call(this, this);
if ('object' === typeof $errorsContainer && $errorsContainer.length) return $errorsContainer.append(this._ui.$errorsWrapper);
return this._inputHolder().after(this._ui.$errorsWrapper);
},
_actualizeTriggers: function _actualizeTriggers() {
var _this3 = this;
var $toBind = this._findRelated();
var trigger;
// Remove Parsley events already bound on this field
$toBind.off('.Parsley');
if (this._failedOnce) $toBind.on(Utils.namespaceEvents(this.options.triggerAfterFailure, 'Parsley'), function () {
_this3._validateIfNeeded();
});else if (trigger = Utils.namespaceEvents(this.options.trigger, 'Parsley')) {
$toBind.on(trigger, function (event) {
_this3._validateIfNeeded(event);
});
}
},
_validateIfNeeded: function _validateIfNeeded(event) {
var _this4 = this;
// For keyup, keypress, keydown, input... events that could be a little bit obstrusive
// do not validate if val length < min threshold on first validation. Once field have been validated once and info
// about success or failure have been displayed, always validate with this trigger to reflect every yalidation change.
if (event && /key|input/.test(event.type)) if (!(this._ui && this._ui.validationInformationVisible) && this.getValue().length <= this.options.validationThreshold) return;
if (this.options.debounce) {
window.clearTimeout(this._debounced);
this._debounced = window.setTimeout(function () {
return _this4.validate();
}, this.options.debounce);
} else this.validate();
},
_resetUI: function _resetUI() {
// Reset all event listeners
this._failedOnce = false;
this._actualizeTriggers();
// Nothing to do if UI never initialized for this field
if ('undefined' === typeof this._ui) return;
// Reset all errors' li
this._ui.$errorsWrapper.removeClass('filled').children().remove();
// Reset validation class
this._resetClass();
// Reset validation flags and last validation result
this._ui.lastValidationResult = [];
this._ui.validationInformationVisible = false;
},
_destroyUI: function _destroyUI() {
this._resetUI();
if ('undefined' !== typeof this._ui) this._ui.$errorsWrapper.remove();
delete this._ui;
},
_successClass: function _successClass() {
this._ui.validationInformationVisible = true;
this._ui.$errorClassHandler.removeClass(this.options.errorClass).addClass(this.options.successClass);
},
_errorClass: function _errorClass() {
this._ui.validationInformationVisible = true;
this._ui.$errorClassHandler.removeClass(this.options.successClass).addClass(this.options.errorClass);
},
_resetClass: function _resetClass() {
this._ui.$errorClassHandler.removeClass(this.options.successClass).removeClass(this.options.errorClass);
}
};
var Form = function Form(element, domOptions, options) {
this.__class__ = 'Form';
this.element = element;
this.$element = $(element);
this.domOptions = domOptions;
this.options = options;
this.parent = window.Parsley;
this.fields = [];
this.validationResult = null;
};
var Form__statusMapping = { pending: null, resolved: true, rejected: false };
Form.prototype = {
onSubmitValidate: function onSubmitValidate(event) {
var _this5 = this;
// This is a Parsley generated submit event, do not validate, do not prevent, simply exit and keep normal behavior
if (true === event.parsley) return;
// If we didn't come here through a submit button, use the first one in the form
var submitSource = this._submitSource || this.$element.find(Utils._SubmitSelector)[0];
this._submitSource = null;
this.$element.find('.parsley-synthetic-submit-button').prop('disabled', true);
if (submitSource && null !== submitSource.getAttribute('formnovalidate')) return;
window.Parsley._remoteCache = {};
var promise = this.whenValidate({ event: event });
if ('resolved' === promise.state() && false !== this._trigger('submit')) {
// All good, let event go through. We make this distinction because browsers
// differ in their handling of `submit` being called from inside a submit event [#1047]
} else {
// Rejected or pending: cancel this submit
event.stopImmediatePropagation();
event.preventDefault();
if ('pending' === promise.state()) promise.done(function () {
_this5._submit(submitSource);
});
}
},
onSubmitButton: function onSubmitButton(event) {
this._submitSource = event.currentTarget;
},
// internal
// _submit submits the form, this time without going through the validations.
// Care must be taken to "fake" the actual submit button being clicked.
_submit: function _submit(submitSource) {
if (false === this._trigger('submit')) return;
// Add submit button's data
if (submitSource) {
var $synthetic = this.$element.find('.parsley-synthetic-submit-button').prop('disabled', false);
if (0 === $synthetic.length) $synthetic = $('<input class="parsley-synthetic-submit-button" type="hidden">').appendTo(this.$element);
$synthetic.attr({
name: submitSource.getAttribute('name'),
value: submitSource.getAttribute('value')
});
}
this.$element.trigger(_extends($.Event('submit'), { parsley: true }));
},
// Performs validation on fields while triggering events.
// @returns `true` if all validations succeeds, `false`
// if a failure is immediately detected, or `null`
// if dependant on a promise.
// Consider using `whenValidate` instead.
validate: function validate(options) {
if (arguments.length >= 1 && !$.isPlainObject(options)) {
Utils.warnOnce('Calling validate on a parsley form without passing arguments as an object is deprecated.');
var _arguments = _slice.call(arguments);
var group = _arguments[0];
var force = _arguments[1];
var event = _arguments[2];
options = { group: group, force: force, event: event };
}
return Form__statusMapping[this.whenValidate(options).state()];
},
whenValidate: function whenValidate() {
var _Utils$all$done$fail$always,
_this6 = this;
var _ref7 = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
var group = _ref7.group;
var force = _ref7.force;
var event = _ref7.event;
this.submitEvent = event;
if (event) {
this.submitEvent = _extends({}, event, { preventDefault: function preventDefault() {
Utils.warnOnce("Using `this.submitEvent.preventDefault()` is deprecated; instead, call `this.validationResult = false`");
_this6.validationResult = false;
} });
}
this.validationResult = true;
// fire validate event to eventually modify things before every validation
this._trigger('validate');
// Refresh form DOM options and form's fields that could have changed
this._refreshFields();
var promises = this._withoutReactualizingFormOptions(function () {
return $.map(_this6.fields, function (field) {
return field.whenValidate({ force: force, group: group });
});
});
return (_Utils$all$done$fail$always = Utils.all(promises).done(function () {
_this6._trigger('success');
}).fail(function () {
_this6.validationResult = false;
_this6.focus();
_this6._trigger('error');
}).always(function () {
_this6._trigger('validated');
})).pipe.apply(_Utils$all$done$fail$always, _toConsumableArray(t