node-form
Version:
form validation engine
858 lines (725 loc) • 31.9 kB
JavaScript
var __extends = this.__extends || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
__.prototype = b.prototype;
d.prototype = new __();
};
var Validation;
(function (_Validation) {
(function (CompareOperator) {
CompareOperator[CompareOperator["LessThan"] = 0] = "LessThan";
CompareOperator[CompareOperator["LessThanEqual"] = 1] = "LessThanEqual";
CompareOperator[CompareOperator["Equal"] = 2] = "Equal";
CompareOperator[CompareOperator["NotEqual"] = 3] = "NotEqual";
CompareOperator[CompareOperator["GreaterThanEqual"] = 4] = "GreaterThanEqual";
CompareOperator[CompareOperator["GreaterThan"] = 5] = "GreaterThan";
})(_Validation.CompareOperator || (_Validation.CompareOperator = {}));
var CompareOperator = _Validation.CompareOperator;
var Error = (function () {
function Error() {
this.HasError = true;
this.ErrorMessage = "";
}
return Error;
})();
_Validation.Error = Error;
var ValidationFailure = (function () {
function ValidationFailure(Error, IsAsync) {
this.Error = Error;
this.IsAsync = IsAsync;
}
Object.defineProperty(ValidationFailure.prototype, "HasError", {
get: function () {
return this.Error.HasError;
},
enumerable: true,
configurable: true
});
Object.defineProperty(ValidationFailure.prototype, "ErrorMessage", {
get: function () {
return this.Error.ErrorMessage;
},
enumerable: true,
configurable: true
});
Object.defineProperty(ValidationFailure.prototype, "TranslateArgs", {
get: function () {
return this.Error.TranslateArgs;
},
enumerable: true,
configurable: true
});
return ValidationFailure;
})();
_Validation.ValidationFailure = ValidationFailure;
var ValidationResult = (function () {
function ValidationResult(Name) {
this.Name = Name;
}
Object.defineProperty(ValidationResult.prototype, "Children", {
get: function () {
return [];
},
enumerable: true,
configurable: true
});
ValidationResult.prototype.Add = function (error) {
throw ("Cannot add to ValidationResult to leaf node.");
};
ValidationResult.prototype.Remove = function (index) {
throw ("Cannot remove ValidationResult from leaf node.");
};
Object.defineProperty(ValidationResult.prototype, "HasErrorsDirty", {
get: function () {
return this.IsDirty && this.HasErrors;
},
enumerable: true,
configurable: true
});
Object.defineProperty(ValidationResult.prototype, "HasErrors", {
get: function () {
return false;
},
enumerable: true,
configurable: true
});
Object.defineProperty(ValidationResult.prototype, "ErrorCount", {
get: function () {
return 0;
},
enumerable: true,
configurable: true
});
Object.defineProperty(ValidationResult.prototype, "ErrorMessage", {
get: function () {
return "";
},
enumerable: true,
configurable: true
});
return ValidationResult;
})();
_Validation.ValidationResult = ValidationResult;
var CompositeValidationResult = (function () {
function CompositeValidationResult(Name) {
this.Name = Name;
this.Children = [];
}
CompositeValidationResult.prototype.AddFirst = function (error) {
this.Children.unshift(error);
};
CompositeValidationResult.prototype.Add = function (error) {
this.Children.push(error);
};
CompositeValidationResult.prototype.Remove = function (index) {
this.Children.splice(index, 1);
};
Object.defineProperty(CompositeValidationResult.prototype, "HasErrorsDirty", {
get: function () {
if (this.Optional !== undefined && _.isFunction(this.Optional) && this.Optional())
return false;
return _.some(this.Children, function (error) {
return error.HasErrorsDirty;
});
},
enumerable: true,
configurable: true
});
Object.defineProperty(CompositeValidationResult.prototype, "HasErrors", {
get: function () {
if (this.Optional !== undefined && _.isFunction(this.Optional) && this.Optional())
return false;
return _.some(this.Children, function (error) {
return error.HasErrors;
});
},
enumerable: true,
configurable: true
});
Object.defineProperty(CompositeValidationResult.prototype, "ErrorCount", {
get: function () {
if (!this.HasErrors)
return 0;
return _.reduce(this.Children, function (memo, error) {
return memo + error.ErrorCount;
}, 0);
},
enumerable: true,
configurable: true
});
Object.defineProperty(CompositeValidationResult.prototype, "ErrorMessage", {
get: function () {
if (!this.HasErrors)
return "";
return _.reduce(this.Children, function (memo, error) {
return memo + error.ErrorMessage;
}, "");
},
enumerable: true,
configurable: true
});
Object.defineProperty(CompositeValidationResult.prototype, "TranslateArgs", {
get: function () {
if (!this.HasErrors)
return [];
var newArgs = [];
_.each(this.Children, function (error) {
newArgs = newArgs.concat(error.TranslateArgs);
});
return newArgs;
},
enumerable: true,
configurable: true
});
CompositeValidationResult.prototype.LogErrors = function (headerMessage) {
if (headerMessage === undefined)
headerMessage = "Output";
console.log("---------------\n");
console.log("--- " + headerMessage + " ----\n");
console.log("---------------\n");
this.traverse(this, 1);
console.log("\n\n\n");
};
Object.defineProperty(CompositeValidationResult.prototype, "Errors", {
get: function () {
var map = {};
_.each(this.Children, function (val) {
map[val.Name] = val;
});
return map;
},
enumerable: true,
configurable: true
});
Object.defineProperty(CompositeValidationResult.prototype, "FlattenErros", {
get: function () {
var errors = [];
this.flattenErrors(this, errors);
return errors;
},
enumerable: true,
configurable: true
});
CompositeValidationResult.prototype.SetDirty = function () {
this.SetDirtyEx(this, true);
};
CompositeValidationResult.prototype.SetPristine = function () {
this.SetDirtyEx(this, false);
};
CompositeValidationResult.prototype.SetDirtyEx = function (node, dirty) {
if (node.Children.length === 0) {
node["IsDirty"] = dirty;
} else {
for (var i = 0, len = node.Children.length; i < len; i++) {
this.SetDirtyEx(node.Children[i], dirty);
}
}
};
CompositeValidationResult.prototype.flattenErrors = function (node, errorCollection) {
if (node.Children.length === 0) {
if (node.HasErrors)
errorCollection.push(node);
} else {
for (var i = 0, len = node.Children.length; i < len; i++) {
if (node.Children[i].HasErrors)
this.flattenErrors(node.Children[i], errorCollection);
}
}
};
CompositeValidationResult.prototype.traverse = function (node, indent) {
console.log(Array(indent++).join("--") + node.Name + " (" + node.ErrorMessage + ")" + '\n\r');
for (var i = 0, len = node.Children.length; i < len; i++) {
this.traverse(node.Children[i], indent);
}
};
return CompositeValidationResult;
})();
_Validation.CompositeValidationResult = CompositeValidationResult;
var AbstractValidator = (function () {
function AbstractValidator() {
this.Validators = {};
this.AbstractValidators = {};
this.ValidationFunctions = {};
this.ForList = false;
}
AbstractValidator.prototype.RuleFor = function (prop, validator) {
if (this.Validators[prop] === undefined) {
this.Validators[prop] = [];
}
this.Validators[prop].push(validator);
};
AbstractValidator.prototype.ValidationFor = function (prop, fce) {
if (this.ValidationFunctions[prop] === undefined) {
this.ValidationFunctions[prop] = [];
}
this.ValidationFunctions[prop].push(fce);
};
AbstractValidator.prototype.Validation = function (fce) {
if (fce.Name === undefined)
throw 'argument must have property Name';
this.ValidationFor(fce.Name, fce);
};
AbstractValidator.prototype.ValidatorFor = function (prop, validator, forList) {
validator.ForList = forList;
this.AbstractValidators[prop] = validator;
};
AbstractValidator.prototype.CreateAbstractRule = function (name) {
return new AbstractValidationRule(name, this);
};
AbstractValidator.prototype.CreateAbstractListRule = function (name) {
return new AbstractListValidationRule(name, this);
};
AbstractValidator.prototype.CreateRule = function (name) {
return new AbstractValidationRule(name, this);
};
return AbstractValidator;
})();
_Validation.AbstractValidator = AbstractValidator;
var AbstractValidationRule = (function () {
function AbstractValidationRule(Name, validator, forList) {
this.Name = Name;
this.validator = validator;
this.Rules = {};
this.Validators = {};
this.Children = {};
this.ForList = false;
this.ValidationResult = new CompositeValidationResult(this.Name);
if (!forList) {
_.each(this.validator.Validators, function (val, key) {
this.createRuleFor(key);
_.each(val, function (validator) {
this.Rules[key].AddValidator(validator);
}, this);
}, this);
_.each(this.validator.ValidationFunctions, function (val) {
_.each(val, function (validation) {
var validator = this.Validators[validation.Name];
if (validator === undefined) {
validator = new Validator(validation.Name, validation.ValidationFce, validation.AsyncValidationFce);
this.Validators[validation.Name] = validator;
this.ValidationResult.Add(validator);
}
}, this);
}, this);
this.addChildren();
}
}
AbstractValidationRule.prototype.addChildren = function () {
_.each(this.validator.AbstractValidators, function (val, key) {
var validationRule;
if (val.ForList) {
validationRule = val.CreateAbstractListRule(key);
} else {
validationRule = val.CreateAbstractRule(key);
}
this.Children[key] = validationRule;
this.ValidationResult.Add(validationRule.ValidationResult);
}, this);
};
AbstractValidationRule.prototype.SetOptional = function (fce) {
this.ValidationResult.Optional = fce;
};
AbstractValidationRule.prototype.createRuleFor = function (prop) {
var propValidationRule = new PropertyValidationRule(prop);
this.Rules[prop] = propValidationRule;
this.ValidationResult.Add(propValidationRule);
};
AbstractValidationRule.prototype.Validate = function (context) {
_.each(this.Children, function (val, key) {
if (context[key] === undefined)
context[key] = {};
val.Validate(context[key]);
}, this);
for (var propName in this.Rules) {
var rule = this.Rules[propName];
rule.Validate(new ValidationContext(propName, context));
}
_.each(this.validator.ValidationFunctions, function (valFunctions) {
_.each(valFunctions, function (valFce) {
var validator = this.Validators[valFce.Name];
if (validator !== undefined)
validator.Validate(context);
}, this);
}, this);
return this.ValidationResult;
};
AbstractValidationRule.prototype.ValidateAsync = function (context) {
var deferred = Q.defer();
var promises = [];
_.each(this.Children, function (val, key) {
promises.push(val.ValidateAsync(context[key]));
}, this);
for (var propName in this.Rules) {
var rule = this.Rules[propName];
promises.push(rule.ValidateAsync(new ValidationContext(propName, context)));
}
_.each(this.validator.ValidationFunctions, function (valFunctions) {
_.each(valFunctions, function (valFce) {
var validator = this.Validators[valFce.Name];
if (validator !== undefined)
promises.push(validator.ValidateAsync(context));
}, this);
}, this);
var self = this;
Q.all(promises).then(function (result) {
deferred.resolve(self.ValidationResult);
});
return deferred.promise;
};
AbstractValidationRule.prototype.ValidateAll = function (context) {
this.Validate(context);
return this.ValidateAsync(context);
};
AbstractValidationRule.prototype.ValidateProperty = function (context, propName) {
var childRule = this.Children[propName];
if (childRule !== undefined)
childRule.Validate(context[propName]);
var rule = this.Rules[propName];
if (rule !== undefined) {
var valContext = new ValidationContext(propName, context);
rule.Validate(valContext);
rule.ValidateAsync(valContext);
}
var validationFces = this.validator.ValidationFunctions[propName];
if (validationFces !== undefined) {
_.each(validationFces, function (valFce) {
var validator = this.Validators[valFce.Name];
if (validator !== undefined)
validator.Validate(context);
}, this);
}
};
return AbstractValidationRule;
})();
var AbstractListValidationRule = (function (_super) {
__extends(AbstractListValidationRule, _super);
function AbstractListValidationRule(Name, validator) {
_super.call(this, Name, validator, true);
this.Name = Name;
this.validator = validator;
}
AbstractListValidationRule.prototype.Validate = function (context) {
this.NotifyListChanged(context);
for (var i = 0; i != context.length; i++) {
var validationRule = this.getValidationRule(i);
if (validationRule !== undefined)
validationRule.Validate(context[i]);
}
return this.ValidationResult;
};
AbstractListValidationRule.prototype.ValidateAsync = function (context) {
var deferred = Q.defer();
var promises = [];
this.NotifyListChanged(context);
for (var i = 0; i != context.length; i++) {
var validationRule = this.getValidationRule(i);
if (validationRule !== undefined)
promises.push(validationRule.ValidateAsync(context[i]));
}
var self = this;
Q.all(promises).then(function (result) {
deferred.resolve(self.ValidationResult);
});
return deferred.promise;
};
AbstractListValidationRule.prototype.getValidationRule = function (i) {
var keyName = this.getIndexedKey(i);
return this.Children[keyName];
};
AbstractListValidationRule.prototype.getIndexedKey = function (i) {
return this.Name + i.toString();
};
AbstractListValidationRule.prototype.NotifyListChanged = function (list) {
for (var i = 0; i != list.length; i++) {
var validationRule = this.getValidationRule(i);
if (validationRule === undefined) {
var keyName = this.getIndexedKey(i);
validationRule = this.validator.CreateAbstractRule(keyName);
this.Children[keyName] = validationRule;
this.ValidationResult.Add(validationRule.ValidationResult);
}
}
};
return AbstractListValidationRule;
})(AbstractValidationRule);
var ValidationContext = (function () {
function ValidationContext(Key, Data) {
this.Key = Key;
this.Data = Data;
}
Object.defineProperty(ValidationContext.prototype, "Value", {
get: function () {
return this.Data[this.Key];
},
enumerable: true,
configurable: true
});
return ValidationContext;
})();
var MessageLocalization = (function () {
function MessageLocalization() {
}
MessageLocalization.GetValidationMessage = function (validator) {
var msgText = MessageLocalization.ValidationMessages[validator.tagName];
if (msgText === undefined || msgText === "" || !_.isString(msgText)) {
msgText = MessageLocalization.customMsg;
}
return StringFce.format(msgText, validator);
};
MessageLocalization.customMsg = "Please, fix the field.";
MessageLocalization.defaultMessages = {
"remote": "Please fix the field.",
"email": "Please enter a valid email address.",
"url": "Please enter a valid URL.",
"date": "Please enter a valid date.",
"dateISO": "Please enter a valid date ( ISO ).",
"number": "Please enter a valid number.",
"digits": "Please enter only digits.",
"signedDigits": "Please enter only signed digits.",
"creditcard": "Please enter a valid credit card number.",
"equalTo": "Please enter the same value again.",
"maxlength": "Please enter no more than {MaxLength} characters.",
"minlength": "Please enter at least {MinLength} characters.",
"rangelength": "Please enter a value between {MinLength} and {MaxLength} characters long.",
"range": "Please enter a value between {Min} and {Max}.",
"max": "Please enter a value less than or equal to {Max}.",
"min": "Please enter a value greater than or equal to {Min}.",
"step": "Please enter a value with step {Step}.",
"contains": "Please enter a value from list of values. Attempted value '{AttemptedValue}'.",
"mask": "Please enter a value corresponding with {Mask}.",
"custom": MessageLocalization.customMsg
};
MessageLocalization.ValidationMessages = MessageLocalization.defaultMessages;
return MessageLocalization;
})();
_Validation.MessageLocalization = MessageLocalization;
var StringFce = (function () {
function StringFce() {
}
StringFce.format = function (s, args) {
var formatted = s;
for (var prop in args) {
var regexp = new RegExp('\\{' + prop + '\\}', 'gi');
formatted = formatted.replace(regexp, args[prop]);
}
return formatted;
};
return StringFce;
})();
var PropertyValidationRule = (function (_super) {
__extends(PropertyValidationRule, _super);
function PropertyValidationRule(Name, validatorsToAdd) {
_super.call(this, Name);
this.Name = Name;
this.Validators = {};
this.ValidationFailures = {};
for (var index in validatorsToAdd) {
this.AddValidator(validatorsToAdd[index]);
}
}
PropertyValidationRule.prototype.AddValidator = function (validator) {
this.Validators[validator.tagName] = validator;
this.ValidationFailures[validator.tagName] = new ValidationFailure(new Error(), !!validator.isAsync);
};
Object.defineProperty(PropertyValidationRule.prototype, "Errors", {
get: function () {
return this.ValidationFailures;
},
enumerable: true,
configurable: true
});
Object.defineProperty(PropertyValidationRule.prototype, "HasErrors", {
get: function () {
if (this.Optional !== undefined && _.isFunction(this.Optional) && this.Optional())
return false;
return _.some(_.values(this.Errors), function (error) {
return error.HasError;
});
},
enumerable: true,
configurable: true
});
Object.defineProperty(PropertyValidationRule.prototype, "ErrorCount", {
get: function () {
return this.HasErrors ? 1 : 0;
},
enumerable: true,
configurable: true
});
Object.defineProperty(PropertyValidationRule.prototype, "ErrorMessage", {
get: function () {
if (!this.HasErrors)
return "";
return _.reduce(_.values(this.Errors), function (memo, error) {
return memo + error.ErrorMessage;
}, "");
},
enumerable: true,
configurable: true
});
Object.defineProperty(PropertyValidationRule.prototype, "TranslateArgs", {
get: function () {
if (!this.HasErrors)
return [];
var newArray = [];
_.each(_.values(this.Errors), function (error) {
if (error.HasError)
newArray.push(error.Error.TranslateArgs);
});
return newArray;
},
enumerable: true,
configurable: true
});
PropertyValidationRule.prototype.Validate = function (context) {
try {
return this.ValidateEx(context.Value);
} catch (e) {
console.log("Exception occurred when checking element " + context.Key + ".", e);
throw e;
}
};
PropertyValidationRule.prototype.ValidateEx = function (value) {
var lastPriority = 0;
var shortCircuited = false;
for (var index in this.ValidationFailures) {
var validation = this.ValidationFailures[index];
if (validation.IsAsync)
continue;
var validator = this.Validators[index];
try {
var priority = 0;
if (shortCircuited && priority > lastPriority) {
validation.Error.HasError = false;
} else {
validation.Error.HasError = hasError;
validation.Error.TranslateArgs = { TranslateId: validator.tagName, MessageArgs: _.extend(validator, { AttemptedValue: value }), CustomMessage: validator.customMessage };
validation.Error.ErrorMessage = hasError ? MessageLocalization.GetValidationMessage(validation.Error.TranslateArgs.MessageArgs) : "";
shortCircuited = hasError;
lastPriority = priority;
}
} catch (e) {
console.log("Exception occurred when checking element'" + validator.tagName + "' method.", e);
throw e;
}
}
return _.filter(this.ValidationFailures, function (item) {
return !item.IsAsync;
});
};
PropertyValidationRule.prototype.ValidateAsync = function (context) {
return this.ValidateAsyncEx(context.Value);
};
PropertyValidationRule.prototype.ValidateAsyncEx = function (value) {
var deferred = Q.defer();
var promises = [];
var setResultFce = function (result) {
var hasError = !result;
validation.Error.HasError = hasError;
validation.Error.TranslateArgs = { TranslateId: validator.tagName, MessageArgs: _.extend(validator, { AttemptedValue: value }) };
validation.Error.ErrorMessage = hasError ? MessageLocalization.GetValidationMessage(validation.Error.TranslateArgs.MessageArgs) : "";
};
for (var index in this.ValidationFailures) {
var validation = this.ValidationFailures[index];
if (!validation.IsAsync)
continue;
var validator = this.Validators[index];
try {
hasErrorPromise.then(setResultFce);
promises.push(hasErrorPromise);
} catch (e) {
console.log("Exception occurred when checking element'" + validator.tagName + "' method.", e);
throw e;
}
}
var self = this;
Q.all(promises).then(function (result) {
deferred.resolve(_.filter(self.ValidationFailures, function (item) {
return item.IsAsync;
}));
});
return deferred.promise;
};
return PropertyValidationRule;
})(ValidationResult);
var Validator = (function (_super) {
__extends(Validator, _super);
function Validator(Name, ValidateFce, AsyncValidationFce) {
_super.call(this, Name);
this.Name = Name;
this.ValidateFce = ValidateFce;
this.AsyncValidationFce = AsyncValidationFce;
this.Error = new Error();
this.ValidationFailures = {};
this.ValidationFailures[this.Name] = new ValidationFailure(this.Error, false);
}
Validator.prototype.Validate = function (context) {
if (this.ValidateFce !== undefined)
this.ValidateFce.bind(context)(this.Error);
return this.ValidationFailures[this.Name];
};
Validator.prototype.ValidateAsync = function (context) {
var deferred = Q.defer();
if (this.AsyncValidationFce === undefined) {
deferred.resolve(this.ValidationFailures[this.Name]);
} else {
var self = this;
this.AsyncValidationFce.bind(context)(this.Error).then(function () {
deferred.resolve(self.ValidationFailures[self.Name]);
});
}
return deferred.promise;
};
Object.defineProperty(Validator.prototype, "HasError", {
get: function () {
return this.HasErrors;
},
enumerable: true,
configurable: true
});
Object.defineProperty(Validator.prototype, "Errors", {
get: function () {
return this.ValidationFailures;
},
enumerable: true,
configurable: true
});
Object.defineProperty(Validator.prototype, "HasErrors", {
get: function () {
if (this.Optional !== undefined && _.isFunction(this.Optional) && this.Optional())
return false;
return this.Error.HasError;
},
enumerable: true,
configurable: true
});
Object.defineProperty(Validator.prototype, "ErrorCount", {
get: function () {
return this.HasErrors ? 1 : 0;
},
enumerable: true,
configurable: true
});
Object.defineProperty(Validator.prototype, "ErrorMessage", {
get: function () {
if (!this.HasErrors)
return "";
return this.Error.ErrorMessage;
},
enumerable: true,
configurable: true
});
Object.defineProperty(Validator.prototype, "TranslateArgs", {
get: function () {
if (!this.HasErrors)
return [];
var newArray = [];
newArray.push(this.Error.TranslateArgs);
return newArray;
},
enumerable: true,
configurable: true
});
return Validator;
})(ValidationResult);
})(Validation || (Validation = {}));