200 lines
7.1 KiB
Plaintext
200 lines
7.1 KiB
Plaintext
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;
|