├── .gitignore ├── .idea ├── libraries │ └── Generated_files.xml └── watcherTasks.xml ├── Gruntfile.js ├── LICENSE ├── README.md ├── app.js ├── bower.json ├── content └── tutorial │ ├── 00_custom_rules.ngdoc │ ├── 01_property_rules.ngdoc │ ├── 02_composition_rules.ngdoc │ ├── 03_list_rules.ngdoc │ ├── 04_localization_rules.ngdoc │ └── index.ngdoc ├── dist ├── README.md ├── amd │ ├── BasicValidators.js │ ├── DateCompareValidator.js │ ├── FormSchema.js │ ├── ICOValidator.js │ ├── ParamValidator.js │ ├── RCValidator.js │ ├── Utils.js │ ├── Validation.js │ └── i18n │ │ ├── messages_cz.js │ │ ├── messages_de.js │ │ └── messages_en.js ├── business-rules-engine.js ├── business-rules-engine.min.js ├── commonjs │ ├── BasicValidators.js │ ├── DateCompareValidator.js │ ├── FormSchema.js │ ├── ICOValidator.js │ ├── ParamValidator.js │ ├── RCValidator.js │ ├── Utils.js │ ├── Validation.js │ └── i18n │ │ ├── messages_cz.js │ │ ├── messages_de.js │ │ └── messages_en.js ├── module │ ├── BasicValidators.js │ ├── DateCompareValidator.js │ ├── FormSchema.js │ ├── ICOValidator.js │ ├── ParamValidator.js │ ├── RCValidator.js │ ├── Utils.js │ ├── Validation.js │ └── i18n │ │ ├── messages_cz.js │ │ ├── messages_cz.json │ │ ├── messages_de.js │ │ ├── messages_de.json │ │ ├── messages_en.js │ │ └── messages_en.json └── package.json ├── form_logo.jpg ├── package.json ├── src ├── customValidators │ ├── DateCompareValidator.ts │ ├── ICOValidator.ts │ ├── ParamValidator.ts │ └── RCValidator.ts ├── localization │ ├── messages_cz.json │ ├── messages_cz.ts │ ├── messages_de.json │ ├── messages_de.ts │ ├── messages_en.json │ └── messages_en.ts ├── metaDataRules │ ├── form.ts │ ├── metaDataRules.ts │ └── util.ts └── validation │ ├── BasicValidators.ts │ ├── FormSchema.ts │ ├── Utils.ts │ └── Validation.ts ├── test ├── customValidators │ ├── DataCompareValidator.ts │ ├── ICOValidator.ts │ ├── ParamValidator.ts │ ├── RCValidator.ts │ └── customValidators.ts └── validation │ ├── basicValidators.ts │ ├── formSchema.ts │ ├── rules.ts │ ├── rulesLists.ts │ ├── rulesNested.ts │ ├── rulesNotifications.ts │ ├── rulesShared.ts │ └── rulesTranslate.ts └── tsd.json /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | .DS_Store 3 | node_modules/ 4 | npm-debug.log 5 | typings 6 | .idea 7 | typings/ -------------------------------------------------------------------------------- /.idea/libraries/Generated_files.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /.idea/watcherTasks.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 22 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | // Generated on 2014-06-22 using generator-nodejs 2.0.0 2 | module.exports = function (grunt) { 3 | grunt.initConfig({ 4 | pkg: grunt.file.readJSON('package.json'), 5 | complexity: { 6 | generic: { 7 | src: ['src/**/*.js'], 8 | options: { 9 | errorsOnly: false, 10 | cyclometric: 6, // default is 3 11 | halstead: 16, // default is 8 12 | maintainability: 100, // default is 100 13 | breakOnErrors:false 14 | } 15 | } 16 | }, 17 | jshint: { 18 | all: [ 19 | 'Gruntfile.js', 20 | 'src/**/*.js' 21 | ], 22 | options: { 23 | jshintrc: '.jshintrc', 24 | force:false 25 | } 26 | }, 27 | mochacli: { 28 | all: ['test/**/*.js'], 29 | options: { 30 | reporter: 'spec', 31 | ui: 'bdd' 32 | } 33 | }, 34 | watch: { 35 | js: { 36 | files: ['**/*.js', '!node_modules/**/*.js'], 37 | tasks: ['default'], 38 | options: { 39 | nospawn: true 40 | } 41 | } 42 | }, 43 | ngdocs: { 44 | options: { 45 | dest: 'docs', 46 | //scripts: ['../app.min.js'], 47 | html5Mode: true, 48 | startPage: '/api', 49 | title: "Validation engine", 50 | //image: "path/to/my/image.png", 51 | imageLink: "http://my-domain.com", 52 | titleLink: "/api", 53 | bestMatch: true, 54 | analytics: { 55 | account: 'UA-08150815-0', 56 | domainName: 'my-domain.com' 57 | }, 58 | discussions: { 59 | shortName: 'my', 60 | url: 'http://my-domain.com', 61 | dev: false 62 | } 63 | }, 64 | tutorial: { 65 | src: ['content/tutorial/*.ngdoc'], 66 | title: 'Tutorial' 67 | }, 68 | api: { 69 | src: ['validation/**/*.js'], 70 | title: 'API Documentation' 71 | } 72 | }, 73 | typedoc: { 74 | build: { 75 | options: { 76 | module: 'commonjs', 77 | out: './docs', 78 | name: 'Business rules engine', 79 | target: 'es5' 80 | }, 81 | src: ['src/validation/*','src/customValidators/*'] 82 | } 83 | }, 84 | typescript: { 85 | amd: { 86 | src: ['src/validation/*.ts'], 87 | dest: 'dist/amd', 88 | options: { 89 | basePath: 'src/validation', 90 | module: 'amd', 91 | target: 'es5', 92 | comments:false 93 | } 94 | }, 95 | commonjs: { 96 | src: ['src/validation/*.ts'], 97 | dest: 'dist/commonjs', 98 | options: { 99 | basePath: 'src/validation', 100 | module: 'commonjs', 101 | target: 'es5', 102 | comments:false 103 | } 104 | }, 105 | customValidatorsCommonjs: { 106 | src: ['src/customValidators/*.ts'], 107 | dest: 'dist/commonjs', 108 | options: { 109 | basePath: 'src/customValidators', 110 | module: 'commonjs', 111 | target: 'es5', 112 | comments:false 113 | } 114 | }, 115 | customValidatorsAmd: { 116 | src: ['src/customValidators/*.ts'], 117 | dest: 'dist/amd', 118 | options: { 119 | basePath: 'src/customValidators', 120 | module: 'amd', 121 | target: 'es5', 122 | comments:false 123 | } 124 | }, 125 | localCommonjs: { 126 | src: ['src/localization/*.ts'], 127 | dest: 'dist/commonjs/i18n', 128 | options: { 129 | basePath: 'src/localization', 130 | module: 'commonjs', 131 | target: 'es5', 132 | comments:false 133 | } 134 | }, 135 | localAmd: { 136 | src: ['src/localization/*.ts'], 137 | dest: 'dist/amd/i18n', 138 | options: { 139 | basePath: 'src/localization', 140 | module: 'amd', 141 | target: 'es5', 142 | comments:false 143 | } 144 | }, 145 | typings:{ 146 | src: ['src/validation/*.ts'], 147 | dest: 'typings/<%= pkg.name %>', 148 | options: { 149 | basePath: 'src/validation', 150 | module: 'commonjs', 151 | target: 'es5', 152 | declaration: true, 153 | comments:false 154 | } 155 | }, 156 | customValidatorsTypings: { 157 | src: ['src/customValidators/*.ts'], 158 | dest: 'typings/<%= pkg.name %>', 159 | options: { 160 | basePath: 'src/customValidators', 161 | module: 'commonjs', 162 | target: 'es5', 163 | declaration: true, 164 | comments:false 165 | } 166 | }, 167 | src:{ 168 | src: ['src/**/*.ts'], 169 | dest: '', 170 | options: { 171 | module: 'commonjs', 172 | target: 'es5', 173 | declaration: false, 174 | comments:false 175 | // noImplicitAny:false, 176 | // ignoreError:true 177 | } 178 | }, 179 | test:{ 180 | src: ['test/**/*.ts'], 181 | dest: '', 182 | options: { 183 | module: 'commonjs', 184 | target: 'es5', 185 | declaration: false, 186 | comments:false 187 | // noImplicitAny:false, 188 | // ignoreError:true 189 | } 190 | } 191 | }, 192 | uglify: { 193 | options: { 194 | // the banner is inserted at the top of the output 195 | banner: '/*! <%= pkg.name %>, v.<%= pkg.version %> <%= grunt.template.today("dd-mm-yyyy") %> */\n' 196 | }, 197 | dist: { 198 | files: { 199 | 'dist/<%= pkg.name %>.min.js': ['dist/<%= pkg.name %>.js'] 200 | } 201 | } 202 | }, 203 | copy: { 204 | main: { 205 | files: [ 206 | // includes files within path 207 | {expand: true, src: ['dist/commonjs/i18n/*.js'], dest: 'dist/module/i18n', filter: 'isFile',flatten:true}, 208 | {expand: true, src: ['dist/commonjs/*.js'], dest: 'dist/module', filter: 'isFile',flatten:true}, 209 | {expand: true, src: ['src/localization/*.json'], dest: 'dist/module/i18n', filter: 'isFile',flatten:true} 210 | //{expand: true, src: ['src/customValidators/*.js'], dest: 'dist/customValidators', filter: 'isFile',flatten:true} 211 | ], 212 | options: { 213 | process: function (content) { 214 | content = content.replace(/.*require\(.*/g,""); 215 | return content.replace(/module.exports.*/g,""); 216 | } 217 | } 218 | } 219 | }, 220 | concat: { 221 | module:{ 222 | options: { 223 | banner: '/*! <%= pkg.name %>, v.<%= pkg.version %> <%= grunt.template.today("dd-mm-yyyy") %> */\n' 224 | }, 225 | files: { 226 | 'dist/business-rules-engine.js': ['dist/module/Validation.js','dist/module/BasicValidators.js','dist/module/Utils.js','dist/module/FormSchema.js'] 227 | } 228 | }, 229 | typings:{ 230 | 231 | options:{ 232 | banner: '// Type definitions for <%= pkg.name %> - v<%= pkg.version %>\n' + 233 | '// Project: https://github.com/rsamec/form\n' + 234 | '// Definitions by: Roman Samec \n' + 235 | '// Definitions: https://github.com/borisyankov/DefinitelyTyped\n\n' + 236 | '\n' + 237 | '/// \n' + 238 | '/// \n' + 239 | '/// \n' + 240 | '/// \n', 241 | footer:'declare module "business-rule-engine" {export = Validation;}', 242 | process: function(src, filepath) { 243 | return '// Source: ' + filepath + '\n' + 244 | src.replace(/.*/<%= pkg.name %>.d.ts': ['typings/<%= pkg.name %>/Utils.d.ts','typings/<%= pkg.name %>/Validation.d.ts','typings/<%= pkg.name %>/BasicValidators.d.ts','typings/<%= pkg.name %>/FormSchema.d.ts'] 251 | } 252 | } 253 | } 254 | }); 255 | 256 | grunt.loadNpmTasks('grunt-complexity'); 257 | grunt.loadNpmTasks('grunt-contrib-jshint'); 258 | grunt.loadNpmTasks('grunt-contrib-watch'); 259 | grunt.loadNpmTasks('grunt-contrib-uglify'); 260 | grunt.loadNpmTasks('grunt-mocha-cli'); 261 | grunt.loadNpmTasks('grunt-ngdocs'); 262 | grunt.loadNpmTasks('grunt-typedoc'); 263 | grunt.loadNpmTasks('grunt-typescript'); 264 | grunt.loadNpmTasks('grunt-contrib-commands'); 265 | grunt.loadNpmTasks('grunt-contrib-copy'); 266 | grunt.loadNpmTasks('grunt-contrib-concat'); 267 | grunt.loadNpmTasks('grunt-run'); 268 | 269 | 270 | grunt.registerTask('test', ['typescript:src','typescript:test', 'mochacli', 'watch']); 271 | //grunt.registerTask('ci', ['complexity', 'jshint', 'mochacli']); 272 | grunt.registerTask('dist', ['typescript:commonjs','typescript:amd','typescript:customValidatorsCommonjs','typescript:customValidatorsAmd','typescript:localCommonjs','typescript:localAmd','copy','concat:module','uglify:dist']); 273 | grunt.registerTask('typings',['typescript:typings','concat:typings']); 274 | grunt.registerTask('document', ['typedoc']); 275 | }; 276 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014, Roman Samec 4 | 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 2. Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 3. Neither the name of rsamec nor the names of its contributors 15 | may be used to endorse or promote products derived from this software 16 | without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY RSAMEC ''AS IS'' AND ANY 19 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL RSAMEC BE LIABLE FOR ANY 22 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by rsamec on 22.6.2014. 3 | * Description: The entry point for app 4 | */ 5 | 6 | var http = require("http"); 7 | http.createServer(function(req, res){ 8 | res.writeHead(200,{'Content-Type': 'text/plain'}); 9 | res.end('Hello worldsdfsdfsasa\n'); 10 | }).listen(1227,'127.0.0.1'); 11 | console.log('Server is running http://127.0.0.1:1227'); 12 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "business-rules-engine", 3 | "main": "dist/module/Validation.js", 4 | "version": "1.2.6", 5 | "homepage": "https://github.com/rsamec/business-rules-engine", 6 | "authors": [ 7 | "rsamec " 8 | ], 9 | "description": "Business rules engine", 10 | "moduleType": [ 11 | "amd", 12 | "globals", 13 | "node" 14 | ], 15 | "keywords": [ 16 | "validation", 17 | "validators", 18 | "business", 19 | "rules" 20 | ], 21 | "license": "MIT", 22 | "ignore": [ 23 | "**/.*", 24 | "node_modules", 25 | "bower_components", 26 | "test", 27 | "tests", 28 | "typings", 29 | "src", 30 | "docs", 31 | "content", 32 | "app.js", 33 | "dist/commonjs" 34 | ], 35 | "dependencies": { 36 | "underscore": "~1.7.0", 37 | "q": "~1.0.1", 38 | "hashmap": "^1.1.0" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /content/tutorial/00_custom_rules.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name 1 - Custom property validator 3 | @step 1 4 | @description 5 | 6 | ## How to create a custom property validator. 7 | 8 | To create a custom validation rule you have to implement IPropertyValidator interface. 9 | 10 | ```typescript 11 | export interface IPropertyValidator{ 12 | isAcceptable(s: any): boolean; 13 | tagName?:string; 14 | } 15 | ``` 16 | 17 | Notice, that inteface has only one method required (isAccepable(any)->boolean). 18 | 19 | Let's create validator that enables to compare two dates. We want to parametrize the validator with operators less, equal or greater. 20 | 21 | First we create enumaration for compare operators that enables us to parametrize validator. 22 | ```typescript 23 | export enum CompareOperator { 24 | //must be less than 25 | LessThan, 26 | 27 | //cannot be more than 28 | LessThanEqual, 29 | 30 | //must be the same as 31 | Equal, 32 | 33 | //must be different from 34 | NotEqual, 35 | 36 | //cannot be less than 37 | GreaterThanEqual, 38 | 39 | //must be more than 40 | GreaterThan 41 | } 42 | ``` 43 | 44 | Next we import dependencies - we need only some utilities. 45 | ```typescript 46 | import moment = require("moment"); 47 | import moment = require("underscore"); 48 | ``` 49 | 50 | 51 | Now we create the validator and implements the method is isAcceptable(any)->boolean. 52 | ```typescript 53 | class DateCompareValidator implements Validation.IPropertyValidator{ 54 | 55 | public isAcceptable(s:any){ 56 | var isValid = false; 57 | 58 | //if value is not date -> there is nothing to compare 59 | if (!_.isDate(s)) return false; 60 | 61 | //if date to compare is not specified - defaults to compare against now 62 | if (this.CompareTo == undefined) Date.now(); 63 | 64 | var now = moment(this.CompareTo); 65 | var then = moment(s); 66 | 67 | var diffs:number = then.diff(now); 68 | if (this.IgnoreTime) diffs = moment.duration(diffs).days(); 69 | 70 | if (diffs < 0) { 71 | isValid = this.CompareOperator == Validation.CompareOperator.LessThan 72 | || this.CompareOperator == Validation.CompareOperator.LessThanEqual 73 | || this.CompareOperator == Validation.CompareOperator.NotEqual; 74 | } 75 | else if (diffs > 0) { 76 | isValid = this.CompareOperator == Validation.CompareOperator.GreaterThan 77 | || this.CompareOperator == Validation.CompareOperator.GreaterThanEqual 78 | || this.CompareOperator == Validation.CompareOperator.NotEqual; 79 | } 80 | else { 81 | isValid = this.CompareOperator == Validation.CompareOperator.LessThanEqual 82 | || this.CompareOperator == Validation.CompareOperator.Equal 83 | || this.CompareOperator == Validation.CompareOperator.GreaterThanEqual; 84 | } 85 | return isValid; 86 | } 87 | 88 | /** 89 | * Set the time of compare between passed date and CompareTo date. 90 | */ 91 | public CompareOperator:Validation.CompareOperator; 92 | 93 | /** 94 | * The datetime against the compare is done. 95 | * If CompareTo is not set, then comparison is done against actual datetime. 96 | */ 97 | public CompareTo:Date; 98 | 99 | /** 100 | * It forces to ignore time part of date by date compare. 101 | * @type {boolean} 102 | */ 103 | public IgnoreTime:boolean = false; 104 | 105 | tagName = 'dateCompare'; 106 | } 107 | 108 | export = DateCompareValidator; 109 | ``` 110 | 111 | 112 |
113 | 114 | 115 | ## Use custom validation rule 116 | 117 | Let`s use the validator 118 | ```typescript 119 | import dateCompareValidator = require('DateCompareValidator'); 120 | 121 | var validator = new dateCompareValidator(); 122 | validator.CompareTo = new Date(2000,2,2); 123 | validator.CompareOperator = Validation.CompareOperator.LessThanEqual; 124 | 125 | //less dates -> return true 126 | var result = validator.isAcceptable(2000,1,1) 127 | 128 | //greater dates -> return false 129 | var result = validator.isAcceptable(2000,2,3) 130 | 131 | ``` -------------------------------------------------------------------------------- /content/tutorial/01_property_rules.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name 2 - Custom object validator 3 | @step 2 4 | @description 5 | 6 |
    7 | 8 | # Custom object validator 9 | You will learn how to create custom validator for your own object and how to assign validation rules to its properties. 10 | You can assigned these rules 11 | 12 | 13 | * property validation rules - use _RuleFor_ property 14 | * property async validation rules - use _RuleFor_ property 15 | * shared validation rules - use _ValidationFor_ property 16 | * custom object validator - use _ValidatorFor_ property - enables composition of child custom validators 17 | 18 | ## How to assign property validation rules 19 | 20 | Let`s image your object has this structure. 21 | ```typescript 22 | interface IPerson{ 23 | FirstName:string; 24 | LastName:string; 25 | } 26 | ``` 27 | 28 | To create validator you have 29 | 30 | 31 | - create AbstractValidator object 32 | - assign some rules to object properties 33 | 34 | ```typescript 35 | //create new validator for object with structure 36 | var personValidator = new Validation.AbstractValidator(); 37 | 38 | //basic validators 39 | var required =new Validation.RequiredValidator(); 40 | var maxLength = new Validators.MaxLengthValidator(15); 41 | 42 | //assigned validators to property 43 | personValidator.RuleFor("FirstName", required); 44 | personValidator.RuleFor("FirstName",maxLength); 45 | 46 | //assigned validators to property 47 | personValidator.RuleFor("LastName", required); 48 | personValidator.RuleFor("LastName",maxLength); 49 | 50 | ``` 51 | 52 | Use validator to get validation results 53 | ```typescript 54 | this.PersonValidator = personValidator.CreateRule("Person"); 55 | var result = this.PersonValidator.Validate(this.Data); 56 | if (result.HasErrors){ 57 | console.log(result.ErrorMessage); 58 | } 59 | ``` 60 | 61 | ### Test - custom object validator with property validation rule test 62 | ```typescript 63 | describe('simple property validators', function() { 64 | 65 | beforeEach(function(){ 66 | //setup 67 | this.Data = {}; 68 | this.PersonValidator = personValidator.CreateRule("Person"); 69 | 70 | }); 71 | 72 | it('fill correct data - no errors', function () { 73 | 74 | //when 75 | this.Data.FirstName = "Jonh"; 76 | this.Data.LastName = "Smith"; 77 | 78 | //excercise 79 | var result = this.PersonValidator.Validate(this.Data); 80 | 81 | //verify 82 | expect(this.PersonValidator.ValidationResult.HasErrors).to.equal(false); 83 | 84 | }); 85 | 86 | it('fill incorrect data - some errors', function () { 87 | 88 | //when 89 | this.Data.FirstName = ""; 90 | this.Data.LastName = "Smith toooooooooooooooooooooooooooooooo long"; 91 | 92 | //excercise 93 | var result = this.PersonValidator.Validate(this.Data); 94 | 95 | //verify 96 | expect(this.PersonValidator.ValidationResult.HasErrors).to.equal(true); 97 | }); 98 | 99 | }); 100 | ``` 101 | 102 | ## How to assign async property validation rules 103 | 104 | Let`s image your object has this structure. 105 | ```typescript 106 | interface IPerson{ 107 | Job:string 108 | } 109 | ``` 110 | 111 | To create validator you have 112 | 113 | 114 | * create AbstractValidator object 115 | * assign some async rules to object properties 116 | 117 | ```typescript 118 | //create new validator for object with structure 119 | var personValidator = new Validation.AbstractValidator(); 120 | 121 | //async functions returning list of values 122 | var optionsFce = function() { 123 | var deferral = Q.defer(); 124 | setTimeout(function () { 125 | deferral.resolve([ 126 | { "value": 1, "text": "aranžér" }, 127 | { "value": 2, "text": "stavař" }, 128 | { "value": 3, "text": "programátor" }, 129 | { "value": 3, "text": "nezaměstnaný" } 130 | ]); 131 | }, 1000); 132 | return deferral.promise; 133 | }; 134 | 135 | //async basic validators - return true if specified param contains any value 136 | var param = new Validation.ParamValidator(); 137 | param.ParamId = "jobs"; 138 | param.Options = optionsFce(); 139 | 140 | //assigned validator to property 141 | personValidator.RuleFor("Job",param); 142 | 143 | ``` 144 | 145 | Use validator to get async validation results 146 | ```typescript 147 | this.PersonValidator = personValidator.CreateRule("Person"); 148 | var promiseResult = this.PersonValidator.ValidateAsync(this.Data); 149 | promiseResult.then(function (result) { 150 | if (result.HasErrors){ 151 | console.log(result.ErrorMessage); 152 | } 153 | }) 154 | ``` 155 | 156 | 157 | ### Test - custom object validator with async property validation rule 158 | ```typescript 159 | beforeEach(function(){ 160 | //setup 161 | this.Data = {}; 162 | this.PersonValidator = personValidator.CreateRule("Person"); 163 | 164 | }); 165 | 166 | 167 | it('fill correct data - no errors', function (done) { 168 | 169 | //when 170 | this.Data.Job = "stavař"; 171 | 172 | //excercise 173 | var promiseResult = this.PersonValidator.ValidateAsync(this.Data); 174 | 175 | var errorInfo = this.PersonValidator.ValidationResult; 176 | promiseResult.then(function (response) { 177 | 178 | //verify 179 | expect(errorInfo.HasErrors).to.equal(false); 180 | 181 | done(); 182 | 183 | }).done(null,done); 184 | }); 185 | 186 | it('fill incorrect data - some errors', function (done) { 187 | 188 | //when 189 | this.Data.Job ="unknow job"; 190 | 191 | //excercise 192 | var promiseResult = this.PersonValidator.ValidateAsync(this.Data); 193 | 194 | var selfValidator = this.PersonValidator; 195 | promiseResult.then(function (response) { 196 | 197 | selfValidator.ValidationResult.LogErrors(); 198 | 199 | //verify 200 | expect(selfValidator.ValidationResult.HasErrors).to.equal(true); 201 | 202 | done(); 203 | 204 | }).done(null,done); 205 | }); 206 | ``` 207 | 208 | 209 | ## How to assign shared validation rule 210 | 211 | Let`s image your object has this structure. 212 | ```typescript 213 | interface IPerson{ 214 | IsChecked:true; 215 | FirstName:string; 216 | LastName:string; 217 | 218 | } 219 | ``` 220 | 221 | To create shared validation rule 222 | 223 | 224 | * create AbstractValidator object 225 | * create validation function 226 | * create named validation function 227 | * use ValidationFor property to assign named validation function to object properties 228 | 229 | ```typescript 230 | //create new validator for object with structure 231 | var personValidator = new Validation.AbstractValidator(); 232 | 233 | //shared validation function 234 | var oneSpaceFce = function (args:any) { 235 | args.HasError = false; 236 | if (!this.Checked) return; 237 | if (this.FirstName.indexOf(' ') != -1 || this.LastName.indexOf(' ') != -1) { 238 | args.HasError = true; 239 | args.ErrorMessage = "Full name can contain only one space."; 240 | return; 241 | } 242 | } 243 | 244 | //create named validation function 245 | var validatorFce = {Name: "OneSpaceForbidden", ValidationFce: oneSpaceFce}; 246 | 247 | //assign validation function to properties 248 | personValidator.ValidationFor("FirstName", validatorFce); 249 | personValidator.ValidationFor("LastName", validatorFce); 250 | ``` 251 | 252 | Use validator to get validation results 253 | ```typescript 254 | this.PersonValidator = personValidator.CreateRule("Person"); 255 | var result = this.PersonValidator.Validate(this.Data); 256 | if (result.HasErrors){ 257 | console.log(result.ErrorMessage); 258 | } 259 | ``` 260 | 261 | ### Test - custom validator object with shared validation rule 262 | ```typescript 263 | beforeEach(function(){ 264 | //setup 265 | this.Data = {}; 266 | this.PersonValidator = personValidator.CreateRule("Person"); 267 | 268 | }); 269 | 270 | 271 | it('fill correct data - no errors', function () { 272 | 273 | //when 274 | this.Data.Checked = true; 275 | this.Data.FirstName = "John"; 276 | this.Data.LastName = "Smith"; 277 | 278 | //excercise 279 | var result = this.PersonValidator.Validate(this.Data); 280 | 281 | //verify 282 | expect(this.PersonValidator.ValidationResult.HasErrors).to.equal(false); 283 | 284 | }); 285 | 286 | it('fill incorrect data - some errors', function () { 287 | 288 | //when 289 | this.Data.Checked = true; 290 | this.Data.FirstName = "John Junior"; 291 | this.Data.LastName = "Smith"; 292 | 293 | //excercise 294 | var result = this.PersonValidator.Validate(this.Data); 295 | 296 | //verify 297 | expect(this.PersonValidator.ValidationResult.HasErrors).to.equal(true); 298 | }); 299 | ``` 300 | 301 | 302 | 303 | -------------------------------------------------------------------------------- /content/tutorial/02_composition_rules.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name 3 - Composite validator 3 | @step 3 4 | @description 5 | 6 |
      7 | 8 | # Composite object validators 9 | You will learn how to create custom validator for your own object with nested structure. 10 | You will learn how to composite more custom validators to one main validator. 11 | 12 | 13 | Let`s image your object has this structure. 14 | 15 | ```typescript 16 | interface IData{ 17 | Person1:IPerson 18 | Person2:IPerson 19 | } 20 | interface IPerson{ 21 | Checked:boolean; 22 | FirstName:string; 23 | LastName:string; 24 | Contact:IContact; 25 | } 26 | interface IContact{ 27 | Email:string; 28 | Mobile:IPhone; 29 | FixedLine:IPhone; 30 | } 31 | interface IPhone{ 32 | CountryCode:string 33 | Number:string 34 | } 35 | ``` 36 | 37 | To create validator you have to 38 | 39 | 40 | - create AbstractValidator IData object 41 | - create AbstractValidator IPerson object 42 | - create AbstractValidator IContact object 43 | - create AbstractValidator IPhone object 44 | - compose these validators to match the object structure with ValidatorFor property 45 | 46 | ```typescript 47 | var required = new Validation.RequiredValidator(); 48 | 49 | var createMainValidator = function(){ 50 | var validator = new Validation.AbstractValidator(); 51 | 52 | var personValidator = createPersonValidator(); 53 | validator.ValidatorFor("Person1",personValidator); 54 | validator.ValidatorFor("Person2",personValidator); 55 | 56 | return validator; 57 | } 58 | var createPersonValidator = function() { 59 | 60 | var maxLength = new Validators.MaxLengthValidator(15); 61 | 62 | var validator = new Validation.AbstractValidator(); 63 | validator.RuleFor("FirstName", required); 64 | validator.RuleFor("FirstName", maxLength); 65 | 66 | validator.RuleFor("LastName", required); 67 | validator.RuleFor("LastName", maxLength); 68 | 69 | var contactValidator = createContactValidator(); 70 | validator.ValidatorFor("Contact",contactValidator); 71 | 72 | return validator; 73 | } 74 | 75 | var createContactValidator = function() { 76 | 77 | var validator = new Validation.AbstractValidator(); 78 | validator.RuleFor("Email", required); 79 | validator.RuleFor("Email", new Validators.MaxLengthValidator(100)); 80 | validator.RuleFor("Email", new Validation.EmailValidator()); 81 | 82 | var phoneValidator = createPhoneValidator(); 83 | validator.ValidatorFor("Mobile", phoneValidator); 84 | validator.ValidatorFor("FixedLine", phoneValidator); 85 | 86 | return validator; 87 | } 88 | 89 | var createPhoneValidator = function() { 90 | 91 | var validator = new Validation.AbstractValidator(); 92 | validator.RuleFor("CountryCode", required); 93 | validator.RuleFor("CountryCode", new Validators.MaxLengthValidator(3)); 94 | 95 | validator.RuleFor("Number", required); 96 | validator.RuleFor("Number", new Validators.MaxLengthValidator(9)); 97 | 98 | var optionsFce = function() { 99 | var deferral = Q.defer(); 100 | setTimeout(function () { 101 | deferral.resolve([ 102 | { "value": 420, "text": "CZE" }, 103 | { "value": 22, "text": "USA" }, 104 | { "value": 33, "text": "GER" }, 105 | { "value": 444, "text": "FRA" } 106 | ]); 107 | }, 500); 108 | return deferral.promise; 109 | }; 110 | 111 | var param = new Validation.ParamValidator(); 112 | param.ParamId = "countryCodes"; 113 | param.Options = optionsFce(); 114 | 115 | validator.RuleFor("CountryCode", param); 116 | 117 | return validator; 118 | } 119 | 120 | var mainValidator = createMainValidator(); 121 | 122 | ``` 123 | 124 | Use validator to get validation results 125 | ```typescript 126 | this.MainValidator = mainValidator.CreateRule("Person"); 127 | 128 | //first call sync validation 129 | var result = this.MainValidator.Validate(this.Data); 130 | 131 | //verify 132 | if (result.HasErrors){ 133 | console.log(result.ErrorMessage); 134 | } 135 | 136 | //second - call async validation 137 | var promiseResult = this.MainValidator.ValidateAsync(this.Data); 138 | 139 | //verify 140 | promiseResult.then(function (resultAsync) { 141 | if (resultAsync.HasErrors){ 142 | console.log(resultAsync.ErrorMessage); 143 | } 144 | } 145 | ``` 146 | 147 | ### Test - custom object validator with composition - nested structures 148 | ```typescript 149 | describe('nested validation rules', function () { 150 | 151 | beforeEach(function(){ 152 | 153 | //setup 154 | var getData = function () { 155 | var contact = { 156 | Email:'mail@gmail.com', 157 | Mobile:{ 158 | CountryCode:'CZE', 159 | Number:'736483690' 160 | }, 161 | FixedLine:{ 162 | CountryCode:'USA', 163 | Number:'736483690' 164 | } 165 | }; 166 | return{ 167 | Person1: { 168 | Checked:true, 169 | FirstName: "John", 170 | LastName: "Smith", 171 | Contact: contact 172 | }, 173 | Person2: { 174 | Checked:true, 175 | FirstName: "Adam", 176 | LastName: "Novak", 177 | Contact: contact 178 | } 179 | } 180 | }; 181 | 182 | this.Data = getData(); 183 | this.MainValidator = mainValidator.CreateRule("Main"); 184 | 185 | }); 186 | 187 | it('fill correct data - no errors', function (done) { 188 | 189 | //when 190 | 191 | //excercise 192 | var result = this.MainValidator.Validate(this.Data); 193 | var promiseResult = this.MainValidator.ValidateAsync(this.Data); 194 | 195 | //verify 196 | var selfValidator = this.MainValidator; 197 | promiseResult.then(function (response) { 198 | 199 | selfValidator.ValidationResult.LogErrors(); 200 | 201 | //verify 202 | expect(selfValidator.ValidationResult.HasErrors).to.equal(false); 203 | 204 | done(); 205 | 206 | }).done(null,done); 207 | }); 208 | 209 | 210 | it('fill incorrect data - some errors', function (done) { 211 | 212 | //when 213 | //nested property error 214 | this.Data.Person1.Contact.Email = ""; 215 | 216 | //async nested property error 217 | this.Data.Person1.Contact.Mobile.CountryCode = "BLA"; 218 | 219 | //excercise 220 | var result = this.MainValidator.Validate(this.Data); 221 | var promiseResult = this.MainValidator.ValidateAsync(this.Data); 222 | 223 | //verify 224 | var selfValidator = this.MainValidator; 225 | promiseResult.then(function (response) { 226 | 227 | selfValidator.ValidationResult.LogErrors(); 228 | 229 | //verify 230 | expect(selfValidator.ValidationResult.HasErrors).to.equal(true); 231 | 232 | done(); 233 | 234 | }).done(null,done); 235 | }); 236 | 237 | 238 | ``` 239 | 240 | 241 | 242 | -------------------------------------------------------------------------------- /content/tutorial/03_list_rules.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name 4 - Validation rules with lists 3 | @step 4 4 | @description 5 | 6 | 7 | # Validation rules for lists 8 | You will learn how to create custom validator for your own object with repeated structures -> lists, arrays. 9 | 10 | Let`s image your object has this structure. 11 | ```typescript 12 | interface IData{ 13 | Person1:IPerson 14 | Person2:IPerson 15 | } 16 | interface IPerson{ 17 | Checked:boolean; 18 | FirstName:string; 19 | LastName:string; 20 | Contact:IContact; 21 | } 22 | interface IContact{ 23 | Email:string; 24 | Mobile:IPhone; 25 | FixedLine:IPhone; 26 | } 27 | interface IPhone{ 28 | CountryCode:string 29 | Number:string 30 | } 31 | ``` 32 | 33 | To create validator you have to 34 | 35 | 36 | - create AbstractValidator object 37 | - create AbstractValidator object 38 | - create AbstractValidator object 39 | - create AbstractValidator object 40 | - compose these validators to match the object structure with ValidatorFor property 41 | 42 | ```typescript 43 | var required = new Validation.RequiredValidator(); 44 | 45 | var createMainValidator = function(){ 46 | var validator = new Validation.AbstractValidator(); 47 | 48 | var personValidator = createPersonValidator(); 49 | validator.ValidatorFor("Person1",personValidator); 50 | validator.ValidatorFor("Person2",personValidator); 51 | 52 | return validator; 53 | } 54 | var createPersonValidator = function() { 55 | 56 | var maxLength = new Validators.MaxLengthValidator(15); 57 | 58 | var validator = new Validation.AbstractValidator(); 59 | validator.RuleFor("FirstName", required); 60 | validator.RuleFor("FirstName", maxLength); 61 | 62 | validator.RuleFor("LastName", required); 63 | validator.RuleFor("LastName", maxLength); 64 | 65 | var contactValidator = createContactValidator(); 66 | validator.ValidatorFor("Contact",contactValidator); 67 | 68 | return validator; 69 | } 70 | 71 | var createContactValidator = function() { 72 | 73 | var validator = new Validation.AbstractValidator(); 74 | validator.RuleFor("Email", required); 75 | validator.RuleFor("Email", new Validators.MaxLengthValidator(100)); 76 | validator.RuleFor("Email", new Validation.EmailValidator()); 77 | 78 | var phoneValidator = createPhoneValidator(); 79 | validator.ValidatorFor("Mobile", phoneValidator); 80 | validator.ValidatorFor("FixedLine", phoneValidator); 81 | 82 | return validator; 83 | } 84 | 85 | var createPhoneValidator = function() { 86 | 87 | var validator = new Validation.AbstractValidator(); 88 | validator.RuleFor("CountryCode", required); 89 | validator.RuleFor("CountryCode", new Validators.MaxLengthValidator(3)); 90 | 91 | validator.RuleFor("Number", required); 92 | validator.RuleFor("Number", new Validators.MaxLengthValidator(9)); 93 | 94 | var optionsFce = function() { 95 | var deferral = Q.defer(); 96 | setTimeout(function () { 97 | deferral.resolve([ 98 | { "value": 420, "text": "CZE" }, 99 | { "value": 22, "text": "USA" }, 100 | { "value": 33, "text": "GER" }, 101 | { "value": 444, "text": "FRA" } 102 | ]); 103 | }, 500); 104 | return deferral.promise; 105 | }; 106 | 107 | var param = new Validation.ParamValidator(); 108 | param.ParamId = "countryCodes"; 109 | param.Options = optionsFce(); 110 | 111 | validator.RuleFor("CountryCode", param); 112 | 113 | return validator; 114 | } 115 | 116 | var mainValidator = createMainValidator(); 117 | 118 | ``` 119 | 120 | Use validator to get validation results 121 | ```typescript 122 | this.MainValidator = mainValidator.CreateRule("Person"); 123 | 124 | //first call sync validation 125 | var result = this.MainValidator.Validate(this.Data); 126 | 127 | //verify 128 | if (result.HasErrors){ 129 | console.log(result.ErrorMessage); 130 | } 131 | 132 | //second - call async validation 133 | var promiseResult = this.MainValidator.ValidateAsync(this.Data); 134 | 135 | //verify 136 | promiseResult.then(function (resultAsync) { 137 | if (resultAsync.HasErrors){ 138 | console.log(resultAsync.ErrorMessage); 139 | } 140 | } 141 | ``` 142 | 143 | ### Test - custom object validator with composition - nested structures 144 | ```typescript 145 | describe('nested validation rules', function () { 146 | 147 | beforeEach(function(){ 148 | 149 | //setup 150 | var getData = function () { 151 | var contact = { 152 | Email:'mail@gmail.com', 153 | Mobile:{ 154 | CountryCode:'CZE', 155 | Number:'736483690' 156 | }, 157 | FixedLine:{ 158 | CountryCode:'USA', 159 | Number:'736483690' 160 | } 161 | }; 162 | return{ 163 | Person1: { 164 | Checked:true, 165 | FirstName: "John", 166 | LastName: "Smith", 167 | Contact: contact 168 | }, 169 | Person2: { 170 | Checked:true, 171 | FirstName: "Adam", 172 | LastName: "Novak", 173 | Contact: contact 174 | } 175 | } 176 | }; 177 | 178 | this.Data = getData(); 179 | this.MainValidator = mainValidator.CreateRule("Main"); 180 | 181 | }); 182 | 183 | it('fill correct data - no errors', function (done) { 184 | 185 | //when 186 | 187 | //excercise 188 | var result = this.MainValidator.Validate(this.Data); 189 | var promiseResult = this.MainValidator.ValidateAsync(this.Data); 190 | 191 | //verify 192 | var selfValidator = this.MainValidator; 193 | promiseResult.then(function (response) { 194 | 195 | selfValidator.ValidationResult.LogErrors(); 196 | 197 | //verify 198 | expect(selfValidator.ValidationResult.HasErrors).to.equal(false); 199 | 200 | done(); 201 | 202 | }).done(null,done); 203 | }); 204 | 205 | 206 | it('fill incorrect data - some errors', function (done) { 207 | 208 | //when 209 | //nested property error 210 | this.Data.Person1.Contact.Email = ""; 211 | 212 | //async nested property error 213 | this.Data.Person1.Contact.Mobile.CountryCode = "BLA"; 214 | 215 | //excercise 216 | var result = this.MainValidator.Validate(this.Data); 217 | var promiseResult = this.MainValidator.ValidateAsync(this.Data); 218 | 219 | //verify 220 | var selfValidator = this.MainValidator; 221 | promiseResult.then(function (response) { 222 | 223 | selfValidator.ValidationResult.LogErrors(); 224 | 225 | //verify 226 | expect(selfValidator.ValidationResult.HasErrors).to.equal(true); 227 | 228 | done(); 229 | 230 | }).done(null,done); 231 | }); 232 | 233 | 234 | ``` 235 | 236 | 237 | 238 | -------------------------------------------------------------------------------- /content/tutorial/index.ngdoc: -------------------------------------------------------------------------------- 1 | @ngdoc overview 2 | @name Tutorial 3 | @step -1 4 | @description 5 | 6 | # Validation engine tutorial 7 | 8 | To participate in enterprise business process, employees, customers and partners need to interact with data stored in various enterprise applications – whether providing information or retrieving it. 9 | These stored data are typically structured data or semi-structured data that must correspond to specific business rules or validation rules. 10 | 11 | 12 | Validation engine offers a way how to define business rules of the product, the contract, or to define validation rules of the forms that captures this data. 13 | 14 | # Basic usage 15 | 16 | ## 1. First define data structure 17 | ```typescript 18 | //data structure 19 | interface IPerson{ 20 | FirstName:string; 21 | LastName:string; 22 | } 23 | ``` 24 | 25 | ## 2. Define business rules 26 | ```typescript 27 | //create new validator for object with structure 28 | var personValidator = new Validation.AbstractValidator(); 29 | 30 | //basic validators 31 | var required =new Validation.RequiredValidator(); 32 | var maxLength = new Validators.MaxLengthValidator(15); 33 | 34 | //assigned validators to property 35 | personValidator.RuleFor("FirstName", required); 36 | personValidator.RuleFor("FirstName",maxLength); 37 | 38 | //assigned validators to property 39 | personValidator.RuleFor("LastName", required); 40 | personValidator.RuleFor("LastName",maxLength); 41 | ``` 42 | 43 | ## 3. Use validation engine 44 | ```typescript 45 | 46 | //concrete validator 47 | var concreteValidator = personValidator.CreateRule("Data"); 48 | 49 | //sample data with errors 50 | var data = { 51 | FirstName : "", 52 | LastName: "Toooooooooooooooooooooooooooooooo long name" 53 | }; 54 | 55 | //validate 56 | var result = concreteValidator.Validate(data); 57 | 58 | //verify by return result 59 | expect(result.HasErrors).to.equal(true); 60 | if (result.HasErrors) console.log(result.ErrorMessage); 61 | 62 | //verify by concrete validator properties 63 | expect(concreteValidator.ValidationResult.HasErrors).to.equal(true); 64 | if (concreteValidator.ValidationResult.HasErrors) console.log(concreteValidator.ValidationResult.ErrorMessage); 65 | 66 | //validator properties enables to check specific rule errors 67 | expect(concreteValidator.Rules["FirstName"].ValidationFailures["required"].HasError).to.equal(true); 68 | expect(concreteValidator.Rules["FirstName"].ValidationFailures["maxlength"].HasError).to.equal(false); 69 | expect(concreteValidator.Rules["LastName"].ValidationFailures["required"].HasError).to.equal(false); 70 | expect(concreteValidator.Rules["LastName"].ValidationFailures["maxlength"].HasError).to.equal(true); 71 | 72 | ``` 73 | 74 | The main benefit is that validation engine is not tight to HTML DOM or any other UI framework. 75 | **This validation engine is UI agnostic** and that is why it can be used as **an independent representation of business rules** of a product, contract, etc. 76 | It can be easily reused by different types of applications, libraries. 77 | 78 | ## Typical usage of validation engine can be: 79 | 80 | * Client - server validation of business rules for an product. 81 | * UI application uses validation engine to enforce all validation rules with quick answer to client. 82 | * Server non-UI application uses the same business rules to enforce validity of business rules of the product once again (according to rule no client is trusted). 83 | * Validation of business rules of automatically generated products, contracts, etc. 84 | * Server non-UI application can automatically generate the product or the contract. Validation engine enforce validation of business rules of the product, the contract, etc. 85 | * Forms application which uses business rules to capture structured or semi-structured data. 86 | 87 | 88 | ## Validation engine advantages 89 | 90 | * There is no dependencies on HTML DOM. 91 | * It enables to decorate custom objects and its properties with validation rules. 92 | * It supports composition of validation rules, that enables to validate custom object with nested structures. 93 | * It is ease to create your own custom validators. 94 | * It supports asynchronous validation rules (uses promises). 95 | * It supports assigning validation rules to collection-based structures - arrays and lists. 96 | * It supports localization of error messages with TranslateArgs. 97 | * It deploys as AMD, CommonJS or plain script module pattern. 98 | * It offers basic build-in constrains validators. Other custom validators can be find in extensible repository of custom validators. 99 | 100 | 101 | 102 | ## Types of validators and validation rules 103 | 104 | * Custom property validator 105 | * Custom object validator 106 | * with property validation rules (sync and async) 107 | * with collection-valued, array-valued and generally Iterable fields and properties 108 | * with shared validation rules 109 | * Composite object validator 110 | 111 | ## Dependencies 112 | 113 | The validation is not dependend on HTML DOM. **The only depedency is on ECMAScript**. 114 | 115 | Temporary there is dependency on the underscore javacript library. 116 | 117 | There can be other dependencies on other javascript libraries for custom validation rules found in repository of custom validators. 118 | 119 | Before using any custom validator found in repository, check the api for the custom validator for possible dependencies. 120 | E.g. DataCompareValidator requires underscore + moment javascript libraries. 121 | 122 | ## List of basic property validators 123 | 124 | The Property validator is designed to validate objects against constraints. They are assertions that a condition is true. 125 | 126 | 127 | ### Basic constrains 128 | * Required 129 | * EqualTo 130 | 131 | ### String constrains 132 | * Email 133 | * Url 134 | * MaxLength,MinLength,RangeLength 135 | * Pattern 136 | 137 | ### Number constrains 138 | * Number 139 | * Digits, SignedDigits 140 | * Max,Min,Range 141 | * Step 142 | 143 | ### Date constrains 144 | * Date 145 | * DateISO 146 | * DateCompare 147 | 148 | ### Financial and other Number Constraints 149 | * Currency 150 | * CreditCard 151 | * Luhn 152 | 153 | ### Collection constraints 154 | * Contains 155 | 156 | ## Localization messages 157 | 158 | It enables to localization of constrains messages. See the list of available localization messages in i18n directory. 159 | 160 | Localization is enabled by default. It is enough to add reference to specific localization files. 161 | For Czech Republic localization add reference to dist/i18n/messages_cs.js to html page where you want to use CZ messages. 162 | 163 | ... 164 | -------------------------------------------------------------------------------- /dist/README.md: -------------------------------------------------------------------------------- 1 | # Business rules engine 2 | 3 | ![logo](https://github.com/rsamec/form/blob/master/form_logo.jpg) 4 | 5 | Business rules engine is a lightweight JavaScript library for easy business rules definition of the product, the contract, the form etc. 6 | 7 | ## Key features 8 | The main benefit is that business rules engine is not tight to HTML DOM or any other UI framework. 9 | This validation engine is **UI agnostic** and that is why it can be used as **an independent representation of business rules** of a product, contract, etc. 10 | It can be easily reused by different types of applications, libraries. 11 | 12 | + It enables to decorate custom objects and its properties with validation rules. 13 | + It supports declarative and imperative validation rules definition 14 | + declarative [JSON schema](http://json-schema.org/) with validation keywords [JSON Schema Validation](http://json-schema.org/latest/json-schema-validation.html) 15 | + declarative raw JSON data annotated with meta data - using keywords from [JQuery validation plugin](http://jqueryvalidation.org/) 16 | + imperative - [validation API](http://rsamec.github.io/business-rules-engine/docs/modules/validation-validation.validation.html) 17 | + It supports composition of validation rules, that enables to validate custom object with nested structures. 18 | + It is ease to create your own custom validators. 19 | + It supports asynchronous validation rules (uses promises). 20 | + It supports shared validation rules. 21 | + It supports assigning validation rules to collection-based structures - arrays and lists. 22 | + It supports notification of errors changed via ErrorChanged dispatch event. 23 | + It supports dot syntax to access to nested rules. 24 | + It supports localization of error messages with TranslateArgs. 25 | + It deploys as AMD, CommonJS or plain script module pattern. 26 | + It offers basic build-in constrains validators. See list [basic build-in constraints](http://rsamec.github.io/business-rules-engine/docs/modules/validation-basicvalidators.validators.html) 27 | + Other custom validators can be find in extensible repository of custom validators (work in progress). 28 | 29 | ## Installation 30 | 31 | This module is installed: 32 | 33 | + Node.js 34 | + npm install business-rules-engine 35 | + use require('business-rules-engine'); 36 | + Bower 37 | + bower install business-rules-engine 38 | + Require.js - require(["business-rules-engine/amd/Validation"], ... 39 | + Script tag -> add reference to business-rules-engine/module/Validation.js file. 40 | 41 | ## Example Usage 42 | 43 | There are 3 ways how to define validation rules 44 | 45 | + declarative [JSON schema](http://json-schema.org/) with validation keywords [JSON Schema Validation](http://json-schema.org/latest/json-schema-validation.html) 46 | + declarative raw JSON data annotated with meta data - using keywords from [JQuery validation plugin](http://jqueryvalidation.org/) 47 | + imperative - [validation API](http://rsamec.github.io/business-rules-engine/docs/modules/validation-validation.validation.html) 48 | 49 | 50 | ### JSON Schema Validation 51 | 52 | ``` js 53 | { 54 | FirstName: { 55 | type: "string", 56 | title: "First name", 57 | required: "true", 58 | maxLength: 15 59 | }, 60 | LastName: { 61 | type: "string", 62 | "title": "Last name", 63 | required: true, 64 | maxLength: 15 65 | }, 66 | Contacts: { 67 | type: "array", 68 | maxItems: 4, 69 | minItems: 2, 70 | items: { 71 | type: "object", 72 | properties: { 73 | Email: { 74 | type: "string", 75 | title: "Email", 76 | default: '', 77 | required: true, 78 | maxLength: 100, 79 | pattern: "S*@S*" }, 80 | Mobile: { 81 | type: "object", 82 | properties: { 83 | CountryCode: { 84 | type: "string", 85 | title: "Country code", 86 | required: true, 87 | maxLength: 3, 88 | enum: ["FRA", "CZE", "USA", "GER"] 89 | }, 90 | Number: { 91 | type: "string", 92 | title: "Phone number", 93 | required: true, 94 | maxLength: 9 95 | } 96 | } 97 | }, 98 | FixedLine: { 99 | type: "object", 100 | properties: { 101 | CountryCode: { 102 | type: "string", 103 | title: "Country code", 104 | required: true, 105 | maxLength: 3, 106 | enum: ["FRA", "CZE", "USA", "GER"] 107 | }, 108 | Number: { 109 | type: "string", 110 | title: "Phone number", 111 | required: true, 112 | maxLength: 9 113 | } 114 | } 115 | } 116 | } 117 | } 118 | } 119 | } 120 | ``` 121 | ### JSON data annotated with meta data 122 | 123 | ``` js 124 | // define data structure + validation rules meta data 125 | { 126 | FirstName: { 127 | rules: {required: true, maxlength: 15}}, 128 | LastName: { 129 | rules: {required: true, maxlength: 15}}, 130 | Contacts: [ 131 | { 132 | Email: { 133 | rules: { 134 | required: true, 135 | maxlength: 100, 136 | email: true 137 | } 138 | }, 139 | Mobile: { 140 | CountryCode: { 141 | rules: {required: true, maxlength: 3, enum: ["FRA", "CZE", "USA", "GER"] } 142 | }, 143 | Number: { 144 | rules: {required: true, maxlength: 9 } 145 | } 146 | }, 147 | FixedLine: { 148 | CountryCode: { 149 | rules: {required: true, maxlength: 3, enum: ["FRA", "CZE", "USA", "GER"] } 150 | }, 151 | Number: { 152 | rules: {required: true, maxlength: 9 } 153 | } 154 | } 155 | },{maxItems: 4, minItems: 2} 156 | ] 157 | } 158 | ``` 159 | 160 | ### Imperative definition - using API 161 | 162 | To define business rules for some object, you have to create abstract validator. 163 | ``` js 164 | //create new validator for object with structure 165 | var personValidator = new Validation.AbstractValidator(); 166 | 167 | //basic validators 168 | var required = new Validators.RequiredValidator(); 169 | var maxLength = new Validators.MaxLengthValidator(15); 170 | 171 | //assigned validators to property 172 | personValidator.RuleFor("FirstName", required); 173 | personValidator.RuleFor("FirstName",maxLength); 174 | 175 | //assigned validators to property 176 | personValidator.RuleFor("LastName", required); 177 | personValidator.RuleFor("LastName",maxLength); 178 | 179 | ... 180 | 181 | ``` 182 | 183 | To use business rules and execute them on particular data 184 | ```js 185 | //create test data 186 | var data = { 187 | Person1: 188 | { 189 | FirstName:'John', 190 | LastName: 'Smith' 191 | }, 192 | Person2:{} 193 | 194 | } 195 | 196 | //create concrete rule 197 | var person1Validator = personValidator.CreateRule("Person 1"); 198 | 199 | //execute validation 200 | var result = person1Validator.Validate(this.Data.Person1); 201 | 202 | //verify results 203 | if (result.HasErrors){ 204 | console.log(result.ErrorMessage); 205 | } 206 | //--------- 207 | //--outputs 208 | //--------- 209 | 210 | //create concrete rule 211 | var person2Validator = personValidator.CreateRule("Person 2"); 212 | 213 | //execute validation 214 | var result = person2Validator.Validate(this.Data.Person1); 215 | 216 | //verify results 217 | if (result.HasErrors){ 218 | console.log(result.ErrorMessage); 219 | } 220 | 221 | //--------- 222 | //--outputs 223 | //--------- 224 | // FirstName: Field is required. 225 | // LastName: Field is required. 226 | 227 | ``` 228 | 229 | ## Additional information 230 | 231 | + [Business rules engine - Tutorial] (https://github.com/rsamec/business-rules-engine/wiki) 232 | + [Business rules engine - API] (http://rsamec.github.io/business-rules-engine/docs/globals.html) 233 | + [Business rules repository - sources] (https://github.com/rsamec/business-rules) 234 | + [Business rules repository - API] (http://rsamec.github.io/business-rules/docs/globals.html) 235 | + [NodeJS Example] (https://github.com/rsamec/node-form-app) 236 | + [AngularJS Example] (https://github.com/rsamec/angular-form-app) 237 | + [AngularJS Demo - Forms app] (http://nodejs-formvalidation.rhcloud.com/) 238 | + [Vacation Request form] (http://nodejs-formvalidation.rhcloud.com/#/vacationApproval/new) 239 | + [Hobbies form] (http://nodejs-formvalidation.rhcloud.com/#/hobbies/new) 240 | 241 | 242 | ## Source code 243 | 244 | All code is written in typescript. 245 | 246 | ``` bash 247 | npm install -g typescript 248 | ``` 249 | 250 | To compile to javascript. 251 | 252 | ``` bash 253 | tsc src/validation/Validation.ts --target ES5 --module commonjs 254 | ``` 255 | 256 | ## Tests 257 | 258 | ``` bash 259 | npm install -g mocha 260 | npm install -g expect.js 261 | ``` 262 | 263 | To run tests 264 | 265 | ``` bash 266 | mocha test 267 | ``` 268 | 269 | 270 | ## Grunt automatization 271 | 272 | ### Basic steps 273 | 274 | + git clone https://github.com/rsamec/business-rules-engine 275 | + npm install - get npm packages 276 | + tsd update - get external typings definition 277 | + grunt typings - create node-form typings definition - used by custom validators 278 | 279 | To build all sources to dist folder (generates AMD, CommonJS and module pattern) 280 | ``` bash 281 | $ grunt dist 282 | ``` 283 | 284 | To run code analyze - complexity + jshint. 285 | ``` bash 286 | $ grunt ci 287 | ``` 288 | 289 | To generate api documentation. 290 | ``` bash 291 | $ grunt document 292 | ``` 293 | 294 | To generate typings -> typescript definition files. 295 | ``` bash 296 | $ grunt typings 297 | ``` 298 | 299 | To run tests 300 | ``` bash 301 | $ grunt test 302 | ``` 303 | 304 | ## Roadmap 305 | 306 | + Add validation groups to shared validation rules 307 | + Separate ValidationResult from execution of validation rules 308 | + Add depedency injection for managing dependencies among components 309 | + Support for meta data definitions 310 | 311 | 312 | -------------------------------------------------------------------------------- /dist/amd/DateCompareValidator.js: -------------------------------------------------------------------------------- 1 | define(["require", "exports", "moment", "underscore"], function(require, exports, moment, _) { 2 | var DateCompareValidator = (function () { 3 | function DateCompareValidator() { 4 | this.IgnoreTime = false; 5 | this.tagName = 'dateCompare'; 6 | } 7 | DateCompareValidator.prototype.isAcceptable = function (s) { 8 | var isValid = false; 9 | 10 | if (!_.isDate(s)) 11 | return false; 12 | 13 | if (this.CompareTo === undefined) 14 | Date.now(); 15 | 16 | var now = moment(this.CompareTo); 17 | var then = moment(s); 18 | 19 | var diffs = then.diff(now); 20 | if (this.IgnoreTime) 21 | diffs = moment.duration(diffs).days(); 22 | 23 | if (diffs < 0) { 24 | isValid = this.CompareOperator === 0 /* LessThan */ || this.CompareOperator === 1 /* LessThanEqual */ || this.CompareOperator === 3 /* NotEqual */; 25 | } else if (diffs > 0) { 26 | isValid = this.CompareOperator === 5 /* GreaterThan */ || this.CompareOperator === 4 /* GreaterThanEqual */ || this.CompareOperator === 3 /* NotEqual */; 27 | } else { 28 | isValid = this.CompareOperator === 1 /* LessThanEqual */ || this.CompareOperator === 2 /* Equal */ || this.CompareOperator === 4 /* GreaterThanEqual */; 29 | } 30 | return isValid; 31 | }; 32 | return DateCompareValidator; 33 | })(); 34 | 35 | 36 | return DateCompareValidator; 37 | }); 38 | -------------------------------------------------------------------------------- /dist/amd/ICOValidator.js: -------------------------------------------------------------------------------- 1 | define(["require", "exports"], function(require, exports) { 2 | var ICOValidator = (function () { 3 | function ICOValidator() { 4 | this.tagName = "ico"; 5 | } 6 | ICOValidator.prototype.isAcceptable = function (input) { 7 | if (input === undefined) 8 | return false; 9 | if (input.length === 0) 10 | return false; 11 | 12 | if (!/^\d+$/.test(input)) 13 | return false; 14 | 15 | var Sci = []; 16 | var Souc; 17 | var Del = input.length; 18 | var kon = parseInt(input.substring(Del, Del - 1), 10); 19 | 20 | Del = Del - 1; 21 | Souc = 0; 22 | for (var a = 0; a < Del; a++) { 23 | Sci[a] = parseInt(input.substr((Del - a) - 1, 1), 10); 24 | Sci[a] = Sci[a] * (a + 2); 25 | Souc = Souc + Sci[a]; 26 | } 27 | 28 | if (Souc > 0) { 29 | var resul = Souc % 11; 30 | var mezi = Souc - resul; 31 | resul = mezi + 11; 32 | resul = resul - Souc; 33 | 34 | if ((resul === 10 && kon === 0) || (resul === 11 && kon === 1) || (resul === kon)) 35 | return true; 36 | } 37 | return false; 38 | }; 39 | return ICOValidator; 40 | })(); 41 | 42 | 43 | return ICOValidator; 44 | }); 45 | -------------------------------------------------------------------------------- /dist/amd/ParamValidator.js: -------------------------------------------------------------------------------- 1 | define(["require", "exports", "q", "underscore"], function(require, exports, Q, _) { 2 | var ParamValidator = (function () { 3 | function ParamValidator() { 4 | this.isAsync = true; 5 | this.tagName = "param"; 6 | } 7 | ParamValidator.prototype.isAcceptable = function (s) { 8 | var deferred = Q.defer(); 9 | 10 | this.Options(this.ParamId).then(function (result) { 11 | var hasSome = _.some(result, function (item) { 12 | return item.text === s; 13 | }); 14 | if (hasSome) 15 | deferred.resolve(true); 16 | deferred.resolve(false); 17 | }); 18 | 19 | return deferred.promise; 20 | }; 21 | return ParamValidator; 22 | })(); 23 | 24 | return ParamValidator; 25 | }); 26 | -------------------------------------------------------------------------------- /dist/amd/RCValidator.js: -------------------------------------------------------------------------------- 1 | define(["require", "exports", "moment", "underscore.string"], function(require, exports, moment, _s) { 2 | var RCValidator = (function () { 3 | function RCValidator() { 4 | this.tagName = 'rc'; 5 | } 6 | RCValidator.prototype.isAcceptable = function (s) { 7 | var old = false; 8 | var month; 9 | var year; 10 | var day; 11 | var numrc; 12 | var dbg = false; 13 | if (s === undefined) 14 | return false; 15 | if (s.toString().length === 0) { 16 | return false; 17 | } 18 | 19 | if (!s.match(/^\d{6}\/?\d{3,4}$/)) 20 | return false; 21 | 22 | if (s.indexOf('/') != -1) 23 | old = s.length === 10; 24 | else 25 | old = s.length === 9; 26 | 27 | if (s.indexOf('/') !== -1) { 28 | numrc = s.split("/"); 29 | numrc = numrc.join(""); 30 | } else { 31 | numrc = s; 32 | } 33 | 34 | day = parseInt(numrc.substring(4, 6), 10); 35 | month = parseInt(numrc.substring(2, 4), 10); 36 | year = parseInt(numrc.substring(0, 2), 10); 37 | 38 | if (s.match(/\/?(0000?|8888?|9999?)$/)) { 39 | dbg = true; 40 | } 41 | 42 | if (!old && !dbg) { 43 | if (numrc % 11 !== 0 && s.substr(s.length - 1) === "0") { 44 | if (parseInt(numrc.substr(0, 9), 10) % 11 !== 10) 45 | return false; 46 | } else if (numrc % 11 !== 0) 47 | return false; 48 | } 49 | 50 | if (year > 54 && old && !dbg) 51 | return false; 52 | 53 | if (!old && year < 54) 54 | year = 2000 + year; 55 | else 56 | year = 1900 + year; 57 | 58 | if (month > 50 && month < 63) 59 | month = month - 50; 60 | if (!old && year >= 2004) { 61 | if (month > 20 && month < 33) 62 | month = month - 20; 63 | if (month > 70 && month < 83) 64 | month = month - 70; 65 | } 66 | 67 | var datum = moment(_s.lpad(day.toString(), 2, '0') + "." + _s.lpad(month.toString(), 2, '0') + "." + year, "DD.MM.YYYY"); 68 | 69 | if (!datum.isValid()) 70 | return false; 71 | return datum.toDate() <= moment(Date.now()).toDate(); 72 | }; 73 | return RCValidator; 74 | })(); 75 | 76 | 77 | return RCValidator; 78 | }); 79 | -------------------------------------------------------------------------------- /dist/amd/Utils.js: -------------------------------------------------------------------------------- 1 | define(["require", "exports"], function(require, exports) { 2 | var Utils; 3 | (function (Utils) { 4 | var StringFce = (function () { 5 | function StringFce() { 6 | } 7 | StringFce.format = function (s, args) { 8 | var formatted = s; 9 | for (var prop in args) { 10 | var regexp = new RegExp('\\{' + prop + '\\}', 'gi'); 11 | formatted = formatted.replace(regexp, args[prop]); 12 | } 13 | return formatted; 14 | }; 15 | return StringFce; 16 | })(); 17 | Utils.StringFce = StringFce; 18 | 19 | var NumberFce = (function () { 20 | function NumberFce() { 21 | } 22 | NumberFce.GetNegDigits = function (value) { 23 | if (value === undefined) 24 | return 0; 25 | var digits = value.toString().split('.'); 26 | if (digits.length > 1) { 27 | var negDigitsLength = digits[1].length; 28 | return negDigitsLength; 29 | } 30 | return 0; 31 | }; 32 | return NumberFce; 33 | })(); 34 | Utils.NumberFce = NumberFce; 35 | 36 | 37 | 38 | var Signal = (function () { 39 | function Signal() { 40 | this.listeners = []; 41 | this.priorities = []; 42 | } 43 | Signal.prototype.add = function (listener, priority) { 44 | if (typeof priority === "undefined") { priority = 0; } 45 | var index = this.listeners.indexOf(listener); 46 | if (index !== -1) { 47 | this.priorities[index] = priority; 48 | return; 49 | } 50 | for (var i = 0, l = this.priorities.length; i < l; i++) { 51 | if (this.priorities[i] < priority) { 52 | this.priorities.splice(i, 0, priority); 53 | this.listeners.splice(i, 0, listener); 54 | return; 55 | } 56 | } 57 | this.priorities.push(priority); 58 | this.listeners.push(listener); 59 | }; 60 | 61 | Signal.prototype.remove = function (listener) { 62 | var index = this.listeners.indexOf(listener); 63 | if (index >= 0) { 64 | this.priorities.splice(index, 1); 65 | this.listeners.splice(index, 1); 66 | } 67 | }; 68 | 69 | Signal.prototype.dispatch = function (parameter) { 70 | var indexesToRemove; 71 | var hasBeenCanceled = this.listeners.every(function (listener) { 72 | var result = listener(parameter); 73 | return result !== false; 74 | }); 75 | 76 | return hasBeenCanceled; 77 | }; 78 | 79 | Signal.prototype.clear = function () { 80 | this.listeners = []; 81 | this.priorities = []; 82 | }; 83 | 84 | Signal.prototype.hasListeners = function () { 85 | return this.listeners.length > 0; 86 | }; 87 | return Signal; 88 | })(); 89 | Utils.Signal = Signal; 90 | 91 | 92 | 93 | var CompositeDotObject = (function () { 94 | function CompositeDotObject() { 95 | } 96 | CompositeDotObject.Transform = function (component, obj) { 97 | if (obj === undefined) 98 | obj = {}; 99 | if (component.isItem()) { 100 | obj[component.getName()] = component; 101 | } else { 102 | var children = component.getChildren(); 103 | var parent = obj[component.getName()] = component; 104 | for (var comp in children) { 105 | CompositeDotObject.Transform(children[comp], parent); 106 | } 107 | } 108 | return obj; 109 | }; 110 | return CompositeDotObject; 111 | })(); 112 | Utils.CompositeDotObject = CompositeDotObject; 113 | })(Utils || (Utils = {})); 114 | 115 | return Utils; 116 | }); 117 | -------------------------------------------------------------------------------- /dist/amd/i18n/messages_cz.js: -------------------------------------------------------------------------------- 1 | define(["require", "exports"], function(require, exports) { 2 | var Localization = (function () { 3 | function Localization() { 4 | } 5 | Object.defineProperty(Localization, "ValidationMessages", { 6 | get: function () { 7 | return { 8 | required: "Tento údaj je povinný.", 9 | remote: "Prosím, opravte tento údaj.", 10 | email: "Prosím, zadejte platný e-mail.", 11 | url: "Prosím, zadejte platné URL.", 12 | date: "Prosím, zadejte platné datum.", 13 | dateISO: "Prosím, zadejte platné datum (ISO).", 14 | number: "Prosím, zadejte číslo.", 15 | digits: "Prosím, zadávejte pouze číslice.", 16 | creditcard: "Prosím, zadejte číslo kreditní karty.", 17 | equalTo: "Prosím, zadejte znovu stejnou hodnotu.", 18 | extension: "Prosím, zadejte soubor se správnou příponou.", 19 | maxlength: "Prosím, zadejte nejvíce {MaxLength} znaků.", 20 | minlength: "Prosím, zadejte nejméně {MinLength} znaků.", 21 | rangelength: "Prosím, zadejte od {MinLength} do {MaxLength} znaků.", 22 | range: "Prosím, zadejte hodnotu od {Min} do {Max}.", 23 | max: "Prosím, zadejte hodnotu menší nebo rovnu {Max}.", 24 | min: "Prosím, zadejte hodnotu větší nebo rovnu {Min}.", 25 | contains: "Prosím, zadejte hodnotu ze seznamu. Zadaná hodnota {AttemptedValue}.", 26 | dateCompare: { 27 | Format: "DD.MM.YYYY", 28 | LessThan: "Prosím, zadejte datum menší než {CompareTo}.", 29 | LessThanEqual: "Prosím, zadejte datum menší nebo rovné {CompareTo}.", 30 | Equal: "Prosím, zadejte {CompareTo}.", 31 | NotEqual: "Prosím, zadejte datum různé od {CompareTo}.", 32 | GreaterThanEqual: "Prosím, zadejte datum větší nebo rovné {CompareTo}.", 33 | GreaterThan: "Prosím, zadejte datum větší než {CompareTo}." 34 | }, 35 | minItems: "Prosím zadejte alespoň {Min} položek.", 36 | maxItems: "Prosím zadejte maximálně {Max} položek.", 37 | uniqItems: "Prosím zadejte pouze unikátní hodnoty.", 38 | enum: "Prosím zadajte povolenou hodnotu.", 39 | type: "Prosím zadejte hodnotu typu '{Type}'.", 40 | multipleOf: "Prosím zadejte hodnotu dělitelnou číslem '{Divider}'." 41 | }; 42 | }, 43 | enumerable: true, 44 | configurable: true 45 | }); 46 | return Localization; 47 | })(); 48 | 49 | 50 | return Localization; 51 | }); 52 | -------------------------------------------------------------------------------- /dist/amd/i18n/messages_de.js: -------------------------------------------------------------------------------- 1 | define(["require", "exports"], function(require, exports) { 2 | var Localization = (function () { 3 | function Localization() { 4 | } 5 | Object.defineProperty(Localization, "ValidationMessages", { 6 | get: function () { 7 | return { 8 | required: "Dieses Feld ist ein Pflichtfeld.", 9 | maxlength: "Geben Sie bitte maximal {MaxLength} Zeichen ein.", 10 | minlength: "Geben Sie bitte mindestens {MinLength} Zeichen ein.", 11 | rangelength: "Geben Sie bitte mindestens {MinLength} und maximal {MaxLength} Zeichen ein.", 12 | email: "Geben Sie bitte eine gültige E-Mail Adresse ein.", 13 | url: "Geben Sie bitte eine gültige URL ein.", 14 | date: "Bitte geben Sie ein gültiges Datum ein.", 15 | number: "Geben Sie bitte eine Nummer ein.", 16 | digits: "Geben Sie bitte nur Ziffern ein.", 17 | equalTo: "Bitte denselben Wert wiederholen.", 18 | range: "Geben Sie bitte einen Wert zwischen {Min} und {Max} ein.", 19 | max: "Geben Sie bitte einen Wert kleiner oder gleich {Max} ein.", 20 | min: "Geben Sie bitte einen Wert größer oder gleich {Min} ein.", 21 | creditcard: "Geben Sie bitte eine gültige Kreditkarten-Nummer ein.", 22 | dateCompare: { 23 | LessThan: "Geben Sie bitte ein datum kleiner {CompareTo}.", 24 | LessThanEqual: "Geben Sie bitte ein datum kleiner oder gleich {CompareTo}.", 25 | Equal: "Geben Sie bitte ein datum {CompareTo}.", 26 | NotEqual: "Geben Sie bitte ein datum anderes von {CompareTo}.", 27 | GreaterThanEqual: "Geben Sie bitte ein datum größer oder gleich {CompareTo}.", 28 | GreaterThan: "Geben Sie bitte ein datum größer {CompareTo}." 29 | }, 30 | minItems: "Geben Sie bitte minimal {Min} Einträge.", 31 | maxItems: "Geben Sie bitte maximal {Max} Einträge.", 32 | uniqItems: "Geben Sie bitte maximal unique Einträge.", 33 | enum: "Geben Sie bitte einen Wert.", 34 | type: "Geben Sie bitte einen Wert von '{Type}'.", 35 | multipleOf: "Geben Sie bitte einen Wert als multiple of {Divider}." 36 | }; 37 | }, 38 | enumerable: true, 39 | configurable: true 40 | }); 41 | return Localization; 42 | })(); 43 | 44 | 45 | return Localization; 46 | }); 47 | -------------------------------------------------------------------------------- /dist/amd/i18n/messages_en.js: -------------------------------------------------------------------------------- 1 | define(["require", "exports"], function(require, exports) { 2 | var Localization = (function () { 3 | function Localization() { 4 | } 5 | Object.defineProperty(Localization, "ValidationMessages", { 6 | get: function () { 7 | return { 8 | required: "This field is required.", 9 | remote: "Please fix the field.", 10 | email: "Please enter a valid email address.", 11 | url: "Please enter a valid URL.", 12 | date: "Please enter a valid date.", 13 | dateISO: "Please enter a valid date ( ISO ).", 14 | number: "Please enter a valid number.", 15 | digits: "Please enter only digits.", 16 | signedDigits: "Please enter only signed digits.", 17 | creditcard: "Please enter a valid credit card number.", 18 | equalTo: "Please enter the same value again.", 19 | maxlength: "Please enter no more than {MaxLength} characters.", 20 | minlength: "Please enter at least {MinLength} characters.", 21 | rangelength: "Please enter a value between {MinLength} and {MaxLength} characters long.", 22 | range: "Please enter a value between {Min} and {Max}.", 23 | max: "Please enter a value less than or equal to {Max}.", 24 | min: "Please enter a value greater than or equal to {Min}.", 25 | step: "Please enter a value with step {Step}.", 26 | contains: "Please enter a value from list of values. Attempted value '{AttemptedValue}'.", 27 | mask: "Please enter a value corresponding with {Mask}.", 28 | dateCompare: { 29 | Format: "MM/DD/YYYY", 30 | LessThan: "Please enter date less than {CompareTo}.", 31 | LessThanEqual: "Please enter date less than or equal {CompareTo}.", 32 | Equal: "Please enter date equal {CompareTo}.", 33 | NotEqual: "Please enter date different than {CompareTo}.", 34 | GreaterThanEqual: "Please enter date greater than or equal {CompareTo}.", 35 | GreaterThan: "Please enter date greter than {CompareTo}." 36 | }, 37 | minItems: "Please enter at least {Min} items.", 38 | maxItems: "Please enter no more than {Max} items.", 39 | uniqItems: "Please enter unique items.", 40 | enum: "Please enter a value from list of permitted values.", 41 | type: "Please enter a value of type '{Type}'.", 42 | multipleOf: "Please enter a value that is multiple of {Divider}." 43 | }; 44 | }, 45 | enumerable: true, 46 | configurable: true 47 | }); 48 | return Localization; 49 | })(); 50 | 51 | 52 | return Localization; 53 | }); 54 | -------------------------------------------------------------------------------- /dist/commonjs/DateCompareValidator.js: -------------------------------------------------------------------------------- 1 | var moment = require("moment"); 2 | var _ = require("underscore"); 3 | 4 | var DateCompareValidator = (function () { 5 | function DateCompareValidator() { 6 | this.IgnoreTime = false; 7 | this.tagName = 'dateCompare'; 8 | } 9 | DateCompareValidator.prototype.isAcceptable = function (s) { 10 | var isValid = false; 11 | 12 | if (!_.isDate(s)) 13 | return false; 14 | 15 | if (this.CompareTo === undefined) 16 | Date.now(); 17 | 18 | var now = moment(this.CompareTo); 19 | var then = moment(s); 20 | 21 | var diffs = then.diff(now); 22 | if (this.IgnoreTime) 23 | diffs = moment.duration(diffs).days(); 24 | 25 | if (diffs < 0) { 26 | isValid = this.CompareOperator === 0 /* LessThan */ || this.CompareOperator === 1 /* LessThanEqual */ || this.CompareOperator === 3 /* NotEqual */; 27 | } else if (diffs > 0) { 28 | isValid = this.CompareOperator === 5 /* GreaterThan */ || this.CompareOperator === 4 /* GreaterThanEqual */ || this.CompareOperator === 3 /* NotEqual */; 29 | } else { 30 | isValid = this.CompareOperator === 1 /* LessThanEqual */ || this.CompareOperator === 2 /* Equal */ || this.CompareOperator === 4 /* GreaterThanEqual */; 31 | } 32 | return isValid; 33 | }; 34 | return DateCompareValidator; 35 | })(); 36 | 37 | module.exports = DateCompareValidator; 38 | -------------------------------------------------------------------------------- /dist/commonjs/ICOValidator.js: -------------------------------------------------------------------------------- 1 | var ICOValidator = (function () { 2 | function ICOValidator() { 3 | this.tagName = "ico"; 4 | } 5 | ICOValidator.prototype.isAcceptable = function (input) { 6 | if (input === undefined) 7 | return false; 8 | if (input.length === 0) 9 | return false; 10 | 11 | if (!/^\d+$/.test(input)) 12 | return false; 13 | 14 | var Sci = []; 15 | var Souc; 16 | var Del = input.length; 17 | var kon = parseInt(input.substring(Del, Del - 1), 10); 18 | 19 | Del = Del - 1; 20 | Souc = 0; 21 | for (var a = 0; a < Del; a++) { 22 | Sci[a] = parseInt(input.substr((Del - a) - 1, 1), 10); 23 | Sci[a] = Sci[a] * (a + 2); 24 | Souc = Souc + Sci[a]; 25 | } 26 | 27 | if (Souc > 0) { 28 | var resul = Souc % 11; 29 | var mezi = Souc - resul; 30 | resul = mezi + 11; 31 | resul = resul - Souc; 32 | 33 | if ((resul === 10 && kon === 0) || (resul === 11 && kon === 1) || (resul === kon)) 34 | return true; 35 | } 36 | return false; 37 | }; 38 | return ICOValidator; 39 | })(); 40 | 41 | module.exports = ICOValidator; 42 | -------------------------------------------------------------------------------- /dist/commonjs/ParamValidator.js: -------------------------------------------------------------------------------- 1 | var Q = require("q"); 2 | var _ = require("underscore"); 3 | 4 | var ParamValidator = (function () { 5 | function ParamValidator() { 6 | this.isAsync = true; 7 | this.tagName = "param"; 8 | } 9 | ParamValidator.prototype.isAcceptable = function (s) { 10 | var deferred = Q.defer(); 11 | 12 | this.Options(this.ParamId).then(function (result) { 13 | var hasSome = _.some(result, function (item) { 14 | return item.text === s; 15 | }); 16 | if (hasSome) 17 | deferred.resolve(true); 18 | deferred.resolve(false); 19 | }); 20 | 21 | return deferred.promise; 22 | }; 23 | return ParamValidator; 24 | })(); 25 | module.exports = ParamValidator; 26 | -------------------------------------------------------------------------------- /dist/commonjs/RCValidator.js: -------------------------------------------------------------------------------- 1 | var moment = require("moment"); 2 | var _s = require("underscore.string"); 3 | 4 | var RCValidator = (function () { 5 | function RCValidator() { 6 | this.tagName = 'rc'; 7 | } 8 | RCValidator.prototype.isAcceptable = function (s) { 9 | var old = false; 10 | var month; 11 | var year; 12 | var day; 13 | var numrc; 14 | var dbg = false; 15 | if (s === undefined) 16 | return false; 17 | if (s.toString().length === 0) { 18 | return false; 19 | } 20 | 21 | if (!s.match(/^\d{6}\/?\d{3,4}$/)) 22 | return false; 23 | 24 | if (s.indexOf('/') != -1) 25 | old = s.length === 10; 26 | else 27 | old = s.length === 9; 28 | 29 | if (s.indexOf('/') !== -1) { 30 | numrc = s.split("/"); 31 | numrc = numrc.join(""); 32 | } else { 33 | numrc = s; 34 | } 35 | 36 | day = parseInt(numrc.substring(4, 6), 10); 37 | month = parseInt(numrc.substring(2, 4), 10); 38 | year = parseInt(numrc.substring(0, 2), 10); 39 | 40 | if (s.match(/\/?(0000?|8888?|9999?)$/)) { 41 | dbg = true; 42 | } 43 | 44 | if (!old && !dbg) { 45 | if (numrc % 11 !== 0 && s.substr(s.length - 1) === "0") { 46 | if (parseInt(numrc.substr(0, 9), 10) % 11 !== 10) 47 | return false; 48 | } else if (numrc % 11 !== 0) 49 | return false; 50 | } 51 | 52 | if (year > 54 && old && !dbg) 53 | return false; 54 | 55 | if (!old && year < 54) 56 | year = 2000 + year; 57 | else 58 | year = 1900 + year; 59 | 60 | if (month > 50 && month < 63) 61 | month = month - 50; 62 | if (!old && year >= 2004) { 63 | if (month > 20 && month < 33) 64 | month = month - 20; 65 | if (month > 70 && month < 83) 66 | month = month - 70; 67 | } 68 | 69 | var datum = moment(_s.lpad(day.toString(), 2, '0') + "." + _s.lpad(month.toString(), 2, '0') + "." + year, "DD.MM.YYYY"); 70 | 71 | if (!datum.isValid()) 72 | return false; 73 | return datum.toDate() <= moment(Date.now()).toDate(); 74 | }; 75 | return RCValidator; 76 | })(); 77 | 78 | module.exports = RCValidator; 79 | -------------------------------------------------------------------------------- /dist/commonjs/Utils.js: -------------------------------------------------------------------------------- 1 | var Utils; 2 | (function (Utils) { 3 | var StringFce = (function () { 4 | function StringFce() { 5 | } 6 | StringFce.format = function (s, args) { 7 | var formatted = s; 8 | for (var prop in args) { 9 | var regexp = new RegExp('\\{' + prop + '\\}', 'gi'); 10 | formatted = formatted.replace(regexp, args[prop]); 11 | } 12 | return formatted; 13 | }; 14 | return StringFce; 15 | })(); 16 | Utils.StringFce = StringFce; 17 | 18 | var NumberFce = (function () { 19 | function NumberFce() { 20 | } 21 | NumberFce.GetNegDigits = function (value) { 22 | if (value === undefined) 23 | return 0; 24 | var digits = value.toString().split('.'); 25 | if (digits.length > 1) { 26 | var negDigitsLength = digits[1].length; 27 | return negDigitsLength; 28 | } 29 | return 0; 30 | }; 31 | return NumberFce; 32 | })(); 33 | Utils.NumberFce = NumberFce; 34 | 35 | 36 | 37 | var Signal = (function () { 38 | function Signal() { 39 | this.listeners = []; 40 | this.priorities = []; 41 | } 42 | Signal.prototype.add = function (listener, priority) { 43 | if (typeof priority === "undefined") { priority = 0; } 44 | var index = this.listeners.indexOf(listener); 45 | if (index !== -1) { 46 | this.priorities[index] = priority; 47 | return; 48 | } 49 | for (var i = 0, l = this.priorities.length; i < l; i++) { 50 | if (this.priorities[i] < priority) { 51 | this.priorities.splice(i, 0, priority); 52 | this.listeners.splice(i, 0, listener); 53 | return; 54 | } 55 | } 56 | this.priorities.push(priority); 57 | this.listeners.push(listener); 58 | }; 59 | 60 | Signal.prototype.remove = function (listener) { 61 | var index = this.listeners.indexOf(listener); 62 | if (index >= 0) { 63 | this.priorities.splice(index, 1); 64 | this.listeners.splice(index, 1); 65 | } 66 | }; 67 | 68 | Signal.prototype.dispatch = function (parameter) { 69 | var indexesToRemove; 70 | var hasBeenCanceled = this.listeners.every(function (listener) { 71 | var result = listener(parameter); 72 | return result !== false; 73 | }); 74 | 75 | return hasBeenCanceled; 76 | }; 77 | 78 | Signal.prototype.clear = function () { 79 | this.listeners = []; 80 | this.priorities = []; 81 | }; 82 | 83 | Signal.prototype.hasListeners = function () { 84 | return this.listeners.length > 0; 85 | }; 86 | return Signal; 87 | })(); 88 | Utils.Signal = Signal; 89 | 90 | 91 | 92 | var CompositeDotObject = (function () { 93 | function CompositeDotObject() { 94 | } 95 | CompositeDotObject.Transform = function (component, obj) { 96 | if (obj === undefined) 97 | obj = {}; 98 | if (component.isItem()) { 99 | obj[component.getName()] = component; 100 | } else { 101 | var children = component.getChildren(); 102 | var parent = obj[component.getName()] = component; 103 | for (var comp in children) { 104 | CompositeDotObject.Transform(children[comp], parent); 105 | } 106 | } 107 | return obj; 108 | }; 109 | return CompositeDotObject; 110 | })(); 111 | Utils.CompositeDotObject = CompositeDotObject; 112 | })(Utils || (Utils = {})); 113 | module.exports = Utils; 114 | -------------------------------------------------------------------------------- /dist/commonjs/i18n/messages_cz.js: -------------------------------------------------------------------------------- 1 | var Localization = (function () { 2 | function Localization() { 3 | } 4 | Object.defineProperty(Localization, "ValidationMessages", { 5 | get: function () { 6 | return { 7 | required: "Tento údaj je povinný.", 8 | remote: "Prosím, opravte tento údaj.", 9 | email: "Prosím, zadejte platný e-mail.", 10 | url: "Prosím, zadejte platné URL.", 11 | date: "Prosím, zadejte platné datum.", 12 | dateISO: "Prosím, zadejte platné datum (ISO).", 13 | number: "Prosím, zadejte číslo.", 14 | digits: "Prosím, zadávejte pouze číslice.", 15 | creditcard: "Prosím, zadejte číslo kreditní karty.", 16 | equalTo: "Prosím, zadejte znovu stejnou hodnotu.", 17 | extension: "Prosím, zadejte soubor se správnou příponou.", 18 | maxlength: "Prosím, zadejte nejvíce {MaxLength} znaků.", 19 | minlength: "Prosím, zadejte nejméně {MinLength} znaků.", 20 | rangelength: "Prosím, zadejte od {MinLength} do {MaxLength} znaků.", 21 | range: "Prosím, zadejte hodnotu od {Min} do {Max}.", 22 | max: "Prosím, zadejte hodnotu menší nebo rovnu {Max}.", 23 | min: "Prosím, zadejte hodnotu větší nebo rovnu {Min}.", 24 | contains: "Prosím, zadejte hodnotu ze seznamu. Zadaná hodnota {AttemptedValue}.", 25 | dateCompare: { 26 | Format: "DD.MM.YYYY", 27 | LessThan: "Prosím, zadejte datum menší než {CompareTo}.", 28 | LessThanEqual: "Prosím, zadejte datum menší nebo rovné {CompareTo}.", 29 | Equal: "Prosím, zadejte {CompareTo}.", 30 | NotEqual: "Prosím, zadejte datum různé od {CompareTo}.", 31 | GreaterThanEqual: "Prosím, zadejte datum větší nebo rovné {CompareTo}.", 32 | GreaterThan: "Prosím, zadejte datum větší než {CompareTo}." 33 | }, 34 | minItems: "Prosím zadejte alespoň {Min} položek.", 35 | maxItems: "Prosím zadejte maximálně {Max} položek.", 36 | uniqItems: "Prosím zadejte pouze unikátní hodnoty.", 37 | enum: "Prosím zadajte povolenou hodnotu.", 38 | type: "Prosím zadejte hodnotu typu '{Type}'.", 39 | multipleOf: "Prosím zadejte hodnotu dělitelnou číslem '{Divider}'." 40 | }; 41 | }, 42 | enumerable: true, 43 | configurable: true 44 | }); 45 | return Localization; 46 | })(); 47 | 48 | module.exports = Localization; 49 | -------------------------------------------------------------------------------- /dist/commonjs/i18n/messages_de.js: -------------------------------------------------------------------------------- 1 | var Localization = (function () { 2 | function Localization() { 3 | } 4 | Object.defineProperty(Localization, "ValidationMessages", { 5 | get: function () { 6 | return { 7 | required: "Dieses Feld ist ein Pflichtfeld.", 8 | maxlength: "Geben Sie bitte maximal {MaxLength} Zeichen ein.", 9 | minlength: "Geben Sie bitte mindestens {MinLength} Zeichen ein.", 10 | rangelength: "Geben Sie bitte mindestens {MinLength} und maximal {MaxLength} Zeichen ein.", 11 | email: "Geben Sie bitte eine gültige E-Mail Adresse ein.", 12 | url: "Geben Sie bitte eine gültige URL ein.", 13 | date: "Bitte geben Sie ein gültiges Datum ein.", 14 | number: "Geben Sie bitte eine Nummer ein.", 15 | digits: "Geben Sie bitte nur Ziffern ein.", 16 | equalTo: "Bitte denselben Wert wiederholen.", 17 | range: "Geben Sie bitte einen Wert zwischen {Min} und {Max} ein.", 18 | max: "Geben Sie bitte einen Wert kleiner oder gleich {Max} ein.", 19 | min: "Geben Sie bitte einen Wert größer oder gleich {Min} ein.", 20 | creditcard: "Geben Sie bitte eine gültige Kreditkarten-Nummer ein.", 21 | dateCompare: { 22 | LessThan: "Geben Sie bitte ein datum kleiner {CompareTo}.", 23 | LessThanEqual: "Geben Sie bitte ein datum kleiner oder gleich {CompareTo}.", 24 | Equal: "Geben Sie bitte ein datum {CompareTo}.", 25 | NotEqual: "Geben Sie bitte ein datum anderes von {CompareTo}.", 26 | GreaterThanEqual: "Geben Sie bitte ein datum größer oder gleich {CompareTo}.", 27 | GreaterThan: "Geben Sie bitte ein datum größer {CompareTo}." 28 | }, 29 | minItems: "Geben Sie bitte minimal {Min} Einträge.", 30 | maxItems: "Geben Sie bitte maximal {Max} Einträge.", 31 | uniqItems: "Geben Sie bitte maximal unique Einträge.", 32 | enum: "Geben Sie bitte einen Wert.", 33 | type: "Geben Sie bitte einen Wert von '{Type}'.", 34 | multipleOf: "Geben Sie bitte einen Wert als multiple of {Divider}." 35 | }; 36 | }, 37 | enumerable: true, 38 | configurable: true 39 | }); 40 | return Localization; 41 | })(); 42 | 43 | module.exports = Localization; 44 | -------------------------------------------------------------------------------- /dist/commonjs/i18n/messages_en.js: -------------------------------------------------------------------------------- 1 | var Localization = (function () { 2 | function Localization() { 3 | } 4 | Object.defineProperty(Localization, "ValidationMessages", { 5 | get: function () { 6 | return { 7 | required: "This field is required.", 8 | remote: "Please fix the field.", 9 | email: "Please enter a valid email address.", 10 | url: "Please enter a valid URL.", 11 | date: "Please enter a valid date.", 12 | dateISO: "Please enter a valid date ( ISO ).", 13 | number: "Please enter a valid number.", 14 | digits: "Please enter only digits.", 15 | signedDigits: "Please enter only signed digits.", 16 | creditcard: "Please enter a valid credit card number.", 17 | equalTo: "Please enter the same value again.", 18 | maxlength: "Please enter no more than {MaxLength} characters.", 19 | minlength: "Please enter at least {MinLength} characters.", 20 | rangelength: "Please enter a value between {MinLength} and {MaxLength} characters long.", 21 | range: "Please enter a value between {Min} and {Max}.", 22 | max: "Please enter a value less than or equal to {Max}.", 23 | min: "Please enter a value greater than or equal to {Min}.", 24 | step: "Please enter a value with step {Step}.", 25 | contains: "Please enter a value from list of values. Attempted value '{AttemptedValue}'.", 26 | mask: "Please enter a value corresponding with {Mask}.", 27 | dateCompare: { 28 | Format: "MM/DD/YYYY", 29 | LessThan: "Please enter date less than {CompareTo}.", 30 | LessThanEqual: "Please enter date less than or equal {CompareTo}.", 31 | Equal: "Please enter date equal {CompareTo}.", 32 | NotEqual: "Please enter date different than {CompareTo}.", 33 | GreaterThanEqual: "Please enter date greater than or equal {CompareTo}.", 34 | GreaterThan: "Please enter date greter than {CompareTo}." 35 | }, 36 | minItems: "Please enter at least {Min} items.", 37 | maxItems: "Please enter no more than {Max} items.", 38 | uniqItems: "Please enter unique items.", 39 | enum: "Please enter a value from list of permitted values.", 40 | type: "Please enter a value of type '{Type}'.", 41 | multipleOf: "Please enter a value that is multiple of {Divider}." 42 | }; 43 | }, 44 | enumerable: true, 45 | configurable: true 46 | }); 47 | return Localization; 48 | })(); 49 | 50 | module.exports = Localization; 51 | -------------------------------------------------------------------------------- /dist/module/DateCompareValidator.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | var DateCompareValidator = (function () { 5 | function DateCompareValidator() { 6 | this.IgnoreTime = false; 7 | this.tagName = 'dateCompare'; 8 | } 9 | DateCompareValidator.prototype.isAcceptable = function (s) { 10 | var isValid = false; 11 | 12 | if (!_.isDate(s)) 13 | return false; 14 | 15 | if (this.CompareTo === undefined) 16 | Date.now(); 17 | 18 | var now = moment(this.CompareTo); 19 | var then = moment(s); 20 | 21 | var diffs = then.diff(now); 22 | if (this.IgnoreTime) 23 | diffs = moment.duration(diffs).days(); 24 | 25 | if (diffs < 0) { 26 | isValid = this.CompareOperator === 0 /* LessThan */ || this.CompareOperator === 1 /* LessThanEqual */ || this.CompareOperator === 3 /* NotEqual */; 27 | } else if (diffs > 0) { 28 | isValid = this.CompareOperator === 5 /* GreaterThan */ || this.CompareOperator === 4 /* GreaterThanEqual */ || this.CompareOperator === 3 /* NotEqual */; 29 | } else { 30 | isValid = this.CompareOperator === 1 /* LessThanEqual */ || this.CompareOperator === 2 /* Equal */ || this.CompareOperator === 4 /* GreaterThanEqual */; 31 | } 32 | return isValid; 33 | }; 34 | return DateCompareValidator; 35 | })(); 36 | 37 | 38 | -------------------------------------------------------------------------------- /dist/module/ICOValidator.js: -------------------------------------------------------------------------------- 1 | var ICOValidator = (function () { 2 | function ICOValidator() { 3 | this.tagName = "ico"; 4 | } 5 | ICOValidator.prototype.isAcceptable = function (input) { 6 | if (input === undefined) 7 | return false; 8 | if (input.length === 0) 9 | return false; 10 | 11 | if (!/^\d+$/.test(input)) 12 | return false; 13 | 14 | var Sci = []; 15 | var Souc; 16 | var Del = input.length; 17 | var kon = parseInt(input.substring(Del, Del - 1), 10); 18 | 19 | Del = Del - 1; 20 | Souc = 0; 21 | for (var a = 0; a < Del; a++) { 22 | Sci[a] = parseInt(input.substr((Del - a) - 1, 1), 10); 23 | Sci[a] = Sci[a] * (a + 2); 24 | Souc = Souc + Sci[a]; 25 | } 26 | 27 | if (Souc > 0) { 28 | var resul = Souc % 11; 29 | var mezi = Souc - resul; 30 | resul = mezi + 11; 31 | resul = resul - Souc; 32 | 33 | if ((resul === 10 && kon === 0) || (resul === 11 && kon === 1) || (resul === kon)) 34 | return true; 35 | } 36 | return false; 37 | }; 38 | return ICOValidator; 39 | })(); 40 | 41 | 42 | -------------------------------------------------------------------------------- /dist/module/ParamValidator.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | var ParamValidator = (function () { 5 | function ParamValidator() { 6 | this.isAsync = true; 7 | this.tagName = "param"; 8 | } 9 | ParamValidator.prototype.isAcceptable = function (s) { 10 | var deferred = Q.defer(); 11 | 12 | this.Options(this.ParamId).then(function (result) { 13 | var hasSome = _.some(result, function (item) { 14 | return item.text === s; 15 | }); 16 | if (hasSome) 17 | deferred.resolve(true); 18 | deferred.resolve(false); 19 | }); 20 | 21 | return deferred.promise; 22 | }; 23 | return ParamValidator; 24 | })(); 25 | 26 | -------------------------------------------------------------------------------- /dist/module/RCValidator.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | var RCValidator = (function () { 5 | function RCValidator() { 6 | this.tagName = 'rc'; 7 | } 8 | RCValidator.prototype.isAcceptable = function (s) { 9 | var old = false; 10 | var month; 11 | var year; 12 | var day; 13 | var numrc; 14 | var dbg = false; 15 | if (s === undefined) 16 | return false; 17 | if (s.toString().length === 0) { 18 | return false; 19 | } 20 | 21 | if (!s.match(/^\d{6}\/?\d{3,4}$/)) 22 | return false; 23 | 24 | if (s.indexOf('/') != -1) 25 | old = s.length === 10; 26 | else 27 | old = s.length === 9; 28 | 29 | if (s.indexOf('/') !== -1) { 30 | numrc = s.split("/"); 31 | numrc = numrc.join(""); 32 | } else { 33 | numrc = s; 34 | } 35 | 36 | day = parseInt(numrc.substring(4, 6), 10); 37 | month = parseInt(numrc.substring(2, 4), 10); 38 | year = parseInt(numrc.substring(0, 2), 10); 39 | 40 | if (s.match(/\/?(0000?|8888?|9999?)$/)) { 41 | dbg = true; 42 | } 43 | 44 | if (!old && !dbg) { 45 | if (numrc % 11 !== 0 && s.substr(s.length - 1) === "0") { 46 | if (parseInt(numrc.substr(0, 9), 10) % 11 !== 10) 47 | return false; 48 | } else if (numrc % 11 !== 0) 49 | return false; 50 | } 51 | 52 | if (year > 54 && old && !dbg) 53 | return false; 54 | 55 | if (!old && year < 54) 56 | year = 2000 + year; 57 | else 58 | year = 1900 + year; 59 | 60 | if (month > 50 && month < 63) 61 | month = month - 50; 62 | if (!old && year >= 2004) { 63 | if (month > 20 && month < 33) 64 | month = month - 20; 65 | if (month > 70 && month < 83) 66 | month = month - 70; 67 | } 68 | 69 | var datum = moment(_s.lpad(day.toString(), 2, '0') + "." + _s.lpad(month.toString(), 2, '0') + "." + year, "DD.MM.YYYY"); 70 | 71 | if (!datum.isValid()) 72 | return false; 73 | return datum.toDate() <= moment(Date.now()).toDate(); 74 | }; 75 | return RCValidator; 76 | })(); 77 | 78 | 79 | -------------------------------------------------------------------------------- /dist/module/Utils.js: -------------------------------------------------------------------------------- 1 | var Utils; 2 | (function (Utils) { 3 | var StringFce = (function () { 4 | function StringFce() { 5 | } 6 | StringFce.format = function (s, args) { 7 | var formatted = s; 8 | for (var prop in args) { 9 | var regexp = new RegExp('\\{' + prop + '\\}', 'gi'); 10 | formatted = formatted.replace(regexp, args[prop]); 11 | } 12 | return formatted; 13 | }; 14 | return StringFce; 15 | })(); 16 | Utils.StringFce = StringFce; 17 | 18 | var NumberFce = (function () { 19 | function NumberFce() { 20 | } 21 | NumberFce.GetNegDigits = function (value) { 22 | if (value === undefined) 23 | return 0; 24 | var digits = value.toString().split('.'); 25 | if (digits.length > 1) { 26 | var negDigitsLength = digits[1].length; 27 | return negDigitsLength; 28 | } 29 | return 0; 30 | }; 31 | return NumberFce; 32 | })(); 33 | Utils.NumberFce = NumberFce; 34 | 35 | 36 | 37 | var Signal = (function () { 38 | function Signal() { 39 | this.listeners = []; 40 | this.priorities = []; 41 | } 42 | Signal.prototype.add = function (listener, priority) { 43 | if (typeof priority === "undefined") { priority = 0; } 44 | var index = this.listeners.indexOf(listener); 45 | if (index !== -1) { 46 | this.priorities[index] = priority; 47 | return; 48 | } 49 | for (var i = 0, l = this.priorities.length; i < l; i++) { 50 | if (this.priorities[i] < priority) { 51 | this.priorities.splice(i, 0, priority); 52 | this.listeners.splice(i, 0, listener); 53 | return; 54 | } 55 | } 56 | this.priorities.push(priority); 57 | this.listeners.push(listener); 58 | }; 59 | 60 | Signal.prototype.remove = function (listener) { 61 | var index = this.listeners.indexOf(listener); 62 | if (index >= 0) { 63 | this.priorities.splice(index, 1); 64 | this.listeners.splice(index, 1); 65 | } 66 | }; 67 | 68 | Signal.prototype.dispatch = function (parameter) { 69 | var indexesToRemove; 70 | var hasBeenCanceled = this.listeners.every(function (listener) { 71 | var result = listener(parameter); 72 | return result !== false; 73 | }); 74 | 75 | return hasBeenCanceled; 76 | }; 77 | 78 | Signal.prototype.clear = function () { 79 | this.listeners = []; 80 | this.priorities = []; 81 | }; 82 | 83 | Signal.prototype.hasListeners = function () { 84 | return this.listeners.length > 0; 85 | }; 86 | return Signal; 87 | })(); 88 | Utils.Signal = Signal; 89 | 90 | 91 | 92 | var CompositeDotObject = (function () { 93 | function CompositeDotObject() { 94 | } 95 | CompositeDotObject.Transform = function (component, obj) { 96 | if (obj === undefined) 97 | obj = {}; 98 | if (component.isItem()) { 99 | obj[component.getName()] = component; 100 | } else { 101 | var children = component.getChildren(); 102 | var parent = obj[component.getName()] = component; 103 | for (var comp in children) { 104 | CompositeDotObject.Transform(children[comp], parent); 105 | } 106 | } 107 | return obj; 108 | }; 109 | return CompositeDotObject; 110 | })(); 111 | Utils.CompositeDotObject = CompositeDotObject; 112 | })(Utils || (Utils = {})); 113 | 114 | -------------------------------------------------------------------------------- /dist/module/i18n/messages_cz.js: -------------------------------------------------------------------------------- 1 | var Localization = (function () { 2 | function Localization() { 3 | } 4 | Object.defineProperty(Localization, "ValidationMessages", { 5 | get: function () { 6 | return { 7 | required: "Tento údaj je povinný.", 8 | remote: "Prosím, opravte tento údaj.", 9 | email: "Prosím, zadejte platný e-mail.", 10 | url: "Prosím, zadejte platné URL.", 11 | date: "Prosím, zadejte platné datum.", 12 | dateISO: "Prosím, zadejte platné datum (ISO).", 13 | number: "Prosím, zadejte číslo.", 14 | digits: "Prosím, zadávejte pouze číslice.", 15 | creditcard: "Prosím, zadejte číslo kreditní karty.", 16 | equalTo: "Prosím, zadejte znovu stejnou hodnotu.", 17 | extension: "Prosím, zadejte soubor se správnou příponou.", 18 | maxlength: "Prosím, zadejte nejvíce {MaxLength} znaků.", 19 | minlength: "Prosím, zadejte nejméně {MinLength} znaků.", 20 | rangelength: "Prosím, zadejte od {MinLength} do {MaxLength} znaků.", 21 | range: "Prosím, zadejte hodnotu od {Min} do {Max}.", 22 | max: "Prosím, zadejte hodnotu menší nebo rovnu {Max}.", 23 | min: "Prosím, zadejte hodnotu větší nebo rovnu {Min}.", 24 | contains: "Prosím, zadejte hodnotu ze seznamu. Zadaná hodnota {AttemptedValue}.", 25 | dateCompare: { 26 | Format: "DD.MM.YYYY", 27 | LessThan: "Prosím, zadejte datum menší než {CompareTo}.", 28 | LessThanEqual: "Prosím, zadejte datum menší nebo rovné {CompareTo}.", 29 | Equal: "Prosím, zadejte {CompareTo}.", 30 | NotEqual: "Prosím, zadejte datum různé od {CompareTo}.", 31 | GreaterThanEqual: "Prosím, zadejte datum větší nebo rovné {CompareTo}.", 32 | GreaterThan: "Prosím, zadejte datum větší než {CompareTo}." 33 | }, 34 | minItems: "Prosím zadejte alespoň {Min} položek.", 35 | maxItems: "Prosím zadejte maximálně {Max} položek.", 36 | uniqItems: "Prosím zadejte pouze unikátní hodnoty.", 37 | enum: "Prosím zadajte povolenou hodnotu.", 38 | type: "Prosím zadejte hodnotu typu '{Type}'.", 39 | multipleOf: "Prosím zadejte hodnotu dělitelnou číslem '{Divider}'." 40 | }; 41 | }, 42 | enumerable: true, 43 | configurable: true 44 | }); 45 | return Localization; 46 | })(); 47 | 48 | 49 | -------------------------------------------------------------------------------- /dist/module/i18n/messages_cz.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": "Tento údaj je povinný.", 3 | "remote": "Prosím, opravte tento údaj.", 4 | "email": "Prosím, zadejte platný e-mail.", 5 | "url": "Prosím, zadejte platné URL.", 6 | "date": "Prosím, zadejte platné datum.", 7 | "dateISO": "Prosím, zadejte platné datum (ISO).", 8 | "number": "Prosím, zadejte číslo.", 9 | "digits": "Prosím, zadávejte pouze číslice.", 10 | "creditcard": "Prosím, zadejte číslo kreditní karty.", 11 | "equalTo": "Prosím, zadejte znovu stejnou hodnotu.", 12 | "extension": "Prosím, zadejte soubor se správnou příponou.", 13 | "maxlength": "Prosím, zadejte nejvíce {MaxLength} znaků.", 14 | "minlength": "Prosím, zadejte nejméně {MinLength} znaků.", 15 | "rangelength": "Prosím, zadejte od {MinLength} do {MaxLength} znaků.", 16 | "range": "Prosím, zadejte hodnotu od {Min} do {Max}.", 17 | "max": "Prosím, zadejte hodnotu menší nebo rovnu {Max}.", 18 | "min": "Prosím, zadejte hodnotu větší nebo rovnu {Min}.", 19 | "contains": "Prosím, zadejte hodnotu ze seznamu. Zadaná hodnota {AttemptedValue}.", 20 | "minItems": "Prosím zadejte alespoň {Min} položek.", 21 | "maxItems": "Prosím zadejte maximálně {Max} položek.", 22 | "uniqItems": "Prosím zadejte pouze unikátní hodnoty.", 23 | "enum":"Prosím zadajte povolenou hodnotu.", 24 | "type":"Prosím zadejte hodnotu typu '{Type}'.", 25 | "multipleOf":"Prosím zadejte hodnotu dělitelnou číslem '{Divider}'." 26 | } -------------------------------------------------------------------------------- /dist/module/i18n/messages_de.js: -------------------------------------------------------------------------------- 1 | var Localization = (function () { 2 | function Localization() { 3 | } 4 | Object.defineProperty(Localization, "ValidationMessages", { 5 | get: function () { 6 | return { 7 | required: "Dieses Feld ist ein Pflichtfeld.", 8 | maxlength: "Geben Sie bitte maximal {MaxLength} Zeichen ein.", 9 | minlength: "Geben Sie bitte mindestens {MinLength} Zeichen ein.", 10 | rangelength: "Geben Sie bitte mindestens {MinLength} und maximal {MaxLength} Zeichen ein.", 11 | email: "Geben Sie bitte eine gültige E-Mail Adresse ein.", 12 | url: "Geben Sie bitte eine gültige URL ein.", 13 | date: "Bitte geben Sie ein gültiges Datum ein.", 14 | number: "Geben Sie bitte eine Nummer ein.", 15 | digits: "Geben Sie bitte nur Ziffern ein.", 16 | equalTo: "Bitte denselben Wert wiederholen.", 17 | range: "Geben Sie bitte einen Wert zwischen {Min} und {Max} ein.", 18 | max: "Geben Sie bitte einen Wert kleiner oder gleich {Max} ein.", 19 | min: "Geben Sie bitte einen Wert größer oder gleich {Min} ein.", 20 | creditcard: "Geben Sie bitte eine gültige Kreditkarten-Nummer ein.", 21 | dateCompare: { 22 | LessThan: "Geben Sie bitte ein datum kleiner {CompareTo}.", 23 | LessThanEqual: "Geben Sie bitte ein datum kleiner oder gleich {CompareTo}.", 24 | Equal: "Geben Sie bitte ein datum {CompareTo}.", 25 | NotEqual: "Geben Sie bitte ein datum anderes von {CompareTo}.", 26 | GreaterThanEqual: "Geben Sie bitte ein datum größer oder gleich {CompareTo}.", 27 | GreaterThan: "Geben Sie bitte ein datum größer {CompareTo}." 28 | }, 29 | minItems: "Geben Sie bitte minimal {Min} Einträge.", 30 | maxItems: "Geben Sie bitte maximal {Max} Einträge.", 31 | uniqItems: "Geben Sie bitte maximal unique Einträge.", 32 | enum: "Geben Sie bitte einen Wert.", 33 | type: "Geben Sie bitte einen Wert von '{Type}'.", 34 | multipleOf: "Geben Sie bitte einen Wert als multiple of {Divider}." 35 | }; 36 | }, 37 | enumerable: true, 38 | configurable: true 39 | }); 40 | return Localization; 41 | })(); 42 | 43 | 44 | -------------------------------------------------------------------------------- /dist/module/i18n/messages_de.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": "Dieses Feld ist ein Pflichtfeld.", 3 | "maxlength": "Geben Sie bitte maximal {MaxLength} Zeichen ein.", 4 | "minlength": "Geben Sie bitte mindestens {MinLength} Zeichen ein.", 5 | "rangelength": "Geben Sie bitte mindestens {MinLength} und maximal {MaxLength} Zeichen ein.", 6 | "email": "Geben Sie bitte eine gültige E-Mail Adresse ein.", 7 | "url": "Geben Sie bitte eine gültige URL ein.", 8 | "date": "Bitte geben Sie ein gültiges Datum ein.", 9 | "number": "Geben Sie bitte eine Nummer ein.", 10 | "digits": "Geben Sie bitte nur Ziffern ein.", 11 | "equalTo": "Bitte denselben Wert wiederholen.", 12 | "range": "Geben Sie bitte einen Wert zwischen {Min} und {Max} ein.", 13 | "max": "Geben Sie bitte einen Wert kleiner oder gleich {Max} ein.", 14 | "min": "Geben Sie bitte einen Wert größer oder gleich {Min} ein.", 15 | "creditcard": "Geben Sie bitte eine gültige Kreditkarten-Nummer ein.", 16 | "minItems": "Geben Sie bitte minimal {Min} Einträge.", 17 | "maxItems": "Geben Sie bitte maximal {Max} Einträge.", 18 | "uniqItems": "Geben Sie bitte maximal unique Einträge.", 19 | "enum": "Geben Sie bitte einen Wert.", 20 | "type": "Geben Sie bitte einen Wert von '{Type}'.", 21 | "multipleOf": "Geben Sie bitte einen Wert als multiple of {Divider}." 22 | } -------------------------------------------------------------------------------- /dist/module/i18n/messages_en.js: -------------------------------------------------------------------------------- 1 | var Localization = (function () { 2 | function Localization() { 3 | } 4 | Object.defineProperty(Localization, "ValidationMessages", { 5 | get: function () { 6 | return { 7 | required: "This field is required.", 8 | remote: "Please fix the field.", 9 | email: "Please enter a valid email address.", 10 | url: "Please enter a valid URL.", 11 | date: "Please enter a valid date.", 12 | dateISO: "Please enter a valid date ( ISO ).", 13 | number: "Please enter a valid number.", 14 | digits: "Please enter only digits.", 15 | signedDigits: "Please enter only signed digits.", 16 | creditcard: "Please enter a valid credit card number.", 17 | equalTo: "Please enter the same value again.", 18 | maxlength: "Please enter no more than {MaxLength} characters.", 19 | minlength: "Please enter at least {MinLength} characters.", 20 | rangelength: "Please enter a value between {MinLength} and {MaxLength} characters long.", 21 | range: "Please enter a value between {Min} and {Max}.", 22 | max: "Please enter a value less than or equal to {Max}.", 23 | min: "Please enter a value greater than or equal to {Min}.", 24 | step: "Please enter a value with step {Step}.", 25 | contains: "Please enter a value from list of values. Attempted value '{AttemptedValue}'.", 26 | mask: "Please enter a value corresponding with {Mask}.", 27 | dateCompare: { 28 | Format: "MM/DD/YYYY", 29 | LessThan: "Please enter date less than {CompareTo}.", 30 | LessThanEqual: "Please enter date less than or equal {CompareTo}.", 31 | Equal: "Please enter date equal {CompareTo}.", 32 | NotEqual: "Please enter date different than {CompareTo}.", 33 | GreaterThanEqual: "Please enter date greater than or equal {CompareTo}.", 34 | GreaterThan: "Please enter date greter than {CompareTo}." 35 | }, 36 | minItems: "Please enter at least {Min} items.", 37 | maxItems: "Please enter no more than {Max} items.", 38 | uniqItems: "Please enter unique items.", 39 | enum: "Please enter a value from list of permitted values.", 40 | type: "Please enter a value of type '{Type}'.", 41 | multipleOf: "Please enter a value that is multiple of {Divider}." 42 | }; 43 | }, 44 | enumerable: true, 45 | configurable: true 46 | }); 47 | return Localization; 48 | })(); 49 | 50 | 51 | -------------------------------------------------------------------------------- /dist/module/i18n/messages_en.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": "This field is required.", 3 | "remote": "Please fix the field.", 4 | "email": "Please enter a valid email address.", 5 | "url": "Please enter a valid URL.", 6 | "date": "Please enter a valid date.", 7 | "dateISO": "Please enter a valid date ( ISO ).", 8 | "number": "Please enter a valid number.", 9 | "digits": "Please enter only digits.", 10 | "signedDigits": "Please enter only signed digits.", 11 | "creditcard": "Please enter a valid credit card number.", 12 | "equalTo": "Please enter the same value again.", 13 | "maxlength": "Please enter no more than {MaxLength} characters.", 14 | "minlength": "Please enter at least {MinLength} characters.", 15 | "rangelength": "Please enter a value between {MinLength} and {MaxLength} characters long.", 16 | "range": "Please enter a value between {Min} and {Max}.", 17 | "max": "Please enter a value less than or equal to {Max}.", 18 | "min": "Please enter a value greater than or equal to {Min}.", 19 | "step": "Please enter a value with step {Step}.", 20 | "contains": "Please enter a value from list of values. Attempted value '{AttemptedValue}'.", 21 | "mask": "Please enter a value corresponding with {Mask}.", 22 | "minItems":"Please enter at least {Min} items.", 23 | "maxItems":"Please enter at least {Max} items.", 24 | "uniqItems":"Please enter unique items.", 25 | "enum":"Please enter a value from list of permitted values.", 26 | "type":"Please enter a value of type '{Type}'.", 27 | "multipleOf":"Please enter a value that is multiple of {Divider}." 28 | } -------------------------------------------------------------------------------- /dist/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "business-rules-engine", 3 | "version": "1.2.8", 4 | "description": "business rules engine", 5 | "main": "./commonjs/Validation", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/rsamec/business-rules-engine" 9 | }, 10 | "keywords": [ 11 | "business rules", 12 | "validation", 13 | "form" 14 | ], 15 | "author": "rsamec ", 16 | "license": "MIT", 17 | "bugs": { 18 | "url": "https://github.com/rsamec/business-rules-engine/issues" 19 | }, 20 | "dependencies": { 21 | "q": "1.0.1", 22 | "axios": "^0.3.1", 23 | "hashmap": "^1.1.0", 24 | "moment": "2.5.0", 25 | "underscore": "^1.0.6", 26 | "underscore.string": "^2.3.3" 27 | }, 28 | "devDependencies": { 29 | "expect.js": "~0.2.0", 30 | "grunt": "^0.4.5", 31 | "grunt-cli": "~0.1.9", 32 | "grunt-complexity": "~0.1.3", 33 | "grunt-contrib-copy": "^0.5.0", 34 | "grunt-contrib-jshint": "~0.6.4", 35 | "grunt-contrib-watch": "~0.5.3", 36 | "grunt-docular": "^0.1.2", 37 | "grunt-mocha-cli": "~1.1.0", 38 | "grunt-ngdocs": "^0.2.2", 39 | "grunt-typedoc": "^0.1.1", 40 | "sinon": "^1.12.1" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /form_logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsamec/business-rules-engine/df9f6955dd4a248b450f3dfeb78ade297f1a52c2/form_logo.jpg -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "business-rules-engine", 3 | "version": "1.2.8", 4 | "description": "business rules engine", 5 | "main": "src/validation", 6 | "scripts": { 7 | "test": "node_modules/.bin/grunt ci" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/rsamec/business-rules-engine" 12 | }, 13 | "keywords": [ 14 | "business rules", 15 | "validation", 16 | "form" 17 | ], 18 | "author": "rsamec ", 19 | "license": "BSD-3-Clause", 20 | "bugs": { 21 | "url": "https://github.com/rsamec/business-rules-engine/issues" 22 | }, 23 | "dependencies": { 24 | "q": "1.0.1", 25 | "axios": "^0.3.1", 26 | "hashmap": "^1.1.0", 27 | "moment": "2.5.0", 28 | "underscore": "^1.0.6", 29 | "underscore.string": "^2.3.3" 30 | }, 31 | "devDependencies": { 32 | "expect.js": "~0.2.0", 33 | "grunt": "~0.4.1", 34 | "grunt-cli": "~0.1.9", 35 | "grunt-complexity": "~0.1.3", 36 | "grunt-contrib-commands": "^0.1.6", 37 | "grunt-contrib-concat": "^0.5.0", 38 | "grunt-contrib-copy": "^0.5.0", 39 | "grunt-contrib-jshint": "~0.6.4", 40 | "grunt-contrib-uglify": "^0.5.1", 41 | "grunt-contrib-watch": "~0.5.3", 42 | "grunt-docular": "^0.1.2", 43 | "grunt-mocha-cli": "^1.9.0", 44 | "grunt-ngdocs": "^0.2.2", 45 | "grunt-run": "^0.2.3", 46 | "grunt-typedoc": "^0.1.1", 47 | "grunt-typescript": "^0.3.8", 48 | "sinon": "^1.12.1", 49 | "typedoc": "^0.1.1" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/customValidators/DateCompareValidator.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | import moment = require("moment"); 5 | import _ = require("underscore"); 6 | 7 | 8 | /** 9 | * @ngdoc object 10 | * @name DateCompareValidator 11 | * 12 | * @requires moment 13 | * @requires underscore 14 | * 15 | * @description 16 | * DateCompareValidator enables to compare date to another date (CompareTo). 17 | * 18 | * @property CompareTo 19 | * The datetime against the compare is done. 20 | * If property is not set, then comparison is done against actual datetime. 21 | * 22 | * @property IgnoreDate 23 | * It forces to ignore time part of date by date compare. 24 | * 25 | * 26 | * @example 27 | * ```typescript 28 | * 29 | * //create validator 30 | * var validator = new dateCompareValidator(); 31 | * validator.CompareTo = new Date(2000,2,2); 32 | * validator.CompareOperator = Validation.CompareOperator.LessThanEqual; 33 | * 34 | * 35 | * //less more than month -> return true 36 | * var result = validator.isAcceptable(new Date(2000,1,1)); 37 | * //equal up to days -> return true 38 | * var result = validator.isAcceptable(new Date(2000,2,2)); 39 | * 40 | * ``` 41 | * 42 | */ 43 | 44 | class DateCompareValidator implements Validation.IPropertyValidator{ 45 | 46 | public isAcceptable(s:any){ 47 | var isValid = false; 48 | 49 | //if date to compare is not specified - defaults to compare against now 50 | if (!_.isDate(s)) return false; 51 | 52 | if (this.CompareTo === undefined) Date.now(); 53 | 54 | var now = moment(this.CompareTo); 55 | var then = moment(s); 56 | 57 | var diffs:number = then.diff(now); 58 | if (this.IgnoreTime) diffs = moment.duration(diffs).days(); 59 | 60 | if (diffs < 0) { 61 | isValid = this.CompareOperator === Validation.CompareOperator.LessThan 62 | || this.CompareOperator === Validation.CompareOperator.LessThanEqual 63 | || this.CompareOperator === Validation.CompareOperator.NotEqual; 64 | } 65 | else if (diffs > 0) { 66 | isValid = this.CompareOperator === Validation.CompareOperator.GreaterThan 67 | || this.CompareOperator === Validation.CompareOperator.GreaterThanEqual 68 | || this.CompareOperator === Validation.CompareOperator.NotEqual; 69 | } 70 | else { 71 | isValid = this.CompareOperator === Validation.CompareOperator.LessThanEqual 72 | || this.CompareOperator === Validation.CompareOperator.Equal 73 | || this.CompareOperator === Validation.CompareOperator.GreaterThanEqual; 74 | } 75 | return isValid; 76 | } 77 | 78 | /** 79 | * Set the time of compare between passed date and CompareTo date. 80 | */ 81 | public CompareOperator:Validation.CompareOperator; 82 | 83 | /** 84 | * The datetime against the compare is done. 85 | * If CompareTo is not set, then comparison is done against actual datetime. 86 | */ 87 | public CompareTo:Date; 88 | 89 | /** 90 | * It forces to ignore time part of date by date compare. 91 | */ 92 | public IgnoreTime:boolean = false; 93 | 94 | tagName = 'dateCompare'; 95 | 96 | // public getErrorMessage(localMessages:any) { 97 | // var msg = ''; 98 | // var messages = localMessages[this.tagName]; 99 | // 100 | // var format:string = messages["Format"]; 101 | // if (format != undefined) { 102 | // _.extend(this, {FormatedCompareTo: moment(this.CompareTo).format(format)}) 103 | // } 104 | // 105 | // switch (this.CompareOperator) { 106 | // case Validation.CompareOperator.LessThan: 107 | // msg = messages["LessThan"]; 108 | // break; 109 | // case Validation.CompareOperator.LessThanEqual: 110 | // msg = messages["LessThanEqual"]; 111 | // break; 112 | // case Validation.CompareOperator.Equal: 113 | // msg = messages["Equal"]; 114 | // break; 115 | // case Validation.CompareOperator.NotEqual: 116 | // msg = messages["NotEqual"]; 117 | // break; 118 | // case Validation.CompareOperator.GreaterThanEqual: 119 | // msg = messages["GreaterThanEqual"]; 120 | // break; 121 | // case Validation.CompareOperator.GreaterThan: 122 | // msg = messages["GreaterThan"]; 123 | // break; 124 | // } 125 | // return DateCompareValidator.format(msg.replace('CompareTo','FormatedCompareTo'),this); 126 | // } 127 | // tagName = 'dateCompare'; 128 | // 129 | // static format(s: string, args: any): string { 130 | // var formatted = s; 131 | // for (var prop in args) { 132 | // var regexp = new RegExp('\\{' + prop + '\\}', 'gi'); 133 | // formatted = formatted.replace(regexp, args[prop]); 134 | // } 135 | // return formatted; 136 | // } 137 | } 138 | 139 | export = DateCompareValidator; 140 | -------------------------------------------------------------------------------- /src/customValidators/ICOValidator.ts: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @ngdoc object 4 | * @name ICOValidator 5 | * 6 | * @description 7 | * Return true for valid identification number of CZE company (called ico), otherwise return false. 8 | * 9 | * @example 10 | * 11 | *
      12 |  *  //create validator
      13 |  *  var validator = new IcoValidator();
      14 |  *
      15 |  *  //valid IC -> return true
      16 |  *  var result = validator.isAcceptable('12312312');
      17 |  *  //unvalid IC  -> return true
      18 |  *  var result = validator.isAcceptable('11111111');
      19 |  *
      20 |  * 
      21 | */ 22 | class ICOValidator { 23 | 24 | /** 25 | * It checks validity of identification number of CZE company (called ico) 26 | * @param input {string} value to check 27 | * @returns {boolean} return true for valid value, otherwise false 28 | */ 29 | public isAcceptable(input: string) { 30 | 31 | if (input === undefined) return false; 32 | if (input.length === 0) return false; 33 | 34 | if (!/^\d+$/.test(input)) return false; 35 | 36 | var Sci = []; 37 | var Souc; 38 | var Del = input.length; 39 | var kon = parseInt(input.substring(Del, Del - 1), 10);// CLng(Right(strInput, 1)); 40 | //var Numer = parseInt(input.substring(0,Del - 1),10); 41 | Del = Del - 1; 42 | Souc = 0; 43 | for (var a = 0; a < Del; a++) { 44 | Sci[a] = parseInt(input.substr((Del - a) - 1, 1), 10); 45 | Sci[a] = Sci[a] * (a + 2); 46 | Souc = Souc + Sci[a]; 47 | } 48 | 49 | if (Souc > 0) { 50 | //var resul = 11 - (Souc % 11); 51 | var resul = Souc % 11; 52 | var mezi = Souc - resul; 53 | resul = mezi + 11; 54 | resul = resul - Souc; 55 | 56 | if ((resul === 10 && kon === 0) || (resul === 11 && kon === 1) || (resul === kon)) 57 | return true; 58 | } 59 | return false; 60 | } 61 | 62 | tagName = "ico"; 63 | } 64 | 65 | export = ICOValidator; -------------------------------------------------------------------------------- /src/customValidators/ParamValidator.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | 5 | 6 | import Q = require("q"); 7 | import _ = require("underscore"); 8 | 9 | /** 10 | * @ngdoc object 11 | * @name ParamValidator 12 | * 13 | * @requires Q 14 | * 15 | * @description 16 | * Returns true if the value is present in the list. Otherwise return false. 17 | * The list is returned via Options property that can be parametrize by the name of the list with ParamId parameter. 18 | * 19 | * @property Options 20 | * Promise that returns list of param items (key-value pairs) 21 | * 22 | *
       23 |  *     var optionsFce = function (paramId:string) {
       24 |  *           var deferral = Q.defer();
       25 |  *           setTimeout(function () {
       26 |  *               var result:Array = [];
       27 |  *               if (paramId == "jobs") {
       28 |  *                   result = [
       29 |  *                       { "value": 1, "text": "manager" },
       30 |  *                       { "value": 2, "text": "programmer" },
       31 |  *                       { "value": 3, "text": "shop assistant" },
       32 |  *                       { "value": 4, "text": "unemployed" },
       33 |  *                       { "value": 5, "text": "machinery" },
       34 |  *                       { "value": 6, "text": "agriculture" },
       35 |  *                       { "value": 7, "text": "academic" },
       36 |  *                       { "value": 8, "text": "goverment" }
       37 |  *                   ];
       38 |  *               }
       39 |  *               if (paramId == "countries") {
       40 |  *                   result = [
       41 |  *                       { "value": "CZE", "text": "Czech Republic" },
       42 |  *                       { "value": "Germany", "text": "Germany" },
       43 |  *                       { "value": "France", "text": "France" },
       44 |  *                   ];
       45 |  *               }
       46 |  *
       47 |  *
       48 |  *               deferral.resolve(result);
       49 |  *           }, 1000);
       50 |  *           return deferral.promise;
       51 |  *       };
       52 |  *  
      53 | * 54 | * @property ParamId - The name of the list to be returned. 55 | * 56 | * @example 57 | * 58 | *
       59 |  *             //when
       60 |  *             var validator = new paramValidator();
       61 |  *             validator.Options = optionsFce;
       62 |  *             validator.ParamId = "jobs";
       63 |  *
       64 |  *             //excercise
       65 |  *             var promiseResult = validator.isAcceptable("programmer");
       66 |  *
       67 |  *             it('value from list should return true', function (done) {
       68 |  *
       69 |  *                 promiseResult.then(function(result) {
       70 |  *
       71 |  *                     //verify
       72 |  *                     expect(result).to.equal(true);
       73 |  *
       74 |  *                     done();
       75 |  *
       76 |  *                 }).done(null, done);
       77 |  *             });
       78 |  *
       79 |  *             //excercise
       80 |  *             var promiseResult2 = validator.isAcceptable("non existing item");
       81 |  *
       82 |  *             it('value out of list should return false', function (done) {
       83 |  *
       84 |  *                 promiseResult2.then(function(result) {
       85 |  *
       86 |  *                     //verify
       87 |  *                     expect(result).to.equal(false);
       88 |  *
       89 |  *                     done();
       90 |  *
       91 |  *                 }).done(null, done);
       92 |  *             });
       93 |  *  
      94 | * 95 | */ 96 | class ParamValidator implements Validation.IAsyncPropertyValidator{ 97 | 98 | /** 99 | * It checks validity of identification number of CZE company (called ico) 100 | * @param s value to check 101 | * @returns return true for valid value, otherwise false 102 | */ 103 | isAcceptable(s:string):Q.Promise { 104 | var deferred = Q.defer(); 105 | 106 | this.Options(this.ParamId).then(function (result) { 107 | var hasSome = _.some(result, function (item) { 108 | return item.text === s; 109 | }); 110 | if (hasSome) deferred.resolve(true); 111 | deferred.resolve(false); 112 | }); 113 | 114 | return deferred.promise; 115 | } 116 | 117 | public ParamId:string; 118 | public Options:{(string): Q.Promise>}; 119 | 120 | isAsync = true; 121 | tagName = "param"; 122 | } 123 | export = ParamValidator; -------------------------------------------------------------------------------- /src/customValidators/RCValidator.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | 5 | import moment = require("moment"); 6 | import _s= require("underscore.string"); 7 | 8 | /** 9 | * @ngdoc object 10 | * @name RCValidator 11 | * 12 | * @requires moment 13 | * @requires underscore.string 14 | * 15 | * @description 16 | * Return true for valid birth day number in Czech Republic, otherwise return false. 17 | * 18 | * @example 19 | * 20 | *
      21 |  *
      22 |  *  //create validator
      23 |  *  var validator = new RCValidator();
      24 |  *
      25 |  *  //valid RC -> return true
      26 |  *  var result = validator.isAcceptable('800101/9999');
      27 |  *  //unvalid RC  -> return false
      28 |  *  var result = validator.isAcceptable('111111/1752');
      29 |  *
      30 |  * 
      31 | */ 32 | class RCValidator implements Validation.IPropertyValidator { 33 | 34 | public tagName:string = 'rc'; 35 | 36 | public isAcceptable(s:any):boolean { 37 | 38 | var old:boolean = false; 39 | var month:number; 40 | var year:number; 41 | var day:number; 42 | var numrc:any; 43 | var dbg = false; 44 | if (s === undefined) return false; 45 | if (s.toString().length === 0) { 46 | return false; 47 | } 48 | 49 | if (!s.match(/^\d{6}\/?\d{3,4}$/)) return false; 50 | 51 | if (s.indexOf('/') != -1) 52 | old = s.length === 10; 53 | else 54 | old = s.length === 9; 55 | 56 | if (s.indexOf('/') !== -1) { 57 | numrc = s.split("/"); 58 | numrc = numrc.join(""); 59 | } else { 60 | numrc = s 61 | } 62 | 63 | day = parseInt(numrc.substring(4, 6), 10); 64 | month = parseInt(numrc.substring(2, 4), 10); 65 | year = parseInt(numrc.substring(0, 2), 10); 66 | 67 | if (s.match(/\/?(0000?|8888?|9999?)$/)) { 68 | dbg = true; 69 | } 70 | 71 | if (!old && !dbg) { 72 | if (numrc % 11 !== 0 && s.substr(s.length - 1) === "0") { 73 | if (parseInt(numrc.substr(0, 9), 10) % 11 !== 10) return false; 74 | } 75 | else if (numrc % 11 !== 0) return false; 76 | } 77 | 78 | if (year > 54 && old && !dbg) return false; 79 | 80 | if (!old && year < 54) year = 2000 + year; 81 | else year = 1900 + year; 82 | 83 | if (month > 50 && month < 63) month = month - 50; 84 | if (!old && year >= 2004) { //vyjimka na pricitani dvojek k mesici 85 | if (month > 20 && month < 33) month = month - 20; 86 | if (month > 70 && month < 83) month = month - 70; 87 | } 88 | 89 | var datum = moment(_s.lpad(day.toString(), 2, '0') + "." + _s.lpad(month.toString(), 2, '0') + "." + year, "DD.MM.YYYY"); 90 | 91 | if (!datum.isValid()) return false; 92 | return datum.toDate() <= moment(Date.now()).toDate(); 93 | 94 | } 95 | } 96 | 97 | export = RCValidator; -------------------------------------------------------------------------------- /src/localization/messages_cz.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": "Tento údaj je povinný.", 3 | "remote": "Prosím, opravte tento údaj.", 4 | "email": "Prosím, zadejte platný e-mail.", 5 | "url": "Prosím, zadejte platné URL.", 6 | "date": "Prosím, zadejte platné datum.", 7 | "dateISO": "Prosím, zadejte platné datum (ISO).", 8 | "number": "Prosím, zadejte číslo.", 9 | "digits": "Prosím, zadávejte pouze číslice.", 10 | "creditcard": "Prosím, zadejte číslo kreditní karty.", 11 | "equalTo": "Prosím, zadejte znovu stejnou hodnotu.", 12 | "extension": "Prosím, zadejte soubor se správnou příponou.", 13 | "maxlength": "Prosím, zadejte nejvíce {MaxLength} znaků.", 14 | "minlength": "Prosím, zadejte nejméně {MinLength} znaků.", 15 | "rangelength": "Prosím, zadejte od {MinLength} do {MaxLength} znaků.", 16 | "range": "Prosím, zadejte hodnotu od {Min} do {Max}.", 17 | "max": "Prosím, zadejte hodnotu menší nebo rovnu {Max}.", 18 | "min": "Prosím, zadejte hodnotu větší nebo rovnu {Min}.", 19 | "contains": "Prosím, zadejte hodnotu ze seznamu. Zadaná hodnota {AttemptedValue}.", 20 | "minItems": "Prosím zadejte alespoň {Min} položek.", 21 | "maxItems": "Prosím zadejte maximálně {Max} položek.", 22 | "uniqItems": "Prosím zadejte pouze unikátní hodnoty.", 23 | "enum":"Prosím zadajte povolenou hodnotu.", 24 | "type":"Prosím zadejte hodnotu typu '{Type}'.", 25 | "multipleOf":"Prosím zadejte hodnotu dělitelnou číslem '{Divider}'." 26 | } -------------------------------------------------------------------------------- /src/localization/messages_cz.ts: -------------------------------------------------------------------------------- 1 | class Localization { 2 | 3 | /* 4 | * Translated default messages for the validation engine. 5 | * Locale: CS (Czech; čeština, český jazyk) 6 | */ 7 | static get ValidationMessages():any { 8 | return { 9 | required: "Tento údaj je povinný.", 10 | remote: "Prosím, opravte tento údaj.", 11 | email: "Prosím, zadejte platný e-mail.", 12 | url: "Prosím, zadejte platné URL.", 13 | date: "Prosím, zadejte platné datum.", 14 | dateISO: "Prosím, zadejte platné datum (ISO).", 15 | number: "Prosím, zadejte číslo.", 16 | digits: "Prosím, zadávejte pouze číslice.", 17 | creditcard: "Prosím, zadejte číslo kreditní karty.", 18 | equalTo: "Prosím, zadejte znovu stejnou hodnotu.", 19 | extension: "Prosím, zadejte soubor se správnou příponou.", 20 | maxlength: "Prosím, zadejte nejvíce {MaxLength} znaků.", 21 | minlength: "Prosím, zadejte nejméně {MinLength} znaků.", 22 | rangelength: "Prosím, zadejte od {MinLength} do {MaxLength} znaků.", 23 | range: "Prosím, zadejte hodnotu od {Min} do {Max}.", 24 | max: "Prosím, zadejte hodnotu menší nebo rovnu {Max}.", 25 | min: "Prosím, zadejte hodnotu větší nebo rovnu {Min}.", 26 | contains: "Prosím, zadejte hodnotu ze seznamu. Zadaná hodnota {AttemptedValue}.", 27 | dateCompare: { 28 | Format: "DD.MM.YYYY", 29 | LessThan: "Prosím, zadejte datum menší než {CompareTo}.", 30 | LessThanEqual: "Prosím, zadejte datum menší nebo rovné {CompareTo}.", 31 | Equal: "Prosím, zadejte {CompareTo}.", 32 | NotEqual: "Prosím, zadejte datum různé od {CompareTo}.", 33 | GreaterThanEqual: "Prosím, zadejte datum větší nebo rovné {CompareTo}.", 34 | GreaterThan: "Prosím, zadejte datum větší než {CompareTo}." 35 | }, 36 | minItems:"Prosím zadejte alespoň {Min} položek.", 37 | maxItems:"Prosím zadejte maximálně {Max} položek.", 38 | uniqItems:"Prosím zadejte pouze unikátní hodnoty.", 39 | enum:"Prosím zadajte povolenou hodnotu.", 40 | type:"Prosím zadejte hodnotu typu '{Type}'.", 41 | multipleOf:"Prosím zadejte hodnotu dělitelnou číslem '{Divider}'." 42 | }; 43 | } 44 | } 45 | 46 | export = Localization; 47 | -------------------------------------------------------------------------------- /src/localization/messages_de.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": "Dieses Feld ist ein Pflichtfeld.", 3 | "maxlength": "Geben Sie bitte maximal {MaxLength} Zeichen ein.", 4 | "minlength": "Geben Sie bitte mindestens {MinLength} Zeichen ein.", 5 | "rangelength": "Geben Sie bitte mindestens {MinLength} und maximal {MaxLength} Zeichen ein.", 6 | "email": "Geben Sie bitte eine gültige E-Mail Adresse ein.", 7 | "url": "Geben Sie bitte eine gültige URL ein.", 8 | "date": "Bitte geben Sie ein gültiges Datum ein.", 9 | "number": "Geben Sie bitte eine Nummer ein.", 10 | "digits": "Geben Sie bitte nur Ziffern ein.", 11 | "equalTo": "Bitte denselben Wert wiederholen.", 12 | "range": "Geben Sie bitte einen Wert zwischen {Min} und {Max} ein.", 13 | "max": "Geben Sie bitte einen Wert kleiner oder gleich {Max} ein.", 14 | "min": "Geben Sie bitte einen Wert größer oder gleich {Min} ein.", 15 | "creditcard": "Geben Sie bitte eine gültige Kreditkarten-Nummer ein.", 16 | "minItems": "Geben Sie bitte minimal {Min} Einträge.", 17 | "maxItems": "Geben Sie bitte maximal {Max} Einträge.", 18 | "uniqItems": "Geben Sie bitte maximal unique Einträge.", 19 | "enum": "Geben Sie bitte einen Wert.", 20 | "type": "Geben Sie bitte einen Wert von '{Type}'.", 21 | "multipleOf": "Geben Sie bitte einen Wert als multiple of {Divider}." 22 | } -------------------------------------------------------------------------------- /src/localization/messages_de.ts: -------------------------------------------------------------------------------- 1 | class Localization { 2 | 3 | /* 4 | * Translated default messages for the validation engine. 5 | * Locale: DE (German, Deutsch) 6 | */ 7 | static get ValidationMessages():any { 8 | return { 9 | required: "Dieses Feld ist ein Pflichtfeld.", 10 | maxlength: "Geben Sie bitte maximal {MaxLength} Zeichen ein.", 11 | minlength: "Geben Sie bitte mindestens {MinLength} Zeichen ein.", 12 | rangelength: "Geben Sie bitte mindestens {MinLength} und maximal {MaxLength} Zeichen ein.", 13 | email: "Geben Sie bitte eine gültige E-Mail Adresse ein.", 14 | url: "Geben Sie bitte eine gültige URL ein.", 15 | date: "Bitte geben Sie ein gültiges Datum ein.", 16 | number: "Geben Sie bitte eine Nummer ein.", 17 | digits: "Geben Sie bitte nur Ziffern ein.", 18 | equalTo: "Bitte denselben Wert wiederholen.", 19 | range: "Geben Sie bitte einen Wert zwischen {Min} und {Max} ein.", 20 | max: "Geben Sie bitte einen Wert kleiner oder gleich {Max} ein.", 21 | min: "Geben Sie bitte einen Wert größer oder gleich {Min} ein.", 22 | creditcard: "Geben Sie bitte eine gültige Kreditkarten-Nummer ein.", 23 | dateCompare: { 24 | LessThan: "Geben Sie bitte ein datum kleiner {CompareTo}.", 25 | LessThanEqual: "Geben Sie bitte ein datum kleiner oder gleich {CompareTo}.", 26 | Equal: "Geben Sie bitte ein datum {CompareTo}.", 27 | NotEqual: "Geben Sie bitte ein datum anderes von {CompareTo}.", 28 | GreaterThanEqual: "Geben Sie bitte ein datum größer oder gleich {CompareTo}.", 29 | GreaterThan: "Geben Sie bitte ein datum größer {CompareTo}." 30 | }, 31 | minItems:"Geben Sie bitte minimal {Min} Einträge.", 32 | maxItems:"Geben Sie bitte maximal {Max} Einträge.", 33 | uniqItems:"Geben Sie bitte maximal unique Einträge.", 34 | enum:"Geben Sie bitte einen Wert.", 35 | type:"Geben Sie bitte einen Wert von '{Type}'.", 36 | multipleOf:"Geben Sie bitte einen Wert als multiple of {Divider}." 37 | } 38 | } 39 | } 40 | 41 | export = Localization; -------------------------------------------------------------------------------- /src/localization/messages_en.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": "This field is required.", 3 | "remote": "Please fix the field.", 4 | "email": "Please enter a valid email address.", 5 | "url": "Please enter a valid URL.", 6 | "date": "Please enter a valid date.", 7 | "dateISO": "Please enter a valid date ( ISO ).", 8 | "number": "Please enter a valid number.", 9 | "digits": "Please enter only digits.", 10 | "signedDigits": "Please enter only signed digits.", 11 | "creditcard": "Please enter a valid credit card number.", 12 | "equalTo": "Please enter the same value again.", 13 | "maxlength": "Please enter no more than {MaxLength} characters.", 14 | "minlength": "Please enter at least {MinLength} characters.", 15 | "rangelength": "Please enter a value between {MinLength} and {MaxLength} characters long.", 16 | "range": "Please enter a value between {Min} and {Max}.", 17 | "max": "Please enter a value less than or equal to {Max}.", 18 | "min": "Please enter a value greater than or equal to {Min}.", 19 | "step": "Please enter a value with step {Step}.", 20 | "contains": "Please enter a value from list of values. Attempted value '{AttemptedValue}'.", 21 | "mask": "Please enter a value corresponding with {Mask}.", 22 | "minItems":"Please enter at least {Min} items.", 23 | "maxItems":"Please enter at least {Max} items.", 24 | "uniqItems":"Please enter unique items.", 25 | "enum":"Please enter a value from list of permitted values.", 26 | "type":"Please enter a value of type '{Type}'.", 27 | "multipleOf":"Please enter a value that is multiple of {Divider}." 28 | } -------------------------------------------------------------------------------- /src/localization/messages_en.ts: -------------------------------------------------------------------------------- 1 | 2 | class Localization { 3 | 4 | /* 5 | * Translated default messages for the validation engine. 6 | * Locale: EN (English; english) 7 | */ 8 | static get ValidationMessages():any { 9 | return { 10 | required: "This field is required.", 11 | remote: "Please fix the field.", 12 | email: "Please enter a valid email address.", 13 | url: "Please enter a valid URL.", 14 | date: "Please enter a valid date.", 15 | dateISO: "Please enter a valid date ( ISO ).", 16 | number: "Please enter a valid number.", 17 | digits: "Please enter only digits.", 18 | signedDigits: "Please enter only signed digits.", 19 | creditcard: "Please enter a valid credit card number.", 20 | equalTo: "Please enter the same value again.", 21 | maxlength: "Please enter no more than {MaxLength} characters.", 22 | minlength: "Please enter at least {MinLength} characters.", 23 | rangelength: "Please enter a value between {MinLength} and {MaxLength} characters long.", 24 | range: "Please enter a value between {Min} and {Max}.", 25 | max: "Please enter a value less than or equal to {Max}.", 26 | min: "Please enter a value greater than or equal to {Min}.", 27 | step: "Please enter a value with step {Step}.", 28 | contains: "Please enter a value from list of values. Attempted value '{AttemptedValue}'.", 29 | mask: "Please enter a value corresponding with {Mask}.", 30 | dateCompare: { 31 | Format: "MM/DD/YYYY", 32 | LessThan: "Please enter date less than {CompareTo}.", 33 | LessThanEqual: "Please enter date less than or equal {CompareTo}.", 34 | Equal: "Please enter date equal {CompareTo}.", 35 | NotEqual: "Please enter date different than {CompareTo}.", 36 | GreaterThanEqual: "Please enter date greater than or equal {CompareTo}.", 37 | GreaterThan: "Please enter date greter than {CompareTo}." 38 | }, 39 | minItems:"Please enter at least {Min} items.", 40 | maxItems:"Please enter no more than {Max} items.", 41 | uniqItems:"Please enter unique items.", 42 | enum:"Please enter a value from list of permitted values.", 43 | type:"Please enter a value of type '{Type}'.", 44 | multipleOf:"Please enter a value that is multiple of {Divider}." 45 | } 46 | } 47 | } 48 | 49 | export = Localization; 50 | -------------------------------------------------------------------------------- /src/metaDataRules/form.ts: -------------------------------------------------------------------------------- 1 | ///// 2 | ///// 3 | // 4 | ///// 5 | ///// 6 | // 7 | // 8 | //module Validation { 9 | // 10 | // export interface IForm { 11 | // Errors:IValidationResult; 12 | // //Validators:Validators; 13 | // 14 | // Validate():void; 15 | // } 16 | // 17 | // /** 18 | // * YUIDoc_comment 19 | // * 20 | // * @class form 21 | // * @constructor 22 | // **/ 23 | // export class MetaForm implements IForm{ 24 | // 25 | // public CLASS_NAME:string = 'form'; 26 | // 27 | // public Errors:IValidationResult = new CompositeValidationResult("Main form"); 28 | // //public Validators:Validators = new Validators(); 29 | // 30 | // public MetaRules:MetaDataRules; 31 | // 32 | // 33 | // constructor(public MetaData:any) { 34 | // this.Data = {}; 35 | // Util.generateDataEx(this.MetaData,this.Data); 36 | // 37 | // this.MetaRules = new MetaDataRules(this.Data,this.MetaData); 38 | // 39 | // _.each(this.MetaRules.Rules, function(rule:MetaDataRule) { 40 | // this.Errors.Add(rule.Error); 41 | // },this); 42 | // } 43 | // 44 | // public Data:any; 45 | // 46 | // public Validate():void 47 | // { 48 | // this.MetaRules.ValidateAll(); 49 | // //this.Validators.ValidateAll(); 50 | // } 51 | // } 52 | //} 53 | ////var _ = require('underscore'); 54 | ////var Q = require('q'); 55 | ////exports.Validation = Validation -------------------------------------------------------------------------------- /src/metaDataRules/metaDataRules.ts: -------------------------------------------------------------------------------- 1 | ///// 2 | ///// 3 | ///// 4 | // 5 | ////import Validators = require("../customValidators/BasicValidators.js"); 6 | // 7 | //module Validation { 8 | // 9 | //* 10 | // * This represents validation rule. 11 | // 12 | // 13 | // export interface IMetaRule { 14 | // Method: string; 15 | // Parameters?: any; 16 | // } 17 | // 18 | //* 19 | // * YUIDoc_comment 20 | // * 21 | // 22 | // export class MetaDataRules { 23 | // 24 | // public CLASS_NAME:string = 'MetaDataRules'; 25 | // 26 | // public Rules: { [index: string]: MetaDataRule; } = {}; 27 | // 28 | // constructor(public Data:any, public MetaData:any) { 29 | // for (var key in this.MetaData) { 30 | // var metaData = this.MetaData[key]; 31 | // if (metaData[Util.RULE_PROPERTY_NAME] !== undefined) { 32 | // this.Rules[key] = new MetaDataRule(metaData,new ValidationContext(key,this.Data)) 33 | // } 34 | // } 35 | // } 36 | // 37 | // public ValidateAll():void { 38 | // for (var key in this.Rules) 39 | // { 40 | // this.Rules[key].Validate(); 41 | // } 42 | // } 43 | // } 44 | // 45 | //* 46 | // * Defines a rule associated with a property which can have multiple validators 47 | // 48 | // 49 | // export class MetaDataRule{ 50 | // 51 | // public Rule: PropertyValidationRule; 52 | // 53 | // public get MetaErrors() { return this.Rule.ValidationFailures;} 54 | // 55 | // public get Error():IValidationResult {return this.Rule;} 56 | // 57 | // constructor(metaData: any, public Context:IValidationContext) { 58 | // 59 | // //read label from metadata 60 | // var label = metaData[Util.LABEL_PROPERTY_NAME]; 61 | // var name = label !== undefined ? label : this.Context.Key; 62 | // 63 | // var validators:Array = []; 64 | // for (var method in metaData.rules) { 65 | // //create validators 66 | // var validator = this.CreateValidator({Method: method, Parameters: metaData.rules[method]}); 67 | // validators.push(validator); 68 | // } 69 | // 70 | // this.Rule = new PropertyValidationRule(name,validators); 71 | // 72 | // 73 | // 74 | // //this.Error.Optional = this.Context.Optional; 75 | // 76 | // } 77 | // 78 | // private CreateValidator(rule:IMetaRule):IPropertyValidator{ 79 | // 80 | //// switch (rule.Method) { 81 | //// case "required": 82 | //// return new Validators.RequiredValidator(); 83 | //// 84 | //// 85 | //// 86 | //// case "minlength": 87 | //// var validator = new Validators.MinLengthValidator(); 88 | //// validator.MinLength = rule.Parameters; 89 | //// return validator; 90 | //// case "maxlength": 91 | //// var maxLengthValidator = new Validators.MaxLengthValidator(); 92 | //// maxLengthValidator.MaxLength = rule.Parameters; 93 | //// return maxLengthValidator; 94 | //// } 95 | // return undefined; 96 | // } 97 | // 98 | // public Validate(): void { 99 | // this.Rule.Validate(this.Context); 100 | // } 101 | // public ClearErrors(): void{ 102 | // for (var key in this.MetaErrors) { 103 | // var target = this.MetaErrors[key]; 104 | // target.Error.ErrorMessage = ""; 105 | // target.Error.HasError = false; 106 | // } 107 | // } 108 | // } 109 | // 110 | //* 111 | // * It represents composition of error objects for rules defined with meta data. 112 | // 113 | // 114 | // export class MetaRulesValidationResult extends CompositeValidationResult implements IValidationResult { 115 | // 116 | // constructor(public Name: string,public MetaRules:any) { 117 | // 118 | // super(Name); 119 | // 120 | // _.each(this.MetaRules.Rules, function(rule:any) { 121 | // this.Add(rule.Error); 122 | // },this); 123 | // } 124 | // 125 | // } 126 | //} 127 | -------------------------------------------------------------------------------- /src/metaDataRules/util.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | module Validation { 4 | 5 | /** 6 | * YUIDoc_comment 7 | * 8 | * @class util 9 | * @constructor 10 | **/ 11 | export class Util { 12 | 13 | public CLASS_NAME:string = 'util'; 14 | 15 | static RULE_PROPERTY_NAME:string = "rules"; 16 | static LABEL_PROPERTY_NAME:string = "label"; 17 | 18 | constructor() { 19 | 20 | } 21 | 22 | static generateData(metaData:any){ 23 | var data = {} 24 | Util.generateDataEx(metaData,data); 25 | return data; 26 | } 27 | 28 | //TODO: find better way how to distinguish between data fields items and meta data information 29 | //TODO: better documentation 30 | static generateDataEx(o, data, parentPath?){ 31 | //TODO: better implementation - like _.defaults() 32 | var metaDataKeys = ["label", "rules", "help", "id", "defaultValue", "options","unit","hint", "Common", "disclaimer","saveOptions","optionsRef","apiId"]; 33 | var tableKey = "RowData"; 34 | 35 | var containsLeafFce = function (key) { return _.contains(this, key) }; 36 | var containsTableFce = function (key) {return key == this }; 37 | for (var key in o) { 38 | if (_.contains(metaDataKeys, key)) continue; 39 | var item = o[key]; 40 | 41 | if (_.isArray(item)) { 42 | data[key] = item; 43 | continue; 44 | } 45 | if (typeof (item) == "object") { 46 | var isLeafNode = _.every(_.keys(item), containsLeafFce, metaDataKeys); 47 | if (isLeafNode) { 48 | data[key] = undefined; 49 | continue; 50 | } 51 | var isTableNode = _.some(_.keys(item),containsTableFce,tableKey); 52 | if (isTableNode) { 53 | data[key] = []; 54 | continue; 55 | } 56 | 57 | 58 | data[key] = {}; 59 | var path = parentPath === undefined ? key : parentPath + "." + key; 60 | //going on step down in the object tree!! 61 | this.generateDataEx(o[key], data[key], path); 62 | } 63 | } 64 | } 65 | 66 | } 67 | } -------------------------------------------------------------------------------- /src/validation/Utils.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | import _ = require('underscore'); 5 | /** 6 | * Utility functions for business rules purposes. 7 | * 8 | * + String functions 9 | * + Number functions 10 | */ 11 | module Utils { 12 | 13 | /* 14 | It represents utility for string manipulation. 15 | */ 16 | export class StringFce { 17 | static format(s:string, args:any):string { 18 | var formatted = s; 19 | for (var prop in args) { 20 | var regexp = new RegExp('\\{' + prop + '\\}', 'gi'); 21 | formatted = formatted.replace(regexp, args[prop]); 22 | } 23 | return formatted; 24 | } 25 | } 26 | 27 | /* 28 | It represents utility for number manipulation. 29 | */ 30 | export class NumberFce { 31 | static GetNegDigits(value:string):number { 32 | if (value === undefined) return 0; 33 | var digits = value.toString().split('.'); 34 | if (digits.length > 1) { 35 | var negDigitsLength = digits[1].length; 36 | return negDigitsLength; 37 | } 38 | return 0; 39 | } 40 | } 41 | 42 | /* 43 | It represents signal (event). 44 | */ 45 | export interface ISignal { 46 | add(listener: (parameter: T) => any, priority?: number): void; 47 | remove(listener: (parameter: T) => any): void; 48 | dispatch(parameter: T): boolean; 49 | clear(): void; 50 | hasListeners(): boolean; 51 | } 52 | 53 | /* 54 | It represents signal (event). 55 | */ 56 | export class Signal implements ISignal { 57 | private listeners: { (parameter: T): any }[] = []; 58 | private priorities: number[] = []; 59 | 60 | add(listener: (parameter: T) => any, priority = 0): void { 61 | var index = this.listeners.indexOf(listener); 62 | if (index !== -1) { 63 | this.priorities[index] = priority; 64 | return; 65 | } 66 | for (var i = 0, l = this.priorities.length; i < l; i++) { 67 | if (this.priorities[i] < priority) { 68 | this.priorities.splice(i, 0, priority); 69 | this.listeners.splice(i, 0, listener); 70 | return; 71 | } 72 | } 73 | this.priorities.push(priority); 74 | this.listeners.push(listener); 75 | } 76 | 77 | remove(listener: (parameter: T) => any): void { 78 | var index = this.listeners.indexOf(listener); 79 | if (index >= 0) { 80 | this.priorities.splice(index, 1); 81 | this.listeners.splice(index, 1); 82 | } 83 | } 84 | 85 | dispatch(parameter: T): boolean { 86 | var indexesToRemove: number[]; 87 | var hasBeenCanceled = this.listeners.every((listener: (parameter: T) => any) => { 88 | var result = listener(parameter); 89 | return result !== false; 90 | }); 91 | 92 | return hasBeenCanceled; 93 | } 94 | 95 | clear(): void { 96 | this.listeners = []; 97 | this.priorities = []; 98 | } 99 | 100 | hasListeners(): boolean { 101 | return this.listeners.length > 0; 102 | } 103 | } 104 | 105 | /* 106 | It is component element from composite design pattern. 107 | */ 108 | export interface IComponent{ 109 | add(child:IComponent):boolean; 110 | remove(child:IComponent):boolean; 111 | getChildren():IComponent[]; 112 | getName():string; 113 | isItem():boolean; 114 | } 115 | 116 | /* 117 | It represents utility for making composite object accessible by dot notation. 118 | */ 119 | export class CompositeDotObject{ 120 | 121 | /* 122 | It transforms composite object to dot accessible composite object. 123 | */ 124 | static Transform(component:IComponent,obj){ 125 | if (obj === undefined) obj = {}; 126 | if (component.isItem()){ 127 | obj[component.getName()] = component; 128 | } 129 | else{ 130 | var children = component.getChildren(); 131 | var parent = obj[component.getName()] = component; 132 | for (var comp in children ) { 133 | CompositeDotObject.Transform(children[comp],parent); 134 | } 135 | } 136 | return obj; 137 | } 138 | } 139 | } 140 | export = Utils; -------------------------------------------------------------------------------- /test/customValidators/DataCompareValidator.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by rsamec on 9.7.2014. 3 | */ 4 | /// 5 | /// 6 | /// 7 | 8 | var expect = require('expect.js'); 9 | import dateCompareValidator = require('../../src/customValidators/DateCompareValidator'); 10 | import moment = require('moment') 11 | 12 | describe('DateCompareValidator', function () { 13 | 14 | describe("isAcceptable", function () { 15 | var params = [ 16 | //less more than month 17 | { input: new Date(2000, 1, 1), result: true}, 18 | //less more than day 19 | { input: new Date(2000, 2, 1), result: true}, 20 | //equal up to days 21 | { input: new Date(2000, 2, 2), result: true }, 22 | //equal up to miliseconds 23 | { input: new Date(2000, 2, 2, 0, 0, 0, 0), result: true }, 24 | //greater about 1 milisecond 25 | { input: new Date(2000, 2, 2, 0, 0, 0, 1), result: false }, 26 | //equal up to miliseconds 27 | { input: new Date(2000, 2, 2, 0, 0, 0, 0), result: true, ignoreTime: true }, 28 | //greater about 1 milisecond 29 | { input: new Date(2000, 2, 2, 0, 0, 0, 1), result: true, ignoreTime: true }, 30 | //greater about max hours 31 | { input: new Date(2000, 2, 2, 23, 59, 59, 99), result: true, ignoreTime: true }, 32 | //greater about one day 33 | { input: new Date(2000, 2, 3, 0, 0, 0, 0), result: false, ignoreTime: true }, 34 | //bad date 35 | { input: "", result: false }, 36 | //bad date 37 | { input: undefined, result: false }, 38 | //bad date 39 | { input: "{}", result: false }, 40 | //bad date 41 | { input: "fasdfa", result: false } 42 | ]; 43 | 44 | var validator = new dateCompareValidator(); 45 | validator.CompareTo = new Date(2000, 2, 2); 46 | validator.CompareOperator = Validation.CompareOperator.LessThanEqual; 47 | 48 | for (var op in params) { 49 | (function (item) { 50 | it('should check date ' + item.input + ' is less then or equal -> ' + item.result, function () { 51 | if (item.ignoreTime !== undefined) validator.IgnoreTime = item.ignoreTime; 52 | expect(item.result).to.equal(validator.isAcceptable(item.input)); 53 | }); 54 | })(params[op]); 55 | } 56 | 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /test/customValidators/ICOValidator.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by rsamec on 9.7.2014. 3 | */ 4 | /// 5 | /// 6 | /// 7 | 8 | var expect = require('expect.js'); 9 | import icoValidator = require('../../src/customValidators/ICOValidator'); 10 | 11 | describe('ICOValidator', function () { 12 | 13 | var params = [ 14 | { input: "70577200", result: true}, 15 | { input: "3457890", result: false }, 16 | { input: "7057720", result: false }, 17 | { input: "45244782", result: true }, 18 | { input: "25578898", result: true }, 19 | { input: "61490041", result: true }, 20 | { input: "11111111", result: false }, 21 | { input: "12312312", result: true }, 22 | { input: "", result: false }, 23 | { input: undefined, result: false }, 24 | { input: "{}", result: false }, 25 | { input: "fasdfa", result: false } 26 | ]; 27 | var validator = new icoValidator(); 28 | 29 | 30 | for (var op in params) { 31 | (function (item) { 32 | it('should check ico number ' + item.input + ' -> ' + item.result, function () { 33 | expect(item.result).to.equal(validator.isAcceptable(item.input)); 34 | }); 35 | })(params[op]); 36 | } 37 | }); 38 | -------------------------------------------------------------------------------- /test/customValidators/ParamValidator.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by rsamec on 9.7.2014. 3 | */ 4 | /// 5 | /// 6 | /// 7 | /// 8 | 9 | 10 | var expect = require('expect.js'); 11 | import paramValidator = require('../../src/customValidators/ParamValidator'); 12 | var Q = require('q'); 13 | 14 | describe('ParamValidator', function () { 15 | 16 | var optionsFce = function (paramId:string) { 17 | var deferral = Q.defer(); 18 | setTimeout(function () { 19 | var result:Array = []; 20 | if (paramId == "jobs") { 21 | result = [ 22 | { "value": 1, "text": "manager" }, 23 | { "value": 2, "text": "programmer" }, 24 | { "value": 3, "text": "shop assistant" }, 25 | { "value": 4, "text": "unemployed" }, 26 | { "value": 5, "text": "machinery" }, 27 | { "value": 6, "text": "agriculture" }, 28 | { "value": 7, "text": "academic" }, 29 | { "value": 8, "text": "goverment" } 30 | ]; 31 | } 32 | if (paramId == "countries") { 33 | result = [ 34 | { "value": "CZE", "text": "Czech Republic" }, 35 | { "value": "Germany", "text": "Germany" }, 36 | { "value": "France", "text": "France" }, 37 | ]; 38 | } 39 | 40 | 41 | deferral.resolve(result); 42 | }, 1000); 43 | return deferral.promise; 44 | }; 45 | 46 | 47 | //when 48 | var validator = new paramValidator(); 49 | validator.Options = optionsFce; 50 | validator.ParamId = "jobs"; 51 | 52 | //excercise 53 | var promiseResult = validator.isAcceptable("programmer"); 54 | 55 | it('value from list should return true', function (done) { 56 | 57 | promiseResult.then(function(result) { 58 | 59 | //verify 60 | expect(result).to.equal(true); 61 | 62 | done(); 63 | 64 | }).done(null, done); 65 | }); 66 | 67 | //excercise 68 | var promiseResult2 = validator.isAcceptable("non existing item"); 69 | 70 | it('value out of list should return false', function (done) { 71 | 72 | promiseResult2.then(function(result) { 73 | 74 | //verify 75 | expect(result).to.equal(false); 76 | 77 | done(); 78 | 79 | }).done(null, done); 80 | }); 81 | 82 | }); 83 | -------------------------------------------------------------------------------- /test/customValidators/RCValidator.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by rsamec on 9.7.2014. 3 | */ 4 | /// 5 | /// 6 | /// 7 | 8 | 9 | var expect = require('expect.js'); 10 | import rcValidator = require('../../src/customValidators/RCValidator'); 11 | 12 | describe('RCValidator', function () { 13 | 14 | var validParams = ["062413/2256", 15 | "042111/2956", 16 | "043111/2957", 17 | "043211/2956", 18 | "045111/4411", 19 | "045211/4410", 20 | "045311/4409", 21 | "047111/3709", 22 | "047130/3712", 23 | "047131/3711", 24 | "047211/3411", 25 | "047311/3311", 26 | "047330/3314", 27 | "047331/3313", 28 | "047430/3313", 29 | "047831/3011", 30 | "440229/234", 31 | "441129/342", 32 | "800122/8763", 33 | "048111/3413", 34 | "048130/1810", 35 | "048231/1610", 36 | "071111/145", 37 | "071111/1610", 38 | "073211/1611", 39 | "076211/1614", 40 | "077111/1616", 41 | "078111/1716", 42 | "083111/2216", 43 | "430228/134", 44 | "431122/155", 45 | "431128/231", 46 | "431129/178", 47 | "431222/141", 48 | "431231/172", 49 | "436122/931", 50 | "436222/165", 51 | "010101/222"]; 52 | 53 | var validator = new rcValidator(); 54 | 55 | for (var op in validParams) { 56 | (function (item) { 57 | it('should check rc number ' + item + ' -> OK', function () { 58 | expect(validator.isAcceptable(item)).to.equal(true); 59 | }); 60 | })(validParams[op]); 61 | } 62 | 63 | var unvalidParams = [ 64 | "062413/3333", 65 | "038111/3029", 66 | "038211/3028", 67 | "038311/2928", 68 | "042111/321", 69 | "043111/929", 70 | "043211/149", 71 | "043311/3428", 72 | "044111/3728", 73 | "046311/3728", 74 | "047132/3028", 75 | "047230/3029", 76 | "047332/3026", 77 | "047431/3026", 78 | "048131/2326", 79 | "048311/2025", 80 | "983211/231", 81 | "983211/2125", 82 | "983231/1720", 83 | "073111/192", 84 | "073211/182", 85 | "073911/182", 86 | "076311/162", 87 | "077111/122", 88 | "078111/122", 89 | "083111/789", 90 | "093111/972", 91 | "430229/231", 92 | "431122/1321", 93 | "432122/1421", 94 | "432122/8763", 95 | "437122/212", 96 | "438122/872", 97 | "438131/1121", 98 | "438231/1120", 99 | "800122/768", 100 | "802222/817", 101 | "802222/1119", 102 | "982111/1619", 103 | "982111/1618"]; 104 | 105 | for (var op in unvalidParams) { 106 | (function (item) { 107 | it('should check rc number ' + item + ' -> unvalid', function () { 108 | expect(validator.isAcceptable(item)).to.equal(false); 109 | }); 110 | })(unvalidParams[op]); 111 | } 112 | }); 113 | -------------------------------------------------------------------------------- /test/customValidators/customValidators.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | 6 | var Validation = require('../../src/validation/Validation.js'); 7 | var Validators = require('../../src/validation/BasicValidators.js'); 8 | 9 | var expect = require('expect.js'); 10 | var _:UnderscoreStatic = require('underscore'); 11 | import Q = require('q'); 12 | 13 | var moment = require('moment'); 14 | import icoValidator = require('../../src/customValidators/ICOValidator'); 15 | import dateCompareValidator = require('../../src/customValidators/DateCompareValidator'); 16 | 17 | /** 18 | * @name Custom async property validator example 19 | * @description 20 | * Return true for valid BranchOfBusiness, otherwise return false. 21 | * 22 | * To create a async custom validator you have to implement IAsyncPropertyValidator interface. 23 | * Async custom validator must have property isAsync set to true; 24 | */ 25 | class BranchOfBusinessValidator { 26 | 27 | /** 28 | * It checks the list of all branches of business. Return true if passed branch of business exists. 29 | * @param s {string} value to check 30 | * @returns {boolean} return true for valid value, otherwise false 31 | */ 32 | isAcceptable(s:string):Q.Promise { 33 | var deferred = Q.defer(); 34 | 35 | setTimeout(function () { 36 | var items = 37 | [ 38 | { "value": 1, "text": "machinery" }, 39 | { "value": 2, "text": "agriculture" }, 40 | { "value": 3, "text": "academic" }, 41 | { "value": 4, "text": "goverment" } 42 | ]; 43 | 44 | var hasSome = _.some(items, function (item) { 45 | return item.text == s; 46 | }); 47 | 48 | if (hasSome) { 49 | deferred.resolve(true); 50 | } 51 | else { 52 | deferred.resolve(false); 53 | } 54 | }, 1000); 55 | 56 | return deferred.promise; 57 | } 58 | 59 | isAsync = true; 60 | tagName = "branchOfBusiness"; 61 | } 62 | 63 | interface ICompany { 64 | Name:string; 65 | ICO:string; 66 | BranchOfBusiness:string; 67 | } 68 | 69 | /** 70 | * @name Custom async property validator example 71 | * @description 72 | * Return true for valid BranchOfBusiness, otherwise return false. 73 | * 74 | * To create a async custom validator you have to implement IAsyncPropertyValidator interface. 75 | * Async custom validator must have property isAsync set to true; 76 | */ 77 | class DateCompareExtValidator extends dateCompareValidator { 78 | public isAcceptable(s:any) { 79 | //if date to compare is not specified - defaults to compare against now 80 | if (!_.isDate(s)) return false; 81 | 82 | var then = moment(s); 83 | 84 | if (this.CompareTo == undefined) this.CompareTo = new Date(); 85 | var now = moment(this.CompareTo); 86 | 87 | if (this.CompareTo2 == undefined) this.CompareTo2 = new Date(); 88 | var now2 = moment(this.CompareTo2); 89 | var isValid = this.isValid(now, then, this.CompareOperator) && this.isValid(now2, then, this.CompareOperator2); 90 | 91 | return isValid; 92 | } 93 | 94 | private isValid(now:any, then:any, compareOperator:Validation.CompareOperator) { 95 | var isValid = false; 96 | var diffs:number = then.diff(now); 97 | if (this.IgnoreTime) diffs = moment.duration(diffs).days(); 98 | 99 | if (diffs < 0) { 100 | isValid = compareOperator == Validation.CompareOperator.LessThan 101 | || compareOperator == Validation.CompareOperator.LessThanEqual 102 | || compareOperator == Validation.CompareOperator.NotEqual; 103 | } 104 | else if (diffs > 0) { 105 | isValid = compareOperator == Validation.CompareOperator.GreaterThan 106 | || compareOperator == Validation.CompareOperator.GreaterThanEqual 107 | || compareOperator == Validation.CompareOperator.NotEqual; 108 | } 109 | else { 110 | isValid = compareOperator == Validation.CompareOperator.LessThanEqual 111 | || compareOperator == Validation.CompareOperator.Equal 112 | || compareOperator == Validation.CompareOperator.GreaterThanEqual; 113 | } 114 | return isValid; 115 | } 116 | 117 | tagName = "dataCompareExt"; 118 | 119 | /** 120 | * Set the time of compare between passed date and CompareTo date. 121 | */ 122 | public CompareOperator2:Validation.CompareOperator; 123 | 124 | /** 125 | * The datetime against the compare is done. 126 | * If CompareTo is not set, then comparison is done against actual datetime. 127 | */ 128 | public CompareTo2:Date; 129 | } 130 | 131 | interface IDuration { 132 | From:Date; 133 | To:Date; 134 | } 135 | 136 | describe('custom validators', function () { 137 | 138 | describe('create new custom validator', function () { 139 | 140 | var required = new Validators.RequiredValidator(); 141 | var ico = new icoValidator(); 142 | var branchOfBusiness = new BranchOfBusinessValidator(); 143 | 144 | var companyValidator = new Validation.AbstractValidator(); 145 | companyValidator.RuleFor("ICO", required); 146 | companyValidator.RuleFor("ICO", ico); 147 | 148 | companyValidator.RuleFor("BranchOfBusiness", required); 149 | companyValidator.RuleFor("BranchOfBusiness", branchOfBusiness); 150 | 151 | 152 | beforeEach(function () { 153 | //setup 154 | this.Data = {}; 155 | this.Validator = companyValidator.CreateRule("Company"); 156 | 157 | }); 158 | 159 | it('fill correct data - no errors', function (done) { 160 | 161 | //when 162 | this.Data.ICO = "12312312"; 163 | this.Data.BranchOfBusiness = "machinery"; 164 | 165 | //excercise 166 | var result = this.Validator.Validate(this.Data); 167 | var promiseResult = this.Validator.ValidateAsync(this.Data); 168 | 169 | promiseResult.then(function (response) { 170 | 171 | //verify 172 | expect(response.HasErrors).to.equal(false); 173 | 174 | done(); 175 | 176 | }).done(null, done); 177 | }); 178 | 179 | it('fill incorrect data - some errors', function (done) { 180 | 181 | //when 182 | this.Data.ICO = "11111111"; 183 | this.Data.BranchOfBusiness = "unknown"; 184 | 185 | //excercise 186 | var result = this.Validator.Validate(this.Data); 187 | var promiseResult = this.Validator.ValidateAsync(this.Data); 188 | 189 | promiseResult.then(function (response) { 190 | 191 | //verify 192 | expect(response.HasErrors).to.equal(true); 193 | expect(response.ErrorCount).to.equal(2); 194 | 195 | done(); 196 | 197 | }).done(null, done); 198 | }); 199 | }); 200 | 201 | 202 | describe('extend existing validator with custom functionality', function () { 203 | 204 | var lowerThanOneYearAndGreaterThanToday = new DateCompareExtValidator(); 205 | lowerThanOneYearAndGreaterThanToday.CompareOperator = Validation.CompareOperator.LessThan; 206 | lowerThanOneYearAndGreaterThanToday.CompareTo = moment(new Date()).add({year: 1}).toDate(); 207 | lowerThanOneYearAndGreaterThanToday.CompareOperator2 = Validation.CompareOperator.GreaterThanEqual; 208 | lowerThanOneYearAndGreaterThanToday.CompareTo2 = new Date(); 209 | 210 | 211 | var durationValidator = new Validation.AbstractValidator(); 212 | durationValidator.RuleFor("From", lowerThanOneYearAndGreaterThanToday); 213 | durationValidator.RuleFor("To", lowerThanOneYearAndGreaterThanToday); 214 | 215 | 216 | beforeEach(function () { 217 | //setup 218 | this.Data = {}; 219 | this.Validator = durationValidator.CreateRule("Duration"); 220 | 221 | }); 222 | 223 | it('fill correct data - no errors', function () { 224 | 225 | //when 226 | this.Data.From = moment(new Date()).add({days: 5}).toDate(); 227 | this.Data.To = moment(new Date()).add({days: 360}).toDate(); 228 | 229 | //excercise 230 | var result = this.Validator.Validate(this.Data); 231 | 232 | //verify 233 | expect(result.HasErrors).to.equal(false); 234 | 235 | }); 236 | 237 | it('fill incorrect data - some errors', function () { 238 | 239 | //when 240 | this.Data.From = moment(new Date()).add({days: -1}).toDate(); 241 | this.Data.To = moment(new Date()).add({days: 370}).toDate(); 242 | 243 | //excercise 244 | var result = this.Validator.Validate(this.Data); 245 | 246 | //verify 247 | expect(result.HasErrors).to.equal(true); 248 | expect(result.ErrorCount).to.equal(2); 249 | 250 | }); 251 | }); 252 | }); -------------------------------------------------------------------------------- /test/validation/basicValidators.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | 6 | //require["config"]( 7 | // { baseUrl: '../../src/validation' } 8 | //); 9 | 10 | 11 | import Validation = require('../../src/validation/Validation'); 12 | import Validators = require('../../src/validation/BasicValidators'); 13 | 14 | var expect = require('expect.js'); 15 | var _:UnderscoreStatic = require('underscore'); 16 | import Q = require('q'); 17 | 18 | describe('basic validators', function () { 19 | 20 | describe('Range validator', function () { 21 | var rangeValidator = new Validators.RemoteValidator(); 22 | it('should return false when value is not in range',function(){ 23 | var range = [5,11] 24 | var rangeValidator = new Validators.RangeValidator(range); 25 | expect(rangeValidator.isAcceptable(4)).to.equal(false); 26 | expect(rangeValidator.isAcceptable(12)).to.equal(false); 27 | } 28 | ) 29 | it('should return true when value is in range',function(){ 30 | var range = [5,11] 31 | var rangeValidator = new Validators.RangeValidator(range); 32 | expect(rangeValidator.isAcceptable(5)).to.equal(true); 33 | expect(rangeValidator.isAcceptable(7)).to.equal(true); 34 | expect(rangeValidator.isAcceptable(11)).to.equal(true); 35 | } 36 | ) 37 | }); 38 | 39 | describe('remote validator', function () { 40 | var remote = new Validators.RemoteValidator(); 41 | remote.Options = { 42 | url:"http://api.automeme.net/text.json" 43 | } 44 | 45 | 46 | it('non-existing country code should return false', function (done) { 47 | 48 | var promiseResult =remote.isAcceptable('abc@gmail.com'); 49 | 50 | promiseResult.then(function (response) { 51 | expect(response).to.equal(false); 52 | 53 | done(); 54 | }).done(null, done); 55 | }); 56 | 57 | // it('existing country code return true', function (done) { 58 | // 59 | // var promiseResult =remote.isAcceptable('abc@gmail.com') 60 | // 61 | // promiseResult.then(function (response) { 62 | // expect(response).to.equal(true); 63 | // 64 | // done(); 65 | // }).done(null, done); 66 | // }); 67 | }); 68 | }); -------------------------------------------------------------------------------- /test/validation/rulesLists.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | 6 | var Validation = require('../../src/validation/Validation.js'); 7 | var Validators = require('../../src/validation/BasicValidators.js'); 8 | var expect = require('expect.js'); 9 | var _:UnderscoreStatic = require('underscore'); 10 | import Q = require('q'); 11 | 12 | interface IPerson{ 13 | Checked:boolean; 14 | FirstName:string; 15 | LastName:string; 16 | Contacts:Array; 17 | } 18 | interface IContact{ 19 | Email:string; 20 | Mobile:IPhone; 21 | FixedLine:IPhone; 22 | } 23 | interface IPhone{ 24 | CountryCode:string 25 | Number:string 26 | } 27 | 28 | describe('validation rules for lists', function () { 29 | 30 | var required = new Validators.RequiredValidator(); 31 | 32 | 33 | var createPersonValidator = function() { 34 | 35 | var maxLength = new Validators.MaxLengthValidator(15); 36 | 37 | var validator = new Validation.AbstractValidator(); 38 | validator.RuleFor("FirstName", required); 39 | validator.RuleFor("FirstName", maxLength); 40 | 41 | validator.RuleFor("LastName", required); 42 | validator.RuleFor("LastName", maxLength); 43 | 44 | var contactValidator = createContactValidator(); 45 | validator.ValidatorFor("Contacts",contactValidator,true); 46 | 47 | return validator; 48 | }; 49 | 50 | var createContactValidator = function() { 51 | 52 | var validator = new Validation.AbstractValidator(); 53 | validator.RuleFor("Email", required); 54 | validator.RuleFor("Email", new Validators.MaxLengthValidator(100)); 55 | validator.RuleFor("Email", new Validators.EmailValidator()); 56 | 57 | var phoneValidator = createPhoneValidator(); 58 | validator.ValidatorFor("Mobile", phoneValidator); 59 | validator.ValidatorFor("FixedLine", phoneValidator); 60 | 61 | return validator; 62 | }; 63 | 64 | var createPhoneValidator = function() { 65 | 66 | var validator = new Validation.AbstractValidator(); 67 | validator.RuleFor("CountryCode", required); 68 | validator.RuleFor("CountryCode", new Validators.MaxLengthValidator(3)); 69 | 70 | validator.RuleFor("Number", required); 71 | validator.RuleFor("Number", new Validators.MaxLengthValidator(9)); 72 | 73 | var optionsFce = function() { 74 | var deferral = Q.defer(); 75 | setTimeout(function () { 76 | deferral.resolve(["FRA","CZE","USA","GER"]); 77 | }, 500); 78 | return deferral.promise; 79 | }; 80 | 81 | var param = new Validators.ContainsValidator(); 82 | param.Options = optionsFce(); 83 | 84 | validator.RuleFor("CountryCode", param); 85 | 86 | return validator; 87 | }; 88 | 89 | var mainValidator = createPersonValidator(); 90 | 91 | 92 | //setup 93 | var getData = function () { 94 | return { 95 | Checked: true, 96 | FirstName: "John", 97 | LastName: "Smith", 98 | Contacts: [] 99 | } 100 | }; 101 | 102 | 103 | var getItemDataTemplate = function() { 104 | return { 105 | Email: 'mail@gmail.com', 106 | Mobile: { 107 | CountryCode: 'CZE', 108 | Number: '736483690' 109 | }, 110 | FixedLine: { 111 | CountryCode: 'USA', 112 | Number: '736483690' 113 | } 114 | } 115 | }; 116 | 117 | beforeEach(function(){ 118 | 119 | 120 | this.Data = getData(); 121 | this.MainValidator = mainValidator.CreateRule("Main"); 122 | 123 | }); 124 | it('fill undefined - some errors', function (done) { 125 | 126 | //when 127 | this.Data.Contacts = undefined; 128 | 129 | //excercise 130 | var result = this.MainValidator.Validate(this.Data); 131 | var promiseResult = this.MainValidator.ValidateAsync(this.Data); 132 | 133 | //verify 134 | promiseResult.then(function (response) { 135 | 136 | //verify 137 | expect(response.HasErrors).to.equal(false); 138 | 139 | done(); 140 | 141 | }).done(null,done); 142 | }); 143 | it('fill correct data - no errors', function (done) { 144 | 145 | //when 146 | this.Data.Contacts.push(getItemDataTemplate()); 147 | this.Data.Contacts.push(getItemDataTemplate()); 148 | this.Data.Contacts.push(getItemDataTemplate()); 149 | 150 | //excercise 151 | var result = this.MainValidator.Validate(this.Data); 152 | var promiseResult = this.MainValidator.ValidateAsync(this.Data); 153 | 154 | //verify 155 | promiseResult.then(function (response) { 156 | 157 | //verify 158 | expect(response.HasErrors).to.equal(false); 159 | 160 | done(); 161 | 162 | }).done(null,done); 163 | }); 164 | 165 | 166 | it('fill incorrect data - some errors', function (done) { 167 | 168 | //when 169 | this.Data.Contacts.push(getItemDataTemplate()); 170 | this.Data.Contacts.push(getItemDataTemplate()); 171 | this.Data.Contacts.push(getItemDataTemplate()); 172 | 173 | //simulate error at second item in list 174 | this.Data.Contacts[1].Email = ""; 175 | 176 | //simulate async error at third item in list 177 | this.Data.Contacts[2].Mobile.CountryCode = "BLA"; 178 | 179 | //excercise 180 | var result = this.MainValidator.Validate(this.Data); 181 | var promiseResult = this.MainValidator.ValidateAsync(this.Data); 182 | 183 | //verify 184 | promiseResult.then(function (response) { 185 | 186 | //verify 187 | expect(response.HasErrors).to.equal(true); 188 | expect(response.Errors["Contacts"].HasErrors).to.equal(true); 189 | expect(response.Errors["Contacts"].Children[0].HasErrors).to.equal(false); 190 | expect(response.Errors["Contacts"].Children[1].HasErrors).to.equal(true); 191 | expect(response.Errors["Contacts"].Children[2].HasErrors).to.equal(true); 192 | 193 | done(); 194 | 195 | }).done(null,done); 196 | }); 197 | 198 | it('delete error item, leave correct item - no errors', function (done) { 199 | 200 | //when 201 | this.Data.Contacts.push(getItemDataTemplate()); 202 | this.Data.Contacts.push(getItemDataTemplate()); 203 | this.Data.Contacts.push(getItemDataTemplate()); 204 | 205 | //item list property error 206 | this.Data.Contacts[2].Email = ""; 207 | 208 | //item list async property error 209 | this.Data.Contacts[2].Mobile.CountryCode = "BLA"; 210 | 211 | //delete last error item 212 | this.Data.Contacts.splice(2,1) 213 | 214 | //excercise 215 | var result = this.MainValidator.Validate(this.Data); 216 | var promiseResult = this.MainValidator.ValidateAsync(this.Data); 217 | 218 | //verify 219 | promiseResult.then(function (response) { 220 | 221 | //verify 222 | expect(response.HasErrors).to.equal(false); 223 | expect(response.Errors["Contacts"].HasErrors).to.equal(false); 224 | expect(response.Errors["Contacts"].Children[0].HasErrors).to.equal(false); 225 | expect(response.Errors["Contacts"].Children[1].HasErrors).to.equal(false); 226 | 227 | done(); 228 | 229 | }).done(null,done); 230 | }); 231 | 232 | it('delete correct item, leave error item - some errors', function (done) { 233 | 234 | //when 235 | this.Data.Contacts.push(getItemDataTemplate()); 236 | this.Data.Contacts.push(getItemDataTemplate()); 237 | this.Data.Contacts.push(getItemDataTemplate()); 238 | 239 | //item list property error 240 | this.Data.Contacts[2].Email = ""; 241 | 242 | //item list async property error 243 | this.Data.Contacts[2].Mobile.CountryCode = "BLA"; 244 | 245 | //delete correct item 246 | this.Data.Contacts.splice(1,1); 247 | 248 | //excercise 249 | var result = this.MainValidator.Validate(this.Data); 250 | var promiseResult = this.MainValidator.ValidateAsync(this.Data); 251 | 252 | //verify 253 | promiseResult.then(function (response) { 254 | 255 | //verify 256 | expect(response.HasErrors).to.equal(true); 257 | expect(response.Errors["Contacts"].HasErrors).to.equal(true); 258 | expect(response.Errors["Contacts"].Children[0].HasErrors).to.equal(false); 259 | expect(response.Errors["Contacts"].Children[1].HasErrors).to.equal(true); 260 | 261 | done(); 262 | 263 | }).done(null,done); 264 | }); 265 | 266 | }); 267 | 268 | -------------------------------------------------------------------------------- /test/validation/rulesNested.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | 6 | var Validation = require('../../src/validation/Validation.js'); 7 | var Validators = require('../../src/validation/BasicValidators.js'); 8 | var Utils = require('../../src/validation/Utils.js'); 9 | var expect = require('expect.js'); 10 | var _:UnderscoreStatic = require('underscore'); 11 | import Q = require('q'); 12 | 13 | interface IData{ 14 | Person1:IPerson 15 | Person2:IPerson 16 | } 17 | interface IPerson{ 18 | Checked:boolean; 19 | FirstName:string; 20 | LastName:string; 21 | Contact:IContact; 22 | } 23 | interface IContact{ 24 | Email:string; 25 | Mobile:IPhone; 26 | FixedLine:IPhone; 27 | } 28 | interface IPhone{ 29 | CountryCode:string 30 | Number:string 31 | } 32 | 33 | describe('nested validation rules', function () { 34 | 35 | var required = new Validators.RequiredValidator(); 36 | 37 | var createMainValidator = function(){ 38 | var validator = new Validation.AbstractValidator(); 39 | 40 | var personValidator = createPersonValidator(); 41 | validator.ValidatorFor("Person1",personValidator); 42 | validator.ValidatorFor("Person2",personValidator); 43 | 44 | return validator; 45 | }; 46 | 47 | var createPersonValidator = function() { 48 | 49 | var maxLength = new Validators.MaxLengthValidator(15); 50 | 51 | var validator = new Validation.AbstractValidator(); 52 | validator.RuleFor("FirstName", required); 53 | validator.RuleFor("FirstName", maxLength); 54 | 55 | validator.RuleFor("LastName", required); 56 | validator.RuleFor("LastName", maxLength); 57 | 58 | var contactValidator = createContactValidator(); 59 | validator.ValidatorFor("Contact",contactValidator); 60 | 61 | return validator; 62 | }; 63 | 64 | var createContactValidator = function() { 65 | 66 | var validator = new Validation.AbstractValidator(); 67 | validator.RuleFor("Email", required); 68 | validator.RuleFor("Email", new Validators.MaxLengthValidator(100)); 69 | validator.RuleFor("Email", new Validators.EmailValidator()); 70 | 71 | var phoneValidator = createPhoneValidator(); 72 | validator.ValidatorFor("Mobile", phoneValidator); 73 | validator.ValidatorFor("FixedLine", phoneValidator); 74 | 75 | return validator; 76 | }; 77 | 78 | var createPhoneValidator = function() { 79 | 80 | var validator = new Validation.AbstractValidator(); 81 | validator.RuleFor("CountryCode", required); 82 | validator.RuleFor("CountryCode", new Validators.MaxLengthValidator(3)); 83 | 84 | validator.RuleFor("Number", required); 85 | validator.RuleFor("Number", new Validators.MaxLengthValidator(9)); 86 | 87 | var optionsFce = function() { 88 | var deferral = Q.defer(); 89 | setTimeout(function () { 90 | deferral.resolve(["FRA","CZE","USA","GER"]); 91 | }, 500); 92 | return deferral.promise; 93 | }; 94 | 95 | var param = new Validators.ContainsValidator(); 96 | param.Options = optionsFce(); 97 | 98 | validator.RuleFor("CountryCode", param); 99 | 100 | return validator; 101 | }; 102 | 103 | var mainValidator = createMainValidator(); 104 | 105 | beforeEach(function(){ 106 | 107 | //setup 108 | var getData = function () { 109 | var contact = { 110 | Email:'mail@gmail.com', 111 | Mobile:{ 112 | CountryCode:'CZE', 113 | Number:'736483690' 114 | }, 115 | FixedLine:{ 116 | CountryCode:'USA', 117 | Number:'736483690' 118 | } 119 | }; 120 | return{ 121 | Person1: { 122 | Checked:true, 123 | FirstName: "John", 124 | LastName: "Smith", 125 | Contact: contact 126 | }, 127 | Person2: { 128 | Checked:true, 129 | FirstName: "Adam", 130 | LastName: "Novak", 131 | Contact: contact 132 | } 133 | } 134 | }; 135 | 136 | this.Data = getData(); 137 | this.MainValidator = mainValidator.CreateRule("Main"); 138 | 139 | }); 140 | 141 | it('fill correct data - no errors', function (done) { 142 | 143 | //when 144 | 145 | //excercise 146 | var result = this.MainValidator.Validate(this.Data); 147 | var promiseResult = this.MainValidator.ValidateAsync(this.Data); 148 | 149 | //verify 150 | promiseResult.then(function (response) { 151 | 152 | //verify 153 | expect(response.HasErrors).to.equal(false); 154 | 155 | done(); 156 | 157 | }).done(null,done); 158 | }); 159 | 160 | 161 | it('fill incorrect data - some errors', function (done) { 162 | 163 | //when 164 | //nested property error 165 | this.Data.Person1.Contact.Email = ""; 166 | 167 | //async nested property error 168 | this.Data.Person1.Contact.Mobile.CountryCode = "BLA"; 169 | 170 | //excercise 171 | var result = this.MainValidator.Validate(this.Data); 172 | var promiseResult = this.MainValidator.ValidateAsync(this.Data); 173 | 174 | //verify 175 | promiseResult.then(function (response) { 176 | 177 | //verify 178 | expect(response.HasErrors).to.equal(true); 179 | 180 | done(); 181 | 182 | }).done(null,done); 183 | }); 184 | 185 | 186 | describe("fill incorrect data - rule optional", function() { 187 | 188 | beforeEach(function () { 189 | //setup 190 | 191 | //set rule optional when checked !=true; 192 | var optional = function () { 193 | return !this.Checked; 194 | }.bind(this.Data); 195 | 196 | 197 | this.MainValidator.SetOptional(optional); 198 | 199 | //nested error 200 | this.Data.Person1.Contact.Email = ""; 201 | 202 | //async nested error 203 | this.Data.Person1.Contact.Mobile.CountryCode = "BLA"; 204 | 205 | }); 206 | 207 | it('is optional -> no errors', function (done) { 208 | 209 | //when 210 | this.Data.Person1.Checked = false; 211 | this.Data.Person2.Checked = false; 212 | 213 | //excercise 214 | var result = this.MainValidator.Validate(this.Data); 215 | var promiseResult = this.MainValidator.ValidateAsync(this.Data); 216 | 217 | //verify 218 | promiseResult.then(function (response) { 219 | 220 | //verify 221 | expect(response.HasErrors).to.equal(false); 222 | 223 | done(); 224 | 225 | }).done(null, done); 226 | }); 227 | 228 | 229 | it('is not optional - some errors', function (done) { 230 | 231 | //when 232 | this.Data.Person1.Checked = true; 233 | this.Data.Person2.Checked = true; 234 | 235 | //excercise 236 | var result = this.MainValidator.Validate(this.Data); 237 | var promiseResult = this.MainValidator.ValidateAsync(this.Data); 238 | 239 | //verify 240 | promiseResult.then(function (response) { 241 | 242 | //verify 243 | expect(response.HasErrors).to.equal(false); 244 | 245 | done(); 246 | 247 | }).done(null, done); 248 | }); 249 | }); 250 | 251 | describe("Dot syntax", function () { 252 | it('fill correct data - no errors', function (done) { 253 | 254 | //when 255 | 256 | //excercise 257 | var result = this.MainValidator.Validate(this.Data); 258 | var promiseResult = this.MainValidator.ValidateAsync(this.Data); 259 | 260 | 261 | 262 | var compDotObject = Utils.CompositeDotObject.Transform(this.MainValidator); 263 | 264 | //verify 265 | promiseResult.then(function (response) { 266 | 267 | var dotResult = Utils.CompositeDotObject.Transform(result); 268 | 269 | //verify 270 | expect(response.HasErrors).to.equal(false); 271 | 272 | expect(dotResult.Main.Person1.Contact.Email.HasErrors).to.equal(false); 273 | expect(dotResult.Main.Person1.Contact.Mobile.CountryCode.HasErrors).to.equal(false); 274 | 275 | 276 | expect(compDotObject.Main.Person1.Contact.Rules["Email"].HasErrors).to.equal(false); 277 | expect(compDotObject.Main.Person1.Contact.Mobile.Rules["CountryCode"].HasErrors).to.equal(false); 278 | 279 | done(); 280 | 281 | }).done(null, done); 282 | }); 283 | 284 | it('fill incorrect data - some errors', function (done) { 285 | 286 | //when 287 | //nested property error 288 | this.Data.Person1.Contact.Email = ""; 289 | 290 | //async nested property error 291 | this.Data.Person1.Contact.Mobile.CountryCode = "BLA"; 292 | 293 | 294 | //excercise 295 | var result = this.MainValidator.Validate(this.Data); 296 | var promiseResult = this.MainValidator.ValidateAsync(this.Data); 297 | 298 | var compDotObject = Utils.CompositeDotObject.Transform(this.MainValidator); 299 | 300 | //verify 301 | promiseResult.then(function (response) { 302 | var dotResult = Utils.CompositeDotObject.Transform(result); 303 | 304 | //verify 305 | expect(response.HasErrors).to.equal(true); 306 | 307 | 308 | expect(dotResult.Main.Person1.Contact.Email.HasErrors).to.equal(true); 309 | expect(dotResult.Main.Person1.Contact.Mobile.CountryCode.HasErrors).to.equal(true); 310 | 311 | 312 | expect(compDotObject.Main.Person1.Contact.Rules["Email"].HasErrors).to.equal(true); 313 | expect(compDotObject.Main.Person1.Contact.Mobile.Rules["CountryCode"].HasErrors).to.equal(true); 314 | 315 | done(); 316 | 317 | }).done(null, done); 318 | 319 | }); 320 | }); 321 | }); 322 | 323 | -------------------------------------------------------------------------------- /test/validation/rulesShared.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | 6 | var Validation = require('../../src/validation/Validation.js'); 7 | var Validators = require('../../src/validation/BasicValidators.js'); 8 | var expect = require('expect.js'); 9 | var _:UnderscoreStatic = require('underscore'); 10 | import Q = require('q'); 11 | 12 | interface IPerson{ 13 | Checked:boolean; 14 | FirstName:string; 15 | LastName:string; 16 | Contacts:Array; 17 | } 18 | interface IContact{ 19 | Email:string; 20 | Mobile:IPhone; 21 | FixedLine:IPhone; 22 | } 23 | interface IPhone{ 24 | CountryCode:string 25 | Number:string 26 | } 27 | 28 | describe('validation rules for shared validations', function () { 29 | 30 | var required = new Validators.RequiredValidator(); 31 | 32 | var createPersonValidator = function() { 33 | 34 | var maxLength = new Validators.MaxLengthValidator(15); 35 | 36 | var validator = new Validation.AbstractValidator(); 37 | validator.RuleFor("FirstName", required); 38 | validator.RuleFor("FirstName", maxLength); 39 | 40 | validator.RuleFor("LastName", required); 41 | validator.RuleFor("LastName", maxLength); 42 | 43 | var contactValidator = createContactValidator(); 44 | validator.ValidatorFor("Contacts",contactValidator,true); 45 | 46 | var uniqContact = function(args){ 47 | args.HasError = false; 48 | args.ErrorMessage = ""; 49 | var fullNames =_.map(this.Contacts,function(contact:any){return contact.Email}); 50 | var itemcounts = _.countBy(fullNames, function (n) { return n; }); 51 | var dupes = _.reduce(itemcounts, function (memo:Array, item, idx) { 52 | if (item > 1) 53 | memo.push(idx); 54 | return memo; 55 | }, []); 56 | 57 | if (dupes.length != 0) { 58 | args.HasError = true; 59 | args.ErrorMessage = _.reduce(dupes, function (memo:string, fullName:string) { 60 | return memo + fullName + " "; 61 | },"Each contact must be unique. Not unique values: "); 62 | 63 | return; 64 | } 65 | }; 66 | 67 | validator.Validation({Name:"UniqueContact",ValidationFce:uniqContact}); 68 | 69 | return validator; 70 | }; 71 | 72 | var createContactValidator = function() { 73 | 74 | var validator = new Validation.AbstractValidator(); 75 | validator.RuleFor("Email", required); 76 | validator.RuleFor("Email", new Validators.MaxLengthValidator(100)); 77 | validator.RuleFor("Email", new Validators.EmailValidator()); 78 | 79 | var phoneValidator = createPhoneValidator(); 80 | validator.ValidatorFor("Mobile", phoneValidator); 81 | validator.ValidatorFor("FixedLine", phoneValidator); 82 | 83 | var uniqPhoneNumber = function(args){ 84 | args.HasError = false; 85 | args.ErrorMessage = ""; 86 | if (this.Mobile.Number == this.FixedLine.Number) { 87 | args.HasError = true; 88 | args.ErrorMessage = "Each phone number must be unique."; 89 | return; 90 | } 91 | }; 92 | 93 | validator.Validation({Name:"UniqPhoneNumber",ValidationFce:uniqPhoneNumber}); 94 | 95 | //validator.GroupFor("MobileContact",namedUniqPhoneNumber); 96 | 97 | 98 | 99 | return validator; 100 | }; 101 | 102 | var createPhoneValidator = function() { 103 | 104 | var validator = new Validation.AbstractValidator(); 105 | validator.RuleFor("CountryCode", required); 106 | validator.RuleFor("CountryCode", new Validators.MaxLengthValidator(3)); 107 | 108 | validator.RuleFor("Number", required); 109 | validator.RuleFor("Number", new Validators.MaxLengthValidator(9)); 110 | 111 | var optionsFce = function() { 112 | var deferral = Q.defer(); 113 | setTimeout(function () { 114 | deferral.resolve(["FRA","CZE","USA","GER"]); 115 | }, 500); 116 | return deferral.promise; 117 | }; 118 | 119 | var param = new Validators.ContainsValidator(); 120 | param.Options = optionsFce(); 121 | 122 | validator.RuleFor("CountryCode", param); 123 | 124 | return validator; 125 | }; 126 | 127 | var mainValidator = createPersonValidator(); 128 | 129 | 130 | //setup 131 | var getData = function () { 132 | return { 133 | Checked: true, 134 | FirstName: "", 135 | LastName: "", 136 | Contacts: [] 137 | } 138 | }; 139 | 140 | 141 | var getItemDataTemplate = function(mail?:string) { 142 | if (mail == undefined) mail = 'mail@gmail.com'; 143 | return { 144 | Email: mail, 145 | Mobile: { 146 | CountryCode: 'CZE', 147 | Number: '736483690' 148 | }, 149 | FixedLine: { 150 | CountryCode: 'USA', 151 | Number: '736483691' 152 | } 153 | } 154 | }; 155 | 156 | describe("call specific validator",function() { 157 | beforeEach(function () { 158 | 159 | 160 | this.Data = getData(); 161 | this.MainValidator = mainValidator.CreateRule("Main"); 162 | 163 | }); 164 | 165 | 166 | it('no errors', function () { 167 | 168 | //when 169 | this.Data.Contacts.push(getItemDataTemplate('mail1@gmail.com')); 170 | this.Data.Contacts.push(getItemDataTemplate('mail2@gmail.com')); 171 | this.Data.Contacts.push(getItemDataTemplate('mail3@gmail.com')); 172 | 173 | //find the validator by name 174 | var validator = this.MainValidator.Validators["UniqueContact"]; 175 | //excercise 176 | var result = validator.Validate(this.Data); 177 | 178 | //verify by return value 179 | expect(result.HasError).to.equal(false); 180 | 181 | //verify by validator properties 182 | expect(validator.HasErrors).to.equal(false); 183 | 184 | }); 185 | it('some errors', function () { 186 | 187 | //when 188 | this.Data.Contacts.push(getItemDataTemplate('mail1@gmail.com')); 189 | this.Data.Contacts.push(getItemDataTemplate('mail2@gmail.com')); 190 | this.Data.Contacts.push(getItemDataTemplate('mail2@gmail.com')); 191 | 192 | //find the validator by name 193 | var validator = this.MainValidator.Validators["UniqueContact"]; 194 | //excercise 195 | var result = validator.Validate(this.Data); 196 | 197 | //verify by return value 198 | expect(result.HasError).to.equal(true); 199 | 200 | //verify by validator properties 201 | expect(validator.HasErrors).to.equal(true); 202 | 203 | }); 204 | }) 205 | }); 206 | 207 | -------------------------------------------------------------------------------- /tsd.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "v4", 3 | "repo": "borisyankov/DefinitelyTyped", 4 | "ref": "master", 5 | "path": "typings", 6 | "bundle": "typings/tsd.d.ts", 7 | "installed": { 8 | "expect.js/expect.js.d.ts": { 9 | "commit": "d3f6bcccdeb052003c96a7c4adbb002828c2a8ae" 10 | }, 11 | "mocha/mocha.d.ts": { 12 | "commit": "d3f6bcccdeb052003c96a7c4adbb002828c2a8ae" 13 | }, 14 | "node/node.d.ts": { 15 | "commit": "d3f6bcccdeb052003c96a7c4adbb002828c2a8ae" 16 | }, 17 | "underscore/underscore.d.ts": { 18 | "commit": "6f3946e020abbdefe93341d9e95a0054adb7d091" 19 | }, 20 | "q/Q.d.ts": { 21 | "commit": "17f8e5aca2392978baf15a142410e3eedcc335cc" 22 | }, 23 | "moment/moment.d.ts": { 24 | "commit": "5c000a23b5d00efe35ab0fcc3b568f71f3aa23c8" 25 | }, 26 | "jquery/jquery.d.ts": { 27 | "commit": "17f8e5aca2392978baf15a142410e3eedcc335cc" 28 | }, 29 | "underscore.string/underscore.string.d.ts": { 30 | "commit": "5c000a23b5d00efe35ab0fcc3b568f71f3aa23c8" 31 | }, 32 | "hashmap/hashmap.d.ts": { 33 | "commit": "3505d15d991ddaa4f5efa13edcd8543bae1e46de" 34 | }, 35 | "sinon/sinon.d.ts": { 36 | "commit": "bba33bcb0b363ae41db8517a09d1a4d07828616b" 37 | } 38 | } 39 | } 40 | --------------------------------------------------------------------------------