384 lines
14 KiB
JavaScript
384 lines
14 KiB
JavaScript
/*!
|
|
* Laravel Javascript Validation
|
|
*
|
|
* https://github.com/proengsoft/laravel-jsvalidation
|
|
*
|
|
* Copyright (c) 2017 Proengsoft
|
|
* Released under the MIT license
|
|
*/
|
|
|
|
var laravelValidation;
|
|
laravelValidation = {
|
|
|
|
implicitRules: ['Required','Confirmed'],
|
|
|
|
/**
|
|
* Initialize laravel validations.
|
|
*/
|
|
init: function () {
|
|
|
|
// jquery-validation requires the field under validation to be present. We're adding a dummy hidden
|
|
// field so that any errors are not visible.
|
|
var constructor = $.fn.validate;
|
|
$.fn.validate = function( options ) {
|
|
var name = 'proengsoft_jsvalidation'; // must match the name defined in JsValidatorFactory.newFormRequestValidator
|
|
var $elm = $(this).find('input[name="' + name + '"]');
|
|
if ($elm.length === 0) {
|
|
$('<input>').attr({type: 'hidden', name: name}).appendTo(this);
|
|
}
|
|
|
|
return constructor.apply(this, [options]);
|
|
};
|
|
|
|
// Disable class rules and attribute rules
|
|
$.validator.classRuleSettings = {};
|
|
$.validator.attributeRules = function () {};
|
|
|
|
$.validator.dataRules = this.arrayRules;
|
|
$.validator.prototype.arrayRulesCache = {};
|
|
|
|
// Register validations methods
|
|
this.setupValidations();
|
|
},
|
|
|
|
arrayRules: function(element) {
|
|
|
|
var rules = {},
|
|
validator = $.data( element.form, "validator"),
|
|
cache = validator.arrayRulesCache;
|
|
|
|
// Is not an Array
|
|
if (element.name.indexOf('[') === -1) {
|
|
return rules;
|
|
}
|
|
|
|
if (! (element.name in cache)) {
|
|
cache[element.name] = {};
|
|
}
|
|
|
|
$.each(validator.settings.rules, function(name, tmpRules) {
|
|
if (name in cache[element.name]) {
|
|
rules = laravelValidation.helpers.mergeRules(rules, cache[element.name][name]);
|
|
} else {
|
|
cache[element.name][name] = {};
|
|
|
|
var nameRegExp = laravelValidation.helpers.regexFromWildcard(name);
|
|
if (element.name.match(nameRegExp)) {
|
|
var newRules = $.validator.normalizeRule(tmpRules) || {};
|
|
cache[element.name][name] = newRules;
|
|
|
|
rules = laravelValidation.helpers.mergeRules(rules, newRules);
|
|
}
|
|
}
|
|
});
|
|
|
|
return rules;
|
|
},
|
|
|
|
setupValidations: function () {
|
|
|
|
/**
|
|
* Get CSRF token.
|
|
*
|
|
* @param params
|
|
* @returns {string}
|
|
*/
|
|
var getCsrfToken = function (params) {
|
|
return params[0][1][1];
|
|
};
|
|
|
|
/**
|
|
* Whether to validate all attributes.
|
|
*
|
|
* @param params
|
|
* @returns {boolean}
|
|
*/
|
|
var isValidateAll = function (params) {
|
|
return params[0][1][2];
|
|
};
|
|
|
|
/**
|
|
* Determine whether the rule is implicit.
|
|
*
|
|
* @param params
|
|
* @returns {boolean}
|
|
*/
|
|
var isImplicit = function (params) {
|
|
var implicit = false;
|
|
$.each(params, function (i, parameters) {
|
|
implicit = implicit || parameters[3];
|
|
});
|
|
|
|
return implicit;
|
|
};
|
|
|
|
/**
|
|
* Get form method from a validator instance.
|
|
*
|
|
* @param validator
|
|
* @returns {string}
|
|
*/
|
|
var formMethod = function (validator) {
|
|
var formMethod = $(validator.currentForm).attr('method');
|
|
if ($(validator.currentForm).find('input[name="_method"]').length) {
|
|
formMethod = $(validator.currentForm).find('input[name="_method"]').val();
|
|
}
|
|
|
|
return formMethod;
|
|
};
|
|
|
|
/**
|
|
* Get AJAX parameters for remote requests.
|
|
*
|
|
* @param validator
|
|
* @param element
|
|
* @param params
|
|
* @param data
|
|
* @returns {object}
|
|
*/
|
|
var ajaxOpts = function (validator, element, params, data) {
|
|
return {
|
|
mode: 'abort',
|
|
port: 'validate' + element.name,
|
|
dataType: 'json',
|
|
data: data,
|
|
context: validator.currentForm,
|
|
url: $(validator.currentForm).attr('action'),
|
|
type: formMethod(validator),
|
|
beforeSend: function (xhr) {
|
|
var token = getCsrfToken(params);
|
|
if (formMethod(validator) !== 'get' && token) {
|
|
return xhr.setRequestHeader('X-XSRF-TOKEN', token);
|
|
}
|
|
},
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Validate a set of local JS based rules against an element.
|
|
*
|
|
* @param validator
|
|
* @param values
|
|
* @param element
|
|
* @param rules
|
|
* @returns {boolean}
|
|
*/
|
|
var validateLocalRules = function (validator, values, element, rules) {
|
|
var validated = true,
|
|
previous = validator.previousValue(element);
|
|
|
|
$.each(rules, function (i, param) {
|
|
var implicit = param[3] || laravelValidation.implicitRules.indexOf(param[0]) !== -1;
|
|
var rule = param[0];
|
|
var message = param[2];
|
|
|
|
if (! implicit && validator.optional(element)) {
|
|
validated = "dependency-mismatch";
|
|
return false;
|
|
}
|
|
|
|
if (laravelValidation.methods[rule] !== undefined) {
|
|
$.each(values, function(index, value) {
|
|
validated = laravelValidation.methods[rule].call(validator, value, element, param[1], function(valid) {
|
|
validator.settings.messages[element.name].laravelValidationRemote = previous.originalMessage;
|
|
if (valid) {
|
|
var submitted = validator.formSubmitted;
|
|
validator.prepareElement(element);
|
|
validator.formSubmitted = submitted;
|
|
validator.successList.push(element);
|
|
delete validator.invalid[element.name];
|
|
validator.showErrors();
|
|
} else {
|
|
var errors = {};
|
|
errors[ element.name ]
|
|
= previous.message
|
|
= typeof message === "function" ? message( value ) : message;
|
|
validator.invalid[element.name] = true;
|
|
validator.showErrors(errors);
|
|
}
|
|
validator.showErrors(validator.errorMap);
|
|
previous.valid = valid;
|
|
});
|
|
|
|
// Break loop.
|
|
if (validated === false) {
|
|
return false;
|
|
}
|
|
});
|
|
} else {
|
|
validated = false;
|
|
}
|
|
|
|
if (validated !== true) {
|
|
if (!validator.settings.messages[element.name] ) {
|
|
validator.settings.messages[element.name] = {};
|
|
}
|
|
|
|
validator.settings.messages[element.name].laravelValidation= message;
|
|
|
|
return false;
|
|
}
|
|
|
|
});
|
|
|
|
return validated;
|
|
};
|
|
|
|
/**
|
|
* Create JQueryValidation check to validate Laravel rules.
|
|
*/
|
|
|
|
$.validator.addMethod("laravelValidation", function (value, element, params) {
|
|
var rules = [],
|
|
arrayRules = [];
|
|
$.each(params, function (i, param) {
|
|
// put Implicit rules in front
|
|
var isArrayRule = param[4].indexOf('[') !== -1;
|
|
if (param[3] || laravelValidation.implicitRules.indexOf(param[0]) !== -1) {
|
|
isArrayRule ? arrayRules.unshift(param) : rules.unshift(param);
|
|
} else {
|
|
isArrayRule ? arrayRules.push(param) : rules.push(param);
|
|
}
|
|
});
|
|
|
|
// Validate normal rules.
|
|
var localRulesResult = validateLocalRules(this, [value], element, rules);
|
|
|
|
// Validate items of the array using array rules.
|
|
var arrayValue = ! Array.isArray(value) ? [value] : value;
|
|
var arrayRulesResult = validateLocalRules(this, arrayValue, element, arrayRules);
|
|
|
|
return localRulesResult && arrayRulesResult;
|
|
}, '');
|
|
|
|
|
|
/**
|
|
* Create JQueryValidation check to validate Remote Laravel rules.
|
|
*/
|
|
$.validator.addMethod("laravelValidationRemote", function (value, element, params) {
|
|
|
|
if (! isImplicit(params) && this.optional( element )) {
|
|
return "dependency-mismatch";
|
|
}
|
|
|
|
var previous = this.previousValue( element ),
|
|
validator, data;
|
|
|
|
if (! this.settings.messages[ element.name ]) {
|
|
this.settings.messages[ element.name ] = {};
|
|
}
|
|
previous.originalMessage = this.settings.messages[ element.name ].laravelValidationRemote;
|
|
this.settings.messages[ element.name ].laravelValidationRemote = previous.message;
|
|
|
|
if (laravelValidation.helpers.arrayEquals(previous.old, value) || previous.old === value) {
|
|
return previous.valid;
|
|
}
|
|
|
|
previous.old = value;
|
|
validator = this;
|
|
this.startRequest( element );
|
|
|
|
data = $(validator.currentForm).serializeArray();
|
|
data.push({'name': '_jsvalidation', 'value': element.name});
|
|
data.push({'name': '_jsvalidation_validate_all', 'value': isValidateAll(params)});
|
|
|
|
$.ajax( ajaxOpts(validator, element, params, data) )
|
|
.always(function( response, textStatus ) {
|
|
var errors, message, submitted, valid;
|
|
|
|
if (textStatus === 'error') {
|
|
valid = false;
|
|
response = laravelValidation.helpers.parseErrorResponse(response);
|
|
} else if (textStatus === 'success') {
|
|
valid = response === true || response === "true";
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
validator.settings.messages[ element.name ].laravelValidationRemote = previous.originalMessage;
|
|
|
|
if ( valid ) {
|
|
submitted = validator.formSubmitted;
|
|
validator.prepareElement( element );
|
|
validator.formSubmitted = submitted;
|
|
validator.successList.push( element );
|
|
delete validator.invalid[ element.name ];
|
|
validator.showErrors();
|
|
} else {
|
|
errors = {};
|
|
message = response || validator.defaultMessage( element, "remote" );
|
|
errors[ element.name ]
|
|
= previous.message
|
|
= typeof message === "function" ? message( value ) : message[0];
|
|
validator.invalid[ element.name ] = true;
|
|
validator.showErrors( errors );
|
|
}
|
|
validator.showErrors(validator.errorMap);
|
|
previous.valid = valid;
|
|
validator.stopRequest( element, valid );
|
|
}
|
|
);
|
|
return "pending";
|
|
}, '');
|
|
|
|
/**
|
|
* Create JQueryValidation check to form requests.
|
|
*/
|
|
$.validator.addMethod("laravelValidationFormRequest", function (value, element, params) {
|
|
|
|
var validator = this,
|
|
previous = validator.previousValue(element);
|
|
|
|
var data = $(validator.currentForm).serializeArray();
|
|
data.push({name: '__proengsoft_form_request', value: 1}); // must match FormRequest.JS_VALIDATION_FIELD
|
|
|
|
// Skip AJAX if the value remains the same as a prior request.
|
|
if (JSON.stringify(previous.old) === JSON.stringify(data)) {
|
|
if (! previous.valid) {
|
|
validator.showErrors(previous.errors || {});
|
|
}
|
|
|
|
return previous.valid;
|
|
}
|
|
|
|
previous.old = data;
|
|
this.startRequest( element );
|
|
|
|
$.ajax(ajaxOpts(validator, element, params, data))
|
|
.always(function( response, textStatus ) {
|
|
var errors = {},
|
|
valid = textStatus === 'success' && (response === true || response === 'true');
|
|
|
|
if (valid) {
|
|
validator.resetInternals();
|
|
validator.toHide = validator.errorsFor( element );
|
|
} else {
|
|
$.each( response, function( fieldName, errorMessages ) {
|
|
var errorElement = laravelValidation.helpers.findByName(validator, fieldName)[0];
|
|
if (errorElement) {
|
|
errors[errorElement.name] = laravelValidation.helpers.encode(errorMessages[0] || '');
|
|
}
|
|
});
|
|
|
|
// Failed to find the error fields so mark the form as valid otherwise user
|
|
// will be left in limbo with no visible error messages.
|
|
if ($.isEmptyObject(errors)) {
|
|
valid = true;
|
|
}
|
|
}
|
|
|
|
previous.valid = valid;
|
|
previous.errors = errors;
|
|
validator.showErrors(errors);
|
|
validator.stopRequest(element, valid);
|
|
});
|
|
|
|
return 'pending';
|
|
}, '');
|
|
}
|
|
};
|
|
|
|
$(function() {
|
|
laravelValidation.init();
|
|
});
|