mirror of
https://gitlab.com/JKANetwork/CheckServer.git
synced 2026-06-22 08:06:23 +02:00
Start again
This commit is contained in:
+145
@@ -0,0 +1,145 @@
|
||||
import $ from 'jquery';
|
||||
import ParsleyUtils from './utils';
|
||||
|
||||
var ParsleyAbstract = function () {
|
||||
this.__id__ = ParsleyUtils.generateID();
|
||||
};
|
||||
|
||||
ParsleyAbstract.prototype = {
|
||||
asyncSupport: true, // Deprecated
|
||||
|
||||
_pipeAccordingToValidationResult: function () {
|
||||
var pipe = () => {
|
||||
var r = $.Deferred();
|
||||
if (true !== this.validationResult)
|
||||
r.reject();
|
||||
return r.resolve().promise();
|
||||
};
|
||||
return [pipe, pipe];
|
||||
},
|
||||
|
||||
actualizeOptions: function () {
|
||||
ParsleyUtils.attr(this.$element, this.options.namespace, this.domOptions);
|
||||
if (this.parent && this.parent.actualizeOptions)
|
||||
this.parent.actualizeOptions();
|
||||
return this;
|
||||
},
|
||||
|
||||
_resetOptions: function (initOptions) {
|
||||
this.domOptions = ParsleyUtils.objectCreate(this.parent.options);
|
||||
this.options = ParsleyUtils.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 (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(name, fn) {
|
||||
$.listenTo(this, name.toLowerCase(), fn);
|
||||
},
|
||||
|
||||
// Unregister a callback (or all if none is given) for the given event name
|
||||
off: function (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(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 (name, target, extraArg) {
|
||||
target = target || this;
|
||||
var queue = this._listeners && this._listeners[name];
|
||||
var result;
|
||||
var parentResult;
|
||||
if (queue) {
|
||||
for (var i = queue.length; i--; ) {
|
||||
result = queue[i].call(target, target, extraArg);
|
||||
if (result === false) return result;
|
||||
}
|
||||
}
|
||||
if (this.parent) {
|
||||
return this.parent.trigger(name, target, extraArg);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
// Reset UI
|
||||
reset: function () {
|
||||
// Field case: just emit a reset event for UI
|
||||
if ('ParsleyForm' !== this.__class__) {
|
||||
this._resetUI();
|
||||
return this._trigger('reset');
|
||||
}
|
||||
|
||||
// Form case: emit a reset event for each field
|
||||
for (var i = 0; i < this.fields.length; i++)
|
||||
this.fields[i].reset();
|
||||
|
||||
this._trigger('reset');
|
||||
},
|
||||
|
||||
// Destroy Parsley instance (+ UI)
|
||||
destroy: function () {
|
||||
// Field case: emit destroy event to clean UI and then destroy stored instance
|
||||
this._destroyUI();
|
||||
if ('ParsleyForm' !== this.__class__) {
|
||||
this.$element.removeData('Parsley');
|
||||
this.$element.removeData('ParsleyFieldMultiple');
|
||||
this._trigger('destroy');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Form case: destroy all its fields and then destroy stored instance
|
||||
for (var i = 0; i < this.fields.length; i++)
|
||||
this.fields[i].destroy();
|
||||
|
||||
this.$element.removeData('Parsley');
|
||||
this._trigger('destroy');
|
||||
},
|
||||
|
||||
asyncIsValid: function (group, force) {
|
||||
ParsleyUtils.warnOnce("asyncIsValid is deprecated; please use whenValid instead");
|
||||
return this.whenValid({group, force});
|
||||
},
|
||||
|
||||
_findRelated: function () {
|
||||
return this.options.multiple ?
|
||||
this.parent.$element.find(`[${this.options.namespace}multiple="${this.options.multiple}"]`)
|
||||
: this.$element;
|
||||
}
|
||||
};
|
||||
|
||||
export default ParsleyAbstract;
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
// All these options could be overriden and specified directly in DOM using
|
||||
// `data-parsley-` default DOM-API
|
||||
// eg: `inputs` can be set in DOM using `data-parsley-inputs="input, textarea"`
|
||||
// eg: `data-parsley-stop-on-first-failing-constraint="false"`
|
||||
|
||||
var ParsleyDefaults = {
|
||||
// ### General
|
||||
|
||||
// Default data-namespace for DOM API
|
||||
namespace: 'data-parsley-',
|
||||
|
||||
// Supported inputs by default
|
||||
inputs: 'input, textarea, select',
|
||||
|
||||
// Excluded inputs by default
|
||||
excluded: 'input[type=button], input[type=submit], input[type=reset], input[type=hidden]',
|
||||
|
||||
// Stop validating field on highest priority failing constraint
|
||||
priorityEnabled: true,
|
||||
|
||||
// ### Field only
|
||||
|
||||
// identifier used to group together inputs (e.g. radio buttons...)
|
||||
multiple: null,
|
||||
|
||||
// identifier (or array of identifiers) used to validate only a select group of inputs
|
||||
group: null,
|
||||
|
||||
// ### UI
|
||||
// Enable\Disable error messages
|
||||
uiEnabled: true,
|
||||
|
||||
// Key events threshold before validation
|
||||
validationThreshold: 3,
|
||||
|
||||
// Focused field on form validation error. 'first'|'last'|'none'
|
||||
focus: 'first',
|
||||
|
||||
// event(s) that will trigger validation before first failure. eg: `input`...
|
||||
trigger: false,
|
||||
|
||||
// event(s) that will trigger validation after first failure.
|
||||
triggerAfterFailure: 'input',
|
||||
|
||||
// Class that would be added on every failing validation Parsley field
|
||||
errorClass: 'parsley-error',
|
||||
|
||||
// Same for success validation
|
||||
successClass: 'parsley-success',
|
||||
|
||||
// Return the `$element` that will receive these above success or error classes
|
||||
// Could also be (and given directly from DOM) a valid selector like `'#div'`
|
||||
classHandler: function (ParsleyField) {},
|
||||
|
||||
// Return the `$element` where errors will be appended
|
||||
// Could also be (and given directly from DOM) a valid selector like `'#div'`
|
||||
errorsContainer: function (ParsleyField) {},
|
||||
|
||||
// ul elem that would receive errors' list
|
||||
errorsWrapper: '<ul class="parsley-errors-list"></ul>',
|
||||
|
||||
// li elem that would receive error message
|
||||
errorTemplate: '<li></li>'
|
||||
};
|
||||
|
||||
export default ParsleyDefaults;
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
import $ from 'jquery';
|
||||
import ParsleyUtils from './utils';
|
||||
import ParsleyAbstract from './abstract';
|
||||
import ParsleyForm from './form';
|
||||
import ParsleyField from './field';
|
||||
import ParsleyMultiple from './multiple';
|
||||
|
||||
var ParsleyFactory = function (element, options, parsleyFormInstance) {
|
||||
this.$element = $(element);
|
||||
|
||||
// If the element has already been bound, returns its saved Parsley instance
|
||||
var savedparsleyFormInstance = this.$element.data('Parsley');
|
||||
if (savedparsleyFormInstance) {
|
||||
|
||||
// If the saved instance has been bound without a ParsleyForm parent and there is one given in this call, add it
|
||||
if ('undefined' !== typeof parsleyFormInstance && savedparsleyFormInstance.parent === window.Parsley) {
|
||||
savedparsleyFormInstance.parent = parsleyFormInstance;
|
||||
savedparsleyFormInstance._resetOptions(savedparsleyFormInstance.options);
|
||||
}
|
||||
|
||||
return savedparsleyFormInstance;
|
||||
}
|
||||
|
||||
// Parsley must be instantiated with a DOM element or jQuery $element
|
||||
if (!this.$element.length)
|
||||
throw new Error('You must bind Parsley on an existing element.');
|
||||
|
||||
if ('undefined' !== typeof parsleyFormInstance && 'ParsleyForm' !== parsleyFormInstance.__class__)
|
||||
throw new Error('Parent instance must be a ParsleyForm instance');
|
||||
|
||||
this.parent = parsleyFormInstance || window.Parsley;
|
||||
return this.init(options);
|
||||
};
|
||||
|
||||
ParsleyFactory.prototype = {
|
||||
init: function (options) {
|
||||
this.__class__ = 'Parsley';
|
||||
this.__version__ = '@@version';
|
||||
this.__id__ = ParsleyUtils.generateID();
|
||||
|
||||
// Pre-compute options
|
||||
this._resetOptions(options);
|
||||
|
||||
// A ParsleyForm 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.is('form') || (ParsleyUtils.checkAttr(this.$element, this.options.namespace, 'validate') && !this.$element.is(this.options.inputs)))
|
||||
return this.bind('parsleyForm');
|
||||
|
||||
// Every other element is bound as a `ParsleyField` or `ParsleyFieldMultiple`
|
||||
return this.isMultiple() ? this.handleMultiple() : this.bind('parsleyField');
|
||||
},
|
||||
|
||||
isMultiple: function () {
|
||||
return (this.$element.is('input[type=radio], input[type=checkbox]')) || (this.$element.is('select') && 'undefined' !== typeof this.$element.attr('multiple'));
|
||||
},
|
||||
|
||||
// Multiples fields are a real nightmare :(
|
||||
// Maybe some refactoring would be appreciated here...
|
||||
handleMultiple: function () {
|
||||
var name;
|
||||
var multiple;
|
||||
var parsleyMultipleInstance;
|
||||
|
||||
// Handle multiple name
|
||||
if (this.options.multiple)
|
||||
; // We already have our 'multiple' identifier
|
||||
else if ('undefined' !== typeof this.$element.attr('name') && this.$element.attr('name').length)
|
||||
this.options.multiple = name = this.$element.attr('name');
|
||||
else if ('undefined' !== typeof this.$element.attr('id') && this.$element.attr('id').length)
|
||||
this.options.multiple = this.$element.attr('id');
|
||||
|
||||
// Special select multiple input
|
||||
if (this.$element.is('select') && 'undefined' !== typeof this.$element.attr('multiple')) {
|
||||
this.options.multiple = this.options.multiple || this.__id__;
|
||||
return this.bind('parsleyFieldMultiple');
|
||||
|
||||
// Else for radio / checkboxes, we need a `name` or `data-parsley-multiple` to properly bind it
|
||||
} else if (!this.options.multiple) {
|
||||
ParsleyUtils.warn('To be bound by Parsley, a radio, a checkbox and a multiple select input must have either a name or a multiple option.', this.$element);
|
||||
return this;
|
||||
}
|
||||
|
||||
// Remove special chars
|
||||
this.options.multiple = this.options.multiple.replace(/(:|\.|\[|\]|\{|\}|\$)/g, '');
|
||||
|
||||
// Add proper `data-parsley-multiple` to siblings if we have a valid multiple name
|
||||
if ('undefined' !== typeof name) {
|
||||
$('input[name="' + name + '"]').each((i, input) => {
|
||||
if ($(input).is('input[type=radio], input[type=checkbox]'))
|
||||
$(input).attr(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('ParsleyFieldMultiple')) {
|
||||
parsleyMultipleInstance.addElement(this.$element);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a secret ParsleyField instance for every multiple field. It will be stored in `data('ParsleyFieldMultiple')`
|
||||
// And will be useful later to access classic `ParsleyField` stuff while being in a `ParsleyFieldMultiple` instance
|
||||
this.bind('parsleyField', true);
|
||||
|
||||
return parsleyMultipleInstance || this.bind('parsleyFieldMultiple');
|
||||
},
|
||||
|
||||
// Return proper `ParsleyForm`, `ParsleyField` or `ParsleyFieldMultiple`
|
||||
bind: function (type, doNotStore) {
|
||||
var parsleyInstance;
|
||||
|
||||
switch (type) {
|
||||
case 'parsleyForm':
|
||||
parsleyInstance = $.extend(
|
||||
new ParsleyForm(this.$element, this.domOptions, this.options),
|
||||
new ParsleyAbstract(),
|
||||
window.ParsleyExtend
|
||||
)._bindFields();
|
||||
break;
|
||||
case 'parsleyField':
|
||||
parsleyInstance = $.extend(
|
||||
new ParsleyField(this.$element, this.domOptions, this.options, this.parent),
|
||||
new ParsleyAbstract(),
|
||||
window.ParsleyExtend
|
||||
);
|
||||
break;
|
||||
case 'parsleyFieldMultiple':
|
||||
parsleyInstance = $.extend(
|
||||
new ParsleyField(this.$element, this.domOptions, this.options, this.parent),
|
||||
new ParsleyMultiple(),
|
||||
new ParsleyAbstract(),
|
||||
window.ParsleyExtend
|
||||
)._init();
|
||||
break;
|
||||
default:
|
||||
throw new Error(type + 'is not a supported Parsley type');
|
||||
}
|
||||
|
||||
if (this.options.multiple)
|
||||
ParsleyUtils.setAttr(this.$element, this.options.namespace, 'multiple', this.options.multiple);
|
||||
|
||||
if ('undefined' !== typeof doNotStore) {
|
||||
this.$element.data('ParsleyFieldMultiple', parsleyInstance);
|
||||
|
||||
return parsleyInstance;
|
||||
}
|
||||
|
||||
// Store the freshly bound instance in a DOM element for later access using jQuery `data()`
|
||||
this.$element.data('Parsley', parsleyInstance);
|
||||
|
||||
// Tell the world we have a new ParsleyForm or ParsleyField instance!
|
||||
parsleyInstance._actualizeTriggers();
|
||||
parsleyInstance._trigger('init');
|
||||
|
||||
return parsleyInstance;
|
||||
}
|
||||
};
|
||||
|
||||
export default ParsleyFactory;
|
||||
@@ -0,0 +1,41 @@
|
||||
import $ from 'jquery';
|
||||
import ParsleyUtils from '../utils';
|
||||
import ParsleyValidator from '../validator';
|
||||
|
||||
|
||||
var ConstraintFactory = function (parsleyField, name, requirements, priority, isDomConstraint) {
|
||||
if (!/ParsleyField/.test(parsleyField.__class__))
|
||||
throw new Error('ParsleyField or ParsleyFieldMultiple instance expected');
|
||||
|
||||
var validatorSpec = window.Parsley._validatorRegistry.validators[name];
|
||||
var validator = new ParsleyValidator(validatorSpec);
|
||||
|
||||
$.extend(this, {
|
||||
validator: validator,
|
||||
name: name,
|
||||
requirements: requirements,
|
||||
priority: priority || parsleyField.options[name + 'Priority'] || validator.priority,
|
||||
isDomConstraint: true === isDomConstraint
|
||||
});
|
||||
this._parseRequirements(parsleyField.options);
|
||||
};
|
||||
|
||||
var capitalize = function(str) {
|
||||
var cap = str[0].toUpperCase();
|
||||
return cap + str.slice(1);
|
||||
};
|
||||
|
||||
ConstraintFactory.prototype = {
|
||||
validate: function(value, instance) {
|
||||
return this.validator.validate(value, ...this.requirementList, instance);
|
||||
},
|
||||
|
||||
_parseRequirements: function(options) {
|
||||
this.requirementList = this.validator.parseRequirements(this.requirements, key => {
|
||||
return options[this.name + capitalize(key)];
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default ConstraintFactory;
|
||||
|
||||
+374
@@ -0,0 +1,374 @@
|
||||
import $ from 'jquery';
|
||||
import ConstraintFactory from './factory/constraint';
|
||||
import ParsleyUI from './ui';
|
||||
import ParsleyUtils from './utils';
|
||||
|
||||
var ParsleyField = function (field, domOptions, options, parsleyFormInstance) {
|
||||
this.__class__ = 'ParsleyField';
|
||||
|
||||
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 = {pending: null, resolved: true, rejected: false};
|
||||
|
||||
ParsleyField.prototype = {
|
||||
// # Public API
|
||||
// Validate field and trigger some events for mainly `ParsleyUI`
|
||||
// @returns `true`, an array of the validators that failed, or
|
||||
// `null` if validation is not finished. Prefer using whenValidate
|
||||
validate: function (options) {
|
||||
if (arguments.length >= 1 && !$.isPlainObject(options)) {
|
||||
ParsleyUtils.warnOnce('Calling validate on a parsley field without passing arguments as an object is deprecated.');
|
||||
options = {options};
|
||||
}
|
||||
var promise = this.whenValidate(options);
|
||||
if (!promise) // If excluded with `group` option
|
||||
return true;
|
||||
switch (promise.state()) {
|
||||
case 'pending': return null;
|
||||
case 'resolved': return true;
|
||||
case 'rejected': return this.validationResult;
|
||||
}
|
||||
},
|
||||
|
||||
// Validate field and trigger some events for mainly `ParsleyUI`
|
||||
// @returns a promise that succeeds only when all validations do
|
||||
// or `undefined` if field is not in the given `group`.
|
||||
whenValidate: function ({force, group} = {}) {
|
||||
// do not validate a field if not the same as given validation group
|
||||
this.refreshConstraints();
|
||||
if (group && !this._isInGroup(group))
|
||||
return;
|
||||
|
||||
this.value = this.getValue();
|
||||
|
||||
// Field Validate event. `this.value` could be altered for custom needs
|
||||
this._trigger('validate');
|
||||
|
||||
return this.whenValid({force, value: this.value, _refreshed: true})
|
||||
.always(() => { this._reflowUI(); })
|
||||
.done(() => { this._trigger('success'); })
|
||||
.fail(() => { this._trigger('error'); })
|
||||
.always(() => { this._trigger('validated'); })
|
||||
.pipe(...this._pipeAccordingToValidationResult());
|
||||
},
|
||||
|
||||
hasConstraints: function () {
|
||||
return 0 !== this.constraints.length;
|
||||
},
|
||||
|
||||
// An empty optional field does not need validation
|
||||
needsValidation: function (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 (group) {
|
||||
if ($.isArray(this.options.group))
|
||||
return -1 !== $.inArray(group, this.options.group);
|
||||
return this.options.group === group;
|
||||
},
|
||||
|
||||
// Just validate field. Do not trigger any event.
|
||||
// Returns `true` iff all constraints pass, `false` if there are failures,
|
||||
// or `null` if the result can not be determined yet (depends on a promise)
|
||||
// See also `whenValid`.
|
||||
isValid: function (options) {
|
||||
if (arguments.length >= 1 && !$.isPlainObject(options)) {
|
||||
ParsleyUtils.warnOnce('Calling isValid on a parsley field without passing arguments as an object is deprecated.');
|
||||
var [force, value] = arguments;
|
||||
options = {force, value};
|
||||
}
|
||||
var promise = this.whenValid(options);
|
||||
if (!promise) // Excluded via `group`
|
||||
return true;
|
||||
return statusMapping[promise.state()];
|
||||
},
|
||||
|
||||
// Just validate field. Do not trigger any event.
|
||||
// @returns a promise that succeeds only when all validations do
|
||||
// or `undefined` if the field is not in the given `group`.
|
||||
// The argument `force` will force validation of empty fields.
|
||||
// If a `value` is given, it will be validated instead of the value of the input.
|
||||
whenValid: function ({force = false, value, group, _refreshed} = {}) {
|
||||
// Recompute options and rebind constraints to have latest changes
|
||||
if (!_refreshed)
|
||||
this.refreshConstraints();
|
||||
// do not validate a field if not the same as given validation group
|
||||
if (group && !this._isInGroup(group))
|
||||
return;
|
||||
|
||||
this.validationResult = true;
|
||||
|
||||
// A field without constraint is valid
|
||||
if (!this.hasConstraints())
|
||||
return $.when();
|
||||
|
||||
// Value could be passed as argument, needed to add more power to '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, (_, constraints) => {
|
||||
// Process one group of constraints at a time, we validate the constraints
|
||||
// and combine the promises together.
|
||||
var promise = $.when(
|
||||
...$.map(constraints, constraint => this._validateConstraint(value, constraint))
|
||||
);
|
||||
promises.push(promise);
|
||||
if (promise.state() === 'rejected')
|
||||
return false; // Interrupt processing if a group has already failed
|
||||
});
|
||||
return $.when.apply($, promises);
|
||||
},
|
||||
|
||||
// @returns a promise
|
||||
_validateConstraint: function(value, constraint) {
|
||||
var result = constraint.validate(value, this);
|
||||
// Map false to a failed promise
|
||||
if (false === result)
|
||||
result = $.Deferred().reject();
|
||||
// Make sure we return a promise and that we record failures
|
||||
return $.when(result).fail(errorMessage => {
|
||||
if (!(this.validationResult instanceof Array))
|
||||
this.validationResult = [];
|
||||
this.validationResult.push({
|
||||
assert: constraint,
|
||||
errorMessage: 'string' === typeof errorMessage && errorMessage
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// @returns Parsley field computed value that could be overrided or configured in DOM
|
||||
getValue: function () {
|
||||
var value;
|
||||
|
||||
// Value could be overriden in DOM or with explicit options
|
||||
if ('function' === typeof this.options.value)
|
||||
value = this.options.value(this);
|
||||
else if ('undefined' !== typeof this.options.value)
|
||||
value = this.options.value;
|
||||
else
|
||||
value = this.$element.val();
|
||||
|
||||
// Handle wrong DOM or configurations
|
||||
if ('undefined' === typeof value || null === value)
|
||||
return '';
|
||||
|
||||
return this._handleWhitespace(value);
|
||||
},
|
||||
|
||||
// Actualize options that could have change since previous validation
|
||||
// Re-bind accordingly constraints (could be some new, removed or updated)
|
||||
refreshConstraints: function () {
|
||||
return this.actualizeOptions()._bindConstraints();
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a new constraint to a field
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {Mixed} requirements optional
|
||||
* @param {Number} priority optional
|
||||
* @param {Boolean} isDomConstraint optional
|
||||
*/
|
||||
addConstraint: function (name, requirements, priority, isDomConstraint) {
|
||||
|
||||
if (window.Parsley._validatorRegistry.validators[name]) {
|
||||
var constraint = new ConstraintFactory(this, name, requirements, priority, isDomConstraint);
|
||||
|
||||
// if constraint already exist, delete it and push new version
|
||||
if ('undefined' !== this.constraintsByName[constraint.name])
|
||||
this.removeConstraint(constraint.name);
|
||||
|
||||
this.constraints.push(constraint);
|
||||
this.constraintsByName[constraint.name] = constraint;
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// Remove a constraint
|
||||
removeConstraint: function (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 (name, parameters, priority) {
|
||||
return this.removeConstraint(name)
|
||||
.addConstraint(name, parameters, priority);
|
||||
},
|
||||
|
||||
// # Internals
|
||||
|
||||
// Internal only.
|
||||
// Bind constraints from config + options + DOM
|
||||
_bindConstraints: function () {
|
||||
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 () {
|
||||
// html5 required
|
||||
if (this.$element.hasClass('required') || this.$element.attr('required'))
|
||||
this.addConstraint('required', true, undefined, true);
|
||||
|
||||
// html5 pattern
|
||||
if ('string' === typeof this.$element.attr('pattern'))
|
||||
this.addConstraint('pattern', this.$element.attr('pattern'), undefined, true);
|
||||
|
||||
// range
|
||||
if ('undefined' !== typeof this.$element.attr('min') && 'undefined' !== typeof this.$element.attr('max'))
|
||||
this.addConstraint('range', [this.$element.attr('min'), this.$element.attr('max')], undefined, true);
|
||||
|
||||
// HTML5 min
|
||||
else if ('undefined' !== typeof this.$element.attr('min'))
|
||||
this.addConstraint('min', this.$element.attr('min'), undefined, true);
|
||||
|
||||
// HTML5 max
|
||||
else if ('undefined' !== typeof this.$element.attr('max'))
|
||||
this.addConstraint('max', this.$element.attr('max'), undefined, true);
|
||||
|
||||
|
||||
// length
|
||||
if ('undefined' !== typeof this.$element.attr('minlength') && 'undefined' !== typeof this.$element.attr('maxlength'))
|
||||
this.addConstraint('length', [this.$element.attr('minlength'), this.$element.attr('maxlength')], undefined, true);
|
||||
|
||||
// HTML5 minlength
|
||||
else if ('undefined' !== typeof this.$element.attr('minlength'))
|
||||
this.addConstraint('minlength', this.$element.attr('minlength'), undefined, true);
|
||||
|
||||
// HTML5 maxlength
|
||||
else if ('undefined' !== typeof this.$element.attr('maxlength'))
|
||||
this.addConstraint('maxlength', this.$element.attr('maxlength'), undefined, true);
|
||||
|
||||
|
||||
// html5 types
|
||||
var type = this.$element.attr('type');
|
||||
|
||||
if ('undefined' === typeof type)
|
||||
return this;
|
||||
|
||||
// Small special case here for HTML5 number: integer validator if step attribute is undefined or an integer value, number otherwise
|
||||
if ('number' === type) {
|
||||
return this.addConstraint('type', ['number', {
|
||||
step: this.$element.attr('step'),
|
||||
base: this.$element.attr('min') || this.$element.attr('value')
|
||||
}], undefined, true);
|
||||
// Regular other HTML5 supported types
|
||||
} else if (/^(email|url|range)$/i.test(type)) {
|
||||
return this.addConstraint('type', type, undefined, true);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
// Internal only.
|
||||
// Field is required if have required constraint without `false` value
|
||||
_isRequired: function () {
|
||||
if ('undefined' === typeof this.constraintsByName.required)
|
||||
return false;
|
||||
|
||||
return false !== this.constraintsByName.required.requirements;
|
||||
},
|
||||
|
||||
// Internal only.
|
||||
// Shortcut to trigger an event
|
||||
_trigger: function (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 (value) {
|
||||
if (true === this.options.trimValue)
|
||||
ParsleyUtils.warnOnce('data-parsley-trim-value="true" is deprecated, please use data-parsley-whitespace="trim"');
|
||||
|
||||
if ('squish' === this.options.whitespace)
|
||||
value = value.replace(/\s{2,}/g, ' ');
|
||||
|
||||
if (('trim' === this.options.whitespace) || ('squish' === this.options.whitespace) || (true === this.options.trimValue))
|
||||
value = ParsleyUtils.trimString(value);
|
||||
|
||||
return value;
|
||||
},
|
||||
|
||||
// Internal only.
|
||||
// Returns the constraints, grouped by descending priority.
|
||||
// The result is thus an array of arrays of constraints.
|
||||
_getGroupedConstraints: function () {
|
||||
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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export default ParsleyField;
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
import $ from 'jquery';
|
||||
import ParsleyAbstract from './abstract';
|
||||
import ParsleyUtils from './utils';
|
||||
|
||||
var ParsleyForm = function (element, domOptions, options) {
|
||||
this.__class__ = 'ParsleyForm';
|
||||
|
||||
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};
|
||||
|
||||
ParsleyForm.prototype = {
|
||||
onSubmitValidate: function (event) {
|
||||
// This is a Parsley generated submit event, do not validate, do not prevent, simply exit and keep normal behavior
|
||||
if (true === event.parsley)
|
||||
return;
|
||||
|
||||
// If we didn't come here through a submit button, use the first one in the form
|
||||
var $submitSource = this._$submitSource || this.$element.find('input[type="submit"], button[type="submit"]').first();
|
||||
this._$submitSource = null;
|
||||
this.$element.find('.parsley-synthetic-submit-button').prop('disabled', true);
|
||||
if ($submitSource.is('[formnovalidate]'))
|
||||
return;
|
||||
|
||||
var promise = this.whenValidate({event});
|
||||
|
||||
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(() => { this._submit($submitSource); });
|
||||
}
|
||||
},
|
||||
|
||||
onSubmitButton: function(event) {
|
||||
this._$submitSource = $(event.target);
|
||||
},
|
||||
// internal
|
||||
// _submit submits the form, this time without going through the validations.
|
||||
// Care must be taken to "fake" the actual submit button being clicked.
|
||||
_submit: function ($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.attr('name'),
|
||||
value: $submitSource.attr('value')
|
||||
});
|
||||
}
|
||||
|
||||
this.$element.trigger($.extend($.Event('submit'), {parsley: true}));
|
||||
},
|
||||
|
||||
// Performs validation on fields while triggering events.
|
||||
// @returns `true` if all validations succeeds, `false`
|
||||
// if a failure is immediately detected, or `null`
|
||||
// if dependant on a promise.
|
||||
// Consider using `whenValidate` instead.
|
||||
validate: function (options) {
|
||||
if (arguments.length >= 1 && !$.isPlainObject(options)) {
|
||||
ParsleyUtils.warnOnce('Calling validate on a parsley form without passing arguments as an object is deprecated.');
|
||||
var [group, force, event] = arguments;
|
||||
options = {group, force, event};
|
||||
}
|
||||
return statusMapping[ this.whenValidate(options).state() ];
|
||||
},
|
||||
|
||||
whenValidate: function ({group, force, event} = {}) {
|
||||
this.submitEvent = event;
|
||||
if (event) {
|
||||
this.submitEvent = $.extend({}, event, {preventDefault: () => {
|
||||
ParsleyUtils.warnOnce("Using `this.submitEvent.preventDefault()` is deprecated; instead, call `this.validationResult = false`");
|
||||
this.validationResult = false;
|
||||
}});
|
||||
}
|
||||
this.validationResult = true;
|
||||
|
||||
// fire validate event to eventually modify things before very validation
|
||||
this._trigger('validate');
|
||||
|
||||
// Refresh form DOM options and form's fields that could have changed
|
||||
this._refreshFields();
|
||||
|
||||
var promises = this._withoutReactualizingFormOptions(() => {
|
||||
return $.map(this.fields, field => {
|
||||
return field.whenValidate({force, group});
|
||||
});
|
||||
});
|
||||
|
||||
return $.when(...promises)
|
||||
.done( () => { this._trigger('success'); })
|
||||
.fail( () => {
|
||||
this.validationResult = false;
|
||||
this.focus();
|
||||
this._trigger('error');
|
||||
})
|
||||
.always(() => { this._trigger('validated'); })
|
||||
.pipe(...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 (options) {
|
||||
if (arguments.length >= 1 && !$.isPlainObject(options)) {
|
||||
ParsleyUtils.warnOnce('Calling isValid on a parsley form without passing arguments as an object is deprecated.');
|
||||
var [group, force] = arguments;
|
||||
options = {group, 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 ({group, force} = {}) {
|
||||
this._refreshFields();
|
||||
|
||||
var promises = this._withoutReactualizingFormOptions(() => {
|
||||
return $.map(this.fields, field => {
|
||||
return field.whenValid({group, force});
|
||||
});
|
||||
});
|
||||
return $.when(...promises);
|
||||
},
|
||||
|
||||
_refreshFields: function () {
|
||||
return this.actualizeOptions()._bindFields();
|
||||
},
|
||||
|
||||
_bindFields: function () {
|
||||
var oldFields = this.fields;
|
||||
|
||||
this.fields = [];
|
||||
this.fieldsMappedById = {};
|
||||
|
||||
this._withoutReactualizingFormOptions(() => {
|
||||
this.$element
|
||||
.find(this.options.inputs)
|
||||
.not(this.options.excluded)
|
||||
.each((_, element) => {
|
||||
var fieldInstance = new window.Parsley.Factory(element, {}, this);
|
||||
|
||||
// Only add valid and not excluded `ParsleyField` and `ParsleyFieldMultiple` children
|
||||
if (('ParsleyField' === fieldInstance.__class__ || 'ParsleyFieldMultiple' === fieldInstance.__class__) && (true !== fieldInstance.options.excluded))
|
||||
if ('undefined' === typeof this.fieldsMappedById[fieldInstance.__class__ + '-' + fieldInstance.__id__]) {
|
||||
this.fieldsMappedById[fieldInstance.__class__ + '-' + fieldInstance.__id__] = fieldInstance;
|
||||
this.fields.push(fieldInstance);
|
||||
}
|
||||
});
|
||||
|
||||
$(oldFields).not(this.fields).each((_, field) => {
|
||||
field._trigger('reset');
|
||||
});
|
||||
});
|
||||
return this;
|
||||
},
|
||||
|
||||
// Internal only.
|
||||
// Looping on a form's fields to do validation or similar
|
||||
// will trigger reactualizing options on all of them, which
|
||||
// in turn will reactualize the form's options.
|
||||
// To avoid calling actualizeOptions so many times on the form
|
||||
// for nothing, _withoutReactualizingFormOptions temporarily disables
|
||||
// the method actualizeOptions on this form while `fn` is called.
|
||||
_withoutReactualizingFormOptions: function (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 (eventName) {
|
||||
return this.trigger('form:' + eventName);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export default ParsleyForm;
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
import $ from 'jquery';
|
||||
import ParsleyUtils from './utils';
|
||||
import ParsleyDefaults from './defaults';
|
||||
import ParsleyAbstract from './abstract';
|
||||
import ParsleyValidatorRegistry from './validator_registry';
|
||||
import ParsleyUI from './ui';
|
||||
import ParsleyForm from './form';
|
||||
import ParsleyField from './field';
|
||||
import ParsleyMultiple from './multiple';
|
||||
import ParsleyFactory from './factory';
|
||||
|
||||
var vernums = $.fn.jquery.split('.');
|
||||
if (parseInt(vernums[0]) <= 1 && parseInt(vernums[1]) < 8) {
|
||||
throw "The loaded version of jQuery is too old. Please upgrade to 1.8.x or better.";
|
||||
}
|
||||
if (!vernums.forEach) {
|
||||
ParsleyUtils.warn('Parsley requires ES5 to run properly. Please include https://github.com/es-shims/es5-shim');
|
||||
}
|
||||
// Inherit `on`, `off` & `trigger` to Parsley:
|
||||
var Parsley = $.extend(new ParsleyAbstract(), {
|
||||
$element: $(document),
|
||||
actualizeOptions: null,
|
||||
_resetOptions: null,
|
||||
Factory: ParsleyFactory,
|
||||
version: '@@version'
|
||||
});
|
||||
|
||||
// Supplement ParsleyField and Form with ParsleyAbstract
|
||||
// This way, the constructors will have access to those methods
|
||||
$.extend(ParsleyField.prototype, ParsleyUI.Field, ParsleyAbstract.prototype);
|
||||
$.extend(ParsleyForm.prototype, ParsleyUI.Form, ParsleyAbstract.prototype);
|
||||
// Inherit actualizeOptions and _resetOptions:
|
||||
$.extend(ParsleyFactory.prototype, ParsleyAbstract.prototype);
|
||||
|
||||
// ### jQuery API
|
||||
// `$('.elem').parsley(options)` or `$('.elem').psly(options)`
|
||||
$.fn.parsley = $.fn.psly = function (options) {
|
||||
if (this.length > 1) {
|
||||
var instances = [];
|
||||
|
||||
this.each(function () {
|
||||
instances.push($(this).parsley(options));
|
||||
});
|
||||
|
||||
return instances;
|
||||
}
|
||||
|
||||
// Return undefined if applied to non existing DOM element
|
||||
if (!$(this).length) {
|
||||
ParsleyUtils.warn('You must bind Parsley on an existing element.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return new ParsleyFactory(this, options);
|
||||
};
|
||||
|
||||
// ### ParsleyField and ParsleyForm extension
|
||||
// Ensure the extension is now defined if it wasn't previously
|
||||
if ('undefined' === typeof window.ParsleyExtend)
|
||||
window.ParsleyExtend = {};
|
||||
|
||||
// ### Parsley config
|
||||
// Inherit from ParsleyDefault, and copy over any existing values
|
||||
Parsley.options = $.extend(ParsleyUtils.objectCreate(ParsleyDefaults), window.ParsleyConfig);
|
||||
window.ParsleyConfig = Parsley.options; // Old way of accessing global options
|
||||
|
||||
// ### Globals
|
||||
window.Parsley = window.psly = Parsley;
|
||||
window.ParsleyUtils = ParsleyUtils;
|
||||
|
||||
// ### Define methods that forward to the registry, and deprecate all access except through window.Parsley
|
||||
var registry = window.Parsley._validatorRegistry = new ParsleyValidatorRegistry(window.ParsleyConfig.validators, window.ParsleyConfig.i18n);
|
||||
window.ParsleyValidator = {};
|
||||
$.each('setLocale addCatalog addMessage addMessages getErrorMessage formatMessage addValidator updateValidator removeValidator'.split(' '), function (i, method) {
|
||||
window.Parsley[method] = $.proxy(registry, method);
|
||||
window.ParsleyValidator[method] = function () {
|
||||
ParsleyUtils.warnOnce(`Accessing the method '${method}' through ParsleyValidator is deprecated. Simply call 'window.Parsley.${method}(...)'`);
|
||||
return window.Parsley[method](...arguments);
|
||||
};
|
||||
});
|
||||
|
||||
// ### ParsleyUI
|
||||
// Deprecated global object
|
||||
window.Parsley.UI = ParsleyUI;
|
||||
window.ParsleyUI = {
|
||||
removeError: function (instance, name, doNotUpdateClass) {
|
||||
var updateClass = true !== doNotUpdateClass;
|
||||
ParsleyUtils.warnOnce(`Accessing ParsleyUI is deprecated. Call 'removeError' on the instance directly. Please comment in issue 1073 as to your need to call this method.`);
|
||||
return instance.removeError(name, {updateClass});
|
||||
},
|
||||
getErrorsMessages: function (instance) {
|
||||
ParsleyUtils.warnOnce(`Accessing ParsleyUI is deprecated. Call 'getErrorsMessages' on the instance directly.`);
|
||||
return instance.getErrorsMessages();
|
||||
}
|
||||
};
|
||||
$.each('addError updateError'.split(' '), function (i, method) {
|
||||
window.ParsleyUI[method] = function (instance, name, message, assert, doNotUpdateClass) {
|
||||
var updateClass = true !== doNotUpdateClass;
|
||||
ParsleyUtils.warnOnce(`Accessing ParsleyUI is deprecated. Call '${method}' on the instance directly. Please comment in issue 1073 as to your need to call this method.`);
|
||||
return instance[method](name, {message, assert, 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();
|
||||
});
|
||||
}
|
||||
|
||||
export default Parsley;
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
import $ from 'jquery';
|
||||
|
||||
var ParsleyMultiple = function () {
|
||||
this.__class__ = 'ParsleyFieldMultiple';
|
||||
};
|
||||
|
||||
ParsleyMultiple.prototype = {
|
||||
// Add new `$element` sibling for multiple field
|
||||
addElement: function ($element) {
|
||||
this.$elements.push($element);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// See `ParsleyField.refreshConstraints()`
|
||||
refreshConstraints: function () {
|
||||
var fieldConstraints;
|
||||
|
||||
this.constraints = [];
|
||||
|
||||
// Select multiple special treatment
|
||||
if (this.$element.is('select')) {
|
||||
this.actualizeOptions()._bindConstraints();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// Gather all constraints for each input in the multiple group
|
||||
for (var i = 0; i < this.$elements.length; i++) {
|
||||
|
||||
// Check if element have not been dynamically removed since last binding
|
||||
if (!$('html').has(this.$elements[i]).length) {
|
||||
this.$elements.splice(i, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
fieldConstraints = this.$elements[i].data('ParsleyFieldMultiple').refreshConstraints().constraints;
|
||||
|
||||
for (var j = 0; j < fieldConstraints.length; j++)
|
||||
this.addConstraint(fieldConstraints[j].name, fieldConstraints[j].requirements, fieldConstraints[j].priority, fieldConstraints[j].isDomConstraint);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// See `ParsleyField.getValue()`
|
||||
getValue: function () {
|
||||
// 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.is('input[type=radio]'))
|
||||
return this._findRelated().filter(':checked').val() || '';
|
||||
|
||||
// checkbox input case
|
||||
if (this.$element.is('input[type=checkbox]')) {
|
||||
var values = [];
|
||||
|
||||
this._findRelated().filter(':checked').each(function () {
|
||||
values.push($(this).val());
|
||||
});
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
// Select multiple case
|
||||
if (this.$element.is('select') && null === this.$element.val())
|
||||
return [];
|
||||
|
||||
// Default case that should never happen
|
||||
return this.$element.val();
|
||||
},
|
||||
|
||||
_init: function () {
|
||||
this.$elements = [this.$element];
|
||||
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
export default ParsleyMultiple;
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
import $ from 'jquery';
|
||||
import ParsleyField from './field';
|
||||
import ParsleyForm from './form';
|
||||
import ParsleyUtils from './utils';
|
||||
|
||||
var o = $({});
|
||||
var deprecated = function () {
|
||||
ParsleyUtils.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 ParsleyField) && !(instance instanceof ParsleyForm))
|
||||
throw new Error('Must give Parsley instance');
|
||||
|
||||
if ('string' !== typeof name || 'function' !== typeof fn)
|
||||
throw new Error('Wrong parameters');
|
||||
|
||||
instance.on(eventName(name), adapt(fn));
|
||||
};
|
||||
|
||||
$.unsubscribe = function (name, fn) {
|
||||
deprecated();
|
||||
if ('string' !== typeof name || 'function' !== typeof fn)
|
||||
throw new Error('Wrong arguments');
|
||||
window.Parsley.off(eventName(name), fn.parsleyAdaptedCallback);
|
||||
};
|
||||
|
||||
$.unsubscribeTo = function (instance, name) {
|
||||
deprecated();
|
||||
if (!(instance instanceof ParsleyField) && !(instance instanceof ParsleyForm))
|
||||
throw new Error('Must give Parsley instance');
|
||||
instance.off(eventName(name));
|
||||
};
|
||||
|
||||
$.unsubscribeAll = function (name) {
|
||||
deprecated();
|
||||
window.Parsley.off(eventName(name));
|
||||
$('form,input,textarea,select').each(function () {
|
||||
var instance = $(this).data('Parsley');
|
||||
if (instance) {
|
||||
instance.off(eventName(name));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// $.emit is deprecated. Use jQuery events instead.
|
||||
$.emit = function (name, instance) {
|
||||
deprecated();
|
||||
var instanceGiven = (instance instanceof ParsleyField) || (instance instanceof ParsleyForm);
|
||||
var args = Array.prototype.slice.call(arguments, instanceGiven ? 2 : 1);
|
||||
args.unshift(eventName(name));
|
||||
if (!instanceGiven) {
|
||||
instance = window.Parsley;
|
||||
}
|
||||
instance.trigger(...args);
|
||||
};
|
||||
|
||||
export default {};
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
import $ from 'jquery';
|
||||
|
||||
import Parsley from './main';
|
||||
|
||||
$.extend(true, Parsley, {
|
||||
asyncValidators: {
|
||||
'default': {
|
||||
fn: function (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 (xhr) {
|
||||
// If reverse option is set, a failing ajax request is considered successful
|
||||
return xhr.status < 200 || xhr.status >= 300;
|
||||
},
|
||||
url: false
|
||||
}
|
||||
},
|
||||
|
||||
addAsyncValidator: function (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 (value, url, options, instance) {
|
||||
var data = {};
|
||||
var ajaxOptions;
|
||||
var csr;
|
||||
var validator = options.validator || (true === options.reverse ? 'reverse' : 'default');
|
||||
|
||||
if ('undefined' === typeof Parsley.asyncValidators[validator])
|
||||
throw new Error('Calling an undefined async validator: `' + validator + '`');
|
||||
|
||||
url = Parsley.asyncValidators[validator].url || url;
|
||||
|
||||
// Fill current value
|
||||
if (url.indexOf('{value}') > -1) {
|
||||
url = url.replace('{value}', encodeURIComponent(value));
|
||||
} else {
|
||||
data[instance.$element.attr('name') || instance.$element.attr('id')] = value;
|
||||
}
|
||||
|
||||
// Merge options passed in from the function with the ones in the attribute
|
||||
var remoteOptions = $.extend(true, options.options || {} , Parsley.asyncValidators[validator].options);
|
||||
|
||||
// All `$.ajax(options)` could be overridden or extended directly from DOM in `data-parsley-remote-options`
|
||||
ajaxOptions = $.extend(true, {}, {
|
||||
url: url,
|
||||
data: data,
|
||||
type: 'GET'
|
||||
}, remoteOptions);
|
||||
|
||||
// Generate store key based on ajax options
|
||||
instance.trigger('field:ajaxoptions', instance, ajaxOptions);
|
||||
|
||||
csr = $.param(ajaxOptions);
|
||||
|
||||
// Initialise querry cache
|
||||
if ('undefined' === typeof Parsley._remoteCache)
|
||||
Parsley._remoteCache = {};
|
||||
|
||||
// Try to retrieve stored xhr
|
||||
var xhr = Parsley._remoteCache[csr] = Parsley._remoteCache[csr] || $.ajax(ajaxOptions);
|
||||
|
||||
var handleXhr = function () {
|
||||
var result = Parsley.asyncValidators[validator].fn.call(instance, xhr, url, options);
|
||||
if (!result) // Map falsy results to rejected promise
|
||||
result = $.Deferred().reject();
|
||||
return $.when(result);
|
||||
};
|
||||
|
||||
return xhr.then(handleXhr, handleXhr);
|
||||
},
|
||||
|
||||
priority: -1
|
||||
});
|
||||
|
||||
Parsley.on('form:submit', function () {
|
||||
Parsley._remoteCache = {};
|
||||
});
|
||||
|
||||
window.ParsleyExtend.addAsyncValidator = function () {
|
||||
ParsleyUtils.warnOnce('Accessing the method `addAsyncValidator` through an instance is deprecated. Simply call `Parsley.addAsyncValidator(...)`');
|
||||
return Parsley.addAsyncValidator(...arguments);
|
||||
};
|
||||
Vendored
+375
@@ -0,0 +1,375 @@
|
||||
import $ from 'jquery';
|
||||
import ParsleyUtils from './utils';
|
||||
|
||||
var ParsleyUI = {};
|
||||
|
||||
var diffResults = function (newResult, oldResult, deep) {
|
||||
var added = [];
|
||||
var kept = [];
|
||||
|
||||
for (var i = 0; i < newResult.length; i++) {
|
||||
var found = false;
|
||||
|
||||
for (var j = 0; j < oldResult.length; j++)
|
||||
if (newResult[i].assert.name === oldResult[j].assert.name) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (found)
|
||||
kept.push(newResult[i]);
|
||||
else
|
||||
added.push(newResult[i]);
|
||||
}
|
||||
|
||||
return {
|
||||
kept: kept,
|
||||
added: added,
|
||||
removed: !deep ? diffResults(oldResult, newResult, true).added : []
|
||||
};
|
||||
};
|
||||
|
||||
ParsleyUI.Form = {
|
||||
|
||||
_actualizeTriggers: function () {
|
||||
this.$element.on('submit.Parsley', evt => { this.onSubmitValidate(evt); });
|
||||
this.$element.on('click.Parsley', 'input[type="submit"], button[type="submit"]', evt => { this.onSubmitButton(evt); });
|
||||
|
||||
// UI could be disabled
|
||||
if (false === this.options.uiEnabled)
|
||||
return;
|
||||
|
||||
this.$element.attr('novalidate', '');
|
||||
},
|
||||
|
||||
focus: function () {
|
||||
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 () {
|
||||
// Reset all event listeners
|
||||
this.$element.off('.Parsley');
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
ParsleyUI.Field = {
|
||||
|
||||
_reflowUI: function () {
|
||||
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 () {
|
||||
// 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 (name, {message, assert, updateClass = true} = {}) {
|
||||
this._buildUI();
|
||||
this._addError(name, {message, assert});
|
||||
|
||||
if (updateClass)
|
||||
this._errorClass();
|
||||
},
|
||||
|
||||
// It's a goal of Parsley that this method is no longer required [#1073]
|
||||
updateError: function (name, {message, assert, updateClass = true} = {}) {
|
||||
this._buildUI();
|
||||
this._updateError(name, {message, assert});
|
||||
|
||||
if (updateClass)
|
||||
this._errorClass();
|
||||
},
|
||||
|
||||
// It's a goal of Parsley that this method is no longer required [#1073]
|
||||
removeError: function (name, {updateClass = true} = {}) {
|
||||
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 () {
|
||||
if (this.hasConstraints() && this.needsValidation() && true === this.validationResult)
|
||||
this._successClass();
|
||||
else if (this.validationResult.length > 0)
|
||||
this._errorClass();
|
||||
else
|
||||
this._resetClass();
|
||||
},
|
||||
|
||||
_manageErrorsMessages: function (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 (name, {message, assert}) {
|
||||
this._insertErrorWrapper();
|
||||
this._ui.$errorsWrapper
|
||||
.addClass('filled')
|
||||
.append(
|
||||
$(this.options.errorTemplate)
|
||||
.addClass('parsley-' + name)
|
||||
.html(message || this._getErrorMessage(assert))
|
||||
);
|
||||
},
|
||||
|
||||
_updateError: function (name, {message, assert}) {
|
||||
this._ui.$errorsWrapper
|
||||
.addClass('filled')
|
||||
.find('.parsley-' + name)
|
||||
.html(message || this._getErrorMessage(assert));
|
||||
},
|
||||
|
||||
_removeError: function (name) {
|
||||
this._ui.$errorsWrapper
|
||||
.removeClass('filled')
|
||||
.find('.parsley-' + name)
|
||||
.remove();
|
||||
},
|
||||
|
||||
_getErrorMessage: function (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 () {
|
||||
// UI could be already built or disabled
|
||||
if (this._ui || false === this.options.uiEnabled)
|
||||
return;
|
||||
|
||||
var _ui = {};
|
||||
|
||||
// Give field its Parsley id in DOM
|
||||
this.$element.attr(this.options.namespace + 'id', this.__id__);
|
||||
|
||||
/** Generate important UI elements and store them in this **/
|
||||
// $errorClassHandler is the $element that woul have parsley-error and parsley-success classes
|
||||
_ui.$errorClassHandler = this._manageClassHandler();
|
||||
|
||||
// $errorsWrapper is a div that would contain the various field errors, it will be appended into $errorsContainer
|
||||
_ui.errorsWrapperId = 'parsley-id-' + (this.options.multiple ? 'multiple-' + this.options.multiple : this.__id__);
|
||||
_ui.$errorsWrapper = $(this.options.errorsWrapper).attr('id', _ui.errorsWrapperId);
|
||||
|
||||
// ValidationResult UI storage to detect what have changed bwt two validations, and update DOM accordingly
|
||||
_ui.lastValidationResult = [];
|
||||
_ui.validationInformationVisible = false;
|
||||
|
||||
// Store it in this for later
|
||||
this._ui = _ui;
|
||||
},
|
||||
|
||||
// Determine which element will have `parsley-error` and `parsley-success` classes
|
||||
_manageClassHandler: function () {
|
||||
// An element selector could be passed through DOM with `data-parsley-class-handler=#foo`
|
||||
if ('string' === typeof this.options.classHandler && $(this.options.classHandler).length)
|
||||
return $(this.options.classHandler);
|
||||
|
||||
// Class handled could also be determined by function given in Parsley options
|
||||
var $handler = this.options.classHandler.call(this, this);
|
||||
|
||||
// If this function returned a valid existing DOM element, go for it
|
||||
if ('undefined' !== typeof $handler && $handler.length)
|
||||
return $handler;
|
||||
|
||||
// Otherwise, if simple element (input, texatrea, select...) it will perfectly host the classes
|
||||
if (!this.options.multiple || this.$element.is('select'))
|
||||
return this.$element;
|
||||
|
||||
// But if multiple element (radio, checkbox), that would be their parent
|
||||
return this.$element.parent();
|
||||
},
|
||||
|
||||
_insertErrorWrapper: function () {
|
||||
var $errorsContainer;
|
||||
|
||||
// Nothing to do if already inserted
|
||||
if (0 !== this._ui.$errorsWrapper.parent().length)
|
||||
return this._ui.$errorsWrapper.parent();
|
||||
|
||||
if ('string' === typeof this.options.errorsContainer) {
|
||||
if ($(this.options.errorsContainer).length)
|
||||
return $(this.options.errorsContainer).append(this._ui.$errorsWrapper);
|
||||
else
|
||||
ParsleyUtils.warn('The errors container `' + this.options.errorsContainer + '` does not exist in DOM');
|
||||
} else if ('function' === typeof this.options.errorsContainer)
|
||||
$errorsContainer = this.options.errorsContainer.call(this, this);
|
||||
|
||||
if ('undefined' !== typeof $errorsContainer && $errorsContainer.length)
|
||||
return $errorsContainer.append(this._ui.$errorsWrapper);
|
||||
|
||||
var $from = this.$element;
|
||||
if (this.options.multiple)
|
||||
$from = $from.parent();
|
||||
return $from.after(this._ui.$errorsWrapper);
|
||||
},
|
||||
|
||||
_actualizeTriggers: function () {
|
||||
var $toBind = this._findRelated();
|
||||
var trigger;
|
||||
|
||||
// Remove Parsley events already bound on this field
|
||||
$toBind.off('.Parsley');
|
||||
if (this._failedOnce)
|
||||
$toBind.on(ParsleyUtils.namespaceEvents(this.options.triggerAfterFailure, 'Parsley'), () => {
|
||||
this.validate();
|
||||
});
|
||||
else if (trigger = ParsleyUtils.namespaceEvents(this.options.trigger, 'Parsley')) {
|
||||
$toBind.on(trigger, event => {
|
||||
this._eventValidate(event);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_eventValidate: function (event) {
|
||||
// For keyup, keypress, keydown, input... events that could be a little bit obstrusive
|
||||
// do not validate if val length < min threshold on first validation. Once field have been validated once and info
|
||||
// about success or failure have been displayed, always validate with this trigger to reflect every yalidation change.
|
||||
if (/key|input/.test(event.type))
|
||||
if (!(this._ui && this._ui.validationInformationVisible) && this.getValue().length <= this.options.validationThreshold)
|
||||
return;
|
||||
|
||||
this.validate();
|
||||
},
|
||||
|
||||
_resetUI: function () {
|
||||
// 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 () {
|
||||
this._resetUI();
|
||||
|
||||
if ('undefined' !== typeof this._ui)
|
||||
this._ui.$errorsWrapper.remove();
|
||||
|
||||
delete this._ui;
|
||||
},
|
||||
|
||||
_successClass: function () {
|
||||
this._ui.validationInformationVisible = true;
|
||||
this._ui.$errorClassHandler.removeClass(this.options.errorClass).addClass(this.options.successClass);
|
||||
},
|
||||
_errorClass: function () {
|
||||
this._ui.validationInformationVisible = true;
|
||||
this._ui.$errorClassHandler.removeClass(this.options.successClass).addClass(this.options.errorClass);
|
||||
},
|
||||
_resetClass: function () {
|
||||
this._ui.$errorClassHandler.removeClass(this.options.successClass).removeClass(this.options.errorClass);
|
||||
}
|
||||
};
|
||||
|
||||
export default ParsleyUI;
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
import $ from 'jquery';
|
||||
|
||||
var globalID = 1;
|
||||
var pastWarnings = {};
|
||||
|
||||
var ParsleyUtils = {
|
||||
// Parsley DOM-API
|
||||
// returns object from dom attributes and values
|
||||
attr: function ($element, namespace, obj) {
|
||||
var i;
|
||||
var attribute;
|
||||
var attributes;
|
||||
var regex = new RegExp('^' + namespace, 'i');
|
||||
|
||||
if ('undefined' === typeof obj)
|
||||
obj = {};
|
||||
else {
|
||||
// Clear all own properties. This won't affect prototype's values
|
||||
for (i in obj) {
|
||||
if (obj.hasOwnProperty(i))
|
||||
delete obj[i];
|
||||
}
|
||||
}
|
||||
|
||||
if ('undefined' === typeof $element || 'undefined' === typeof $element[0])
|
||||
return obj;
|
||||
|
||||
attributes = $element[0].attributes;
|
||||
for (i = attributes.length; i--; ) {
|
||||
attribute = attributes[i];
|
||||
|
||||
if (attribute && attribute.specified && regex.test(attribute.name)) {
|
||||
obj[this.camelize(attribute.name.slice(namespace.length))] = this.deserializeValue(attribute.value);
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
},
|
||||
|
||||
checkAttr: function ($element, namespace, checkAttr) {
|
||||
return $element.is('[' + namespace + checkAttr + ']');
|
||||
},
|
||||
|
||||
setAttr: function ($element, namespace, attr, value) {
|
||||
$element[0].setAttribute(this.dasherize(namespace + attr), String(value));
|
||||
},
|
||||
|
||||
generateID: function () {
|
||||
return '' + globalID++;
|
||||
},
|
||||
|
||||
/** Third party functions **/
|
||||
// Zepto deserialize function
|
||||
deserializeValue: function (value) {
|
||||
var num;
|
||||
|
||||
try {
|
||||
return value ?
|
||||
value == "true" ||
|
||||
(value == "false" ? false :
|
||||
value == "null" ? null :
|
||||
!isNaN(num = Number(value)) ? num :
|
||||
/^[\[\{]/.test(value) ? $.parseJSON(value) :
|
||||
value)
|
||||
: value;
|
||||
} catch (e) { return value; }
|
||||
},
|
||||
|
||||
// Zepto camelize function
|
||||
camelize: function (str) {
|
||||
return str.replace(/-+(.)?/g, function (match, chr) {
|
||||
return chr ? chr.toUpperCase() : '';
|
||||
});
|
||||
},
|
||||
|
||||
// Zepto dasherize function
|
||||
dasherize: function (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 () {
|
||||
if (window.console && 'function' === typeof window.console.warn)
|
||||
window.console.warn(...arguments);
|
||||
},
|
||||
|
||||
warnOnce: function(msg) {
|
||||
if (!pastWarnings[msg]) {
|
||||
pastWarnings[msg] = true;
|
||||
this.warn(...arguments);
|
||||
}
|
||||
},
|
||||
|
||||
_resetWarnings: function () {
|
||||
pastWarnings = {};
|
||||
},
|
||||
|
||||
trimString: function(string) {
|
||||
return string.replace(/^\s+|\s+$/g, '');
|
||||
},
|
||||
|
||||
namespaceEvents: function(events, namespace) {
|
||||
events = this.trimString(events || '').split(/\s+/);
|
||||
if (!events[0])
|
||||
return '';
|
||||
return $.map(events, evt => { return `${evt}.${namespace}`; }).join(' ');
|
||||
},
|
||||
|
||||
// Object.create polyfill, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Polyfill
|
||||
objectCreate: Object.create || (function () {
|
||||
var Object = function () {};
|
||||
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;
|
||||
};
|
||||
})()
|
||||
};
|
||||
|
||||
export default ParsleyUtils;
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
import $ from 'jquery';
|
||||
import ParsleyUtils from './utils';
|
||||
|
||||
var requirementConverters = {
|
||||
string: function(string) {
|
||||
return string;
|
||||
},
|
||||
integer: function(string) {
|
||||
if (isNaN(string))
|
||||
throw 'Requirement is not an integer: "' + string + '"';
|
||||
return parseInt(string, 10);
|
||||
},
|
||||
number: function(string) {
|
||||
if (isNaN(string))
|
||||
throw 'Requirement is not a number: "' + string + '"';
|
||||
return parseFloat(string);
|
||||
},
|
||||
reference: function(string) { // Unused for now
|
||||
var result = $(string);
|
||||
if (result.length === 0)
|
||||
throw 'No such reference: "' + string + '"';
|
||||
return result;
|
||||
},
|
||||
boolean: function(string) {
|
||||
return string !== 'false';
|
||||
},
|
||||
object: function(string) {
|
||||
return ParsleyUtils.deserializeValue(string);
|
||||
},
|
||||
regexp: function(regexp) {
|
||||
var flags = '';
|
||||
|
||||
// Test if RegExp is literal, if not, nothing to be done, otherwise, we need to isolate flags and pattern
|
||||
if (/^\/.*\/(?:[gimy]*)$/.test(regexp)) {
|
||||
// Replace the regexp literal string with the first match group: ([gimy]*)
|
||||
// If no flag is present, this will be a blank string
|
||||
flags = regexp.replace(/.*\/([gimy]*)$/, '$1');
|
||||
// Again, replace the regexp literal string with the first match group:
|
||||
// everything excluding the opening and closing slashes and the flags
|
||||
regexp = regexp.replace(new RegExp('^/(.*?)/' + flags + '$'), '$1');
|
||||
} else {
|
||||
// Anchor regexp:
|
||||
regexp = '^' + regexp + '$';
|
||||
}
|
||||
return new RegExp(regexp, flags);
|
||||
}
|
||||
};
|
||||
|
||||
var convertArrayRequirement = function(string, length) {
|
||||
var m = string.match(/^\s*\[(.*)\]\s*$/);
|
||||
if (!m)
|
||||
throw 'Requirement is not an array: "' + string + '"';
|
||||
var values = m[1].split(',').map(ParsleyUtils.trimString);
|
||||
if (values.length !== length)
|
||||
throw 'Requirement has ' + values.length + ' values when ' + length + ' are needed';
|
||||
return values;
|
||||
};
|
||||
|
||||
var convertRequirement = function(requirementType, string) {
|
||||
var converter = requirementConverters[requirementType || 'string'];
|
||||
if (!converter)
|
||||
throw 'Unknown requirement specification: "' + requirementType + '"';
|
||||
return converter(string);
|
||||
};
|
||||
|
||||
var convertExtraOptionRequirement = function(requirementSpec, string, extraOptionReader) {
|
||||
var main = null;
|
||||
var extra = {};
|
||||
for (var key in requirementSpec) {
|
||||
if (key) {
|
||||
var value = extraOptionReader(key);
|
||||
if ('string' === typeof value)
|
||||
value = convertRequirement(requirementSpec[key], value);
|
||||
extra[key] = value;
|
||||
} else {
|
||||
main = convertRequirement(requirementSpec[key], string);
|
||||
}
|
||||
}
|
||||
return [main, extra];
|
||||
};
|
||||
|
||||
// A Validator needs to implement the methods `validate` and `parseRequirements`
|
||||
|
||||
var ParsleyValidator = function(spec) {
|
||||
$.extend(true, this, spec);
|
||||
};
|
||||
|
||||
ParsleyValidator.prototype = {
|
||||
// Returns `true` iff the given `value` is valid according the given requirements.
|
||||
validate: function(value, requirementFirstArg) {
|
||||
if (this.fn) { // Legacy style validator
|
||||
|
||||
if (arguments.length > 3) // If more args then value, requirement, instance...
|
||||
requirementFirstArg = [].slice.call(arguments, 1, -1); // Skip first arg (value) and last (instance), combining the rest
|
||||
return this.fn.call(this, value, requirementFirstArg);
|
||||
}
|
||||
|
||||
if ($.isArray(value)) {
|
||||
if (!this.validateMultiple)
|
||||
throw 'Validator `' + this.name + '` does not handle multiple values';
|
||||
return this.validateMultiple(...arguments);
|
||||
} else {
|
||||
if (this.validateNumber) {
|
||||
if (isNaN(value))
|
||||
return false;
|
||||
arguments[0] = parseFloat(arguments[0]);
|
||||
return this.validateNumber(...arguments);
|
||||
}
|
||||
if (this.validateString) {
|
||||
return this.validateString(...arguments);
|
||||
}
|
||||
throw 'Validator `' + this.name + '` only handles multiple values';
|
||||
}
|
||||
},
|
||||
|
||||
// Parses `requirements` into an array of arguments,
|
||||
// according to `this.requirementType`
|
||||
parseRequirements: function(requirements, extraOptionReader) {
|
||||
if ('string' !== typeof requirements) {
|
||||
// Assume requirement already parsed
|
||||
// but make sure we return an array
|
||||
return $.isArray(requirements) ? requirements : [requirements];
|
||||
}
|
||||
var type = this.requirementType;
|
||||
if ($.isArray(type)) {
|
||||
var values = convertArrayRequirement(requirements, type.length);
|
||||
for (var i = 0; i < values.length; i++)
|
||||
values[i] = convertRequirement(type[i], values[i]);
|
||||
return values;
|
||||
} else if ($.isPlainObject(type)) {
|
||||
return convertExtraOptionRequirement(type, requirements, extraOptionReader);
|
||||
} else {
|
||||
return [convertRequirement(type, requirements)];
|
||||
}
|
||||
},
|
||||
// Defaults:
|
||||
requirementType: 'string',
|
||||
|
||||
priority: 2
|
||||
|
||||
};
|
||||
|
||||
export default ParsleyValidator;
|
||||
+347
@@ -0,0 +1,347 @@
|
||||
import $ from 'jquery';
|
||||
import ParsleyUtils from './utils';
|
||||
import ParsleyDefaults from './defaults';
|
||||
import ParsleyValidator from './validator';
|
||||
|
||||
var ParsleyValidatorRegistry = function (validators, catalog) {
|
||||
this.__class__ = 'ParsleyValidatorRegistry';
|
||||
|
||||
// Default Parsley locale is en
|
||||
this.locale = 'en';
|
||||
|
||||
this.init(validators || {}, catalog || {});
|
||||
};
|
||||
|
||||
var typeRegexes = {
|
||||
email: /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i,
|
||||
|
||||
// Follow https://www.w3.org/TR/html5/infrastructure.html#floating-point-numbers
|
||||
number: /^-?(\d*\.)?\d+(e[-+]?\d+)?$/i,
|
||||
|
||||
integer: /^-?\d+$/,
|
||||
|
||||
digits: /^\d+$/,
|
||||
|
||||
alphanum: /^\w+$/i,
|
||||
|
||||
url: new RegExp(
|
||||
"^" +
|
||||
// protocol identifier
|
||||
"(?:(?:https?|ftp)://)?" + // ** mod: make scheme optional
|
||||
// user:pass authentication
|
||||
"(?:\\S+(?::\\S*)?@)?" +
|
||||
"(?:" +
|
||||
// IP address exclusion
|
||||
// private & local networks
|
||||
// "(?!(?:10|127)(?:\\.\\d{1,3}){3})" + // ** mod: allow local networks
|
||||
// "(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})" + // ** mod: allow local networks
|
||||
// "(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})" + // ** mod: allow local networks
|
||||
// IP address dotted notation octets
|
||||
// excludes loopback network 0.0.0.0
|
||||
// excludes reserved space >= 224.0.0.0
|
||||
// excludes network & broacast addresses
|
||||
// (first & last IP address of each class)
|
||||
"(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" +
|
||||
"(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" +
|
||||
"(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" +
|
||||
"|" +
|
||||
// host name
|
||||
"(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)" +
|
||||
// domain name
|
||||
"(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*" +
|
||||
// TLD identifier
|
||||
"(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))" +
|
||||
")" +
|
||||
// port number
|
||||
"(?::\\d{2,5})?" +
|
||||
// resource path
|
||||
"(?:/\\S*)?" +
|
||||
"$", 'i'
|
||||
)
|
||||
};
|
||||
typeRegexes.range = typeRegexes.number;
|
||||
|
||||
// See http://stackoverflow.com/a/10454560/8279
|
||||
var decimalPlaces = num => {
|
||||
var match = ('' + num).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);
|
||||
if (!match) { return 0; }
|
||||
return Math.max(
|
||||
0,
|
||||
// Number of digits right of decimal point.
|
||||
(match[1] ? match[1].length : 0) -
|
||||
// Adjust for scientific notation.
|
||||
(match[2] ? +match[2] : 0));
|
||||
};
|
||||
|
||||
ParsleyValidatorRegistry.prototype = {
|
||||
init: function (validators, catalog) {
|
||||
this.catalog = catalog;
|
||||
// Copy prototype's validators:
|
||||
this.validators = $.extend({}, this.validators);
|
||||
|
||||
for (var name in validators)
|
||||
this.addValidator(name, validators[name].fn, validators[name].priority);
|
||||
|
||||
window.Parsley.trigger('parsley:validator:init');
|
||||
},
|
||||
|
||||
// Set new messages locale if we have dictionary loaded in ParsleyConfig.i18n
|
||||
setLocale: function (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 (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 (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 (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 (name, arg1, arg2) {
|
||||
if (this.validators[name])
|
||||
ParsleyUtils.warn('Validator "' + name + '" is already defined.');
|
||||
else if (ParsleyDefaults.hasOwnProperty(name)) {
|
||||
ParsleyUtils.warn('"' + name + '" is a restricted keyword and is not a valid validator name.');
|
||||
return;
|
||||
}
|
||||
return this._setValidator(...arguments);
|
||||
},
|
||||
|
||||
updateValidator: function (name, arg1, arg2) {
|
||||
if (!this.validators[name]) {
|
||||
ParsleyUtils.warn('Validator "' + name + '" is not already defined.');
|
||||
return this.addValidator(...arguments);
|
||||
}
|
||||
return this._setValidator(...arguments);
|
||||
},
|
||||
|
||||
removeValidator: function (name) {
|
||||
if (!this.validators[name])
|
||||
ParsleyUtils.warn('Validator "' + name + '" is not defined.');
|
||||
|
||||
delete this.validators[name];
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_setValidator: function (name, validator, priority) {
|
||||
if ('object' !== typeof validator) {
|
||||
// Old style validator, with `fn` and `priority`
|
||||
validator = {
|
||||
fn: validator,
|
||||
priority: priority
|
||||
};
|
||||
}
|
||||
if (!validator.validate) {
|
||||
validator = new ParsleyValidator(validator);
|
||||
}
|
||||
this.validators[name] = validator;
|
||||
|
||||
for (var locale in validator.messages || {})
|
||||
this.addMessage(locale, name, validator.messages[locale]);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
getErrorMessage: function (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 (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(value) {
|
||||
return /\S/.test(value);
|
||||
},
|
||||
priority: 2
|
||||
},
|
||||
required: {
|
||||
validateMultiple: function(values) {
|
||||
return values.length > 0;
|
||||
},
|
||||
validateString: function(value) {
|
||||
return /\S/.test(value);
|
||||
},
|
||||
priority: 512
|
||||
},
|
||||
type: {
|
||||
validateString: function(value, type, {step = '1', base = 0} = {}) {
|
||||
var regex = typeRegexes[type];
|
||||
if (!regex) {
|
||||
throw new Error('validator type `' + type + '` is not supported');
|
||||
}
|
||||
if (!regex.test(value))
|
||||
return false;
|
||||
if ('number' === type) {
|
||||
if (!/^any$/i.test(step || '')) {
|
||||
var nb = Number(value);
|
||||
var decimals = Math.max(decimalPlaces(step), decimalPlaces(base));
|
||||
if (decimalPlaces(nb) > decimals) // Value can't have too many decimals
|
||||
return false;
|
||||
// Be careful of rounding errors by using integers.
|
||||
var toInt = 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(value, regexp) {
|
||||
return regexp.test(value);
|
||||
},
|
||||
requirementType: 'regexp',
|
||||
priority: 64
|
||||
},
|
||||
minlength: {
|
||||
validateString: function (value, requirement) {
|
||||
return value.length >= requirement;
|
||||
},
|
||||
requirementType: 'integer',
|
||||
priority: 30
|
||||
},
|
||||
maxlength: {
|
||||
validateString: function (value, requirement) {
|
||||
return value.length <= requirement;
|
||||
},
|
||||
requirementType: 'integer',
|
||||
priority: 30
|
||||
},
|
||||
length: {
|
||||
validateString: function (value, min, max) {
|
||||
return value.length >= min && value.length <= max;
|
||||
},
|
||||
requirementType: ['integer', 'integer'],
|
||||
priority: 30
|
||||
},
|
||||
mincheck: {
|
||||
validateMultiple: function (values, requirement) {
|
||||
return values.length >= requirement;
|
||||
},
|
||||
requirementType: 'integer',
|
||||
priority: 30
|
||||
},
|
||||
maxcheck: {
|
||||
validateMultiple: function (values, requirement) {
|
||||
return values.length <= requirement;
|
||||
},
|
||||
requirementType: 'integer',
|
||||
priority: 30
|
||||
},
|
||||
check: {
|
||||
validateMultiple: function (values, min, max) {
|
||||
return values.length >= min && values.length <= max;
|
||||
},
|
||||
requirementType: ['integer', 'integer'],
|
||||
priority: 30
|
||||
},
|
||||
min: {
|
||||
validateNumber: function (value, requirement) {
|
||||
return value >= requirement;
|
||||
},
|
||||
requirementType: 'number',
|
||||
priority: 30
|
||||
},
|
||||
max: {
|
||||
validateNumber: function (value, requirement) {
|
||||
return value <= requirement;
|
||||
},
|
||||
requirementType: 'number',
|
||||
priority: 30
|
||||
},
|
||||
range: {
|
||||
validateNumber: function (value, min, max) {
|
||||
return value >= min && value <= max;
|
||||
},
|
||||
requirementType: ['number', 'number'],
|
||||
priority: 30
|
||||
},
|
||||
equalto: {
|
||||
validateString: function (value, refOrValue) {
|
||||
var $reference = $(refOrValue);
|
||||
if ($reference.length)
|
||||
return value === $reference.val();
|
||||
else
|
||||
return value === refOrValue;
|
||||
},
|
||||
priority: 256
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default ParsleyValidatorRegistry;
|
||||
Reference in New Issue
Block a user