diff --git a/README.md b/README.md index 83d5086..178c82f 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,8 @@ rekord-validation adds rules, expressions, transforms, and custom validation fun The easiest way to install rekord-validation is through bower via `bower install rekord-validation`. -- rekord-validation.js is `45KB` (`7.6KB` gzipped) -- rekord-validation.min.js is `18KB` (`5.3KB` gzipped) +- rekord-validation.js is `48KB` (`8.32KB` gzipped) +- rekord-validation.min.js is `19KB` (`5.69KB` gzipped) ### Example @@ -34,7 +34,7 @@ var Task = Rekord({ required: true // the rules must pass to $save model instances }, methods: { - $custom: function(value, getAlias, specifiedMessage) { + $custom: function(value, getAlias, specifiedMessage, chain) { if ( value.length > 140 ) { return 'Details can be no larger than 140 characters.' } diff --git a/bower.json b/bower.json index ddcffe6..350f7bd 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "rekord-validation", - "version": "1.4.4", + "version": "1.5.0", "homepage": "https://github.com/Rekord/rekord-validation", "authors": [ "Philip Diffenderfer " @@ -23,6 +23,9 @@ "tests" ], "dependencies": { - "rekord": "~1.4.0" + "rekord": "~1.5.0" + }, + "devDependencies": { + "qunit": "^1.23.1" } } diff --git a/build/rekord-validation.js b/build/rekord-validation.js index d89263a..6e65f83 100644 --- a/build/rekord-validation.js +++ b/build/rekord-validation.js @@ -1,4 +1,4 @@ -/* rekord-validation 1.4.4 - Advanced validation rules for rekord by Philip Diffenderfer */ +/* rekord-validation 1.5.0 - Advanced validation rules for rekord by Philip Diffenderfer */ // UMD (Universal Module Definition) (function (root, factory) { @@ -29,6 +29,7 @@ var Promise = Rekord.Promise; var Collection = Rekord.Collection; var ModelCollection = Rekord.ModelCollection; + var Class = Rekord.Class; var isEmpty = Rekord.isEmpty; var isString = Rekord.isString; @@ -55,8 +56,87 @@ var parseDate = Rekord.parseDate; - var addMethod = Rekord.addMethod; - var replaceMethod = Rekord.replaceMethod; + +function ValidationChain(model, field, validations, onEnd) +{ + this.model = model; + this.field = field; + this.validations = validations; + this.onEnd = onEnd; +} + +Class.create( ValidationChain, +{ + + reset: function(value) + { + this.value = value !== undefined ? value : this.model.$get( this.field ); + this.updated = false; + this.valid = true; + this.message = ''; + this.linkIndex = 0; + }, + + start: function(value) + { + this.reset( value ); + this.call(); + }, + + call: function() + { + this.validations[ this.linkIndex ]( this.value, this.model, this ); + }, + + update: function(newValue) + { + this.value = newValue; + this.updated = true; + + return this; + }, + + next: function() + { + var n = this.validations.length; + + this.linkIndex++; + + if (this.linkIndex === n) + { + this.onEnd( this ); + } + else if (this.linkIndex < n) + { + this.call(); + } + + return this; + }, + + stop: function() + { + var n = this.validations.length; + + if (this.linkIndex < n) + { + this.linkIndex = n - 1; + this.next(); + } + + return this; + }, + + invalid: function(message) + { + this.message = message; + this.valid = false; + this.stop(); + + return this; + } + +}); function tryParseFloat(x) { @@ -118,19 +198,16 @@ function ruleGenerator(ruleName, defaultMessage, isInvalid) var messageTemplate = determineMessage( ruleName, message ); - return function(value, model, setMessage) + return function(value, model, chain) { - function setValue( newValue ) + if ( isInvalid( value, model, chain ) ) { - value = newValue; + chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate ) ); } - - if ( isInvalid( value, model, setValue ) ) + else { - setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate ) ); + chain.next(); } - - return value; }; }; @@ -210,7 +287,7 @@ function generateMessage(field, alias, value, model, message, extra) return format( message, base ); } -Rekord.on( Rekord.Events.Plugins, function(model, db, options) +Rekord.addPlugin(function(model, db, options) { var validation = options.validation || Database.Defaults.validation; @@ -236,47 +313,56 @@ Rekord.on( Rekord.Events.Plugins, function(model, db, options) db.validations[ field ] = Validation.parseRules( rules[ field ], field, db, getAlias, messages[ field ] ); } - addMethod( model.prototype, '$validate', function() + Class.method( model, '$validate', function(callback, context) { - var $this = this; - this.$trigger( Model.Events.PreValidate, [this] ); this.$valid = true; this.$validations = {}; this.$validationMessages.length = 0; - for (var field in db.validations) + var chainEnds = 0; + var chains = []; + + var onChainEnd = function(chain) { - var chain = db.validations[ field ]; - var value = this.$get( field ); - var fieldValid = true; + var model = chain.model; - var setMessage = function(message) // jshint ignore:line + if (!chain.valid) { - // Only accept for the first valid message - if ( message && fieldValid ) - { - fieldValid = false; - - $this.$validations[ field ] = message; - $this.$validationMessages.push( message ); - $this.$valid = false; - } - }; + model.$validations[ chain.field ] = chain.message; + model.$validationMessages.push( chain.message ); + model.$valid = false; + } - for (var i = 0; i < chain.length && fieldValid && value !== Validation.Stop; i++) + if (++chainEnds === chains.length) { - value = chain[ i ]( value, this, setMessage ); + model.$trigger( model.$valid ? Model.Events.ValidatePass : Model.Events.ValidateFail, [model] ); + + if ( isFunction( callback ) ) + { + callback.call( context || model, model.$valid ); + } } + }; + + for (var field in db.validations) + { + var validations = db.validations[ field ]; + var chain = new ValidationChain( this, field, validations, onChainEnd ); + + chains.push( chain ); } - this.$trigger( this.$valid ? Model.Events.ValidatePass : Model.Events.ValidateFail, [this] ); + for (var i = 0; i < chains.length; i++) + { + chains[ i ].start(); + } return this.$valid; }); - replaceMethod( model.prototype, '$init', function($init) + Class.replace( model, '$init', function($init) { return function() { @@ -290,7 +376,7 @@ Rekord.on( Rekord.Events.Plugins, function(model, db, options) if ( required ) { - replaceMethod( model.prototype, '$save', function($save) + Class.replace( model, '$save', function($save) { return function() { @@ -301,12 +387,25 @@ Rekord.on( Rekord.Events.Plugins, function(model, db, options) return Promise.resolve( this ); } - if ( !this.$validate() ) + var promise = new Rekord.Promise(); + var modelInstance = this; + var args = arguments; + + this.$validate(function(valid) { - return Promise.resolve( this ); - } + if (!valid) + { + promise.reject( modelInstance ); + } + else + { + var saving = $save.apply( modelInstance, args ); + + saving.then( promise.resolve, promise.reject, promise.noline, promise.cancel, promise ); + } + }); - return $save.apply( this, arguments ); + return promise; }; }); } @@ -409,16 +508,18 @@ var Validation = customValidator: function(functionName, field, database, getAlias, message) { - return function(value, model, setMessage) + return function(value, model, chain) { - var result = model[ functionName ]( value, getAlias, message ); + var result = model[ functionName ]( value, getAlias, message, chain ); if ( isString( result ) ) { - setMessage( result ); + chain.invalid( result ); + } + else if ( result !== false ) + { + chain.next(); } - - return value; }; } }; @@ -575,17 +676,19 @@ Validation.Rules.accepted = function(field, params, database, getAlias, message) var messageTemplate = determineMessage( 'accepted', message ); var acceptable = Validation.Rules.accepted.acceptable; - return function(value, model, setMessage) + return function(value, model, chain) { var valueString = (value + '').toLowerCase(); var accepted = acceptable[ valueString ]; if ( !accepted ) { - setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate ) ); + chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate ) ); + } + else + { + chain.next(); } - - return value; }; }; @@ -677,14 +780,16 @@ function collectionRuleGenerator(ruleName, defaultMessage, isInvalid) $matchValue: matchValue }; - return function(value, model, setMessage) + return function(value, model, chain) { if ( isInvalid( value, model, matchField, matchValue, equality ) ) { - setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) ); + chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) ); + } + else + { + chain.next(); } - - return value; }; }; @@ -697,7 +802,7 @@ Validation.Rules.validate = function(field, params, database, getAlias, message) var messageOption = params || 'message'; var messageTemplate = determineMessage( 'validate', message ); - return function(value, model, setMessage) + return function(value, model, chain) { if ( isArray( value ) ) { @@ -718,19 +823,25 @@ Validation.Rules.validate = function(field, params, database, getAlias, message) switch (messageOption) { case 'models': - setMessage( invalid ); + chain.invalid( invalid ); break; case 'validations': - setMessage( invalid.pluck( '$validations', '$$key' ) ); + chain.invalid( invalid.pluck( '$validations', '$$key' ) ); break; default: // message - setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate ) ); + chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate ) ); break; } } + else + { + chain.next(); + } + } + else + { + chain.next(); } - - return value; }; }; @@ -771,11 +882,11 @@ dateRuleGenerator('before_on', // date ruleGenerator('date_like', '{$alias} must be a valid date.', - function isInvalid(value, model, setValue) { + function isInvalid(value, model, chain) { var parsed = parseDate( value ); var invalid = parsed === false; if ( !invalid ) { - setValue( parsed.getTime() ); + chain.update( parsed.getTime() ); } return invalid; } @@ -825,7 +936,7 @@ function dateRuleGenerator(ruleName, defaultMessage, isInvalid) $date: params }; - return function(value, model, setMessage) + return function(value, model, chain) { var parsed = parseDate( value ); @@ -837,11 +948,17 @@ function dateRuleGenerator(ruleName, defaultMessage, isInvalid) if ( isNumber( date ) && isInvalid( value, date ) ) { - setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) ); + chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) ); + } + else + { + chain.next(); } } - - return value; + else + { + chain.next(); + } }; }; @@ -913,14 +1030,16 @@ function fieldListRuleGenerator(ruleName, defaultMessage, isInvalid) }; var map = mapFromArray( matchValues, true ); - return function(value, model, setMessage) + return function(value, model, chain) { if ( isInvalid( value, model, matchField, matchValues, map ) ) { - setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) ); + chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) ); + } + else + { + chain.next(); } - - return value; }; }; @@ -930,7 +1049,7 @@ function fieldListRuleGenerator(ruleName, defaultMessage, isInvalid) // confirmed:X fieldsRuleGenerator('confirmed', '{$alias} must match {$fieldAliases}.', - function isInvalid(value, model, fields, setValue) { + function isInvalid(value, model, fields, chain) { var confirmed = true; for (var i = 0; i < fields.length; i++) @@ -948,7 +1067,7 @@ fieldsRuleGenerator('confirmed', // different:X fieldsRuleGenerator('different', '{$alias} must not match {$fieldAliases}.', - function isInvalid(value, model, fields, setValue) { + function isInvalid(value, model, fields, chain) { var different = false; for (var i = 0; i < fields.length; i++) @@ -966,7 +1085,7 @@ fieldsRuleGenerator('different', // if_valid:X fieldsRuleGenerator('if_valid', '', - function isInvalid(value, model, fields, setValue) { + function isInvalid(value, model, fields, chain) { var valid = true; for (var i = 0; i < fields.length && valid; i++) @@ -979,7 +1098,7 @@ fieldsRuleGenerator('if_valid', if ( !valid ) { - setValue( Validation.Stop ); + chain.stop(); } return false; @@ -990,7 +1109,7 @@ fieldsRuleGenerator('if_valid', // required_with:X,Y,... fieldsRuleGenerator('required_with', '{$alias} is required.', - function isInvalid(value, model, fields, setValue) { + function isInvalid(value, model, fields, chain) { var required = false; for (var i = 0; i < fields.length && !required; i++) @@ -1009,7 +1128,7 @@ fieldsRuleGenerator('required_with', // required_with_all:X,Y,... fieldsRuleGenerator('required_with_all', '{$alias} is required.', - function isInvalid(value, model, fields, setValue) { + function isInvalid(value, model, fields, chain) { var required = true; for (var i = 0; i < fields.length && required; i++) @@ -1028,7 +1147,7 @@ fieldsRuleGenerator('required_with_all', // required_without:X,Y,... fieldsRuleGenerator('required_without', '{$alias} is required.', - function isInvalid(value, model, fields, setValue) { + function isInvalid(value, model, fields, chain) { var required = false; for (var i = 0; i < fields.length && !required; i++) @@ -1047,7 +1166,7 @@ fieldsRuleGenerator('required_without', // required_without_all:X,Y,... fieldsRuleGenerator('required_without_all', '{$alias} is required.', - function isInvalid(value, model, fields, setValue) { + function isInvalid(value, model, fields, chain) { var required = true; for (var i = 0; i < fields.length && required; i++) @@ -1089,19 +1208,16 @@ function fieldsRuleGenerator(ruleName, defaultMessage, isInvalid) $fieldAliases: fieldAliases }; - return function(value, model, setMessage) + return function(value, model, chain) { - function setValue( newValue ) + if ( isInvalid( value, model, fields, chain ) ) { - value = newValue; + chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) ); } - - if ( isInvalid( value, model, fields, setValue ) ) + else { - setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) ); + chain.next(); } - - return value; }; }; @@ -1201,17 +1317,23 @@ function foreignRuleGenerator(ruleName, defaultMessage, isInvalid) $matchAlias: getAlias( fieldName ) }; - return function(value, model, setMessage) + return function(value, model, chain) { if ( models && isValue( value ) ) { if ( isInvalid( value, model, models, fieldName ) ) { - setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) ); + chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) ); + } + else + { + chain.next(); } } - - return value; + else + { + chain.next(); + } }; }; @@ -1289,26 +1411,39 @@ function subRuleGenerator(ruleName, isInvalid) var validators = Validation.parseRules( otherRules, otherField, database, getAlias ); - return function(value, model, setMessage) + return function(value, model, chain) { var invalids = 0; + var chainCount = 0; - var setInvalid = function(message) + var onChainEnd = function(innerChain) { - if ( message ) + if (!innerChain.valid) { invalids++; } + + if (++chainCount === validators.length) + { + if ( isInvalid( invalids, chainCount ) ) + { + chain.stop(); + } + else + { + chain.next(); + } + } }; var testValue = otherField === field ? value : model.$get( otherField ); for (var i = 0; i < validators.length; i++) { - validators[ i ]( testValue, model, setInvalid ); - } + var innerChain = new ValidationChain( model, otherField, [validators[ i ]], onChainEnd ); - return isInvalid( invalids, validators.length ) ? Validation.Stop : value; + innerChain.start( testValue ); + } }; }; } @@ -1387,14 +1522,16 @@ function listRuleGenerator(ruleName, defaultMessage, isInvalid) $list: list }; - return function(value, model, setMessage) + return function(value, model, chain) { if ( isInvalid( value, model, inList ) ) { - setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) ); + chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) ); + } + else + { + chain.next(); } - - return value; }; }; @@ -1473,7 +1610,7 @@ function rangeRuleGenerator(ruleName, defaultMessages, isInvalid) $end: end }; - return function(value, model, setMessage) + return function(value, model, chain) { var size = sizeof( value ); var type = typeof( value ); @@ -1483,10 +1620,12 @@ function rangeRuleGenerator(ruleName, defaultMessages, isInvalid) { extra.$size = size; - setMessage( generateMessage( field, getAlias( field ), value, model, typeMessage, extra ) ); + chain.invalid( generateMessage( field, getAlias( field ), value, model, typeMessage, extra ) ); + } + else + { + chain.next(); } - - return value; }; }; @@ -1538,11 +1677,15 @@ function regexRuleGenerator(ruleName, defaultMessage, regex) var messageTemplate = determineMessage( ruleName, message ); - return function(value, model, setMessage) + return function(value, model, chain) { if ( !regex.test( value ) ) { - setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate ) ); + chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate ) ); + } + else + { + chain.next(); } return value; @@ -1577,14 +1720,16 @@ Validation.Rules.regex = function(field, params, database, getAlias, message) var messageTemplate = determineMessage( 'regex', message ); - return function(value, model, setMessage) + return function(value, model, chain) { if ( !regex.test( value ) ) { - setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate ) ); + chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate ) ); + } + else + { + chain.next(); } - - return value; }; }; @@ -1698,7 +1843,7 @@ function sizeRuleGenerator(ruleName, defaultMessages, isInvalid) $number: params }; - return function(value, model, setMessage) + return function(value, model, chain) { var size = sizeof( value ); var type = typeof( value ); @@ -1708,10 +1853,12 @@ function sizeRuleGenerator(ruleName, defaultMessages, isInvalid) { extra.$size = size; - setMessage( generateMessage( field, getAlias( field ), value, model, typeMessage, extra ) ); + chain.invalid( generateMessage( field, getAlias( field ), value, model, typeMessage, extra ) ); + } + else + { + chain.next(); } - - return value; }; }; @@ -1763,14 +1910,14 @@ ruleGenerator('model', ruleGenerator('whole', '{$alias} must be a whole number.', - function isInvalid(value, model, setValue) { + function isInvalid(value, model, chain) { var parsed = tryParseInt( value ); var numeric = parseFloat( value ); var invalid = !isNumber( parsed ); if ( !invalid ) { invalid = Math.floor( parsed ) !== numeric; if ( !invalid ) { - setValue( parsed ); + chain.update( parsed ); } } return invalid; @@ -1779,11 +1926,11 @@ ruleGenerator('whole', ruleGenerator('numeric', '{$alias} must be numeric.', - function isInvalid(value, model, setValue) { + function isInvalid(value, model, chain) { var parsed = tryParseFloat( value ); var invalid = !isNumber( parsed ); if ( !invalid ) { - setValue( parsed ); + chain.update( parsed ); } return invalid; } @@ -1791,11 +1938,11 @@ ruleGenerator('numeric', ruleGenerator('yesno', '{$alias} must be a yes or no.', - function isInvalid(value, model, setValue) { + function isInvalid(value, model, chain) { var mapped = Validation.Rules.yesno.map[ value ]; var invalid = !isBoolean( mapped ); if ( !invalid ) { - setValue( mapped ); + chain.update( mapped ); } return invalid; } @@ -1817,68 +1964,70 @@ Validation.Rules.yesno.map = Validation.Rules.abs = function(field, params, database, alias, message) { - return function(value, model, setMessage) + return function(value, model, chain) { value = tryParseFloat( value ); if ( isNumber( value ) ) { - value = Math.abs( value ); + chain.update( Math.abs( value ) ); } - return value; + chain.next(); }; }; Validation.Rules.apply = function(field, params, database, alias, message) { - return function(value, model, setMessage) + return function(value, model, chain) { model.$set( field, value ); - - return value; + + chain.next(); }; }; Validation.Rules.base64 = function(field, params, database, alias, message) { - return function(value, model, setMessage) + return function(value, model, chain) { if ( global.btoa ) { - value = global.btoa( value ); + chain.update( global.btoa( value ) ); } - return value; + chain.next(); }; }; Validation.Rules.ceil = function(field, params, database, alias, message) { - return function(value, model, setMessage) + return function(value, model, chain) { value = tryParseFloat( value ); - + if ( isNumber( value ) ) { - value = Math.ceil( value ); + chain.update( Math.ceil( value ) ); } - return value; + chain.next(); }; }; Validation.Rules.endOfDay = function(field, params, database, alias, message) { - return function(value, model, setMessage) + return function(value, model, chain) { - return endOfDay( value ); + chain.update( endOfDay( value ) ); + + chain.next(); }; }; Validation.Rules.filter = function(field, params, database, alias, message) { - return function(value, model, setMessage) + return function(value, model, chain) { if ( isArray( value ) ) { @@ -1889,6 +2038,8 @@ Validation.Rules.filter = function(field, params, database, alias, message) value.splice( i, 1 ); } } + + chain.update( value ); } else if ( isObject( value ) ) { @@ -1899,24 +2050,26 @@ Validation.Rules.filter = function(field, params, database, alias, message) delete value[ prop ]; } } + + chain.update( value ); } - return value; + chain.next(); }; }; Validation.Rules.floor = function(field, params, database, alias, message) { - return function(value, model, setMessage) + return function(value, model, chain) { value = tryParseFloat( value ); - + if ( isNumber( value ) ) { - value = Math.floor( value ); + chain.update( Math.floor( value ) ); } - return value; + chain.next(); }; }; @@ -1929,49 +2082,79 @@ Validation.Rules.mod = function(field, params, database, alias, message) throw '"' + number + '" is not a valid number for the mod rule.'; } - return function(value, model, setMessage) + return function(value, model, chain) { value = tryParseFloat( value ); if ( isNumber( value ) ) { - value = value % number; + chain.update( value % number ); } - return value; + chain.next(); }; }; Validation.Rules.null = function(field, params, database, alias, message) { - return function(value, model, setMessage) + return function(value, model, chain) { model.$set( field, null ); - return null; + chain.update( null ); + + chain.next(); }; }; Validation.Rules.round = function(field, params, database, alias, message) { - return function(value, model, setMessage) + return function(value, model, chain) { value = tryParseFloat( value ); if ( isNumber( value ) ) { - value = Math.round( value ); + chain.update( Math.round( value ) ); } - return value; + chain.next(); }; }; Validation.Rules.startOfDay = function(field, params, database, alias, message) { - return function(value, model, setMessage) + return function(value, model, chain) + { + chain.update( startOfDay( value ) ); + + chain.next(); + }; +}; + +Validation.Rules.stripEnts = function(field, params, database, alias, message) +{ + return function(value, model, chain) + { + if ( isString( value ) ) + { + chain.update( value.replace( /&[a-z]+;/gi, '' ) ); + } + + chain.next(); + }; +}; + +Validation.Rules.stripTags = function(field, params, database, alias, message) +{ + return function(value, model, chain) { - return startOfDay( value ); + if ( isString( value ) ) + { + chain.update( value.replace( /<(?:.|\n)*?>/gm, '' ) ); + } + + chain.next(); }; }; @@ -1995,27 +2178,27 @@ Validation.Rules.trim = function(field, params, database, alias, message) })(); - return function(value, model, setMessage) + return function(value, model, chain) { if ( isString( value ) ) { - value = trim( value ); + chain.update( trim( value ) ); } - return value; + chain.next(); }; }; Validation.Rules.unbase64 = function(field, params, database, alias, message) { - return function(value, model, setMessage) + return function(value, model, chain) { if ( global.atob ) { - value = global.atob( value ); + chain.update( global.atob( value ) ); } - return value; + chain.next(); }; }; diff --git a/build/rekord-validation.min.js b/build/rekord-validation.min.js index 922756b..0bd9e67 100644 --- a/build/rekord-validation.min.js +++ b/build/rekord-validation.min.js @@ -1,3 +1,3 @@ -/* rekord-validation 1.4.4 - Advanced validation rules for rekord by Philip Diffenderfer */ -!function(e,t){"function"==typeof define&&define.amd?define(["rekord"],function(r){return t(e,r)}):"object"==typeof module&&module.exports?module.exports=t(global,require("rekord")):e.Rekord=t(e,e.Rekord)}(this,function(e,t,r){function n(e){var t=parseFloat(e);return isNaN(t)||(e=t),e}function a(e){var t=parseInt(e);return isNaN(t)||(e=t),e}function i(e){return V(e)?e.setHours(0,0,0,0):z(e)&&(e-=e%864e5),e}function s(e){return V(e)?e.setHours(23,59,59,999):z(e)&&(e=e-e%864e5+864e5-1),e}function u(e,t,r){K.Rules[e]=function(t,n,a,i,s){c(e,t,n);var u=o(e,s);return function(e,n,a){function s(t){e=t}return r(e,n,s)&&a(d(t,i(t),e,n,u)),e}},K.Rules[e].message=t}function o(e,t){return t||K.Rules[e].message}function l(e,t,r,n){var a=e.slice();if(n)for(var i=0;ie}),h("before","{$alias} must be before {$date}.",function(e,t){return e>t}),h("before_on","{$alias} must be before or equal to {$date}.",function(e,t){return e>s(t)}),u("date_like","{$alias} must be a valid date.",function(e,t,r){var n=U(e),a=n===!1;return a||r(n.getTime()),a}),v("required_if","{$alias} is required.",function(e,t,r,n,a){var i=a[t.$get(r)];return i&&A(e)}),v("required_unless","{$alias} is required.",function(e,t,r,n,a){var i=!a[t.$get(r)];return i&&A(e)}),$("confirmed","{$alias} must match {$fieldAliases}.",function(e,t,r,n){for(var a=!0,i=0;i0}),b("if_any",function(e,t){return e>=t}),b("if_not",function(e,t){return t>e}),p("in","{$alias} must be one of {$list}.",function(e,t,r){return!r(e,t)}),p("not_in","{$alias} must not be one of {$list}.",function(e,t,r){return r(e,t)}),R("between",{string:"{$alias} must have between {$start} to {$end} characters.",number:"{$alias} must be between {$start} and {$end}.",object:"{$alias} must have between {$start} to {$end} items."},function(e,t,r){return t>e||e>r}),R("not_between",{string:"{$alias} must not have between {$start} to {$end} characters.",number:"{$alias} must not be between {$start} and {$end}.",object:"{$alias} must not have between {$start} to {$end} items."},function(e,t,r){return e>=t&&r>=e}),w("alpha","{$alias} should only contain alphabetic characters.",/^[a-zA-Z]*$/),w("alpha_dash","{$alias} should only contain alpha-numeric characters, dashes, and underscores.",/^[a-zA-Z0-9_-]*$/),w("alpha_num","{$alias} should only contain alpha-numeric characters.",/^[a-zA-Z0-9]*$/),w("email","{$alias} is not a valid email.",/^.+@.+\..+$/),w("url","{$alias} is not a valid URL.",/^(https?:\/\/)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&\/\/=]*)$/),w("uri","{$alias} is not a valid URI.",/^(\w+:\/\/)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&\/\/=]*)$/),w("phone","{$alias} is not a valid phone number.",/^1?\W*([2-9][0-8][0-9])\W*([2-9][0-9]{2})\W*([0-9]{4})(\se?x?t?(\d*))?$/),K.Rules.regex=function(e,t,r,n,a){var i;if(D(t)){var s=/^\/(.*)\/([gmi]*)$/.exec(t);s&&(i=new RegExp(s[1],s[2]))}else P(t)&&(i=t);if(!i)throw t+" is not a valid regular expression for the regex rule";var u=o("regex",a);return function(t,r,a){return i.test(t)||a(d(e,n(e),t,r,u)),t}},K.Rules.regex.message="{$alias} is not a valid value.",u("required","{$alias} is required.",function(e){return A(e)}),y("min",{string:"{$alias} must have a minimum of {$number} characters.",number:"{$alias} must be at least {$number}.",object:"{$alias} must have at least {$number} items."},function(e,t){return t>e}),y("greater_than",{string:"{$alias} must have more than {$number} characters.",number:"{$alias} must be greater than {$number}.",object:"{$alias} must have more than {$number} items."},function(e,t){return t>=e}),y("max",{string:"{$alias} must have no more than {$number} characters.",number:"{$alias} must be no more than {$number}.",object:"{$alias} must have no more than {$number} items."},function(e,t){return e>t}),y("less_than",{string:"{$alias} must have less than {$number} characters.",number:"{$alias} must be less than {$number}.",object:"{$alias} must have less than {$number} items."},function(e,t){return e>=t}),y("equal",{string:"{$alias} must have {$number} characters.",number:"{$alias} must equal {$number}.",object:"{$alias} must have {$number} items."},function(e,t){return e!==t}),y("not_equal",{string:"{$alias} must not have {$number} characters.",number:"{$alias} must not equal {$number}.",object:"{$alias} must not have {$number} items."},function(e,t){return e===t}),u("array","{$alias} must be an array.",function(e){return!M(e)}),u("object","{$alias} must be an object.",function(e){return!j(e)}),u("string","{$alias} must be a string.",function(e){return!D(e)}),u("number","{$alias} must be a number.",function(e){return!z(e)}),u("boolean","{$alias} must be a true or false.",function(e){return!N(e)}),u("model","{$alias} must have a value.",function(e){return!(e instanceof x)}),u("whole","{$alias} must be a whole number.",function(e,t,r){var n=a(e),i=parseFloat(e),s=!z(n);return s||(s=Math.floor(n)!==i,s||r(n)),s}),u("numeric","{$alias} must be numeric.",function(e,t,r){var a=n(e),i=!z(a);return i||r(a),i}),u("yesno","{$alias} must be a yes or no.",function(e,t,r){var n=K.Rules.yesno.map[e],a=!N(n);return a||r(n),a}),K.Rules.yesno.map={"true":!0,t:!0,yes:!0,y:!0,1:!0,"false":!1,f:!1,no:!1,n:!1,0:!1},K.Rules.abs=function(e,t,r,a,i){return function(e,t,r){return e=n(e),z(e)&&(e=Math.abs(e)),e}},K.Rules.apply=function(e,t,r,n,a){return function(t,r,n){return r.$set(e,t),t}},K.Rules.base64=function(t,r,n,a,i){return function(t,r,n){return e.btoa&&(t=e.btoa(t)),t}},K.Rules.ceil=function(e,t,r,a,i){return function(e,t,r){return e=n(e),z(e)&&(e=Math.ceil(e)),e}},K.Rules.endOfDay=function(e,t,r,n,a){return function(e,t,r){return s(e)}},K.Rules.filter=function(e,t,r,n,a){return function(e,t,r){if(M(e))for(var n=e.length-1;n>=0;n--)G(e[n])||e.splice(n,1);else if(j(e))for(var a in e)G(e[a])||delete e[a];return e}},K.Rules.floor=function(e,t,r,a,i){return function(e,t,r){return e=n(e),z(e)&&(e=Math.floor(e)),e}},K.Rules.mod=function(e,t,r,a,i){var s=n(t);if(!z(s))throw'"'+s+'" is not a valid number for the mod rule.';return function(e,t,r){return e=n(e),z(e)&&(e%=s),e}},K.Rules["null"]=function(e,t,r,n,a){return function(t,r,n){return r.$set(e,null),null}},K.Rules.round=function(e,t,r,a,i){return function(e,t,r){return e=n(e),z(e)&&(e=Math.round(e)),e}},K.Rules.startOfDay=function(e,t,r,n,a){return function(e,t,r){return i(e)}},K.Rules.trim=function(e,t,r,n,a){var i=function(){if(String.prototype.trim)return function(e){return e.trim()};var e=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;return function(t){return t.replace(e,"")}}();return function(e,t,r){return D(e)&&(e=i(e)),e}},K.Rules.unbase64=function(t,r,n,a,i){return function(t,r,n){return e.atob&&(t=e.atob(t)),t}},t.Validation=K,t.ruleGenerator=u,t.rangeRuleGenerator=R,t.collectionRuleGenerator=m,t.dateRuleGenerator=h,t.fieldListRuleGenerator=v,t.fieldsRuleGenerator=$,t.foreignRuleGenerator=g,t.subRuleGenerator=b,t.listRuleGenerator=p,t.regexRuleGenerator=w,t.sizeRuleGenerator=y,t.joinFriendly=l,t.tryParseFloat=n,t.tryParseInt=a,t.startOfDay=i,t.endOfDay=s,t.determineMessage=o,t.mapFromArray=f,t.checkNoParams=c,t.generateMessage=d,t}); +/* rekord-validation 1.5.0 - Advanced validation rules for rekord by Philip Diffenderfer */ +!function(e,t){"function"==typeof define&&define.amd?define(["rekord"],function(n){return t(e,n)}):"object"==typeof module&&module.exports?module.exports=t(global,require("rekord")):e.Rekord=t(e,e.Rekord)}(this,function(e,t,n){function a(e,t,n,a){this.model=e,this.field=t,this.validations=n,this.onEnd=a}function i(e){var t=parseFloat(e);return isNaN(t)||(e=t),e}function r(e){var t=parseInt(e);return isNaN(t)||(e=t),e}function s(e){return N(e)?e.setHours(0,0,0,0):T(e)&&(e-=e%864e5),e}function u(e){return N(e)?e.setHours(23,59,59,999):T(e)&&(e=e-e%864e5+864e5-1),e}function l(e,t,n){K.Rules[e]=function(t,a,i,r,s){d(e,t,a);var u=o(e,s);return function(e,a,i){n(e,a,i)?i.invalid(h(t,r(t),e,a,u)):i.next()}},K.Rules[e].message=t}function o(e,t){return t||K.Rules[e].message}function f(e,t,n,a){var i=e.slice();if(a)for(var r=0;re}),m("before","{$alias} must be before {$date}.",function(e,t){return e>t}),m("before_on","{$alias} must be before or equal to {$date}.",function(e,t){return e>u(t)}),l("date_like","{$alias} must be a valid date.",function(e,t,n){var a=J(e),i=a===!1;return i||n.update(a.getTime()),i}),$("required_if","{$alias} is required.",function(e,t,n,a,i){var r=i[t.$get(n)];return r&&k(e)}),$("required_unless","{$alias} is required.",function(e,t,n,a,i){var r=!i[t.$get(n)];return r&&k(e)}),g("confirmed","{$alias} must match {$fieldAliases}.",function(e,t,n,a){for(var i=!0,r=0;r0}),b("if_any",function(e,t){return e>=t}),b("if_not",function(e,t){return t>e}),x("in","{$alias} must be one of {$list}.",function(e,t,n){return!n(e,t)}),x("not_in","{$alias} must not be one of {$list}.",function(e,t,n){return n(e,t)}),R("between",{string:"{$alias} must have between {$start} to {$end} characters.",number:"{$alias} must be between {$start} and {$end}.",object:"{$alias} must have between {$start} to {$end} items."},function(e,t,n){return t>e||e>n}),R("not_between",{string:"{$alias} must not have between {$start} to {$end} characters.",number:"{$alias} must not be between {$start} and {$end}.",object:"{$alias} must not have between {$start} to {$end} items."},function(e,t,n){return e>=t&&n>=e}),w("alpha","{$alias} should only contain alphabetic characters.",/^[a-zA-Z]*$/),w("alpha_dash","{$alias} should only contain alpha-numeric characters, dashes, and underscores.",/^[a-zA-Z0-9_-]*$/),w("alpha_num","{$alias} should only contain alpha-numeric characters.",/^[a-zA-Z0-9]*$/),w("email","{$alias} is not a valid email.",/^.+@.+\..+$/),w("url","{$alias} is not a valid URL.",/^(https?:\/\/)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&\/\/=]*)$/),w("uri","{$alias} is not a valid URI.",/^(\w+:\/\/)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&\/\/=]*)$/),w("phone","{$alias} is not a valid phone number.",/^1?\W*([2-9][0-8][0-9])\W*([2-9][0-9]{2})\W*([0-9]{4})(\se?x?t?(\d*))?$/),K.Rules.regex=function(e,t,n,a,i){var r;if(j(t)){var s=/^\/(.*)\/([gmi]*)$/.exec(t);s&&(r=new RegExp(s[1],s[2]))}else O(t)&&(r=t);if(!r)throw t+" is not a valid regular expression for the regex rule";var u=o("regex",i);return function(t,n,i){r.test(t)?i.next():i.invalid(h(e,a(e),t,n,u))}},K.Rules.regex.message="{$alias} is not a valid value.",l("required","{$alias} is required.",function(e){return k(e)}),y("min",{string:"{$alias} must have a minimum of {$number} characters.",number:"{$alias} must be at least {$number}.",object:"{$alias} must have at least {$number} items."},function(e,t){return t>e}),y("greater_than",{string:"{$alias} must have more than {$number} characters.",number:"{$alias} must be greater than {$number}.",object:"{$alias} must have more than {$number} items."},function(e,t){return t>=e}),y("max",{string:"{$alias} must have no more than {$number} characters.",number:"{$alias} must be no more than {$number}.",object:"{$alias} must have no more than {$number} items."},function(e,t){return e>t}),y("less_than",{string:"{$alias} must have less than {$number} characters.",number:"{$alias} must be less than {$number}.",object:"{$alias} must have less than {$number} items."},function(e,t){return e>=t}),y("equal",{string:"{$alias} must have {$number} characters.",number:"{$alias} must equal {$number}.",object:"{$alias} must have {$number} items."},function(e,t){return e!==t}),y("not_equal",{string:"{$alias} must not have {$number} characters.",number:"{$alias} must not equal {$number}.",object:"{$alias} must not have {$number} items."},function(e,t){return e===t}),l("array","{$alias} must be an array.",function(e){return!M(e)}),l("object","{$alias} must be an object.",function(e){return!z(e)}),l("string","{$alias} must be a string.",function(e){return!j(e)}),l("number","{$alias} must be a number.",function(e){return!T(e)}),l("boolean","{$alias} must be a true or false.",function(e){return!G(e)}),l("model","{$alias} must have a value.",function(e){return!(e instanceof q)}),l("whole","{$alias} must be a whole number.",function(e,t,n){var a=r(e),i=parseFloat(e),s=!T(a);return s||(s=Math.floor(a)!==i,s||n.update(a)),s}),l("numeric","{$alias} must be numeric.",function(e,t,n){var a=i(e),r=!T(a);return r||n.update(a),r}),l("yesno","{$alias} must be a yes or no.",function(e,t,n){var a=K.Rules.yesno.map[e],i=!G(a);return i||n.update(a),i}),K.Rules.yesno.map={"true":!0,t:!0,yes:!0,y:!0,1:!0,"false":!1,f:!1,no:!1,n:!1,0:!1},K.Rules.abs=function(e,t,n,a,r){return function(e,t,n){e=i(e),T(e)&&n.update(Math.abs(e)),n.next()}},K.Rules.apply=function(e,t,n,a,i){return function(t,n,a){n.$set(e,t),a.next()}},K.Rules.base64=function(t,n,a,i,r){return function(t,n,a){e.btoa&&a.update(e.btoa(t)),a.next()}},K.Rules.ceil=function(e,t,n,a,r){return function(e,t,n){e=i(e),T(e)&&n.update(Math.ceil(e)),n.next()}},K.Rules.endOfDay=function(e,t,n,a,i){return function(e,t,n){n.update(u(e)),n.next()}},K.Rules.filter=function(e,t,n,a,i){return function(e,t,n){if(M(e)){for(var a=e.length-1;a>=0;a--)P(e[a])||e.splice(a,1);n.update(e)}else if(z(e)){for(var i in e)P(e[i])||delete e[i];n.update(e)}n.next()}},K.Rules.floor=function(e,t,n,a,r){return function(e,t,n){e=i(e),T(e)&&n.update(Math.floor(e)),n.next()}},K.Rules.mod=function(e,t,n,a,r){var s=i(t);if(!T(s))throw'"'+s+'" is not a valid number for the mod rule.';return function(e,t,n){e=i(e),T(e)&&n.update(e%s),n.next()}},K.Rules["null"]=function(e,t,n,a,i){return function(t,n,a){n.$set(e,null),a.update(null),a.next()}},K.Rules.round=function(e,t,n,a,r){return function(e,t,n){e=i(e),T(e)&&n.update(Math.round(e)),n.next()}},K.Rules.startOfDay=function(e,t,n,a,i){return function(e,t,n){n.update(s(e)),n.next()}},K.Rules.stripEnts=function(e,t,n,a,i){return function(e,t,n){j(e)&&n.update(e.replace(/&[a-z]+;/gi,"")),n.next()}},K.Rules.stripTags=function(e,t,n,a,i){return function(e,t,n){j(e)&&n.update(e.replace(/<(?:.|\n)*?>/gm,"")),n.next()}},K.Rules.trim=function(e,t,n,a,i){var r=function(){if(String.prototype.trim)return function(e){return e.trim()};var e=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;return function(t){return t.replace(e,"")}}();return function(e,t,n){j(e)&&n.update(r(e)),n.next()}},K.Rules.unbase64=function(t,n,a,i,r){return function(t,n,a){e.atob&&a.update(e.atob(t)),a.next()}},t.Validation=K,t.ruleGenerator=l,t.rangeRuleGenerator=R,t.collectionRuleGenerator=v,t.dateRuleGenerator=m,t.fieldListRuleGenerator=$,t.fieldsRuleGenerator=g,t.foreignRuleGenerator=p,t.subRuleGenerator=b,t.listRuleGenerator=x,t.regexRuleGenerator=w,t.sizeRuleGenerator=y,t.joinFriendly=f,t.tryParseFloat=i,t.tryParseInt=r,t.startOfDay=s,t.endOfDay=u,t.determineMessage=o,t.mapFromArray=c,t.checkNoParams=d,t.generateMessage=h,t}); //# sourceMappingURL=rekord-validation.min.js.map diff --git a/build/rekord-validation.min.js.map b/build/rekord-validation.min.js.map index 61edadf..1869874 100644 --- a/build/rekord-validation.min.js.map +++ b/build/rekord-validation.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["rekord-validation.min.js"],"names":["root","factory","define","amd","Rekord","module","exports","global","require","this","undefined","tryParseFloat","x","parsed","parseFloat","isNaN","tryParseInt","parseInt","startOfDay","d","isDate","setHours","isNumber","endOfDay","ruleGenerator","ruleName","defaultMessage","isInvalid","Validation","Rules","field","params","database","getAlias","message","checkNoParams","messageTemplate","determineMessage","value","model","setMessage","setValue","newValue","generateMessage","joinFriendly","arr","lastSeparatorCustom","itemSeparatorCustom","copy","slice","i","length","last","pop","lastSeparator","itemSeparator","join","mapFromArray","map","alias","extra","isFunction","base","$field","$alias","$value","transfer","isObject","format","collectionRuleGenerator","matchField","matchValue","equality","isString","comma","indexOf","substring","isArray","equals","equalsCompare","fields","$matchField","$matchAlias","$matchValue","dateRuleGenerator","dateExpression","parseExpression","parseDate","parsedTime","getTime","noop","$date","date","fieldListRuleGenerator","matchValues","parts","split","shift","values","list","$params","$list","fieldsRuleGenerator","fieldNames","fieldAliases","$fields","$fieldAliases","foreignRuleGenerator","modelName","models","fieldName","isValue","name","ModelCollection","get","success","modelClass","all","isRekord","$class","subRuleGenerator","otherField","otherRules","colon","rules","validators","parseRules","invalids","setInvalid","testValue","$get","Stop","listRuleGenerator","inList","isPrimitiveArray","rangeRuleGenerator","defaultMessages","start","end","range","string","number","object","$start","$end","size","sizeof","type","typeMessage","$size","regexRuleGenerator","regex","test","sizeRuleGenerator","$number","Model","Database","Promise","Collection","isEmpty","isBoolean","isRegExp","addMethod","replaceMethod","on","Events","Plugins","db","options","aliases","validation","Defaults","messages","required","validations","prototype","$this","$trigger","PreValidate","$valid","$validations","$validationMessages","chain","fieldValid","push","ValidatePass","ValidateFail","$init","apply","arguments","$save","$isDeleted","debug","Debugs","SAVE_DELETED","$db","resolve","$validate","Expression","Expressions","Delimiter","Escape","RuleSeparator","rule","defaultMessageValidator","parseRule","ruleProperty","ruleMessageOrData","ruleMessage","ruleInput","input","customMessageValidator","charAt","customValidator","ruleParams","validatorFactory","expr","parsers","parser","expressionFunction","functionName","result","RELATIVE_REGEX","RELATIVE_UNITS","ms","millisecond","milliseconds","s","second","seconds","min","mins","minute","minutes","hr","hour","hours","day","days","wk","week","weeks","month","months","yr","year","years","relative","exec","amount","unit","unitScale","Date","setTime","getter","setter","today","tomorrow","setDate","getDate","yesterday","accepted","acceptable","valueString","toLowerCase","1","yes","y","true","contains","m","validate","messageOption","invalid","related","pluck","confirmed","different","valid","invalidCount","totalCount","RegExp","numeric","Math","floor","mapped","yesno","t","false","f","no","n","0","abs","$set","base64","btoa","ceil","filter","splice","prop","mod","round","trim","String","replace","unbase64","atob"],"mappings":"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","file":"rekord-validation.min.js","sourcesContent":["/* rekord-validation 1.4.4 - Advanced validation rules for rekord by Philip Diffenderfer */\n// UMD (Universal Module Definition)\n(function (root, factory)\n{\n if (typeof define === 'function' && define.amd) // jshint ignore:line\n {\n // AMD. Register as an anonymous module.\n define(['rekord'], function(Rekord) { // jshint ignore:line\n return factory(root, Rekord);\n });\n }\n else if (typeof module === 'object' && module.exports) // jshint ignore:line\n {\n // Node. Does not work with strict CommonJS, but\n // only CommonJS-like environments that support module.exports,\n // like Node.\n module.exports = factory(global, require('rekord')); // jshint ignore:line\n }\n else\n {\n // Browser globals (root is window)\n root.Rekord = factory(root, root.Rekord);\n }\n}(this, function(global, Rekord, undefined)\n{\n\n var Model = Rekord.Model;\n var Database = Rekord.Database;\n var Promise = Rekord.Promise;\n var Collection = Rekord.Collection;\n var ModelCollection = Rekord.ModelCollection;\n\n var isEmpty = Rekord.isEmpty;\n var isString = Rekord.isString;\n var isArray = Rekord.isArray;\n var isObject = Rekord.isObject;\n var isFunction = Rekord.isFunction;\n var isDate = Rekord.isDate;\n var isNumber = Rekord.isNumber;\n var isBoolean = Rekord.isBoolean;\n var isValue = Rekord.isValue;\n var isPrimitiveArray = Rekord.isPrimitiveArray;\n var isRegExp = Rekord.isRegExp;\n var isRekord = Rekord.isRekord;\n\n var noop = Rekord.noop;\n var equalsCompare = Rekord.equalsCompare;\n var equals = Rekord.equals;\n var indexOf = Rekord.indexOf;\n var sizeof = Rekord.sizeof;\n\n var split = Rekord.split;\n var transfer = Rekord.transfer;\n var format = Rekord.format;\n\n var parseDate = Rekord.parseDate;\n\n var addMethod = Rekord.addMethod;\n var replaceMethod = Rekord.replaceMethod;\n\nfunction tryParseFloat(x)\n{\n var parsed = parseFloat( x );\n\n if ( !isNaN( parsed ) )\n {\n x = parsed;\n }\n\n return x;\n}\n\nfunction tryParseInt(x)\n{\n var parsed = parseInt( x );\n\n if ( !isNaN( parsed ) )\n {\n x = parsed;\n }\n\n return x;\n}\n\nfunction startOfDay(d)\n{\n if ( isDate( d ) )\n {\n d.setHours( 0, 0, 0, 0 );\n }\n else if ( isNumber( d ) )\n {\n d = d - (d % 86400000);\n }\n\n return d;\n}\n\nfunction endOfDay(d)\n{\n if ( isDate( d ) )\n {\n d.setHours( 23, 59, 59, 999 );\n }\n else if ( isNumber( d ) )\n {\n d = d - (d % 86400000) + 86400000 - 1;\n }\n\n return d;\n}\n\nfunction ruleGenerator(ruleName, defaultMessage, isInvalid)\n{\n Validation.Rules[ ruleName ] = function(field, params, database, getAlias, message)\n {\n checkNoParams( ruleName, field, params );\n\n var messageTemplate = determineMessage( ruleName, message );\n\n return function(value, model, setMessage)\n {\n function setValue( newValue )\n {\n value = newValue;\n }\n\n if ( isInvalid( value, model, setValue ) )\n {\n setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate ) );\n }\n\n return value;\n };\n };\n\n Validation.Rules[ ruleName ].message = defaultMessage;\n}\n\nfunction determineMessage(ruleName, message)\n{\n return message || Validation.Rules[ ruleName ].message;\n}\n\nfunction joinFriendly(arr, lastSeparatorCustom, itemSeparatorCustom, getAlias)\n{\n var copy = arr.slice();\n\n if ( getAlias )\n {\n for (var i = 0; i < copy.length; i++)\n {\n copy[ i ] = getAlias( copy[ i ] );\n }\n }\n\n var last = copy.pop();\n var lastSeparator = lastSeparatorCustom || 'and';\n var itemSeparator = itemSeparatorCustom || ', ';\n\n switch (copy.length) {\n case 0:\n return last;\n case 1:\n return copy[ 0 ] + ' ' + lastSeparator + ' ' + last;\n default:\n return copy.join( itemSeparator ) + itemSeparator + lastSeparator + ' ' + last;\n }\n}\n\nfunction mapFromArray(arr, value)\n{\n var map = {};\n\n for (var i = 0; i < arr.length; i++)\n {\n map[ arr[ i ] ] = value;\n }\n\n return map;\n}\n\nfunction checkNoParams(ruleName, field, params)\n{\n if ( params )\n {\n throw 'the rule ' + ruleName + ' for field ' + field + ' has no arguments';\n }\n}\n\nfunction generateMessage(field, alias, value, model, message, extra)\n{\n if ( isFunction( message ) )\n {\n message = message( field, alias, value, model, extra );\n }\n\n var base = {};\n base.$field = field;\n base.$alias = alias;\n base.$value = value;\n\n transfer( model, base );\n\n if ( isObject( extra ) )\n {\n transfer( extra, base );\n }\n\n return format( message, base );\n}\n\nRekord.on( Rekord.Events.Plugins, function(model, db, options)\n{\n var validation = options.validation || Database.Defaults.validation;\n\n if ( isEmpty( validation ) )\n {\n return;\n }\n\n var rules = validation.rules || {};\n var messages = validation.messages || {};\n var aliases = validation.aliases || {};\n var required = !!validation.required;\n\n function getAlias(field)\n {\n return aliases[ field ] || field;\n }\n\n db.validations = {};\n\n for ( var field in rules )\n {\n db.validations[ field ] = Validation.parseRules( rules[ field ], field, db, getAlias, messages[ field ] );\n }\n\n addMethod( model.prototype, '$validate', function()\n {\n var $this = this;\n\n this.$trigger( Model.Events.PreValidate, [this] );\n\n this.$valid = true;\n this.$validations = {};\n this.$validationMessages.length = 0;\n\n for (var field in db.validations)\n {\n var chain = db.validations[ field ];\n var value = this.$get( field );\n var fieldValid = true;\n\n var setMessage = function(message) // jshint ignore:line\n {\n // Only accept for the first valid message\n if ( message && fieldValid )\n {\n fieldValid = false;\n\n $this.$validations[ field ] = message;\n $this.$validationMessages.push( message );\n $this.$valid = false;\n }\n };\n\n for (var i = 0; i < chain.length && fieldValid && value !== Validation.Stop; i++)\n {\n value = chain[ i ]( value, this, setMessage );\n }\n }\n\n this.$trigger( this.$valid ? Model.Events.ValidatePass : Model.Events.ValidateFail, [this] );\n\n return this.$valid;\n });\n\n replaceMethod( model.prototype, '$init', function($init)\n {\n return function()\n {\n this.$valid = undefined;\n this.$validations = {};\n this.$validationMessages = [];\n\n return $init.apply( this, arguments );\n };\n });\n\n if ( required )\n {\n replaceMethod( model.prototype, '$save', function($save)\n {\n return function()\n {\n if ( this.$isDeleted() )\n {\n Rekord.debug( Rekord.Debugs.SAVE_DELETED, this.$db, this );\n\n return Promise.resolve( this );\n }\n\n if ( !this.$validate() )\n {\n return Promise.resolve( this );\n }\n\n return $save.apply( this, arguments );\n };\n });\n }\n});\n\nModel.Events.PreValidate = 'pre-validate';\n\nModel.Events.ValidatePass = 'validate-pass';\n\nModel.Events.ValidateFail = 'validate-fail';\n\nvar Validation =\n{\n Rules: {},\n Expression: {},\n Expressions: [],\n Delimiter: /([|])/,\n Escape: '\\\\',\n RuleSeparator: ':',\n Stop: {},\n\n parseRules: function(rules, field, database, getAlias, message)\n {\n var validators = [];\n\n if ( isString( rules ) )\n {\n rules = split( rules, this.Delimiter, this.Escape );\n }\n\n if ( isArray( rules ) )\n {\n for (var i = 0; i < rules.length; i++)\n {\n var rule = rules[ i ];\n var defaultMessageValidator = this.parseRule( rule, field, database, getAlias, message );\n\n validators.push( defaultMessageValidator );\n }\n }\n else if ( isObject( rules ) )\n {\n for (var ruleProperty in rules)\n {\n var ruleMessageOrData = rules[ ruleProperty ];\n\n var ruleMessage = isObject( ruleMessageOrData ) ? ruleMessageOrData.message :\n ( isString( ruleMessageOrData ) ? ruleMessageOrData : undefined );\n\n var ruleInput = isObject( ruleMessageOrData ) && ruleMessageOrData.message ? ruleMessageOrData.input :\n ( isString( ruleMessageOrData ) ? undefined : ruleMessageOrData );\n\n var customMessageValidator = this.parseRule( ruleProperty, field, database, getAlias, ruleMessage || message, ruleInput );\n\n validators.push( customMessageValidator );\n }\n }\n\n return validators;\n },\n\n parseRule: function(rule, field, database, getAlias, message, input)\n {\n var colon = rule.indexOf( this.RuleSeparator );\n var ruleName = colon === -1 ? rule : rule.substring( 0, colon );\n\n if ( ruleName.charAt( 0 ) === '$' )\n {\n return this.customValidator( ruleName, field, database, getAlias, message );\n }\n\n var ruleParams = colon === -1 ? input : rule.substring( colon + 1 );\n var validatorFactory = Validation.Rules[ ruleName ];\n\n if ( !validatorFactory )\n {\n throw ruleName + ' is not a valid rule';\n }\n\n return validatorFactory( field, ruleParams, database, getAlias, message );\n },\n\n parseExpression: function(expr, database)\n {\n var parsers = Validation.Expressions;\n\n for (var i = 0; i < parsers.length; i++)\n {\n var parser = parsers[ i ];\n var expressionFunction = parser( expr, database );\n\n if ( isFunction( expressionFunction ) )\n {\n return expressionFunction; // (value, model)\n }\n }\n\n return noop;\n },\n\n customValidator: function(functionName, field, database, getAlias, message)\n {\n return function(value, model, setMessage)\n {\n var result = model[ functionName ]( value, getAlias, message );\n\n if ( isString( result ) )\n {\n setMessage( result );\n }\n\n return value;\n };\n }\n};\n\nValidation.Expression.date =\nValidation.Expressions.push(function(expr, database)\n{\n var parsed = parseDate( expr );\n\n if ( parsed !== false )\n {\n var parsedTime = parsed.getTime();\n\n return function(value, model)\n {\n return parsedTime;\n };\n }\n}) - 1;\n\nValidation.Expression.field =\nValidation.Expressions.push(function(expr, database)\n{\n if ( indexOf( database.fields, expr ) )\n {\n return function(value, model)\n {\n return model.$get( expr );\n };\n }\n}) - 1;\n\n\nvar RELATIVE_REGEX = /^([+-]\\d+(\\.\\d+)?)\\s*(.+)$/;\n\nvar RELATIVE_UNITS = {\n ms: 1,\n millisecond: 1,\n milliseconds: 1,\n s: 1000,\n second: 1000,\n seconds: 1000,\n min: 1000 * 60,\n mins: 1000 * 60,\n minute: 1000 * 60,\n minutes: 1000 * 60,\n hr: 1000 * 60 * 60,\n hour: 1000 * 60 * 60,\n hours: 1000 * 60 * 60,\n day: 1000 * 60 * 60 * 24,\n days: 1000 * 60 * 60 * 24,\n wk: 1000 * 60 * 60 * 24 * 7,\n week: 1000 * 60 * 60 * 24 * 7,\n weeks: 1000 * 60 * 60 * 24 * 7,\n month: ['getMonth', 'setMonth'],\n months: ['getMonth', 'setMonth'],\n yr: ['getFullYear', 'setFullYear'],\n year: ['getFullYear', 'setFullYear'],\n years: ['getFullYear', 'setFullYear']\n};\n\nValidation.Expression.relative =\nValidation.Expressions.push(function(expr, database)\n{\n var parsed = RELATIVE_REGEX.exec( expr );\n\n if ( parsed !== null )\n {\n var amount = parseFloat( parsed[ 1 ] );\n var unit = parsed[ 3 ];\n var unitScale = RELATIVE_UNITS[ unit ];\n\n if ( !unitScale )\n {\n throw unit + ' is not a valid unit.';\n }\n\n return function(value, model)\n {\n var relative = new Date();\n\n if ( isNumber( unitScale ) )\n {\n relative.setTime( relative.getTime() + unitScale * amount );\n }\n else\n {\n var getter = unitScale[0];\n var setter = unitScale[1];\n\n relative[ setter ]( relative[ getter ]() + amount );\n }\n\n return relative.getTime();\n };\n }\n}) - 1;\n\nValidation.Expression.today =\nValidation.Expressions.push(function(expr, database)\n{\n if ( expr === 'today' )\n {\n return function(value, model)\n {\n var today = new Date();\n\n startOfDay( today );\n\n return today.getTime();\n };\n }\n}) - 1;\n\nValidation.Expression.tomorrow =\nValidation.Expressions.push(function(expr, database)\n{\n if ( expr === 'tomorrow' )\n {\n return function(value, model)\n {\n var tomorrow = new Date();\n\n tomorrow.setDate( tomorrow.getDate() + 1 );\n startOfDay( tomorrow );\n\n return tomorrow.getTime();\n };\n }\n}) - 1;\n\nValidation.Expression.yesterday =\nValidation.Expressions.push(function(expr, database)\n{\n if ( expr === 'yesterday' )\n {\n return function(value, model)\n {\n var yesterday = new Date();\n\n yesterday.setDate( yesterday.getDate() - 1 );\n startOfDay( yesterday );\n\n return yesterday.getTime();\n };\n }\n}) - 1;\n\n// accepted\nValidation.Rules.accepted = function(field, params, database, getAlias, message)\n{\n checkNoParams( 'accepted', field, params );\n\n var messageTemplate = determineMessage( 'accepted', message );\n var acceptable = Validation.Rules.accepted.acceptable;\n\n return function(value, model, setMessage)\n {\n var valueString = (value + '').toLowerCase();\n var accepted = acceptable[ valueString ];\n\n if ( !accepted )\n {\n setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate ) );\n }\n\n return value;\n };\n};\n\nValidation.Rules.accepted.message = '{$alias} has not been accepted.';\n\nValidation.Rules.accepted.acceptable =\n{\n '1': true,\n 'yes': true,\n 'on': true,\n 'y': true,\n 'true': true\n};\n\n// contains:field,value\ncollectionRuleGenerator('contains',\n '{$alias} does not contain an item whose {$matchAlias} equals {$matchValue}.',\n function isInvalid(value, model, matchField, matchValue, equality)\n {\n return !value.contains(function isMatch(m)\n {\n return m !== model && equality( matchValue, m.$get( matchField ) );\n });\n }\n);\n\n// not_contains:field,value\ncollectionRuleGenerator('not_contains',\n '{$alias} contains an item whose {$matchAlias} equals {$matchValue}.',\n function isInvalid(value, model, matchField, matchValue, equality)\n {\n return value.contains(function isMatch(m)\n {\n return m !== model && equality( matchValue, m.$get( matchField ) );\n });\n }\n);\n\nfunction collectionRuleGenerator(ruleName, defaultMessage, isInvalid)\n{\n Validation.Rules[ ruleName ] = function(field, params, database, getAlias, message)\n {\n if ( !params )\n {\n throw ruleName + ' validation rule requires field & value arguments';\n }\n\n var matchField, matchValue, equality;\n\n if ( isString( params ) )\n {\n var comma = params.indexOf(',');\n\n if ( comma === -1 )\n {\n throw ruleName + ' validation rule requires field & value arguments';\n }\n\n matchField = params.substring( 0, comma );\n matchValue = params.substring( comma + 1 );\n }\n else if ( isArray( params ) )\n {\n matchField = params[ 0 ];\n matchValue = params[ 1 ];\n equality = params[ 2 ];\n }\n else if ( isObject( params ) )\n {\n matchField = params.field;\n matchValue = params.value;\n equality = params.equals;\n }\n\n if ( !isFunction( equality ) )\n {\n equality = equalsCompare;\n }\n\n if ( indexOf( database.fields, matchField ) === -1 )\n {\n throw matchField + ' is not a valid field for the ' + ruleName + ' rule';\n }\n\n var messageTemplate = determineMessage( ruleName, message );\n var extra = {\n $matchField: matchField,\n $matchAlias: getAlias( matchField ),\n $matchValue: matchValue\n };\n\n return function(value, model, setMessage)\n {\n if ( isInvalid( value, model, matchField, matchValue, equality ) )\n {\n setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) );\n }\n\n return value;\n };\n };\n\n Validation.Rules[ ruleName ].message = defaultMessage;\n}\n\nValidation.Rules.validate = function(field, params, database, getAlias, message)\n{\n // message, models, validations\n var messageOption = params || 'message';\n var messageTemplate = determineMessage( 'validate', message );\n\n return function(value, model, setMessage)\n {\n if ( isArray( value ) )\n {\n var invalid = new Collection();\n\n for (var i = 0; i < value.length; i++)\n {\n var related = value[ i ];\n\n if ( related && related.$validate && !related.$validate() )\n {\n invalid.push( related );\n }\n }\n\n if ( invalid.length )\n {\n switch (messageOption)\n {\n case 'models':\n setMessage( invalid );\n break;\n case 'validations':\n setMessage( invalid.pluck( '$validations', '$$key' ) );\n break;\n default: // message\n setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate ) );\n break;\n }\n }\n }\n\n return value;\n };\n};\n\nValidation.Rules.validate.message = '{$alias} is not valid.';\n\n// after:today\ndateRuleGenerator('after',\n '{$alias} must be after {$date}.',\n function isInvalid(value, date) {\n return value < endOfDay( date );\n }\n);\n\n// after_on:tomorrow\ndateRuleGenerator('after_on',\n '{$alias} must be after or equal to {$date}.',\n function isInvalid(value, date) {\n return value < date;\n }\n);\n\n// before:yesterday\ndateRuleGenerator('before',\n '{$alias} must be before {$date}.',\n function isInvalid(value, date) {\n return value > date;\n }\n);\n\n// before_on:+2days\ndateRuleGenerator('before_on',\n '{$alias} must be before or equal to {$date}.',\n function isInvalid(value, date) {\n return value > endOfDay( date );\n }\n);\n\n// date\nruleGenerator('date_like',\n '{$alias} must be a valid date.',\n function isInvalid(value, model, setValue) {\n var parsed = parseDate( value );\n var invalid = parsed === false;\n if ( !invalid ) {\n setValue( parsed.getTime() );\n }\n return invalid;\n }\n);\n\nfunction dateRuleGenerator(ruleName, defaultMessage, isInvalid)\n{\n Validation.Rules[ ruleName ] = function(field, params, database, getAlias, message)\n {\n if ( !params )\n {\n throw ruleName + ' validation rule requires a date expression argument';\n }\n\n var dateExpression;\n\n if ( isString( params ) )\n {\n dateExpression = Validation.parseExpression( params, database );\n }\n else if ( isFunction( params ) )\n {\n dateExpression = params;\n }\n else\n {\n var parsed = parseDate( params );\n\n if ( parsed !== false )\n {\n var parsedTime = parsed.getTime();\n\n dateExpression = function()\n {\n return parsedTime;\n };\n }\n }\n\n if ( !dateExpression || dateExpression === noop )\n {\n throw params + ' is not a valid date expression for the ' + ruleName + ' rule';\n }\n\n var messageTemplate = determineMessage( ruleName, message );\n var extra = {\n $date: params\n };\n\n return function(value, model, setMessage)\n {\n var parsed = parseDate( value );\n\n if ( parsed !== false )\n {\n value = parsed.getTime();\n\n var date = dateExpression( value, model );\n\n if ( isNumber( date ) && isInvalid( value, date ) )\n {\n setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) );\n }\n }\n\n return value;\n };\n };\n\n Validation.Rules[ ruleName ].message = defaultMessage;\n}\n\n\n// required_if:X,Y,...\nfieldListRuleGenerator('required_if',\n '{$alias} is required.',\n function isInvalid(value, model, field, values, map) {\n var required = map[ model.$get( field ) ];\n\n return required && isEmpty( value );\n }\n);\n\n// required_unless:X,Y,...\nfieldListRuleGenerator('required_unless',\n '{$alias} is required.',\n function isInvalid(value, model, field, values, map) {\n var required = !map[ model.$get( field ) ];\n\n return required && isEmpty( value );\n }\n);\n\nfunction fieldListRuleGenerator(ruleName, defaultMessage, isInvalid)\n{\n Validation.Rules[ ruleName ] = function(field, params, database, getAlias, message)\n {\n if ( !params )\n {\n throw ruleName + ' validation rule requires a field and list arguments';\n }\n\n var matchField, matchValues;\n\n if ( isString( params ) )\n {\n var parts = split( params, /(,)/, '\\\\' );\n\n matchField = parts.shift();\n matchValues = parts;\n }\n else if ( isArray( params ) )\n {\n matchField = params.shift();\n matchValues = params;\n }\n else if ( isObject( params ) )\n {\n matchField = params.field;\n matchValues = params.values;\n }\n\n if ( indexOf( database.fields, matchField ) === false )\n {\n throw matchField + ' is not a valid field for the ' + ruleName + ' rule';\n }\n\n var messageTemplate = determineMessage( ruleName, message );\n var list = joinFriendly( matchValues );\n var extra = {\n $params: params,\n $matchField: matchField,\n $matchAlias: getAlias( matchField ),\n $list: list\n };\n var map = mapFromArray( matchValues, true );\n\n return function(value, model, setMessage)\n {\n if ( isInvalid( value, model, matchField, matchValues, map ) )\n {\n setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) );\n }\n\n return value;\n };\n };\n\n Validation.Rules[ ruleName ].message = defaultMessage;\n}\n\n// confirmed:X\nfieldsRuleGenerator('confirmed',\n '{$alias} must match {$fieldAliases}.',\n function isInvalid(value, model, fields, setValue) {\n var confirmed = true;\n\n for (var i = 0; i < fields.length; i++)\n {\n if ( !equals( value, model.$get( fields[ i ] ) ) )\n {\n confirmed = false;\n }\n }\n\n return !confirmed;\n }\n);\n\n// different:X\nfieldsRuleGenerator('different',\n '{$alias} must not match {$fieldAliases}.',\n function isInvalid(value, model, fields, setValue) {\n var different = false;\n\n for (var i = 0; i < fields.length; i++)\n {\n if ( !equals( value, model.$get( fields[ i ] ) ) )\n {\n different = true;\n }\n }\n\n return !different;\n }\n);\n\n// if_valid:X\nfieldsRuleGenerator('if_valid',\n '',\n function isInvalid(value, model, fields, setValue) {\n var valid = true;\n\n for (var i = 0; i < fields.length && valid; i++)\n {\n if ( model.$validations[ fields[ i ] ] )\n {\n valid = false;\n }\n }\n\n if ( !valid )\n {\n setValue( Validation.Stop );\n }\n\n return false;\n }\n);\n\n// The field under validation must be present only if any of the other specified fields are present.\n// required_with:X,Y,...\nfieldsRuleGenerator('required_with',\n '{$alias} is required.',\n function isInvalid(value, model, fields, setValue) {\n var required = false;\n\n for (var i = 0; i < fields.length && !required; i++)\n {\n if ( !isEmpty( model.$get( fields[ i ] ) ) )\n {\n required = true;\n }\n }\n\n return required && isEmpty( value );\n }\n);\n\n// The field under validation must be present only if all of the other specified fields are present.\n// required_with_all:X,Y,...\nfieldsRuleGenerator('required_with_all',\n '{$alias} is required.',\n function isInvalid(value, model, fields, setValue) {\n var required = true;\n\n for (var i = 0; i < fields.length && required; i++)\n {\n if ( isEmpty( model.$get( fields[ i ] ) ) )\n {\n required = false;\n }\n }\n\n return required && isEmpty( value );\n }\n);\n\n// The field under validation must be present only when any of the other specified fields are not present.\n// required_without:X,Y,...\nfieldsRuleGenerator('required_without',\n '{$alias} is required.',\n function isInvalid(value, model, fields, setValue) {\n var required = false;\n\n for (var i = 0; i < fields.length && !required; i++)\n {\n if ( isEmpty( model.$get( fields[ i ] ) ) )\n {\n required = true;\n }\n }\n\n return required && isEmpty( value );\n }\n);\n\n// The field under validation must be present only when all of the other specified fields are not present.\n// required_without_all:X,Y,...\nfieldsRuleGenerator('required_without_all',\n '{$alias} is required.',\n function isInvalid(value, model, fields, setValue) {\n var required = true;\n\n for (var i = 0; i < fields.length && required; i++)\n {\n if ( !isEmpty( model.$get( fields[ i ] ) ) )\n {\n required = false;\n }\n }\n\n return required && isEmpty( value );\n }\n);\n\nfunction fieldsRuleGenerator(ruleName, defaultMessage, isInvalid)\n{\n Validation.Rules[ ruleName ] = function(field, params, database, getAlias, message)\n {\n if ( !params )\n {\n throw ruleName + ' validation rule requires an array of fields argument';\n }\n\n var fields = split( params, /(\\s*,\\s*)/, '\\\\' );\n\n for (var i = 0; i < fields.length; i++)\n {\n if ( indexOf( database.fields, fields[ i ] ) === -1 )\n {\n throw fields[ i ] + ' is not a valid field for the ' + ruleName + ' rule';\n }\n }\n\n var messageTemplate = determineMessage( ruleName, message );\n var fieldNames = joinFriendly( fields );\n var fieldAliases = joinFriendly( fields, false, false, getAlias );\n var extra = {\n $fields: fieldNames,\n $fieldAliases: fieldAliases\n };\n\n return function(value, model, setMessage)\n {\n function setValue( newValue )\n {\n value = newValue;\n }\n\n if ( isInvalid( value, model, fields, setValue ) )\n {\n setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) );\n }\n\n return value;\n };\n };\n\n Validation.Rules[ ruleName ].message = defaultMessage;\n}\n\n// exists:X,Y\nforeignRuleGenerator('exists',\n '{$alias} must match an existing {$matchAlias} in a {$class}',\n function isInvalid(value, model, models, fieldName)\n {\n return !models.contains(function isDifferentMatch(m)\n {\n return m !== model && equals( value, m.$get( fieldName ) );\n });\n }\n);\n\n// unique:X,Y\nforeignRuleGenerator('unique',\n '{$alias} must be a unique {$matchAlias} in a {$class}',\n function isInvalid(value, model, models, fieldName)\n {\n return models.contains(function isDifferentMatch(m)\n {\n return m !== model && equals( value, m.$get( fieldName ) );\n });\n }\n);\n\n// 'ruleName'\n// 'ruleName:name'\n// 'ruleName:,field'\n// 'ruleName:name,field'\n// 'ruleName:name,field': '...'\n// 'ruleName': {input: {field: 'field', model: 'name'}, message: '...'}\n// 'ruleName': {input: {field: 'field', model: ModelClass}, message: '...'}\n// 'ruleName': {input: {field: 'field', models: [...]}, message: '...'}\n// 'ruleName': {field: 'field', model: 'name'}\n// 'ruleName': {field: 'field', model: ModelClass}\n// 'ruleName': {field: 'field', models: [...]}\nfunction foreignRuleGenerator(ruleName, defaultMessage, isInvalid)\n{\n Validation.Rules[ ruleName ] = function(field, params, database, getAlias, message)\n {\n var modelName, models, fieldName;\n\n if ( !isValue( params ) || isString( params ) )\n {\n var parts = split( params || '', /(\\s*,\\s*)/, '\\\\' );\n modelName = parts[0] || database.name;\n fieldName = parts[1] || field;\n models = null;\n }\n else if ( isArray( params ) )\n {\n modelName = isString( params[0] ) ? params.shift() : database.name;\n fieldName = isString( params[0] ) ? params.shift() : field;\n models = new ModelCollection( database, params );\n }\n else if ( isObject( params ) )\n {\n modelName = params.model || database.name;\n fieldName = params.field || field;\n models = params.models;\n }\n\n if ( !models )\n {\n if ( !modelName )\n {\n throw 'model, model class, or models is required for ' + ruleName + ' rule';\n }\n\n if ( isString( modelName ) )\n {\n Rekord.get( modelName ).success(function(modelClass)\n {\n models = modelClass.all();\n });\n }\n else if ( isRekord( modelName ) )\n {\n models = modelName.all();\n }\n }\n\n if ( indexOf( database.fields, fieldName ) === false )\n {\n throw fieldName + ' is not a valid field for the ' + ruleName + ' rule';\n }\n\n var messageTemplate = determineMessage( ruleName, message );\n var extra = {\n $class: modelName,\n $matchField: fieldName,\n $matchAlias: getAlias( fieldName )\n };\n\n return function(value, model, setMessage)\n {\n if ( models && isValue( value ) )\n {\n if ( isInvalid( value, model, models, fieldName ) )\n {\n setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) );\n }\n }\n\n return value;\n };\n };\n\n Validation.Rules[ ruleName ].message = defaultMessage;\n}\n\n// if:due_date:before:today|required\n\n// if all rules pass for the given field, continue with remaining rules\nsubRuleGenerator('if',\n function isInvalid(invalidCount, totalCount) {\n return invalidCount > 0;\n }\n);\n\n// if any rules pass for the given field, continue with remaining rules\nsubRuleGenerator('if_any',\n function isInvalid(invalidCount, totalCount) {\n return invalidCount >= totalCount;\n }\n);\n\n// if no rules pass for the given field, continue with remaining rules\nsubRuleGenerator('if_not',\n function isInvalid(invalidCount, totalCount) {\n return invalidCount < totalCount;\n }\n);\n\n\n\nfunction subRuleGenerator(ruleName, isInvalid)\n{\n Validation.Rules[ ruleName ] = function(field, params, database, getAlias, message)\n {\n if ( !params )\n {\n throw ruleName + ' validation rule requires a validation rule argument';\n }\n\n var otherField, otherRules;\n\n if ( isString( params ) )\n {\n var colon = params.indexOf( ':' );\n\n if ( colon === -1 )\n {\n throw params + ' is not a valid argument for the ' + ruleName + ' rule';\n }\n\n otherField = params.substring( 0, colon ) || field;\n otherRules = params.substring( colon + 1 );\n }\n else if ( isArray( params ) )\n {\n otherField = params.shift() || field;\n otherRules = params;\n }\n else if ( isObject( params ) )\n {\n otherField = params.field || field;\n otherRules = params.rules;\n }\n\n if ( indexOf( database.fields, otherField ) === -1 )\n {\n throw otherField + ' is not a valid field for the ' + ruleName + ' rule';\n }\n\n if ( !otherRules )\n {\n throw 'rules are required for the ' + ruleName + ' rule';\n }\n\n var validators = Validation.parseRules( otherRules, otherField, database, getAlias );\n\n return function(value, model, setMessage)\n {\n var invalids = 0;\n\n var setInvalid = function(message)\n {\n if ( message )\n {\n invalids++;\n }\n };\n\n var testValue = otherField === field ? value : model.$get( otherField );\n\n for (var i = 0; i < validators.length; i++)\n {\n validators[ i ]( testValue, model, setInvalid );\n }\n\n return isInvalid( invalids, validators.length ) ? Validation.Stop : value;\n };\n };\n}\n\n// in:X,Y,Z,...\nlistRuleGenerator('in',\n '{$alias} must be one of {$list}.',\n function isInvalid(value, model, inList)\n {\n return !inList( value, model );\n }\n);\n\n// not_in:X,Y,Z,...\nlistRuleGenerator('not_in',\n '{$alias} must not be one of {$list}.',\n function isInvalid(value, model, inList)\n {\n return inList( value, model );\n }\n);\n\nfunction listRuleGenerator(ruleName, defaultMessage, isInvalid)\n{\n Validation.Rules[ ruleName ] = function(field, params, database, getAlias, message)\n {\n if ( !params )\n {\n throw ruleName + ' validation rule requires a list argument';\n }\n\n var values, inList = false;\n\n if ( isString( params ) )\n {\n values = split( params, /(,)/, '\\\\' );\n }\n else if ( isArray( params ) )\n {\n values = params;\n }\n else if ( isFunction( params ) )\n {\n values = inList;\n }\n\n if ( inList !== false )\n {\n if ( !values || values.length === 0 )\n {\n throw params + ' is not a valid list of values for the ' + ruleName + ' rule';\n }\n }\n\n if ( isPrimitiveArray( values ) )\n {\n var map = mapFromArray( values, true );\n\n inList = function(value)\n {\n return map[ value ];\n };\n }\n else\n {\n inList = function(value)\n {\n return indexOf( values, value, equals );\n };\n }\n\n var messageTemplate = determineMessage( ruleName, message );\n var list = joinFriendly( values, 'or' );\n var extra = {\n $params: params,\n $list: list\n };\n\n return function(value, model, setMessage)\n {\n if ( isInvalid( value, model, inList ) )\n {\n setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) );\n }\n\n return value;\n };\n };\n\n\n Validation.Rules[ ruleName ].message = defaultMessage;\n}\n\n// between:3,10\nrangeRuleGenerator('between', {\n 'string': '{$alias} must have between {$start} to {$end} characters.',\n 'number': '{$alias} must be between {$start} and {$end}.',\n 'object': '{$alias} must have between {$start} to {$end} items.'\n },\n function isInvalid(value, start, end) {\n return value < start || value > end;\n }\n);\n\n// not_between\nrangeRuleGenerator('not_between', {\n 'string': '{$alias} must not have between {$start} to {$end} characters.',\n 'number': '{$alias} must not be between {$start} and {$end}.',\n 'object': '{$alias} must not have between {$start} to {$end} items.'\n },\n function isInvalid(value, start, end) {\n return value >= start && value <= end;\n }\n);\n\nfunction rangeRuleGenerator(ruleName, defaultMessages, isInvalid)\n{\n Validation.Rules[ ruleName ] = function(field, params, database, getAlias, message)\n {\n if ( !params )\n {\n throw ruleName + ' validation rule requires a range argument';\n }\n\n var start, end;\n\n if ( isString( params ) )\n {\n var range = split( params, /(\\s*,\\s*)/, '\\\\' );\n\n start = parseFloat( range[0] );\n end = parseFloat( range[1] );\n }\n else if ( isArray( params ) )\n {\n start = params[ 0 ];\n end = params[ 1 ];\n }\n else if ( isObject( params ) )\n {\n start = params.start;\n end = params.end;\n }\n\n if ( isNaN( start ) || isNaN( end ) )\n {\n throw params + ' is not a valid range of numbers for the ' + ruleName + ' rule';\n }\n\n if ( isString( message ) )\n {\n message = {\n 'string': message,\n 'number': message,\n 'object': message\n };\n }\n\n var messageTemplate = determineMessage( ruleName, message );\n var extra = {\n $start: start,\n $end: end\n };\n\n return function(value, model, setMessage)\n {\n var size = sizeof( value );\n var type = typeof( value );\n var typeMessage = messageTemplate[ type ];\n\n if ( typeMessage && isInvalid( size, start, end ) )\n {\n extra.$size = size;\n\n setMessage( generateMessage( field, getAlias( field ), value, model, typeMessage, extra ) );\n }\n\n return value;\n };\n };\n\n Validation.Rules[ ruleName ].message = defaultMessages;\n}\n\n\n\nregexRuleGenerator('alpha',\n '{$alias} should only contain alphabetic characters.',\n /^[a-zA-Z]*$/\n);\n\nregexRuleGenerator('alpha_dash',\n '{$alias} should only contain alpha-numeric characters, dashes, and underscores.',\n /^[a-zA-Z0-9_-]*$/\n);\n\nregexRuleGenerator('alpha_num',\n '{$alias} should only contain alpha-numeric characters.',\n /^[a-zA-Z0-9]*$/\n);\n\nregexRuleGenerator('email',\n '{$alias} is not a valid email.',\n /^.+@.+\\..+$/\n);\n\nregexRuleGenerator('url',\n '{$alias} is not a valid URL.',\n /^(https?:\\/\\/)?[-a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)$/\n);\n\nregexRuleGenerator('uri',\n '{$alias} is not a valid URI.',\n /^(\\w+:\\/\\/)?[-a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)$/\n);\n\nregexRuleGenerator('phone',\n '{$alias} is not a valid phone number.',\n /^1?\\W*([2-9][0-8][0-9])\\W*([2-9][0-9]{2})\\W*([0-9]{4})(\\se?x?t?(\\d*))?$/\n);\n\nfunction regexRuleGenerator(ruleName, defaultMessage, regex)\n{\n Validation.Rules[ ruleName ] = function(field, params, database, getAlias, message)\n {\n checkNoParams( ruleName, field, params );\n\n var messageTemplate = determineMessage( ruleName, message );\n\n return function(value, model, setMessage)\n {\n if ( !regex.test( value ) )\n {\n setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate ) );\n }\n\n return value;\n };\n };\n\n Validation.Rules[ ruleName ].message = defaultMessage;\n}\n\nValidation.Rules.regex = function(field, params, database, getAlias, message)\n{\n var regex;\n\n if ( isString( params ) )\n {\n var parsed = /^\\/(.*)\\/([gmi]*)$/.exec( params );\n\n if ( parsed )\n {\n regex = new RegExp( parsed[1], parsed[2] );\n }\n }\n else if ( isRegExp( params ) )\n {\n regex = params;\n }\n\n if ( !regex )\n {\n throw params + ' is not a valid regular expression for the regex rule';\n }\n\n var messageTemplate = determineMessage( 'regex', message );\n\n return function(value, model, setMessage)\n {\n if ( !regex.test( value ) )\n {\n setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate ) );\n }\n\n return value;\n };\n};\n\nValidation.Rules.regex.message = '{$alias} is not a valid value.';\n\n// required\nruleGenerator('required',\n '{$alias} is required.',\n function isInvalid(value) {\n return isEmpty( value );\n }\n);\n\n// min:3\nsizeRuleGenerator('min', {\n 'string': '{$alias} must have a minimum of {$number} characters.',\n 'number': '{$alias} must be at least {$number}.',\n 'object': '{$alias} must have at least {$number} items.'\n },\n function isInvalid(value, number) {\n return value < number;\n }\n);\n\n// greater_than:0\nsizeRuleGenerator('greater_than', {\n 'string': '{$alias} must have more than {$number} characters.',\n 'number': '{$alias} must be greater than {$number}.',\n 'object': '{$alias} must have more than {$number} items.'\n },\n function isInvalid(value, number) {\n return value <= number;\n }\n);\n\n// max:10\nsizeRuleGenerator('max', {\n 'string': '{$alias} must have no more than {$number} characters.',\n 'number': '{$alias} must be no more than {$number}.',\n 'object': '{$alias} must have no more than {$number} items.'\n },\n function isInvalid(value, number) {\n return value > number;\n }\n);\n\n// less_than:5\nsizeRuleGenerator('less_than', {\n 'string': '{$alias} must have less than {$number} characters.',\n 'number': '{$alias} must be less than {$number}.',\n 'object': '{$alias} must have less than {$number} items.'\n },\n function isInvalid(value, number) {\n return value >= number;\n }\n);\n\n// equal:4.5\nsizeRuleGenerator('equal', {\n 'string': '{$alias} must have {$number} characters.',\n 'number': '{$alias} must equal {$number}.',\n 'object': '{$alias} must have {$number} items.'\n },\n function isInvalid(value, number) {\n return value !== number;\n }\n);\n\n// not_equal:0\nsizeRuleGenerator('not_equal', {\n 'string': '{$alias} must not have {$number} characters.',\n 'number': '{$alias} must not equal {$number}.',\n 'object': '{$alias} must not have {$number} items.'\n },\n function isInvalid(value, number) {\n return value === number;\n }\n);\n\nfunction sizeRuleGenerator(ruleName, defaultMessages, isInvalid)\n{\n Validation.Rules[ ruleName ] = function(field, params, database, getAlias, message)\n {\n var number;\n\n if ( isString( params ) )\n {\n number = parseFloat( params );\n }\n else if ( isNumber( params ) )\n {\n number = params;\n }\n\n if ( isNaN( number ) )\n {\n throw '\"' + params + '\" is not a valid number for the ' + ruleName + ' rule';\n }\n\n if ( isString( message ) )\n {\n message = {\n 'string': message,\n 'number': message,\n 'object': message\n };\n }\n\n var messageTemplate = determineMessage( ruleName, message );\n var extra = {\n $number: params\n };\n\n return function(value, model, setMessage)\n {\n var size = sizeof( value );\n var type = typeof( value );\n var typeMessage = messageTemplate[ type ];\n\n if ( typeMessage && isInvalid( size, number ) )\n {\n extra.$size = size;\n\n setMessage( generateMessage( field, getAlias( field ), value, model, typeMessage, extra ) );\n }\n\n return value;\n };\n };\n\n Validation.Rules[ ruleName ].message = defaultMessages;\n}\n\n\nruleGenerator('array',\n '{$alias} must be an array.',\n function isInvalid(value) {\n return !isArray( value );\n }\n);\n\nruleGenerator('object',\n '{$alias} must be an object.',\n function isInvalid(value) {\n return !isObject( value );\n }\n);\n\nruleGenerator('string',\n '{$alias} must be a string.',\n function isInvalid(value) {\n return !isString( value );\n }\n);\n\nruleGenerator('number',\n '{$alias} must be a number.',\n function isInvalid(value) {\n return !isNumber( value );\n }\n);\n\nruleGenerator('boolean',\n '{$alias} must be a true or false.',\n function isInvalid(value) {\n return !isBoolean( value );\n }\n);\n\nruleGenerator('model',\n '{$alias} must have a value.',\n function isInvalid(value) {\n return !(value instanceof Model);\n }\n);\n\nruleGenerator('whole',\n '{$alias} must be a whole number.',\n function isInvalid(value, model, setValue) {\n var parsed = tryParseInt( value );\n var numeric = parseFloat( value );\n var invalid = !isNumber( parsed );\n if ( !invalid ) {\n invalid = Math.floor( parsed ) !== numeric;\n if ( !invalid ) {\n setValue( parsed );\n }\n }\n return invalid;\n }\n);\n\nruleGenerator('numeric',\n '{$alias} must be numeric.',\n function isInvalid(value, model, setValue) {\n var parsed = tryParseFloat( value );\n var invalid = !isNumber( parsed );\n if ( !invalid ) {\n setValue( parsed );\n }\n return invalid;\n }\n);\n\nruleGenerator('yesno',\n '{$alias} must be a yes or no.',\n function isInvalid(value, model, setValue) {\n var mapped = Validation.Rules.yesno.map[ value ];\n var invalid = !isBoolean( mapped );\n if ( !invalid ) {\n setValue( mapped );\n }\n return invalid;\n }\n);\n\nValidation.Rules.yesno.map =\n{\n 'true': true,\n 't': true,\n 'yes': true,\n 'y': true,\n '1': true,\n 'false': false,\n 'f': false,\n 'no': false,\n 'n': false,\n '0': false\n};\n\nValidation.Rules.abs = function(field, params, database, alias, message)\n{\n return function(value, model, setMessage)\n {\n value = tryParseFloat( value );\n\n if ( isNumber( value ) )\n {\n value = Math.abs( value );\n }\n\n return value;\n };\n};\n\nValidation.Rules.apply = function(field, params, database, alias, message)\n{\n return function(value, model, setMessage)\n {\n model.$set( field, value );\n \n return value;\n };\n};\n\nValidation.Rules.base64 = function(field, params, database, alias, message)\n{\n return function(value, model, setMessage)\n {\n if ( global.btoa )\n {\n value = global.btoa( value );\n }\n\n return value;\n };\n};\n\nValidation.Rules.ceil = function(field, params, database, alias, message)\n{\n return function(value, model, setMessage)\n {\n value = tryParseFloat( value );\n \n if ( isNumber( value ) )\n {\n value = Math.ceil( value );\n }\n\n return value;\n };\n};\n\nValidation.Rules.endOfDay = function(field, params, database, alias, message)\n{\n return function(value, model, setMessage)\n {\n return endOfDay( value );\n };\n};\n\nValidation.Rules.filter = function(field, params, database, alias, message)\n{\n return function(value, model, setMessage)\n {\n if ( isArray( value ) )\n {\n for (var i = value.length - 1; i >= 0; i--)\n {\n if ( !isValue( value[ i ] ) )\n {\n value.splice( i, 1 );\n }\n }\n }\n else if ( isObject( value ) )\n {\n for (var prop in value)\n {\n if ( !isValue( value[ prop ] ) )\n {\n delete value[ prop ];\n }\n }\n }\n\n return value;\n };\n};\n\nValidation.Rules.floor = function(field, params, database, alias, message)\n{\n return function(value, model, setMessage)\n {\n value = tryParseFloat( value );\n \n if ( isNumber( value ) )\n {\n value = Math.floor( value );\n }\n\n return value;\n };\n};\n\nValidation.Rules.mod = function(field, params, database, alias, message)\n{\n var number = tryParseFloat( params );\n\n if ( !isNumber( number ) )\n {\n throw '\"' + number + '\" is not a valid number for the mod rule.';\n }\n\n return function(value, model, setMessage)\n {\n value = tryParseFloat( value );\n\n if ( isNumber( value ) )\n {\n value = value % number;\n }\n\n return value;\n };\n};\n\nValidation.Rules.null = function(field, params, database, alias, message)\n{\n return function(value, model, setMessage)\n {\n model.$set( field, null );\n\n return null;\n };\n};\n\nValidation.Rules.round = function(field, params, database, alias, message)\n{\n return function(value, model, setMessage)\n {\n value = tryParseFloat( value );\n\n if ( isNumber( value ) )\n {\n value = Math.round( value );\n }\n\n return value;\n };\n};\n\nValidation.Rules.startOfDay = function(field, params, database, alias, message)\n{\n return function(value, model, setMessage)\n {\n return startOfDay( value );\n };\n};\n\nValidation.Rules.trim = function(field, params, database, alias, message)\n{\n var trim = (function()\n {\n if ( String.prototype.trim )\n {\n return function(x) {\n return x.trim();\n };\n }\n\n var regex = /^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g;\n\n return function(x)\n {\n return x.replace( regex, '' );\n };\n\n })();\n\n return function(value, model, setMessage)\n {\n if ( isString( value ) )\n {\n value = trim( value );\n }\n\n return value;\n };\n};\n\nValidation.Rules.unbase64 = function(field, params, database, alias, message)\n{\n return function(value, model, setMessage)\n {\n if ( global.atob )\n {\n value = global.atob( value );\n }\n\n return value;\n };\n};\n\n\n Rekord.Validation = Validation;\n\n Rekord.ruleGenerator = ruleGenerator;\n Rekord.rangeRuleGenerator = rangeRuleGenerator;\n Rekord.collectionRuleGenerator = collectionRuleGenerator;\n Rekord.dateRuleGenerator = dateRuleGenerator;\n Rekord.fieldListRuleGenerator = fieldListRuleGenerator;\n Rekord.fieldsRuleGenerator = fieldsRuleGenerator;\n Rekord.foreignRuleGenerator = foreignRuleGenerator;\n Rekord.subRuleGenerator = subRuleGenerator;\n Rekord.listRuleGenerator = listRuleGenerator;\n Rekord.regexRuleGenerator = regexRuleGenerator;\n Rekord.sizeRuleGenerator = sizeRuleGenerator;\n\n Rekord.joinFriendly = joinFriendly;\n Rekord.tryParseFloat = tryParseFloat;\n Rekord.tryParseInt = tryParseInt;\n Rekord.startOfDay = startOfDay;\n Rekord.endOfDay = endOfDay;\n Rekord.determineMessage = determineMessage;\n Rekord.mapFromArray = mapFromArray;\n Rekord.checkNoParams = checkNoParams;\n Rekord.generateMessage = generateMessage;\n\n return Rekord;\n\n}));\n"],"sourceRoot":"/source/"} \ No newline at end of file +{"version":3,"sources":["rekord-validation.min.js"],"names":["root","factory","define","amd","Rekord","module","exports","global","require","this","undefined","ValidationChain","model","field","validations","onEnd","tryParseFloat","x","parsed","parseFloat","isNaN","tryParseInt","parseInt","startOfDay","d","isDate","setHours","isNumber","endOfDay","ruleGenerator","ruleName","defaultMessage","isInvalid","Validation","Rules","params","database","getAlias","message","checkNoParams","messageTemplate","determineMessage","value","chain","invalid","generateMessage","next","joinFriendly","arr","lastSeparatorCustom","itemSeparatorCustom","copy","slice","i","length","last","pop","lastSeparator","itemSeparator","join","mapFromArray","map","alias","extra","isFunction","base","$field","$alias","$value","transfer","isObject","format","collectionRuleGenerator","matchField","matchValue","equality","isString","comma","indexOf","substring","isArray","equals","equalsCompare","fields","$matchField","$matchAlias","$matchValue","dateRuleGenerator","dateExpression","parseExpression","parseDate","parsedTime","getTime","noop","$date","date","fieldListRuleGenerator","matchValues","parts","split","shift","values","list","$params","$list","fieldsRuleGenerator","fieldNames","fieldAliases","$fields","$fieldAliases","foreignRuleGenerator","modelName","models","fieldName","isValue","name","ModelCollection","get","success","modelClass","all","isRekord","$class","subRuleGenerator","otherField","otherRules","colon","rules","validators","parseRules","invalids","chainCount","onChainEnd","innerChain","valid","stop","testValue","$get","start","listRuleGenerator","inList","isPrimitiveArray","rangeRuleGenerator","defaultMessages","end","range","string","number","object","$start","$end","size","sizeof","type","typeMessage","$size","regexRuleGenerator","regex","test","sizeRuleGenerator","$number","Model","Database","Promise","Collection","Class","isEmpty","isBoolean","isRegExp","create","reset","updated","linkIndex","call","update","newValue","n","addPlugin","db","options","aliases","validation","Defaults","messages","required","method","callback","context","$trigger","Events","PreValidate","$valid","$validations","$validationMessages","chainEnds","chains","push","ValidatePass","ValidateFail","replace","$init","apply","arguments","$save","$isDeleted","debug","Debugs","SAVE_DELETED","$db","resolve","promise","modelInstance","args","$validate","saving","then","reject","noline","cancel","Expression","Expressions","Delimiter","Escape","RuleSeparator","Stop","rule","defaultMessageValidator","parseRule","ruleProperty","ruleMessageOrData","ruleMessage","ruleInput","input","customMessageValidator","charAt","customValidator","ruleParams","validatorFactory","expr","parsers","parser","expressionFunction","functionName","result","RELATIVE_REGEX","RELATIVE_UNITS","ms","millisecond","milliseconds","s","second","seconds","min","mins","minute","minutes","hr","hour","hours","day","days","wk","week","weeks","month","months","yr","year","years","relative","exec","amount","unit","unitScale","Date","setTime","getter","setter","today","tomorrow","setDate","getDate","yesterday","accepted","acceptable","valueString","toLowerCase","1","yes","on","y","true","contains","m","validate","messageOption","related","pluck","confirmed","different","invalidCount","totalCount","RegExp","numeric","Math","floor","mapped","yesno","t","false","f","no","0","abs","$set","base64","btoa","ceil","filter","splice","prop","mod","round","stripEnts","stripTags","trim","String","prototype","unbase64","atob"],"mappings":"CAEC,SAAUA,EAAMC,GAEO,kBAAXC,SAAyBA,OAAOC,IAGzCD,QAAQ,UAAW,SAASE,GAC1B,MAAOH,GAAQD,EAAMI,KAGE,gBAAXC,SAAuBA,OAAOC,QAK5CD,OAAOC,QAAUL,EAAQM,OAAQC,QAAQ,WAKzCR,EAAKI,OAASH,EAAQD,EAAMA,EAAKI,SAEnCK,KAAM,SAASF,EAAQH,EAAQM,GAoCjC,QAASC,GAAgBC,EAAOC,EAAOC,EAAaC,GAElDN,KAAKG,MAAQA,EACbH,KAAKI,MAAQA,EACbJ,KAAKK,YAAcA,EACnBL,KAAKM,MAAQA,EA4Ef,QAASC,GAAcC,GAErB,GAAIC,GAASC,WAAYF,EAOzB,OALMG,OAAOF,KAEXD,EAAIC,GAGCD,EAGT,QAASI,GAAYJ,GAEnB,GAAIC,GAASI,SAAUL,EAOvB,OALMG,OAAOF,KAEXD,EAAIC,GAGCD,EAGT,QAASM,GAAWC,GAWlB,MATKC,GAAQD,GAEXA,EAAEE,SAAU,EAAG,EAAG,EAAG,GAEbC,EAAUH,KAElBA,GAASA,EAAI,OAGRA,EAGT,QAASI,GAASJ,GAWhB,MATKC,GAAQD,GAEXA,EAAEE,SAAU,GAAI,GAAI,GAAI,KAEhBC,EAAUH,KAElBA,EAAIA,EAAKA,EAAI,MAAY,MAAW,GAG/BA,EAGT,QAASK,GAAcC,EAAUC,EAAgBC,GAE/CC,EAAWC,MAAOJ,GAAa,SAASjB,EAAOsB,EAAQC,EAAUC,EAAUC,GAEzEC,EAAeT,EAAUjB,EAAOsB,EAEhC,IAAIK,GAAkBC,EAAkBX,EAAUQ,EAElD,OAAO,UAASI,EAAO9B,EAAO+B,GAEvBX,EAAWU,EAAO9B,EAAO+B,GAE5BA,EAAMC,QAASC,EAAiBhC,EAAOwB,EAAUxB,GAAS6B,EAAO9B,EAAO4B,IAIxEG,EAAMG,SAKZb,EAAWC,MAAOJ,GAAWQ,QAAUP,EAGzC,QAASU,GAAiBX,EAAUQ,GAElC,MAAOA,IAAWL,EAAWC,MAAOJ,GAAWQ,QAGjD,QAASS,GAAaC,EAAKC,EAAqBC,EAAqBb,GAEnE,GAAIc,GAAOH,EAAII,OAEf,IAAKf,EAEH,IAAK,GAAIgB,GAAI,EAAGA,EAAIF,EAAKG,OAAQD,IAE/BF,EAAME,GAAMhB,EAAUc,EAAME,GAIhC,IAAIE,GAAOJ,EAAKK,MACZC,EAAgBR,GAAuB,MACvCS,EAAgBR,GAAuB,IAE3C,QAAQC,EAAKG,QACX,IAAK,GACH,MAAOC,EACT,KAAK,GACH,MAAOJ,GAAM,GAAM,IAAMM,EAAgB,IAAMF,CACjD,SACE,MAAOJ,GAAKQ,KAAMD,GAAkBA,EAAgBD,EAAgB,IAAMF,GAIhF,QAASK,GAAaZ,EAAKN,GAIzB,IAAK,GAFDmB,MAEKR,EAAI,EAAGA,EAAIL,EAAIM,OAAQD,IAE9BQ,EAAKb,EAAKK,IAAQX,CAGpB,OAAOmB,GAGT,QAAStB,GAAcT,EAAUjB,EAAOsB,GAEtC,GAAKA,EAEH,KAAM,YAAcL,EAAW,cAAgBjB,EAAQ,oBAI3D,QAASgC,GAAgBhC,EAAOiD,EAAOpB,EAAO9B,EAAO0B,EAASyB,GAEvDC,EAAY1B,KAEfA,EAAUA,EAASzB,EAAOiD,EAAOpB,EAAO9B,EAAOmD,GAGjD,IAAIE,KAYJ,OAXAA,GAAKC,OAASrD,EACdoD,EAAKE,OAASL,EACdG,EAAKG,OAAS1B,EAEd2B,EAAUzD,EAAOqD,GAEZK,EAAUP,IAEbM,EAAUN,EAAOE,GAGZM,EAAQjC,EAAS2B,GA2b1B,QAASO,GAAwB1C,EAAUC,EAAgBC,GAEzDC,EAAWC,MAAOJ,GAAa,SAASjB,EAAOsB,EAAQC,EAAUC,EAAUC,GAEzE,IAAMH,EAEJ,KAAML,GAAW,mDAGnB,IAAI2C,GAAYC,EAAYC,CAE5B,IAAKC,EAAUzC,GACf,CACE,GAAI0C,GAAQ1C,EAAO2C,QAAQ,IAE3B,IAAe,KAAVD,EAEH,KAAM/C,GAAW,mDAGnB2C,GAAatC,EAAO4C,UAAW,EAAGF,GAClCH,EAAavC,EAAO4C,UAAWF,EAAQ,OAE/BG,GAAS7C,IAEjBsC,EAAatC,EAAQ,GACrBuC,EAAavC,EAAQ,GACrBwC,EAAWxC,EAAQ,IAEXmC,EAAUnC,KAElBsC,EAAatC,EAAOtB,MACpB6D,EAAavC,EAAOO,MACpBiC,EAAWxC,EAAO8C,OAQpB,IALMjB,EAAYW,KAEhBA,EAAWO,GAGmC,KAA3CJ,EAAS1C,EAAS+C,OAAQV,GAE7B,KAAMA,GAAa,iCAAmC3C,EAAW,OAGnE,IAAIU,GAAkBC,EAAkBX,EAAUQ,GAC9CyB,GACFqB,YAAaX,EACbY,YAAahD,EAAUoC,GACvBa,YAAaZ,EAGf,OAAO,UAAShC,EAAO9B,EAAO+B,GAEvBX,EAAWU,EAAO9B,EAAO6D,EAAYC,EAAYC,GAEpDhC,EAAMC,QAASC,EAAiBhC,EAAOwB,EAAUxB,GAAS6B,EAAO9B,EAAO4B,EAAiBuB,IAIzFpB,EAAMG,SAKZb,EAAWC,MAAOJ,GAAWQ,QAAUP,EAmGzC,QAASwD,GAAkBzD,EAAUC,EAAgBC,GAEnDC,EAAWC,MAAOJ,GAAa,SAASjB,EAAOsB,EAAQC,EAAUC,EAAUC,GAEzE,IAAMH,EAEJ,KAAML,GAAW,sDAGnB,IAAI0D,EAEJ,IAAKZ,EAAUzC,GAEbqD,EAAiBvD,EAAWwD,gBAAiBtD,EAAQC,OAElD,IAAK4B,EAAY7B,GAEpBqD,EAAiBrD,MAGnB,CACE,GAAIjB,GAASwE,EAAWvD,EAExB,IAAKjB,KAAW,EAChB,CACE,GAAIyE,GAAazE,EAAO0E,SAExBJ,GAAiB,WAEf,MAAOG,KAKb,IAAMH,GAAkBA,IAAmBK,EAEzC,KAAM1D,GAAS,2CAA6CL,EAAW,OAGzE,IAAIU,GAAkBC,EAAkBX,EAAUQ,GAC9CyB,GACF+B,MAAO3D,EAGT,OAAO,UAASO,EAAO9B,EAAO+B,GAE5B,GAAIzB,GAASwE,EAAWhD,EAExB,IAAKxB,KAAW,EAChB,CACEwB,EAAQxB,EAAO0E,SAEf,IAAIG,GAAOP,EAAgB9C,EAAO9B,EAE7Be,GAAUoE,IAAU/D,EAAWU,EAAOqD,GAEzCpD,EAAMC,QAASC,EAAiBhC,EAAOwB,EAAUxB,GAAS6B,EAAO9B,EAAO4B,EAAiBuB,IAIzFpB,EAAMG,WAKRH,GAAMG,SAKZb,EAAWC,MAAOJ,GAAWQ,QAAUP,EAwBzC,QAASiE,GAAuBlE,EAAUC,EAAgBC,GAExDC,EAAWC,MAAOJ,GAAa,SAASjB,EAAOsB,EAAQC,EAAUC,EAAUC,GAEzE,IAAMH,EAEJ,KAAML,GAAW,sDAGnB,IAAI2C,GAAYwB,CAEhB,IAAKrB,EAAUzC,GACf,CACE,GAAI+D,GAAQC,EAAOhE,EAAQ,MAAO,KAElCsC,GAAayB,EAAME,QACnBH,EAAcC,MAENlB,GAAS7C,IAEjBsC,EAAatC,EAAOiE,QACpBH,EAAc9D,GAENmC,EAAUnC,KAElBsC,EAAatC,EAAOtB,MACpBoF,EAAc9D,EAAOkE,OAGvB,IAAKvB,EAAS1C,EAAS+C,OAAQV,MAAiB,EAE9C,KAAMA,GAAa,iCAAmC3C,EAAW,OAGnE,IAAIU,GAAkBC,EAAkBX,EAAUQ,GAC9CgE,EAAOvD,EAAckD,GACrBlC,GACFwC,QAASpE,EACTiD,YAAaX,EACbY,YAAahD,EAAUoC,GACvB+B,MAAOF,GAELzC,EAAMD,EAAcqC,GAAa,EAErC,OAAO,UAASvD,EAAO9B,EAAO+B,GAEvBX,EAAWU,EAAO9B,EAAO6D,EAAYwB,EAAapC,GAErDlB,EAAMC,QAASC,EAAiBhC,EAAOwB,EAAUxB,GAAS6B,EAAO9B,EAAO4B,EAAiBuB,IAIzFpB,EAAMG,SAKZb,EAAWC,MAAOJ,GAAWQ,QAAUP,EA0IzC,QAAS0E,GAAoB3E,EAAUC,EAAgBC,GAErDC,EAAWC,MAAOJ,GAAa,SAASjB,EAAOsB,EAAQC,EAAUC,EAAUC,GAEzE,IAAMH,EAEJ,KAAML,GAAW,uDAKnB,KAAK,GAFDqD,GAASgB,EAAOhE,EAAQ,YAAa,MAEhCkB,EAAI,EAAGA,EAAI8B,EAAO7B,OAAQD,IAEjC,GAAiD,KAA5CyB,EAAS1C,EAAS+C,OAAQA,EAAQ9B,IAErC,KAAM8B,GAAQ9B,GAAM,iCAAmCvB,EAAW,OAItE,IAAIU,GAAkBC,EAAkBX,EAAUQ,GAC9CoE,EAAa3D,EAAcoC,GAC3BwB,EAAe5D,EAAcoC,GAAQ,GAAO,EAAO9C,GACnD0B,GACF6C,QAASF,EACTG,cAAeF,EAGjB,OAAO,UAASjE,EAAO9B,EAAO+B,GAEvBX,EAAWU,EAAO9B,EAAOuE,EAAQxC,GAEpCA,EAAMC,QAASC,EAAiBhC,EAAOwB,EAAUxB,GAAS6B,EAAO9B,EAAO4B,EAAiBuB,IAIzFpB,EAAMG,SAKZb,EAAWC,MAAOJ,GAAWQ,QAAUP,EAsCzC,QAAS+E,GAAqBhF,EAAUC,EAAgBC,GAEtDC,EAAWC,MAAOJ,GAAa,SAASjB,EAAOsB,EAAQC,EAAUC,EAAUC,GAEzE,GAAIyE,GAAWC,EAAQC,CAEvB,KAAMC,EAAS/E,IAAYyC,EAAUzC,GACrC,CACE,GAAI+D,GAAQC,EAAOhE,GAAU,GAAI,YAAa,KAC9C4E,GAAYb,EAAM,IAAM9D,EAAS+E,KACjCF,EAAYf,EAAM,IAAMrF,EACxBmG,EAAS,SAEDhC,GAAS7C,IAEjB4E,EAAYnC,EAAUzC,EAAO,IAAOA,EAAOiE,QAAUhE,EAAS+E,KAC9DF,EAAYrC,EAAUzC,EAAO,IAAOA,EAAOiE,QAAUvF,EACrDmG,EAAS,GAAII,GAAiBhF,EAAUD,IAEhCmC,EAAUnC,KAElB4E,EAAY5E,EAAOvB,OAASwB,EAAS+E,KACrCF,EAAY9E,EAAOtB,OAASA,EAC5BmG,EAAS7E,EAAO6E,OAGlB,KAAMA,EACN,CACE,IAAMD,EAEJ,KAAM,iDAAmDjF,EAAW,OAGjE8C,GAAUmC,GAEb3G,EAAOiH,IAAKN,GAAYO,QAAQ,SAASC,GAEvCP,EAASO,EAAWC,QAGdC,EAAUV,KAElBC,EAASD,EAAUS,OAIvB,GAAK1C,EAAS1C,EAAS+C,OAAQ8B,MAAgB,EAE7C,KAAMA,GAAY,iCAAmCnF,EAAW,OAGlE,IAAIU,GAAkBC,EAAkBX,EAAUQ,GAC9CyB,GACF2D,OAAQX,EACR3B,YAAa6B,EACb5B,YAAahD,EAAU4E,GAGzB,OAAO,UAASvE,EAAO9B,EAAO+B,GAEvBqE,GAAUE,EAASxE,IAEjBV,EAAWU,EAAO9B,EAAOoG,EAAQC,GAEpCtE,EAAMC,QAASC,EAAiBhC,EAAOwB,EAAUxB,GAAS6B,EAAO9B,EAAO4B,EAAiBuB,IAS3FpB,EAAMG,SAKZb,EAAWC,MAAOJ,GAAWQ,QAAUP,EA4BzC,QAAS4F,GAAiB7F,EAAUE,GAElCC,EAAWC,MAAOJ,GAAa,SAASjB,EAAOsB,EAAQC,EAAUC,EAAUC,GAEzE,IAAMH,EAEJ,KAAML,GAAW,sDAGnB,IAAI8F,GAAYC,CAEhB,IAAKjD,EAAUzC,GACf,CACE,GAAI2F,GAAQ3F,EAAO2C,QAAS,IAE5B,IAAe,KAAVgD,EAEH,KAAM3F,GAAS,oCAAsCL,EAAW,OAGlE8F,GAAazF,EAAO4C,UAAW,EAAG+C,IAAWjH,EAC7CgH,EAAa1F,EAAO4C,UAAW+C,EAAQ,OAE/B9C,GAAS7C,IAEjByF,EAAazF,EAAOiE,SAAWvF,EAC/BgH,EAAa1F,GAELmC,EAAUnC,KAElByF,EAAazF,EAAOtB,OAASA,EAC7BgH,EAAa1F,EAAO4F,MAGtB,IAAgD,KAA3CjD,EAAS1C,EAAS+C,OAAQyC,GAE7B,KAAMA,GAAa,iCAAmC9F,EAAW,OAGnE,KAAM+F,EAEJ,KAAM,8BAAgC/F,EAAW,OAGnD,IAAIkG,GAAa/F,EAAWgG,WAAYJ,EAAYD,EAAYxF,EAAUC,EAE1E,OAAO,UAASK,EAAO9B,EAAO+B,GA2B5B,IAAK,GAzBDuF,GAAW,EACXC,EAAa,EAEbC,EAAa,SAASC,GAEnBA,EAAWC,OAEdJ,MAGIC,IAAeH,EAAW1E,SAEzBtB,EAAWkG,EAAUC,GAExBxF,EAAM4F,OAIN5F,EAAMG,SAKR0F,EAAYZ,IAAe/G,EAAQ6B,EAAQ9B,EAAM6H,KAAMb,GAElDvE,EAAI,EAAGA,EAAI2E,EAAW1E,OAAQD,IACvC,CACE,GAAIgF,GAAa,GAAI1H,GAAiBC,EAAOgH,GAAaI,EAAY3E,IAAM+E,EAE5EC,GAAWK,MAAOF,MAwB1B,QAASG,GAAkB7G,EAAUC,EAAgBC,GAEnDC,EAAWC,MAAOJ,GAAa,SAASjB,EAAOsB,EAAQC,EAAUC,EAAUC,GAEzE,IAAMH,EAEJ,KAAML,GAAW,2CAGnB,IAAIuE,GAAQuC,GAAS,CAerB,IAbKhE,EAAUzC,GAEbkE,EAASF,EAAOhE,EAAQ,MAAO,MAEvB6C,EAAS7C,GAEjBkE,EAASlE,EAED6B,EAAY7B,KAEpBkE,EAASuC,GAGNA,KAAW,KAERvC,GAA4B,IAAlBA,EAAO/C,QAErB,KAAMnB,GAAS,0CAA4CL,EAAW,OAI1E,IAAK+G,EAAkBxC,GACvB,CACE,GAAIxC,GAAMD,EAAcyC,GAAQ,EAEhCuC,GAAS,SAASlG,GAEhB,MAAOmB,GAAKnB,QAKdkG,GAAS,SAASlG,GAEhB,MAAOoC,GAASuB,EAAQ3D,EAAOuC,GAInC,IAAIzC,GAAkBC,EAAkBX,EAAUQ,GAC9CgE,EAAOvD,EAAcsD,EAAQ,MAC7BtC,GACFwC,QAASpE,EACTqE,MAAOF,EAGT,OAAO,UAAS5D,EAAO9B,EAAO+B,GAEvBX,EAAWU,EAAO9B,EAAOgI,GAE5BjG,EAAMC,QAASC,EAAiBhC,EAAOwB,EAAUxB,GAAS6B,EAAO9B,EAAO4B,EAAiBuB,IAIzFpB,EAAMG,SAMZb,EAAWC,MAAOJ,GAAWQ,QAAUP,EAyBzC,QAAS+G,GAAmBhH,EAAUiH,EAAiB/G,GAErDC,EAAWC,MAAOJ,GAAa,SAASjB,EAAOsB,EAAQC,EAAUC,EAAUC,GAEzE,IAAMH,EAEJ,KAAML,GAAW,4CAGnB,IAAI4G,GAAOM,CAEX,IAAKpE,EAAUzC,GACf,CACE,GAAI8G,GAAQ9C,EAAOhE,EAAQ,YAAa,KAExCuG,GAAQvH,WAAY8H,EAAM,IAC1BD,EAAM7H,WAAY8H,EAAM,QAEhBjE,GAAS7C,IAEjBuG,EAAQvG,EAAQ,GAChB6G,EAAM7G,EAAQ,IAENmC,EAAUnC,KAElBuG,EAAQvG,EAAOuG,MACfM,EAAM7G,EAAO6G,IAGf,IAAK5H,MAAOsH,IAAWtH,MAAO4H,GAE5B,KAAM7G,GAAS,4CAA8CL,EAAW,OAGrE8C,GAAUtC,KAEbA,GACE4G,OAAU5G,EACV6G,OAAU7G,EACV8G,OAAU9G,GAId,IAAIE,GAAkBC,EAAkBX,EAAUQ,GAC9CyB,GACFsF,OAAQX,EACRY,KAAMN,EAGR,OAAO,UAAStG,EAAO9B,EAAO+B,GAE5B,GAAI4G,GAAOC,EAAQ9G,GACf+G,QAAa,GACbC,EAAclH,EAAiBiH,EAE9BC,IAAe1H,EAAWuH,EAAMb,EAAOM,IAE1CjF,EAAM4F,MAAQJ,EAEd5G,EAAMC,QAASC,EAAiBhC,EAAOwB,EAAUxB,GAAS6B,EAAO9B,EAAO8I,EAAa3F,KAIrFpB,EAAMG,SAKZb,EAAWC,MAAOJ,GAAWQ,QAAUyG,EAwCzC,QAASa,GAAmB9H,EAAUC,EAAgB8H,GAEpD5H,EAAWC,MAAOJ,GAAa,SAASjB,EAAOsB,EAAQC,EAAUC,EAAUC,GAEzEC,EAAeT,EAAUjB,EAAOsB,EAEhC,IAAIK,GAAkBC,EAAkBX,EAAUQ,EAElD,OAAO,UAASI,EAAO9B,EAAO+B,GAW5B,MATMkH,GAAMC,KAAMpH,GAMhBC,EAAMG,OAJNH,EAAMC,QAASC,EAAiBhC,EAAOwB,EAAUxB,GAAS6B,EAAO9B,EAAO4B,IAOnEE,IAIXT,EAAWC,MAAOJ,GAAWQ,QAAUP,EAqHzC,QAASgI,GAAkBjI,EAAUiH,EAAiB/G,GAEpDC,EAAWC,MAAOJ,GAAa,SAASjB,EAAOsB,EAAQC,EAAUC,EAAUC,GAEzE,GAAI6G,EAWJ,IATKvE,EAAUzC,GAEbgH,EAAShI,WAAYgB,GAEbR,EAAUQ,KAElBgH,EAAShH,GAGNf,MAAO+H,GAEV,KAAM,IAAMhH,EAAS,mCAAqCL,EAAW,OAGlE8C,GAAUtC,KAEbA,GACE4G,OAAU5G,EACV6G,OAAU7G,EACV8G,OAAU9G,GAId,IAAIE,GAAkBC,EAAkBX,EAAUQ,GAC9CyB,GACFiG,QAAS7H,EAGX,OAAO,UAASO,EAAO9B,EAAO+B,GAE5B,GAAI4G,GAAOC,EAAQ9G,GACf+G,QAAa,GACbC,EAAclH,EAAiBiH,EAE9BC,IAAe1H,EAAWuH,EAAMJ,IAEnCpF,EAAM4F,MAAQJ,EAEd5G,EAAMC,QAASC,EAAiBhC,EAAOwB,EAAUxB,GAAS6B,EAAO9B,EAAO8I,EAAa3F,KAIrFpB,EAAMG,SAKZb,EAAWC,MAAOJ,GAAWQ,QAAUyG,EA9yDvC,GAAIkB,GAAQ7J,EAAO6J,MACfC,EAAW9J,EAAO8J,SAClBC,EAAU/J,EAAO+J,QACjBC,EAAahK,EAAOgK,WACpBhD,EAAkBhH,EAAOgH,gBACzBiD,EAAQjK,EAAOiK,MAEfC,EAAUlK,EAAOkK,QACjB1F,EAAWxE,EAAOwE,SAClBI,EAAU5E,EAAO4E,QACjBV,EAAWlE,EAAOkE,SAClBN,EAAa5D,EAAO4D,WACpBvC,EAASrB,EAAOqB,OAChBE,EAAWvB,EAAOuB,SAClB4I,EAAYnK,EAAOmK,UACnBrD,EAAU9G,EAAO8G,QACjB2B,EAAmBzI,EAAOyI,iBAC1B2B,EAAWpK,EAAOoK,SAClB/C,EAAWrH,EAAOqH,SAElB5B,EAAOzF,EAAOyF,KACdX,EAAgB9E,EAAO8E,cACvBD,EAAS7E,EAAO6E,OAChBH,EAAU1E,EAAO0E,QACjB0E,EAASpJ,EAAOoJ,OAEhBrD,EAAQ/F,EAAO+F,MACf9B,EAAWjE,EAAOiE,SAClBE,EAASnE,EAAOmE,OAEhBmB,EAAYtF,EAAOsF,SAWzB2E,GAAMI,OAAQ9J,GAGZ+J,MAAO,SAAShI,GAEdjC,KAAKiC,MAAQA,IAAUhC,EAAYgC,EAAQjC,KAAKG,MAAM6H,KAAMhI,KAAKI,OACjEJ,KAAKkK,SAAU,EACflK,KAAK6H,OAAQ,EACb7H,KAAK6B,QAAU,GACf7B,KAAKmK,UAAY,GAGnBlC,MAAO,SAAShG,GAEdjC,KAAKiK,MAAOhI,GACZjC,KAAKoK,QAGPA,KAAM,WAEJpK,KAAKK,YAAaL,KAAKmK,WAAanK,KAAKiC,MAAOjC,KAAKG,MAAOH,OAG9DqK,OAAQ,SAASC,GAKf,MAHAtK,MAAKiC,MAAQqI,EACbtK,KAAKkK,SAAU,EAERlK,MAGTqC,KAAM,WAEJ,GAAIkI,GAAIvK,KAAKK,YAAYwC,MAazB,OAXA7C,MAAKmK,YAEDnK,KAAKmK,YAAcI,EAErBvK,KAAKM,MAAON,MAELA,KAAKmK,UAAYI,GAExBvK,KAAKoK,OAGApK,MAGT8H,KAAM,WAEJ,GAAIyC,GAAIvK,KAAKK,YAAYwC,MAQzB,OANI7C,MAAKmK,UAAYI,IAEnBvK,KAAKmK,UAAYI,EAAI,EACrBvK,KAAKqC,QAGArC,MAGTmC,QAAS,SAASN,GAMhB,MAJA7B,MAAK6B,QAAUA,EACf7B,KAAK6H,OAAQ,EACb7H,KAAK8H,OAEE9H,QA0JXL,EAAO6K,UAAU,SAASrK,EAAOsK,EAAIC,GAcnC,QAAS9I,GAASxB,GAEhB,MAAOuK,GAASvK,IAAWA,EAd7B,GAAIwK,GAAaF,EAAQE,YAAcnB,EAASoB,SAASD,UAEzD,KAAKf,EAASe,GAAd,CAKA,GAAItD,GAAQsD,EAAWtD,UACnBwD,EAAWF,EAAWE,aACtBH,EAAUC,EAAWD,YACrBI,IAAaH,EAAWG,QAO5BN,GAAGpK,cAEH,KAAM,GAAID,KAASkH,GAEjBmD,EAAGpK,YAAaD,GAAUoB,EAAWgG,WAAYF,EAAOlH,GAASA,EAAOqK,EAAI7I,EAAUkJ,EAAU1K,GAGlGwJ,GAAMoB,OAAQ7K,EAAO,YAAa,SAAS8K,EAAUC,GAEnDlL,KAAKmL,SAAU3B,EAAM4B,OAAOC,aAAcrL,OAE1CA,KAAKsL,QAAS,EACdtL,KAAKuL,gBACLvL,KAAKwL,oBAAoB3I,OAAS,CAElC,IAAI4I,GAAY,EACZC,KAEA/D,EAAa,SAASzF,GAExB,GAAI/B,GAAQ+B,EAAM/B,KAEb+B,GAAM2F,QAET1H,EAAMoL,aAAcrJ,EAAM9B,OAAU8B,EAAML,QAC1C1B,EAAMqL,oBAAoBG,KAAMzJ,EAAML,SACtC1B,EAAMmL,QAAS,KAGXG,IAAcC,EAAO7I,SAEzB1C,EAAMgL,SAAUhL,EAAMmL,OAAS9B,EAAM4B,OAAOQ,aAAepC,EAAM4B,OAAOS,cAAe1L,IAElFoD,EAAY0H,IAEfA,EAASb,KAAMc,GAAW/K,EAAOA,EAAMmL,SAK7C,KAAK,GAAIlL,KAASqK,GAAGpK,YACrB,CACE,GAAIA,GAAcoK,EAAGpK,YAAaD,GAC9B8B,EAAQ,GAAIhC,GAAiBF,KAAMI,EAAOC,EAAasH,EAE3D+D,GAAOC,KAAMzJ,GAGf,IAAK,GAAIU,GAAI,EAAGA,EAAI8I,EAAO7I,OAAQD,IAEjC8I,EAAQ9I,GAAIqF,OAGd,OAAOjI,MAAKsL,SAGd1B,EAAMkC,QAAS3L,EAAO,QAAS,SAAS4L,GAEtC,MAAO,YAML,MAJA/L,MAAKsL,OAASrL,EACdD,KAAKuL,gBACLvL,KAAKwL,uBAEEO,EAAMC,MAAOhM,KAAMiM,cAIzBlB,GAEHnB,EAAMkC,QAAS3L,EAAO,QAAS,SAAS+L,GAEtC,MAAO,YAEL,GAAKlM,KAAKmM,aAIR,MAFAxM,GAAOyM,MAAOzM,EAAO0M,OAAOC,aAActM,KAAKuM,IAAKvM,MAE7C0J,EAAQ8C,QAASxM,KAG1B,IAAIyM,GAAU,GAAI9M,GAAO+J,QACrBgD,EAAgB1M,KAChB2M,EAAOV,SAgBX,OAdAjM,MAAK4M,UAAU,SAAS/E,GAEtB,GAAKA,EAKL,CACE,GAAIgF,GAASX,EAAMF,MAAOU,EAAeC,EAEzCE,GAAOC,KAAML,EAAQD,QAASC,EAAQM,OAAQN,EAAQO,OAAQP,EAAQQ,OAAQR,OAN9EA,GAAQM,OAAQL,KAUbD,QAMfjD,EAAM4B,OAAOC,YAAc,eAE3B7B,EAAM4B,OAAOQ,aAAe,gBAE5BpC,EAAM4B,OAAOS,aAAe,eAE5B,IAAIrK,IAEFC,SACAyL,cACAC,eACAC,UAAW,QACXC,OAAQ,KACRC,cAAe,IACfC,QAEA/F,WAAY,SAASF,EAAOlH,EAAOuB,EAAUC,EAAUC,GAErD,GAAI0F,KAOJ,IALKpD,EAAUmD,KAEbA,EAAQ5B,EAAO4B,EAAOtH,KAAKoN,UAAWpN,KAAKqN,SAGxC9I,EAAS+C,GAEZ,IAAK,GAAI1E,GAAI,EAAGA,EAAI0E,EAAMzE,OAAQD,IAClC,CACE,GAAI4K,GAAOlG,EAAO1E,GACd6K,EAA0BzN,KAAK0N,UAAWF,EAAMpN,EAAOuB,EAAUC,EAAUC,EAE/E0F,GAAWoE,KAAM8B,OAGhB,IAAK5J,EAAUyD,GAElB,IAAK,GAAIqG,KAAgBrG,GACzB,CACE,GAAIsG,GAAoBtG,EAAOqG,GAE3BE,EAAchK,EAAU+J,GAAsBA,EAAkB/L,QAChEsC,EAAUyJ,GAAsBA,EAAoB3N,EAEpD6N,EAAYjK,EAAU+J,IAAuBA,EAAkB/L,QAAU+L,EAAkBG,MAC3F5J,EAAUyJ,GAAsB3N,EAAY2N,EAE5CI,EAAyBhO,KAAK0N,UAAWC,EAAcvN,EAAOuB,EAAUC,EAAUiM,GAAehM,EAASiM,EAE9GvG,GAAWoE,KAAMqC,GAIrB,MAAOzG,IAGTmG,UAAW,SAASF,EAAMpN,EAAOuB,EAAUC,EAAUC,EAASkM,GAE5D,GAAI1G,GAAQmG,EAAKnJ,QAASrE,KAAKsN,eAC3BjM,EAAqB,KAAVgG,EAAemG,EAAOA,EAAKlJ,UAAW,EAAG+C,EAExD,IAA8B,MAAzBhG,EAAS4M,OAAQ,GAEpB,MAAOjO,MAAKkO,gBAAiB7M,EAAUjB,EAAOuB,EAAUC,EAAUC,EAGpE,IAAIsM,GAAuB,KAAV9G,EAAe0G,EAAQP,EAAKlJ,UAAW+C,EAAQ,GAC5D+G,EAAmB5M,EAAWC,MAAOJ,EAEzC,KAAM+M,EAEJ,KAAM/M,GAAW,sBAGnB,OAAO+M,GAAkBhO,EAAO+N,EAAYxM,EAAUC,EAAUC,IAGlEmD,gBAAiB,SAASqJ,EAAM1M,GAI9B,IAAK,GAFD2M,GAAU9M,EAAW2L,YAEhBvK,EAAI,EAAGA,EAAI0L,EAAQzL,OAAQD,IACpC,CACE,GAAI2L,GAASD,EAAS1L,GAClB4L,EAAqBD,EAAQF,EAAM1M,EAEvC,IAAK4B,EAAYiL,GAEf,MAAOA,GAIX,MAAOpJ,IAGT8I,gBAAiB,SAASO,EAAcrO,EAAOuB,EAAUC,EAAUC,GAEjE,MAAO,UAASI,EAAO9B,EAAO+B,GAE5B,GAAIwM,GAASvO,EAAOsO,GAAgBxM,EAAOL,EAAUC,EAASK,EAEzDiC,GAAUuK,GAEbxM,EAAMC,QAASuM,GAEPA,KAAW,GAEnBxM,EAAMG,SAMdb,GAAW0L,WAAW5H,KACtB9D,EAAW2L,YAAYxB,KAAK,SAAS0C,EAAM1M,GAEzC,GAAIlB,GAASwE,EAAWoJ,EAExB,IAAK5N,KAAW,EAChB,CACE,GAAIyE,GAAazE,EAAO0E,SAExB,OAAO,UAASlD,EAAO9B,GAErB,MAAO+E,OAGR,EAEL1D,EAAW0L,WAAW9M,MACtBoB,EAAW2L,YAAYxB,KAAK,SAAS0C,EAAM1M,GAEzC,MAAK0C,GAAS1C,EAAS+C,OAAQ2J,GAEtB,SAASpM,EAAO9B,GAErB,MAAOA,GAAM6H,KAAMqG,IAJvB,SAOG,CAGL,IAAIM,GAAiB,6BAEjBC,GACFC,GAAI,EACJC,YAAa,EACbC,aAAc,EACdC,EAAG,IACHC,OAAQ,IACRC,QAAS,IACTC,IAAK,IACLC,KAAM,IACNC,OAAQ,IACRC,QAAS,IACTC,GAAI,KACJC,KAAM,KACNC,MAAO,KACPC,IAAK,MACLC,KAAM,MACNC,GAAI,OACJC,KAAM,OACNC,MAAO,OACPC,OAAQ,WAAY,YACpBC,QAAS,WAAY,YACrBC,IAAK,cAAe,eACpBC,MAAO,cAAe,eACtBC,OAAQ,cAAe,eAinDvB,OA9mDF3O,GAAW0L,WAAWkD,SACtB5O,EAAW2L,YAAYxB,KAAK,SAAS0C,EAAM1M,GAEzC,GAAIlB,GAASkO,EAAe0B,KAAMhC,EAElC,IAAgB,OAAX5N,EACL,CACE,GAAI6P,GAAS5P,WAAYD,EAAQ,IAC7B8P,EAAO9P,EAAQ,GACf+P,EAAY5B,EAAgB2B,EAEhC,KAAMC,EAEJ,KAAMD,GAAO,uBAGf,OAAO,UAAStO,EAAO9B,GAErB,GAAIiQ,GAAW,GAAIK,KAEnB,IAAKvP,EAAUsP,GAEbJ,EAASM,QAASN,EAASjL,UAAYqL,EAAYF,OAGrD,CACE,GAAIK,GAASH,EAAU,GACnBI,EAASJ,EAAU,EAEvBJ,GAAUQ,GAAUR,EAAUO,KAAaL,GAG7C,MAAOF,GAASjL,cAGjB,EAEL3D,EAAW0L,WAAW2D,MACtBrP,EAAW2L,YAAYxB,KAAK,SAAS0C,EAAM1M,GAEzC,MAAc,UAAT0M,EAEI,SAASpM,EAAO9B,GAErB,GAAI0Q,GAAQ,GAAIJ,KAIhB,OAFA3P,GAAY+P,GAELA,EAAM1L,WARjB,SAWG,EAEL3D,EAAW0L,WAAW4D,SACtBtP,EAAW2L,YAAYxB,KAAK,SAAS0C,EAAM1M,GAEzC,MAAc,aAAT0M,EAEI,SAASpM,EAAO9B,GAErB,GAAI2Q,GAAW,GAAIL,KAKnB,OAHAK,GAASC,QAASD,EAASE,UAAY,GACvClQ,EAAYgQ,GAELA,EAAS3L,WATpB,SAYG,EAEL3D,EAAW0L,WAAW+D,UACtBzP,EAAW2L,YAAYxB,KAAK,SAAS0C,EAAM1M,GAEzC,MAAc,cAAT0M,EAEI,SAASpM,EAAO9B,GAErB,GAAI8Q,GAAY,GAAIR,KAKpB,OAHAQ,GAAUF,QAASE,EAAUD,UAAY,GACzClQ,EAAYmQ,GAELA,EAAU9L,WATrB,SAYG,EAGL3D,EAAWC,MAAMyP,SAAW,SAAS9Q,EAAOsB,EAAQC,EAAUC,EAAUC,GAEtEC,EAAe,WAAY1B,EAAOsB,EAElC,IAAIK,GAAkBC,EAAkB,WAAYH,GAChDsP,EAAa3P,EAAWC,MAAMyP,SAASC,UAE3C,OAAO,UAASlP,EAAO9B,EAAO+B,GAE5B,GAAIkP,IAAenP,EAAQ,IAAIoP,cAC3BH,EAAWC,EAAYC,EAErBF,GAMJhP,EAAMG,OAJNH,EAAMC,QAASC,EAAiBhC,EAAOwB,EAAUxB,GAAS6B,EAAO9B,EAAO4B,MAS9EP,EAAWC,MAAMyP,SAASrP,QAAU,kCAEpCL,EAAWC,MAAMyP,SAASC,YAExBG,GAAQ,EACRC,KAAQ,EACRC,IAAQ,EACRC,GAAQ,EACRC,QAAQ,GAIV3N,EAAwB,WACtB,8EACA,SAAmB9B,EAAO9B,EAAO6D,EAAYC,EAAYC,GAEvD,OAAQjC,EAAM0P,SAAS,SAAiBC,GAEtC,MAAOA,KAAMzR,GAAS+D,EAAUD,EAAY2N,EAAE5J,KAAMhE,QAM1DD,EAAwB,eACtB,sEACA,SAAmB9B,EAAO9B,EAAO6D,EAAYC,EAAYC,GAEvD,MAAOjC,GAAM0P,SAAS,SAAiBC,GAErC,MAAOA,KAAMzR,GAAS+D,EAAUD,EAAY2N,EAAE5J,KAAMhE,QA0E1DxC,EAAWC,MAAMoQ,SAAW,SAASzR,EAAOsB,EAAQC,EAAUC,EAAUC,GAGtE,GAAIiQ,GAAgBpQ,GAAU,UAC1BK,EAAkBC,EAAkB,WAAYH,EAEpD,OAAO,UAASI,EAAO9B,EAAO+B,GAE5B,GAAKqC,EAAStC,GACd,CAGE,IAAK,GAFDE,GAAU,GAAIwH,GAET/G,EAAI,EAAGA,EAAIX,EAAMY,OAAQD,IAClC,CACE,GAAImP,GAAU9P,EAAOW,EAEhBmP,IAAWA,EAAQnF,YAAcmF,EAAQnF,aAE5CzK,EAAQwJ,KAAMoG,GAIlB,GAAK5P,EAAQU,OAEX,OAAQiP,GAEN,IAAK,SACH5P,EAAMC,QAASA,EACf,MACF,KAAK,cACHD,EAAMC,QAASA,EAAQ6P,MAAO,eAAgB,SAC9C,MACF,SACE9P,EAAMC,QAASC,EAAiBhC,EAAOwB,EAAUxB,GAAS6B,EAAO9B,EAAO4B,QAM5EG,GAAMG,WAKRH,GAAMG,SAKZb,EAAWC,MAAMoQ,SAAShQ,QAAU,yBAGpCiD,EAAkB,QAChB,kCACA,SAAmB7C,EAAOqD,GACxB,MAAOrD,GAAQd,EAAUmE,KAK7BR,EAAkB,WAChB,8CACA,SAAmB7C,EAAOqD,GACxB,MAAeA,GAARrD,IAKX6C,EAAkB,SAChB,mCACA,SAAmB7C,EAAOqD,GACxB,MAAOrD,GAAQqD,IAKnBR,EAAkB,YAChB,+CACA,SAAmB7C,EAAOqD,GACxB,MAAOrD,GAAQd,EAAUmE,KAK7BlE,EAAc,YACZ,iCACA,SAAmBa,EAAO9B,EAAO+B,GAC/B,GAAIzB,GAASwE,EAAWhD,GACpBE,EAAU1B,KAAW,CAIzB,OAHM0B,IACJD,EAAMmI,OAAQ5J,EAAO0E,WAEhBhD,IA+EXoD,EAAuB,cACrB,wBACA,SAAmBtD,EAAO9B,EAAOC,EAAOwF,EAAQxC,GAC9C,GAAI2H,GAAW3H,EAAKjD,EAAM6H,KAAM5H,GAEhC,OAAO2K,IAAYlB,EAAS5H,KAKhCsD,EAAuB,kBACrB,wBACA,SAAmBtD,EAAO9B,EAAOC,EAAOwF,EAAQxC,GAC9C,GAAI2H,IAAY3H,EAAKjD,EAAM6H,KAAM5H,GAEjC,OAAO2K,IAAYlB,EAAS5H,KAiEhC+D,EAAoB,YAClB,uCACA,SAAmB/D,EAAO9B,EAAOuE,EAAQxC,GAGvC,IAAK,GAFD+P,IAAY,EAEPrP,EAAI,EAAGA,EAAI8B,EAAO7B,OAAQD,IAE3B4B,EAAQvC,EAAO9B,EAAM6H,KAAMtD,EAAQ9B,OAEvCqP,GAAY,EAIhB,QAAQA,IAKZjM,EAAoB,YAClB,2CACA,SAAmB/D,EAAO9B,EAAOuE,EAAQxC,GAGvC,IAAK,GAFDgQ,IAAY,EAEPtP,EAAI,EAAGA,EAAI8B,EAAO7B,OAAQD,IAE3B4B,EAAQvC,EAAO9B,EAAM6H,KAAMtD,EAAQ9B,OAEvCsP,GAAY,EAIhB,QAAQA,IAKZlM,EAAoB,WAClB,GACA,SAAmB/D,EAAO9B,EAAOuE,EAAQxC,GAGvC,IAAK,GAFD2F,IAAQ,EAEHjF,EAAI,EAAGA,EAAI8B,EAAO7B,QAAUgF,EAAOjF,IAErCzC,EAAMoL,aAAc7G,EAAQ9B,MAE/BiF,GAAQ,EASZ,OALMA,IAEJ3F,EAAM4F,QAGD,IAMX9B,EAAoB,gBAClB,wBACA,SAAmB/D,EAAO9B,EAAOuE,EAAQxC,GAGvC,IAAK,GAFD6I,IAAW,EAENnI,EAAI,EAAGA,EAAI8B,EAAO7B,SAAWkI,EAAUnI,IAExCiH,EAAS1J,EAAM6H,KAAMtD,EAAQ9B,OAEjCmI,GAAW,EAIf,OAAOA,IAAYlB,EAAS5H,KAMhC+D,EAAoB,oBAClB,wBACA,SAAmB/D,EAAO9B,EAAOuE,EAAQxC,GAGvC,IAAK,GAFD6I,IAAW,EAENnI,EAAI,EAAGA,EAAI8B,EAAO7B,QAAUkI,EAAUnI,IAExCiH,EAAS1J,EAAM6H,KAAMtD,EAAQ9B,OAEhCmI,GAAW,EAIf,OAAOA,IAAYlB,EAAS5H,KAMhC+D,EAAoB,mBAClB,wBACA,SAAmB/D,EAAO9B,EAAOuE,EAAQxC,GAGvC,IAAK,GAFD6I,IAAW,EAENnI,EAAI,EAAGA,EAAI8B,EAAO7B,SAAWkI,EAAUnI,IAEzCiH,EAAS1J,EAAM6H,KAAMtD,EAAQ9B,OAEhCmI,GAAW,EAIf,OAAOA,IAAYlB,EAAS5H,KAMhC+D,EAAoB,uBAClB,wBACA,SAAmB/D,EAAO9B,EAAOuE,EAAQxC,GAGvC,IAAK,GAFD6I,IAAW,EAENnI,EAAI,EAAGA,EAAI8B,EAAO7B,QAAUkI,EAAUnI,IAEvCiH,EAAS1J,EAAM6H,KAAMtD,EAAQ9B,OAEjCmI,GAAW,EAIf,OAAOA,IAAYlB,EAAS5H,KAgDhCoE,EAAqB,SACnB,8DACA,SAAmBpE,EAAO9B,EAAOoG,EAAQC,GAEvC,OAAQD,EAAOoL,SAAS,SAA0BC,GAEhD,MAAOA,KAAMzR,GAASqE,EAAQvC,EAAO2P,EAAE5J,KAAMxB,QAMnDH,EAAqB,SACnB,wDACA,SAAmBpE,EAAO9B,EAAOoG,EAAQC,GAEvC,MAAOD,GAAOoL,SAAS,SAA0BC,GAE/C,MAAOA,KAAMzR,GAASqE,EAAQvC,EAAO2P,EAAE5J,KAAMxB,QAoGnDU,EAAiB,KACf,SAAmBiL,EAAcC,GAC/B,MAAOD,GAAe,IAK1BjL,EAAiB,SACf,SAAmBiL,EAAcC,GAC/B,MAAOD,IAAgBC,IAK3BlL,EAAiB,SACf,SAAmBiL,EAAcC,GAC/B,MAAsBA,GAAfD,IA0FXjK,EAAkB,KAChB,mCACA,SAAmBjG,EAAO9B,EAAOgI,GAE/B,OAAQA,EAAQlG,EAAO9B,KAK3B+H,EAAkB,SAChB,uCACA,SAAmBjG,EAAO9B,EAAOgI,GAE/B,MAAOA,GAAQlG,EAAO9B,KA8E1BkI,EAAmB,WACfI,OAAU,4DACVC,OAAU,gDACVC,OAAU,wDAEZ,SAAmB1G,EAAOgG,EAAOM,GAC/B,MAAeN,GAARhG,GAAiBA,EAAQsG,IAKpCF,EAAmB,eACfI,OAAU,gEACVC,OAAU,oDACVC,OAAU,4DAEZ,SAAmB1G,EAAOgG,EAAOM,GAC/B,MAAOtG,IAASgG,GAAkBM,GAATtG,IA6E7BkH,EAAmB,QACjB,sDACE,eAGJA,EAAmB,aACjB,kFACA,oBAGFA,EAAmB,YACjB,yDACA,kBAGFA,EAAmB,QACjB,iCACA,eAGFA,EAAmB,MACjB,+BACA,8FAGFA,EAAmB,MACjB,+BACA,2FAGFA,EAAmB,QACjB,wCACA,2EA6BF3H,EAAWC,MAAM2H,MAAQ,SAAShJ,EAAOsB,EAAQC,EAAUC,EAAUC,GAEnE,GAAIuH,EAEJ,IAAKjF,EAAUzC,GACf,CACE,GAAIjB,GAAS,qBAAqB4P,KAAM3O,EAEnCjB,KAEH2I,EAAQ,GAAIiJ,QAAQ5R,EAAO,GAAIA,EAAO,SAGhCsJ,GAAUrI,KAElB0H,EAAQ1H,EAGV,KAAM0H,EAEJ,KAAM1H,GAAS,uDAGjB,IAAIK,GAAkBC,EAAkB,QAASH,EAEjD,OAAO,UAASI,EAAO9B,EAAO+B,GAEtBkH,EAAMC,KAAMpH,GAMhBC,EAAMG,OAJNH,EAAMC,QAASC,EAAiBhC,EAAOwB,EAAUxB,GAAS6B,EAAO9B,EAAO4B,MAS9EP,EAAWC,MAAM2H,MAAMvH,QAAU,iCAGjCT,EAAc,WACZ,wBACA,SAAmBa,GACjB,MAAO4H,GAAS5H,KAKpBqH,EAAkB,OACdb,OAAU,wDACVC,OAAU,uCACVC,OAAU,gDAEZ,SAAmB1G,EAAOyG,GACxB,MAAeA,GAARzG,IAKXqH,EAAkB,gBACdb,OAAU,qDACVC,OAAU,2CACVC,OAAU,iDAEZ,SAAmB1G,EAAOyG,GACxB,MAAgBA,IAATzG,IAKXqH,EAAkB,OACdb,OAAU,wDACVC,OAAU,2CACVC,OAAU,oDAEZ,SAAmB1G,EAAOyG,GACxB,MAAOzG,GAAQyG,IAKnBY,EAAkB,aACdb,OAAU,qDACVC,OAAU,wCACVC,OAAU,iDAEZ,SAAmB1G,EAAOyG,GACxB,MAAOzG,IAASyG,IAKpBY,EAAkB,SACdb,OAAU,2CACVC,OAAU,iCACVC,OAAU,uCAEZ,SAAmB1G,EAAOyG,GACxB,MAAOzG,KAAUyG,IAKrBY,EAAkB,aACdb,OAAU,+CACVC,OAAU,qCACVC,OAAU,2CAEZ,SAAmB1G,EAAOyG,GACxB,MAAOzG,KAAUyG,IA6DrBtH,EAAc,QACZ,6BACA,SAAmBa,GACjB,OAAQsC,EAAStC,KAIrBb,EAAc,SACZ,8BACA,SAAmBa,GACjB,OAAQ4B,EAAU5B,KAItBb,EAAc,SACZ,6BACA,SAAmBa,GACjB,OAAQkC,EAAUlC,KAItBb,EAAc,SACZ,6BACA,SAAmBa,GACjB,OAAQf,EAAUe,KAItBb,EAAc,UACZ,oCACA,SAAmBa,GACjB,OAAQ6H,EAAW7H,KAIvBb,EAAc,QACZ,8BACA,SAAmBa,GACjB,QAASA,YAAiBuH,MAI9BpI,EAAc,QACZ,mCACA,SAAmBa,EAAO9B,EAAO+B,GAC/B,GAAIzB,GAASG,EAAaqB,GACtBqQ,EAAU5R,WAAYuB,GACtBE,GAAWjB,EAAUT,EAOzB,OANM0B,KACJA,EAAUoQ,KAAKC,MAAO/R,KAAa6R,EAC7BnQ,GACJD,EAAMmI,OAAQ5J,IAGX0B,IAIXf,EAAc,UACZ,4BACA,SAAmBa,EAAO9B,EAAO+B,GAC/B,GAAIzB,GAASF,EAAe0B,GACxBE,GAAWjB,EAAUT,EAIzB,OAHM0B,IACJD,EAAMmI,OAAQ5J,GAET0B,IAIXf,EAAc,QACZ,gCACA,SAAmBa,EAAO9B,EAAO+B,GAC/B,GAAIuQ,GAASjR,EAAWC,MAAMiR,MAAMtP,IAAKnB,GACrCE,GAAW2H,EAAW2I,EAI1B,OAHMtQ,IACJD,EAAMmI,OAAQoI,GAETtQ,IAIXX,EAAWC,MAAMiR,MAAMtP,KAErBsO,QAAU,EACViB,GAAU,EACVpB,KAAU,EACVE,GAAU,EACVH,GAAU,EACVsB,SAAU,EACVC,GAAU,EACVC,IAAU,EACVvI,GAAU,EACVwI,GAAU,GAGZvR,EAAWC,MAAMuR,IAAM,SAAS5S,EAAOsB,EAAQC,EAAU0B,EAAOxB,GAE9D,MAAO,UAASI,EAAO9B,EAAO+B,GAE5BD,EAAQ1B,EAAe0B,GAElBf,EAAUe,IAEbC,EAAMmI,OAAQkI,KAAKS,IAAK/Q,IAG1BC,EAAMG,SAIVb,EAAWC,MAAMuK,MAAQ,SAAS5L,EAAOsB,EAAQC,EAAU0B,EAAOxB,GAEhE,MAAO,UAASI,EAAO9B,EAAO+B,GAE5B/B,EAAM8S,KAAM7S,EAAO6B,GAEnBC,EAAMG,SAIVb,EAAWC,MAAMyR,OAAS,SAAS9S,EAAOsB,EAAQC,EAAU0B,EAAOxB,GAEjE,MAAO,UAASI,EAAO9B,EAAO+B,GAEvBpC,EAAOqT,MAEVjR,EAAMmI,OAAQvK,EAAOqT,KAAMlR,IAG7BC,EAAMG,SAIVb,EAAWC,MAAM2R,KAAO,SAAShT,EAAOsB,EAAQC,EAAU0B,EAAOxB,GAE/D,MAAO,UAASI,EAAO9B,EAAO+B,GAE5BD,EAAQ1B,EAAe0B,GAElBf,EAAUe,IAEbC,EAAMmI,OAAQkI,KAAKa,KAAMnR,IAG3BC,EAAMG,SAIVb,EAAWC,MAAMN,SAAW,SAASf,EAAOsB,EAAQC,EAAU0B,EAAOxB,GAEnE,MAAO,UAASI,EAAO9B,EAAO+B,GAE5BA,EAAMmI,OAAQlJ,EAAUc,IAExBC,EAAMG,SAIVb,EAAWC,MAAM4R,OAAS,SAASjT,EAAOsB,EAAQC,EAAU0B,EAAOxB,GAEjE,MAAO,UAASI,EAAO9B,EAAO+B,GAE5B,GAAKqC,EAAStC,GACd,CACE,IAAK,GAAIW,GAAIX,EAAMY,OAAS,EAAGD,GAAK,EAAGA,IAE/B6D,EAASxE,EAAOW,KAEpBX,EAAMqR,OAAQ1Q,EAAG,EAIrBV,GAAMmI,OAAQpI,OAEX,IAAK4B,EAAU5B,GACpB,CACE,IAAK,GAAIsR,KAAQtR,GAETwE,EAASxE,EAAOsR,WAEbtR,GAAOsR,EAIlBrR,GAAMmI,OAAQpI,GAGhBC,EAAMG,SAIVb,EAAWC,MAAM+Q,MAAQ,SAASpS,EAAOsB,EAAQC,EAAU0B,EAAOxB,GAEhE,MAAO,UAASI,EAAO9B,EAAO+B,GAE5BD,EAAQ1B,EAAe0B,GAElBf,EAAUe,IAEbC,EAAMmI,OAAQkI,KAAKC,MAAOvQ,IAG5BC,EAAMG,SAIVb,EAAWC,MAAM+R,IAAM,SAASpT,EAAOsB,EAAQC,EAAU0B,EAAOxB,GAE9D,GAAI6G,GAASnI,EAAemB,EAE5B,KAAMR,EAAUwH,GAEd,KAAM,IAAMA,EAAS,2CAGvB,OAAO,UAASzG,EAAO9B,EAAO+B,GAE5BD,EAAQ1B,EAAe0B,GAElBf,EAAUe,IAEbC,EAAMmI,OAAQpI,EAAQyG,GAGxBxG,EAAMG,SAIVb,EAAWC,MAAXD,QAAwB,SAASpB,EAAOsB,EAAQC,EAAU0B,EAAOxB,GAE/D,MAAO,UAASI,EAAO9B,EAAO+B,GAE5B/B,EAAM8S,KAAM7S,EAAO,MAEnB8B,EAAMmI,OAAQ,MAEdnI,EAAMG,SAIVb,EAAWC,MAAMgS,MAAQ,SAASrT,EAAOsB,EAAQC,EAAU0B,EAAOxB,GAEhE,MAAO,UAASI,EAAO9B,EAAO+B,GAE5BD,EAAQ1B,EAAe0B,GAElBf,EAAUe,IAEbC,EAAMmI,OAAQkI,KAAKkB,MAAOxR,IAG5BC,EAAMG,SAIVb,EAAWC,MAAMX,WAAa,SAASV,EAAOsB,EAAQC,EAAU0B,EAAOxB,GAErE,MAAO,UAASI,EAAO9B,EAAO+B,GAE5BA,EAAMmI,OAAQvJ,EAAYmB,IAE1BC,EAAMG,SAIVb,EAAWC,MAAMiS,UAAY,SAAStT,EAAOsB,EAAQC,EAAU0B,EAAOxB,GAEpE,MAAO,UAASI,EAAO9B,EAAO+B,GAEvBiC,EAAUlC,IAEbC,EAAMmI,OAAQpI,EAAM6J,QAAS,aAAc,KAG7C5J,EAAMG,SAIVb,EAAWC,MAAMkS,UAAY,SAASvT,EAAOsB,EAAQC,EAAU0B,EAAOxB,GAEpE,MAAO,UAASI,EAAO9B,EAAO+B,GAEvBiC,EAAUlC,IAEbC,EAAMmI,OAAQpI,EAAM6J,QAAS,iBAAkB,KAGjD5J,EAAMG,SAIVb,EAAWC,MAAMmS,KAAO,SAASxT,EAAOsB,EAAQC,EAAU0B,EAAOxB,GAE/D,GAAI+R,GAAO,WAET,GAAKC,OAAOC,UAAUF,KAEpB,MAAO,UAASpT,GACd,MAAOA,GAAEoT,OAIb,IAAIxK,GAAQ,oCAEZ,OAAO,UAAS5I,GAEd,MAAOA,GAAEsL,QAAS1C,EAAO,OAK7B,OAAO,UAASnH,EAAO9B,EAAO+B,GAEvBiC,EAAUlC,IAEbC,EAAMmI,OAAQuJ,EAAM3R,IAGtBC,EAAMG,SAIVb,EAAWC,MAAMsS,SAAW,SAAS3T,EAAOsB,EAAQC,EAAU0B,EAAOxB,GAEnE,MAAO,UAASI,EAAO9B,EAAO+B,GAEvBpC,EAAOkU,MAEV9R,EAAMmI,OAAQvK,EAAOkU,KAAM/R,IAG7BC,EAAMG,SAKR1C,EAAO6B,WAAaA,EAEpB7B,EAAOyB,cAAgBA,EACvBzB,EAAO0I,mBAAqBA,EAC5B1I,EAAOoE,wBAA0BA,EACjCpE,EAAOmF,kBAAoBA,EAC3BnF,EAAO4F,uBAAyBA,EAChC5F,EAAOqG,oBAAsBA,EAC7BrG,EAAO0G,qBAAuBA,EAC9B1G,EAAOuH,iBAAmBA,EAC1BvH,EAAOuI,kBAAoBA,EAC3BvI,EAAOwJ,mBAAqBA,EAC5BxJ,EAAO2J,kBAAoBA,EAE3B3J,EAAO2C,aAAeA,EACtB3C,EAAOY,cAAgBA,EACvBZ,EAAOiB,YAAcA,EACrBjB,EAAOmB,WAAaA,EACpBnB,EAAOwB,SAAWA,EAClBxB,EAAOqC,iBAAmBA,EAC1BrC,EAAOwD,aAAeA,EACtBxD,EAAOmC,cAAgBA,EACvBnC,EAAOyC,gBAAkBA,EAElBzC","file":"rekord-validation.min.js","sourcesContent":["/* rekord-validation 1.5.0 - Advanced validation rules for rekord by Philip Diffenderfer */\n// UMD (Universal Module Definition)\n(function (root, factory)\n{\n if (typeof define === 'function' && define.amd) // jshint ignore:line\n {\n // AMD. Register as an anonymous module.\n define(['rekord'], function(Rekord) { // jshint ignore:line\n return factory(root, Rekord);\n });\n }\n else if (typeof module === 'object' && module.exports) // jshint ignore:line\n {\n // Node. Does not work with strict CommonJS, but\n // only CommonJS-like environments that support module.exports,\n // like Node.\n module.exports = factory(global, require('rekord')); // jshint ignore:line\n }\n else\n {\n // Browser globals (root is window)\n root.Rekord = factory(root, root.Rekord);\n }\n}(this, function(global, Rekord, undefined)\n{\n\n var Model = Rekord.Model;\n var Database = Rekord.Database;\n var Promise = Rekord.Promise;\n var Collection = Rekord.Collection;\n var ModelCollection = Rekord.ModelCollection;\n var Class = Rekord.Class;\n\n var isEmpty = Rekord.isEmpty;\n var isString = Rekord.isString;\n var isArray = Rekord.isArray;\n var isObject = Rekord.isObject;\n var isFunction = Rekord.isFunction;\n var isDate = Rekord.isDate;\n var isNumber = Rekord.isNumber;\n var isBoolean = Rekord.isBoolean;\n var isValue = Rekord.isValue;\n var isPrimitiveArray = Rekord.isPrimitiveArray;\n var isRegExp = Rekord.isRegExp;\n var isRekord = Rekord.isRekord;\n\n var noop = Rekord.noop;\n var equalsCompare = Rekord.equalsCompare;\n var equals = Rekord.equals;\n var indexOf = Rekord.indexOf;\n var sizeof = Rekord.sizeof;\n\n var split = Rekord.split;\n var transfer = Rekord.transfer;\n var format = Rekord.format;\n\n var parseDate = Rekord.parseDate;\n\n\nfunction ValidationChain(model, field, validations, onEnd)\n{\n this.model = model;\n this.field = field;\n this.validations = validations;\n this.onEnd = onEnd;\n}\n\nClass.create( ValidationChain,\n{\n\n reset: function(value)\n {\n this.value = value !== undefined ? value : this.model.$get( this.field );\n this.updated = false;\n this.valid = true;\n this.message = '';\n this.linkIndex = 0;\n },\n\n start: function(value)\n {\n this.reset( value );\n this.call();\n },\n\n call: function()\n {\n this.validations[ this.linkIndex ]( this.value, this.model, this );\n },\n\n update: function(newValue)\n {\n this.value = newValue;\n this.updated = true;\n\n return this;\n },\n\n next: function()\n {\n var n = this.validations.length;\n\n this.linkIndex++;\n\n if (this.linkIndex === n)\n {\n this.onEnd( this );\n }\n else if (this.linkIndex < n)\n {\n this.call();\n }\n\n return this;\n },\n\n stop: function()\n {\n var n = this.validations.length;\n\n if (this.linkIndex < n)\n {\n this.linkIndex = n - 1;\n this.next();\n }\n\n return this;\n },\n\n invalid: function(message)\n {\n this.message = message;\n this.valid = false;\n this.stop();\n\n return this;\n }\n\n});\n\nfunction tryParseFloat(x)\n{\n var parsed = parseFloat( x );\n\n if ( !isNaN( parsed ) )\n {\n x = parsed;\n }\n\n return x;\n}\n\nfunction tryParseInt(x)\n{\n var parsed = parseInt( x );\n\n if ( !isNaN( parsed ) )\n {\n x = parsed;\n }\n\n return x;\n}\n\nfunction startOfDay(d)\n{\n if ( isDate( d ) )\n {\n d.setHours( 0, 0, 0, 0 );\n }\n else if ( isNumber( d ) )\n {\n d = d - (d % 86400000);\n }\n\n return d;\n}\n\nfunction endOfDay(d)\n{\n if ( isDate( d ) )\n {\n d.setHours( 23, 59, 59, 999 );\n }\n else if ( isNumber( d ) )\n {\n d = d - (d % 86400000) + 86400000 - 1;\n }\n\n return d;\n}\n\nfunction ruleGenerator(ruleName, defaultMessage, isInvalid)\n{\n Validation.Rules[ ruleName ] = function(field, params, database, getAlias, message)\n {\n checkNoParams( ruleName, field, params );\n\n var messageTemplate = determineMessage( ruleName, message );\n\n return function(value, model, chain)\n {\n if ( isInvalid( value, model, chain ) )\n {\n chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate ) );\n }\n else\n {\n chain.next();\n }\n };\n };\n\n Validation.Rules[ ruleName ].message = defaultMessage;\n}\n\nfunction determineMessage(ruleName, message)\n{\n return message || Validation.Rules[ ruleName ].message;\n}\n\nfunction joinFriendly(arr, lastSeparatorCustom, itemSeparatorCustom, getAlias)\n{\n var copy = arr.slice();\n\n if ( getAlias )\n {\n for (var i = 0; i < copy.length; i++)\n {\n copy[ i ] = getAlias( copy[ i ] );\n }\n }\n\n var last = copy.pop();\n var lastSeparator = lastSeparatorCustom || 'and';\n var itemSeparator = itemSeparatorCustom || ', ';\n\n switch (copy.length) {\n case 0:\n return last;\n case 1:\n return copy[ 0 ] + ' ' + lastSeparator + ' ' + last;\n default:\n return copy.join( itemSeparator ) + itemSeparator + lastSeparator + ' ' + last;\n }\n}\n\nfunction mapFromArray(arr, value)\n{\n var map = {};\n\n for (var i = 0; i < arr.length; i++)\n {\n map[ arr[ i ] ] = value;\n }\n\n return map;\n}\n\nfunction checkNoParams(ruleName, field, params)\n{\n if ( params )\n {\n throw 'the rule ' + ruleName + ' for field ' + field + ' has no arguments';\n }\n}\n\nfunction generateMessage(field, alias, value, model, message, extra)\n{\n if ( isFunction( message ) )\n {\n message = message( field, alias, value, model, extra );\n }\n\n var base = {};\n base.$field = field;\n base.$alias = alias;\n base.$value = value;\n\n transfer( model, base );\n\n if ( isObject( extra ) )\n {\n transfer( extra, base );\n }\n\n return format( message, base );\n}\n\nRekord.addPlugin(function(model, db, options)\n{\n var validation = options.validation || Database.Defaults.validation;\n\n if ( isEmpty( validation ) )\n {\n return;\n }\n\n var rules = validation.rules || {};\n var messages = validation.messages || {};\n var aliases = validation.aliases || {};\n var required = !!validation.required;\n\n function getAlias(field)\n {\n return aliases[ field ] || field;\n }\n\n db.validations = {};\n\n for ( var field in rules )\n {\n db.validations[ field ] = Validation.parseRules( rules[ field ], field, db, getAlias, messages[ field ] );\n }\n\n Class.method( model, '$validate', function(callback, context)\n {\n this.$trigger( Model.Events.PreValidate, [this] );\n\n this.$valid = true;\n this.$validations = {};\n this.$validationMessages.length = 0;\n\n var chainEnds = 0;\n var chains = [];\n\n var onChainEnd = function(chain)\n {\n var model = chain.model;\n\n if (!chain.valid)\n {\n model.$validations[ chain.field ] = chain.message;\n model.$validationMessages.push( chain.message );\n model.$valid = false;\n }\n\n if (++chainEnds === chains.length)\n {\n model.$trigger( model.$valid ? Model.Events.ValidatePass : Model.Events.ValidateFail, [model] );\n\n if ( isFunction( callback ) )\n {\n callback.call( context || model, model.$valid );\n }\n }\n };\n\n for (var field in db.validations)\n {\n var validations = db.validations[ field ];\n var chain = new ValidationChain( this, field, validations, onChainEnd );\n\n chains.push( chain );\n }\n\n for (var i = 0; i < chains.length; i++)\n {\n chains[ i ].start();\n }\n\n return this.$valid;\n });\n\n Class.replace( model, '$init', function($init)\n {\n return function()\n {\n this.$valid = undefined;\n this.$validations = {};\n this.$validationMessages = [];\n\n return $init.apply( this, arguments );\n };\n });\n\n if ( required )\n {\n Class.replace( model, '$save', function($save)\n {\n return function()\n {\n if ( this.$isDeleted() )\n {\n Rekord.debug( Rekord.Debugs.SAVE_DELETED, this.$db, this );\n\n return Promise.resolve( this );\n }\n\n var promise = new Rekord.Promise();\n var modelInstance = this;\n var args = arguments;\n\n this.$validate(function(valid)\n {\n if (!valid)\n {\n promise.reject( modelInstance );\n }\n else\n {\n var saving = $save.apply( modelInstance, args );\n\n saving.then( promise.resolve, promise.reject, promise.noline, promise.cancel, promise );\n }\n });\n\n return promise;\n };\n });\n }\n});\n\nModel.Events.PreValidate = 'pre-validate';\n\nModel.Events.ValidatePass = 'validate-pass';\n\nModel.Events.ValidateFail = 'validate-fail';\n\nvar Validation =\n{\n Rules: {},\n Expression: {},\n Expressions: [],\n Delimiter: /([|])/,\n Escape: '\\\\',\n RuleSeparator: ':',\n Stop: {},\n\n parseRules: function(rules, field, database, getAlias, message)\n {\n var validators = [];\n\n if ( isString( rules ) )\n {\n rules = split( rules, this.Delimiter, this.Escape );\n }\n\n if ( isArray( rules ) )\n {\n for (var i = 0; i < rules.length; i++)\n {\n var rule = rules[ i ];\n var defaultMessageValidator = this.parseRule( rule, field, database, getAlias, message );\n\n validators.push( defaultMessageValidator );\n }\n }\n else if ( isObject( rules ) )\n {\n for (var ruleProperty in rules)\n {\n var ruleMessageOrData = rules[ ruleProperty ];\n\n var ruleMessage = isObject( ruleMessageOrData ) ? ruleMessageOrData.message :\n ( isString( ruleMessageOrData ) ? ruleMessageOrData : undefined );\n\n var ruleInput = isObject( ruleMessageOrData ) && ruleMessageOrData.message ? ruleMessageOrData.input :\n ( isString( ruleMessageOrData ) ? undefined : ruleMessageOrData );\n\n var customMessageValidator = this.parseRule( ruleProperty, field, database, getAlias, ruleMessage || message, ruleInput );\n\n validators.push( customMessageValidator );\n }\n }\n\n return validators;\n },\n\n parseRule: function(rule, field, database, getAlias, message, input)\n {\n var colon = rule.indexOf( this.RuleSeparator );\n var ruleName = colon === -1 ? rule : rule.substring( 0, colon );\n\n if ( ruleName.charAt( 0 ) === '$' )\n {\n return this.customValidator( ruleName, field, database, getAlias, message );\n }\n\n var ruleParams = colon === -1 ? input : rule.substring( colon + 1 );\n var validatorFactory = Validation.Rules[ ruleName ];\n\n if ( !validatorFactory )\n {\n throw ruleName + ' is not a valid rule';\n }\n\n return validatorFactory( field, ruleParams, database, getAlias, message );\n },\n\n parseExpression: function(expr, database)\n {\n var parsers = Validation.Expressions;\n\n for (var i = 0; i < parsers.length; i++)\n {\n var parser = parsers[ i ];\n var expressionFunction = parser( expr, database );\n\n if ( isFunction( expressionFunction ) )\n {\n return expressionFunction; // (value, model)\n }\n }\n\n return noop;\n },\n\n customValidator: function(functionName, field, database, getAlias, message)\n {\n return function(value, model, chain)\n {\n var result = model[ functionName ]( value, getAlias, message, chain );\n\n if ( isString( result ) )\n {\n chain.invalid( result );\n }\n else if ( result !== false )\n {\n chain.next();\n }\n };\n }\n};\n\nValidation.Expression.date =\nValidation.Expressions.push(function(expr, database)\n{\n var parsed = parseDate( expr );\n\n if ( parsed !== false )\n {\n var parsedTime = parsed.getTime();\n\n return function(value, model)\n {\n return parsedTime;\n };\n }\n}) - 1;\n\nValidation.Expression.field =\nValidation.Expressions.push(function(expr, database)\n{\n if ( indexOf( database.fields, expr ) )\n {\n return function(value, model)\n {\n return model.$get( expr );\n };\n }\n}) - 1;\n\n\nvar RELATIVE_REGEX = /^([+-]\\d+(\\.\\d+)?)\\s*(.+)$/;\n\nvar RELATIVE_UNITS = {\n ms: 1,\n millisecond: 1,\n milliseconds: 1,\n s: 1000,\n second: 1000,\n seconds: 1000,\n min: 1000 * 60,\n mins: 1000 * 60,\n minute: 1000 * 60,\n minutes: 1000 * 60,\n hr: 1000 * 60 * 60,\n hour: 1000 * 60 * 60,\n hours: 1000 * 60 * 60,\n day: 1000 * 60 * 60 * 24,\n days: 1000 * 60 * 60 * 24,\n wk: 1000 * 60 * 60 * 24 * 7,\n week: 1000 * 60 * 60 * 24 * 7,\n weeks: 1000 * 60 * 60 * 24 * 7,\n month: ['getMonth', 'setMonth'],\n months: ['getMonth', 'setMonth'],\n yr: ['getFullYear', 'setFullYear'],\n year: ['getFullYear', 'setFullYear'],\n years: ['getFullYear', 'setFullYear']\n};\n\nValidation.Expression.relative =\nValidation.Expressions.push(function(expr, database)\n{\n var parsed = RELATIVE_REGEX.exec( expr );\n\n if ( parsed !== null )\n {\n var amount = parseFloat( parsed[ 1 ] );\n var unit = parsed[ 3 ];\n var unitScale = RELATIVE_UNITS[ unit ];\n\n if ( !unitScale )\n {\n throw unit + ' is not a valid unit.';\n }\n\n return function(value, model)\n {\n var relative = new Date();\n\n if ( isNumber( unitScale ) )\n {\n relative.setTime( relative.getTime() + unitScale * amount );\n }\n else\n {\n var getter = unitScale[0];\n var setter = unitScale[1];\n\n relative[ setter ]( relative[ getter ]() + amount );\n }\n\n return relative.getTime();\n };\n }\n}) - 1;\n\nValidation.Expression.today =\nValidation.Expressions.push(function(expr, database)\n{\n if ( expr === 'today' )\n {\n return function(value, model)\n {\n var today = new Date();\n\n startOfDay( today );\n\n return today.getTime();\n };\n }\n}) - 1;\n\nValidation.Expression.tomorrow =\nValidation.Expressions.push(function(expr, database)\n{\n if ( expr === 'tomorrow' )\n {\n return function(value, model)\n {\n var tomorrow = new Date();\n\n tomorrow.setDate( tomorrow.getDate() + 1 );\n startOfDay( tomorrow );\n\n return tomorrow.getTime();\n };\n }\n}) - 1;\n\nValidation.Expression.yesterday =\nValidation.Expressions.push(function(expr, database)\n{\n if ( expr === 'yesterday' )\n {\n return function(value, model)\n {\n var yesterday = new Date();\n\n yesterday.setDate( yesterday.getDate() - 1 );\n startOfDay( yesterday );\n\n return yesterday.getTime();\n };\n }\n}) - 1;\n\n// accepted\nValidation.Rules.accepted = function(field, params, database, getAlias, message)\n{\n checkNoParams( 'accepted', field, params );\n\n var messageTemplate = determineMessage( 'accepted', message );\n var acceptable = Validation.Rules.accepted.acceptable;\n\n return function(value, model, chain)\n {\n var valueString = (value + '').toLowerCase();\n var accepted = acceptable[ valueString ];\n\n if ( !accepted )\n {\n chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate ) );\n }\n else\n {\n chain.next();\n }\n };\n};\n\nValidation.Rules.accepted.message = '{$alias} has not been accepted.';\n\nValidation.Rules.accepted.acceptable =\n{\n '1': true,\n 'yes': true,\n 'on': true,\n 'y': true,\n 'true': true\n};\n\n// contains:field,value\ncollectionRuleGenerator('contains',\n '{$alias} does not contain an item whose {$matchAlias} equals {$matchValue}.',\n function isInvalid(value, model, matchField, matchValue, equality)\n {\n return !value.contains(function isMatch(m)\n {\n return m !== model && equality( matchValue, m.$get( matchField ) );\n });\n }\n);\n\n// not_contains:field,value\ncollectionRuleGenerator('not_contains',\n '{$alias} contains an item whose {$matchAlias} equals {$matchValue}.',\n function isInvalid(value, model, matchField, matchValue, equality)\n {\n return value.contains(function isMatch(m)\n {\n return m !== model && equality( matchValue, m.$get( matchField ) );\n });\n }\n);\n\nfunction collectionRuleGenerator(ruleName, defaultMessage, isInvalid)\n{\n Validation.Rules[ ruleName ] = function(field, params, database, getAlias, message)\n {\n if ( !params )\n {\n throw ruleName + ' validation rule requires field & value arguments';\n }\n\n var matchField, matchValue, equality;\n\n if ( isString( params ) )\n {\n var comma = params.indexOf(',');\n\n if ( comma === -1 )\n {\n throw ruleName + ' validation rule requires field & value arguments';\n }\n\n matchField = params.substring( 0, comma );\n matchValue = params.substring( comma + 1 );\n }\n else if ( isArray( params ) )\n {\n matchField = params[ 0 ];\n matchValue = params[ 1 ];\n equality = params[ 2 ];\n }\n else if ( isObject( params ) )\n {\n matchField = params.field;\n matchValue = params.value;\n equality = params.equals;\n }\n\n if ( !isFunction( equality ) )\n {\n equality = equalsCompare;\n }\n\n if ( indexOf( database.fields, matchField ) === -1 )\n {\n throw matchField + ' is not a valid field for the ' + ruleName + ' rule';\n }\n\n var messageTemplate = determineMessage( ruleName, message );\n var extra = {\n $matchField: matchField,\n $matchAlias: getAlias( matchField ),\n $matchValue: matchValue\n };\n\n return function(value, model, chain)\n {\n if ( isInvalid( value, model, matchField, matchValue, equality ) )\n {\n chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) );\n }\n else\n {\n chain.next();\n }\n };\n };\n\n Validation.Rules[ ruleName ].message = defaultMessage;\n}\n\nValidation.Rules.validate = function(field, params, database, getAlias, message)\n{\n // message, models, validations\n var messageOption = params || 'message';\n var messageTemplate = determineMessage( 'validate', message );\n\n return function(value, model, chain)\n {\n if ( isArray( value ) )\n {\n var invalid = new Collection();\n\n for (var i = 0; i < value.length; i++)\n {\n var related = value[ i ];\n\n if ( related && related.$validate && !related.$validate() )\n {\n invalid.push( related );\n }\n }\n\n if ( invalid.length )\n {\n switch (messageOption)\n {\n case 'models':\n chain.invalid( invalid );\n break;\n case 'validations':\n chain.invalid( invalid.pluck( '$validations', '$$key' ) );\n break;\n default: // message\n chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate ) );\n break;\n }\n }\n else\n {\n chain.next();\n }\n }\n else\n {\n chain.next();\n }\n };\n};\n\nValidation.Rules.validate.message = '{$alias} is not valid.';\n\n// after:today\ndateRuleGenerator('after',\n '{$alias} must be after {$date}.',\n function isInvalid(value, date) {\n return value < endOfDay( date );\n }\n);\n\n// after_on:tomorrow\ndateRuleGenerator('after_on',\n '{$alias} must be after or equal to {$date}.',\n function isInvalid(value, date) {\n return value < date;\n }\n);\n\n// before:yesterday\ndateRuleGenerator('before',\n '{$alias} must be before {$date}.',\n function isInvalid(value, date) {\n return value > date;\n }\n);\n\n// before_on:+2days\ndateRuleGenerator('before_on',\n '{$alias} must be before or equal to {$date}.',\n function isInvalid(value, date) {\n return value > endOfDay( date );\n }\n);\n\n// date\nruleGenerator('date_like',\n '{$alias} must be a valid date.',\n function isInvalid(value, model, chain) {\n var parsed = parseDate( value );\n var invalid = parsed === false;\n if ( !invalid ) {\n chain.update( parsed.getTime() );\n }\n return invalid;\n }\n);\n\nfunction dateRuleGenerator(ruleName, defaultMessage, isInvalid)\n{\n Validation.Rules[ ruleName ] = function(field, params, database, getAlias, message)\n {\n if ( !params )\n {\n throw ruleName + ' validation rule requires a date expression argument';\n }\n\n var dateExpression;\n\n if ( isString( params ) )\n {\n dateExpression = Validation.parseExpression( params, database );\n }\n else if ( isFunction( params ) )\n {\n dateExpression = params;\n }\n else\n {\n var parsed = parseDate( params );\n\n if ( parsed !== false )\n {\n var parsedTime = parsed.getTime();\n\n dateExpression = function()\n {\n return parsedTime;\n };\n }\n }\n\n if ( !dateExpression || dateExpression === noop )\n {\n throw params + ' is not a valid date expression for the ' + ruleName + ' rule';\n }\n\n var messageTemplate = determineMessage( ruleName, message );\n var extra = {\n $date: params\n };\n\n return function(value, model, chain)\n {\n var parsed = parseDate( value );\n\n if ( parsed !== false )\n {\n value = parsed.getTime();\n\n var date = dateExpression( value, model );\n\n if ( isNumber( date ) && isInvalid( value, date ) )\n {\n chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) );\n }\n else\n {\n chain.next();\n }\n }\n else\n {\n chain.next();\n }\n };\n };\n\n Validation.Rules[ ruleName ].message = defaultMessage;\n}\n\n\n// required_if:X,Y,...\nfieldListRuleGenerator('required_if',\n '{$alias} is required.',\n function isInvalid(value, model, field, values, map) {\n var required = map[ model.$get( field ) ];\n\n return required && isEmpty( value );\n }\n);\n\n// required_unless:X,Y,...\nfieldListRuleGenerator('required_unless',\n '{$alias} is required.',\n function isInvalid(value, model, field, values, map) {\n var required = !map[ model.$get( field ) ];\n\n return required && isEmpty( value );\n }\n);\n\nfunction fieldListRuleGenerator(ruleName, defaultMessage, isInvalid)\n{\n Validation.Rules[ ruleName ] = function(field, params, database, getAlias, message)\n {\n if ( !params )\n {\n throw ruleName + ' validation rule requires a field and list arguments';\n }\n\n var matchField, matchValues;\n\n if ( isString( params ) )\n {\n var parts = split( params, /(,)/, '\\\\' );\n\n matchField = parts.shift();\n matchValues = parts;\n }\n else if ( isArray( params ) )\n {\n matchField = params.shift();\n matchValues = params;\n }\n else if ( isObject( params ) )\n {\n matchField = params.field;\n matchValues = params.values;\n }\n\n if ( indexOf( database.fields, matchField ) === false )\n {\n throw matchField + ' is not a valid field for the ' + ruleName + ' rule';\n }\n\n var messageTemplate = determineMessage( ruleName, message );\n var list = joinFriendly( matchValues );\n var extra = {\n $params: params,\n $matchField: matchField,\n $matchAlias: getAlias( matchField ),\n $list: list\n };\n var map = mapFromArray( matchValues, true );\n\n return function(value, model, chain)\n {\n if ( isInvalid( value, model, matchField, matchValues, map ) )\n {\n chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) );\n }\n else\n {\n chain.next();\n }\n };\n };\n\n Validation.Rules[ ruleName ].message = defaultMessage;\n}\n\n// confirmed:X\nfieldsRuleGenerator('confirmed',\n '{$alias} must match {$fieldAliases}.',\n function isInvalid(value, model, fields, chain) {\n var confirmed = true;\n\n for (var i = 0; i < fields.length; i++)\n {\n if ( !equals( value, model.$get( fields[ i ] ) ) )\n {\n confirmed = false;\n }\n }\n\n return !confirmed;\n }\n);\n\n// different:X\nfieldsRuleGenerator('different',\n '{$alias} must not match {$fieldAliases}.',\n function isInvalid(value, model, fields, chain) {\n var different = false;\n\n for (var i = 0; i < fields.length; i++)\n {\n if ( !equals( value, model.$get( fields[ i ] ) ) )\n {\n different = true;\n }\n }\n\n return !different;\n }\n);\n\n// if_valid:X\nfieldsRuleGenerator('if_valid',\n '',\n function isInvalid(value, model, fields, chain) {\n var valid = true;\n\n for (var i = 0; i < fields.length && valid; i++)\n {\n if ( model.$validations[ fields[ i ] ] )\n {\n valid = false;\n }\n }\n\n if ( !valid )\n {\n chain.stop();\n }\n\n return false;\n }\n);\n\n// The field under validation must be present only if any of the other specified fields are present.\n// required_with:X,Y,...\nfieldsRuleGenerator('required_with',\n '{$alias} is required.',\n function isInvalid(value, model, fields, chain) {\n var required = false;\n\n for (var i = 0; i < fields.length && !required; i++)\n {\n if ( !isEmpty( model.$get( fields[ i ] ) ) )\n {\n required = true;\n }\n }\n\n return required && isEmpty( value );\n }\n);\n\n// The field under validation must be present only if all of the other specified fields are present.\n// required_with_all:X,Y,...\nfieldsRuleGenerator('required_with_all',\n '{$alias} is required.',\n function isInvalid(value, model, fields, chain) {\n var required = true;\n\n for (var i = 0; i < fields.length && required; i++)\n {\n if ( isEmpty( model.$get( fields[ i ] ) ) )\n {\n required = false;\n }\n }\n\n return required && isEmpty( value );\n }\n);\n\n// The field under validation must be present only when any of the other specified fields are not present.\n// required_without:X,Y,...\nfieldsRuleGenerator('required_without',\n '{$alias} is required.',\n function isInvalid(value, model, fields, chain) {\n var required = false;\n\n for (var i = 0; i < fields.length && !required; i++)\n {\n if ( isEmpty( model.$get( fields[ i ] ) ) )\n {\n required = true;\n }\n }\n\n return required && isEmpty( value );\n }\n);\n\n// The field under validation must be present only when all of the other specified fields are not present.\n// required_without_all:X,Y,...\nfieldsRuleGenerator('required_without_all',\n '{$alias} is required.',\n function isInvalid(value, model, fields, chain) {\n var required = true;\n\n for (var i = 0; i < fields.length && required; i++)\n {\n if ( !isEmpty( model.$get( fields[ i ] ) ) )\n {\n required = false;\n }\n }\n\n return required && isEmpty( value );\n }\n);\n\nfunction fieldsRuleGenerator(ruleName, defaultMessage, isInvalid)\n{\n Validation.Rules[ ruleName ] = function(field, params, database, getAlias, message)\n {\n if ( !params )\n {\n throw ruleName + ' validation rule requires an array of fields argument';\n }\n\n var fields = split( params, /(\\s*,\\s*)/, '\\\\' );\n\n for (var i = 0; i < fields.length; i++)\n {\n if ( indexOf( database.fields, fields[ i ] ) === -1 )\n {\n throw fields[ i ] + ' is not a valid field for the ' + ruleName + ' rule';\n }\n }\n\n var messageTemplate = determineMessage( ruleName, message );\n var fieldNames = joinFriendly( fields );\n var fieldAliases = joinFriendly( fields, false, false, getAlias );\n var extra = {\n $fields: fieldNames,\n $fieldAliases: fieldAliases\n };\n\n return function(value, model, chain)\n {\n if ( isInvalid( value, model, fields, chain ) )\n {\n chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) );\n }\n else\n {\n chain.next();\n }\n };\n };\n\n Validation.Rules[ ruleName ].message = defaultMessage;\n}\n\n// exists:X,Y\nforeignRuleGenerator('exists',\n '{$alias} must match an existing {$matchAlias} in a {$class}',\n function isInvalid(value, model, models, fieldName)\n {\n return !models.contains(function isDifferentMatch(m)\n {\n return m !== model && equals( value, m.$get( fieldName ) );\n });\n }\n);\n\n// unique:X,Y\nforeignRuleGenerator('unique',\n '{$alias} must be a unique {$matchAlias} in a {$class}',\n function isInvalid(value, model, models, fieldName)\n {\n return models.contains(function isDifferentMatch(m)\n {\n return m !== model && equals( value, m.$get( fieldName ) );\n });\n }\n);\n\n// 'ruleName'\n// 'ruleName:name'\n// 'ruleName:,field'\n// 'ruleName:name,field'\n// 'ruleName:name,field': '...'\n// 'ruleName': {input: {field: 'field', model: 'name'}, message: '...'}\n// 'ruleName': {input: {field: 'field', model: ModelClass}, message: '...'}\n// 'ruleName': {input: {field: 'field', models: [...]}, message: '...'}\n// 'ruleName': {field: 'field', model: 'name'}\n// 'ruleName': {field: 'field', model: ModelClass}\n// 'ruleName': {field: 'field', models: [...]}\nfunction foreignRuleGenerator(ruleName, defaultMessage, isInvalid)\n{\n Validation.Rules[ ruleName ] = function(field, params, database, getAlias, message)\n {\n var modelName, models, fieldName;\n\n if ( !isValue( params ) || isString( params ) )\n {\n var parts = split( params || '', /(\\s*,\\s*)/, '\\\\' );\n modelName = parts[0] || database.name;\n fieldName = parts[1] || field;\n models = null;\n }\n else if ( isArray( params ) )\n {\n modelName = isString( params[0] ) ? params.shift() : database.name;\n fieldName = isString( params[0] ) ? params.shift() : field;\n models = new ModelCollection( database, params );\n }\n else if ( isObject( params ) )\n {\n modelName = params.model || database.name;\n fieldName = params.field || field;\n models = params.models;\n }\n\n if ( !models )\n {\n if ( !modelName )\n {\n throw 'model, model class, or models is required for ' + ruleName + ' rule';\n }\n\n if ( isString( modelName ) )\n {\n Rekord.get( modelName ).success(function(modelClass)\n {\n models = modelClass.all();\n });\n }\n else if ( isRekord( modelName ) )\n {\n models = modelName.all();\n }\n }\n\n if ( indexOf( database.fields, fieldName ) === false )\n {\n throw fieldName + ' is not a valid field for the ' + ruleName + ' rule';\n }\n\n var messageTemplate = determineMessage( ruleName, message );\n var extra = {\n $class: modelName,\n $matchField: fieldName,\n $matchAlias: getAlias( fieldName )\n };\n\n return function(value, model, chain)\n {\n if ( models && isValue( value ) )\n {\n if ( isInvalid( value, model, models, fieldName ) )\n {\n chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) );\n }\n else\n {\n chain.next();\n }\n }\n else\n {\n chain.next();\n }\n };\n };\n\n Validation.Rules[ ruleName ].message = defaultMessage;\n}\n\n// if:due_date:before:today|required\n\n// if all rules pass for the given field, continue with remaining rules\nsubRuleGenerator('if',\n function isInvalid(invalidCount, totalCount) {\n return invalidCount > 0;\n }\n);\n\n// if any rules pass for the given field, continue with remaining rules\nsubRuleGenerator('if_any',\n function isInvalid(invalidCount, totalCount) {\n return invalidCount >= totalCount;\n }\n);\n\n// if no rules pass for the given field, continue with remaining rules\nsubRuleGenerator('if_not',\n function isInvalid(invalidCount, totalCount) {\n return invalidCount < totalCount;\n }\n);\n\n\n\nfunction subRuleGenerator(ruleName, isInvalid)\n{\n Validation.Rules[ ruleName ] = function(field, params, database, getAlias, message)\n {\n if ( !params )\n {\n throw ruleName + ' validation rule requires a validation rule argument';\n }\n\n var otherField, otherRules;\n\n if ( isString( params ) )\n {\n var colon = params.indexOf( ':' );\n\n if ( colon === -1 )\n {\n throw params + ' is not a valid argument for the ' + ruleName + ' rule';\n }\n\n otherField = params.substring( 0, colon ) || field;\n otherRules = params.substring( colon + 1 );\n }\n else if ( isArray( params ) )\n {\n otherField = params.shift() || field;\n otherRules = params;\n }\n else if ( isObject( params ) )\n {\n otherField = params.field || field;\n otherRules = params.rules;\n }\n\n if ( indexOf( database.fields, otherField ) === -1 )\n {\n throw otherField + ' is not a valid field for the ' + ruleName + ' rule';\n }\n\n if ( !otherRules )\n {\n throw 'rules are required for the ' + ruleName + ' rule';\n }\n\n var validators = Validation.parseRules( otherRules, otherField, database, getAlias );\n\n return function(value, model, chain)\n {\n var invalids = 0;\n var chainCount = 0;\n\n var onChainEnd = function(innerChain)\n {\n if (!innerChain.valid)\n {\n invalids++;\n }\n\n if (++chainCount === validators.length)\n {\n if ( isInvalid( invalids, chainCount ) )\n {\n chain.stop();\n }\n else\n {\n chain.next();\n }\n }\n };\n\n var testValue = otherField === field ? value : model.$get( otherField );\n\n for (var i = 0; i < validators.length; i++)\n {\n var innerChain = new ValidationChain( model, otherField, [validators[ i ]], onChainEnd );\n\n innerChain.start( testValue );\n }\n };\n };\n}\n\n// in:X,Y,Z,...\nlistRuleGenerator('in',\n '{$alias} must be one of {$list}.',\n function isInvalid(value, model, inList)\n {\n return !inList( value, model );\n }\n);\n\n// not_in:X,Y,Z,...\nlistRuleGenerator('not_in',\n '{$alias} must not be one of {$list}.',\n function isInvalid(value, model, inList)\n {\n return inList( value, model );\n }\n);\n\nfunction listRuleGenerator(ruleName, defaultMessage, isInvalid)\n{\n Validation.Rules[ ruleName ] = function(field, params, database, getAlias, message)\n {\n if ( !params )\n {\n throw ruleName + ' validation rule requires a list argument';\n }\n\n var values, inList = false;\n\n if ( isString( params ) )\n {\n values = split( params, /(,)/, '\\\\' );\n }\n else if ( isArray( params ) )\n {\n values = params;\n }\n else if ( isFunction( params ) )\n {\n values = inList;\n }\n\n if ( inList !== false )\n {\n if ( !values || values.length === 0 )\n {\n throw params + ' is not a valid list of values for the ' + ruleName + ' rule';\n }\n }\n\n if ( isPrimitiveArray( values ) )\n {\n var map = mapFromArray( values, true );\n\n inList = function(value)\n {\n return map[ value ];\n };\n }\n else\n {\n inList = function(value)\n {\n return indexOf( values, value, equals );\n };\n }\n\n var messageTemplate = determineMessage( ruleName, message );\n var list = joinFriendly( values, 'or' );\n var extra = {\n $params: params,\n $list: list\n };\n\n return function(value, model, chain)\n {\n if ( isInvalid( value, model, inList ) )\n {\n chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) );\n }\n else\n {\n chain.next();\n }\n };\n };\n\n\n Validation.Rules[ ruleName ].message = defaultMessage;\n}\n\n// between:3,10\nrangeRuleGenerator('between', {\n 'string': '{$alias} must have between {$start} to {$end} characters.',\n 'number': '{$alias} must be between {$start} and {$end}.',\n 'object': '{$alias} must have between {$start} to {$end} items.'\n },\n function isInvalid(value, start, end) {\n return value < start || value > end;\n }\n);\n\n// not_between\nrangeRuleGenerator('not_between', {\n 'string': '{$alias} must not have between {$start} to {$end} characters.',\n 'number': '{$alias} must not be between {$start} and {$end}.',\n 'object': '{$alias} must not have between {$start} to {$end} items.'\n },\n function isInvalid(value, start, end) {\n return value >= start && value <= end;\n }\n);\n\nfunction rangeRuleGenerator(ruleName, defaultMessages, isInvalid)\n{\n Validation.Rules[ ruleName ] = function(field, params, database, getAlias, message)\n {\n if ( !params )\n {\n throw ruleName + ' validation rule requires a range argument';\n }\n\n var start, end;\n\n if ( isString( params ) )\n {\n var range = split( params, /(\\s*,\\s*)/, '\\\\' );\n\n start = parseFloat( range[0] );\n end = parseFloat( range[1] );\n }\n else if ( isArray( params ) )\n {\n start = params[ 0 ];\n end = params[ 1 ];\n }\n else if ( isObject( params ) )\n {\n start = params.start;\n end = params.end;\n }\n\n if ( isNaN( start ) || isNaN( end ) )\n {\n throw params + ' is not a valid range of numbers for the ' + ruleName + ' rule';\n }\n\n if ( isString( message ) )\n {\n message = {\n 'string': message,\n 'number': message,\n 'object': message\n };\n }\n\n var messageTemplate = determineMessage( ruleName, message );\n var extra = {\n $start: start,\n $end: end\n };\n\n return function(value, model, chain)\n {\n var size = sizeof( value );\n var type = typeof( value );\n var typeMessage = messageTemplate[ type ];\n\n if ( typeMessage && isInvalid( size, start, end ) )\n {\n extra.$size = size;\n\n chain.invalid( generateMessage( field, getAlias( field ), value, model, typeMessage, extra ) );\n }\n else\n {\n chain.next();\n }\n };\n };\n\n Validation.Rules[ ruleName ].message = defaultMessages;\n}\n\n\n\nregexRuleGenerator('alpha',\n '{$alias} should only contain alphabetic characters.',\n /^[a-zA-Z]*$/\n);\n\nregexRuleGenerator('alpha_dash',\n '{$alias} should only contain alpha-numeric characters, dashes, and underscores.',\n /^[a-zA-Z0-9_-]*$/\n);\n\nregexRuleGenerator('alpha_num',\n '{$alias} should only contain alpha-numeric characters.',\n /^[a-zA-Z0-9]*$/\n);\n\nregexRuleGenerator('email',\n '{$alias} is not a valid email.',\n /^.+@.+\\..+$/\n);\n\nregexRuleGenerator('url',\n '{$alias} is not a valid URL.',\n /^(https?:\\/\\/)?[-a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)$/\n);\n\nregexRuleGenerator('uri',\n '{$alias} is not a valid URI.',\n /^(\\w+:\\/\\/)?[-a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)$/\n);\n\nregexRuleGenerator('phone',\n '{$alias} is not a valid phone number.',\n /^1?\\W*([2-9][0-8][0-9])\\W*([2-9][0-9]{2})\\W*([0-9]{4})(\\se?x?t?(\\d*))?$/\n);\n\nfunction regexRuleGenerator(ruleName, defaultMessage, regex)\n{\n Validation.Rules[ ruleName ] = function(field, params, database, getAlias, message)\n {\n checkNoParams( ruleName, field, params );\n\n var messageTemplate = determineMessage( ruleName, message );\n\n return function(value, model, chain)\n {\n if ( !regex.test( value ) )\n {\n chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate ) );\n }\n else\n {\n chain.next();\n }\n\n return value;\n };\n };\n\n Validation.Rules[ ruleName ].message = defaultMessage;\n}\n\nValidation.Rules.regex = function(field, params, database, getAlias, message)\n{\n var regex;\n\n if ( isString( params ) )\n {\n var parsed = /^\\/(.*)\\/([gmi]*)$/.exec( params );\n\n if ( parsed )\n {\n regex = new RegExp( parsed[1], parsed[2] );\n }\n }\n else if ( isRegExp( params ) )\n {\n regex = params;\n }\n\n if ( !regex )\n {\n throw params + ' is not a valid regular expression for the regex rule';\n }\n\n var messageTemplate = determineMessage( 'regex', message );\n\n return function(value, model, chain)\n {\n if ( !regex.test( value ) )\n {\n chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate ) );\n }\n else\n {\n chain.next();\n }\n };\n};\n\nValidation.Rules.regex.message = '{$alias} is not a valid value.';\n\n// required\nruleGenerator('required',\n '{$alias} is required.',\n function isInvalid(value) {\n return isEmpty( value );\n }\n);\n\n// min:3\nsizeRuleGenerator('min', {\n 'string': '{$alias} must have a minimum of {$number} characters.',\n 'number': '{$alias} must be at least {$number}.',\n 'object': '{$alias} must have at least {$number} items.'\n },\n function isInvalid(value, number) {\n return value < number;\n }\n);\n\n// greater_than:0\nsizeRuleGenerator('greater_than', {\n 'string': '{$alias} must have more than {$number} characters.',\n 'number': '{$alias} must be greater than {$number}.',\n 'object': '{$alias} must have more than {$number} items.'\n },\n function isInvalid(value, number) {\n return value <= number;\n }\n);\n\n// max:10\nsizeRuleGenerator('max', {\n 'string': '{$alias} must have no more than {$number} characters.',\n 'number': '{$alias} must be no more than {$number}.',\n 'object': '{$alias} must have no more than {$number} items.'\n },\n function isInvalid(value, number) {\n return value > number;\n }\n);\n\n// less_than:5\nsizeRuleGenerator('less_than', {\n 'string': '{$alias} must have less than {$number} characters.',\n 'number': '{$alias} must be less than {$number}.',\n 'object': '{$alias} must have less than {$number} items.'\n },\n function isInvalid(value, number) {\n return value >= number;\n }\n);\n\n// equal:4.5\nsizeRuleGenerator('equal', {\n 'string': '{$alias} must have {$number} characters.',\n 'number': '{$alias} must equal {$number}.',\n 'object': '{$alias} must have {$number} items.'\n },\n function isInvalid(value, number) {\n return value !== number;\n }\n);\n\n// not_equal:0\nsizeRuleGenerator('not_equal', {\n 'string': '{$alias} must not have {$number} characters.',\n 'number': '{$alias} must not equal {$number}.',\n 'object': '{$alias} must not have {$number} items.'\n },\n function isInvalid(value, number) {\n return value === number;\n }\n);\n\nfunction sizeRuleGenerator(ruleName, defaultMessages, isInvalid)\n{\n Validation.Rules[ ruleName ] = function(field, params, database, getAlias, message)\n {\n var number;\n\n if ( isString( params ) )\n {\n number = parseFloat( params );\n }\n else if ( isNumber( params ) )\n {\n number = params;\n }\n\n if ( isNaN( number ) )\n {\n throw '\"' + params + '\" is not a valid number for the ' + ruleName + ' rule';\n }\n\n if ( isString( message ) )\n {\n message = {\n 'string': message,\n 'number': message,\n 'object': message\n };\n }\n\n var messageTemplate = determineMessage( ruleName, message );\n var extra = {\n $number: params\n };\n\n return function(value, model, chain)\n {\n var size = sizeof( value );\n var type = typeof( value );\n var typeMessage = messageTemplate[ type ];\n\n if ( typeMessage && isInvalid( size, number ) )\n {\n extra.$size = size;\n\n chain.invalid( generateMessage( field, getAlias( field ), value, model, typeMessage, extra ) );\n }\n else\n {\n chain.next();\n }\n };\n };\n\n Validation.Rules[ ruleName ].message = defaultMessages;\n}\n\n\nruleGenerator('array',\n '{$alias} must be an array.',\n function isInvalid(value) {\n return !isArray( value );\n }\n);\n\nruleGenerator('object',\n '{$alias} must be an object.',\n function isInvalid(value) {\n return !isObject( value );\n }\n);\n\nruleGenerator('string',\n '{$alias} must be a string.',\n function isInvalid(value) {\n return !isString( value );\n }\n);\n\nruleGenerator('number',\n '{$alias} must be a number.',\n function isInvalid(value) {\n return !isNumber( value );\n }\n);\n\nruleGenerator('boolean',\n '{$alias} must be a true or false.',\n function isInvalid(value) {\n return !isBoolean( value );\n }\n);\n\nruleGenerator('model',\n '{$alias} must have a value.',\n function isInvalid(value) {\n return !(value instanceof Model);\n }\n);\n\nruleGenerator('whole',\n '{$alias} must be a whole number.',\n function isInvalid(value, model, chain) {\n var parsed = tryParseInt( value );\n var numeric = parseFloat( value );\n var invalid = !isNumber( parsed );\n if ( !invalid ) {\n invalid = Math.floor( parsed ) !== numeric;\n if ( !invalid ) {\n chain.update( parsed );\n }\n }\n return invalid;\n }\n);\n\nruleGenerator('numeric',\n '{$alias} must be numeric.',\n function isInvalid(value, model, chain) {\n var parsed = tryParseFloat( value );\n var invalid = !isNumber( parsed );\n if ( !invalid ) {\n chain.update( parsed );\n }\n return invalid;\n }\n);\n\nruleGenerator('yesno',\n '{$alias} must be a yes or no.',\n function isInvalid(value, model, chain) {\n var mapped = Validation.Rules.yesno.map[ value ];\n var invalid = !isBoolean( mapped );\n if ( !invalid ) {\n chain.update( mapped );\n }\n return invalid;\n }\n);\n\nValidation.Rules.yesno.map =\n{\n 'true': true,\n 't': true,\n 'yes': true,\n 'y': true,\n '1': true,\n 'false': false,\n 'f': false,\n 'no': false,\n 'n': false,\n '0': false\n};\n\nValidation.Rules.abs = function(field, params, database, alias, message)\n{\n return function(value, model, chain)\n {\n value = tryParseFloat( value );\n\n if ( isNumber( value ) )\n {\n chain.update( Math.abs( value ) );\n }\n\n chain.next();\n };\n};\n\nValidation.Rules.apply = function(field, params, database, alias, message)\n{\n return function(value, model, chain)\n {\n model.$set( field, value );\n\n chain.next();\n };\n};\n\nValidation.Rules.base64 = function(field, params, database, alias, message)\n{\n return function(value, model, chain)\n {\n if ( global.btoa )\n {\n chain.update( global.btoa( value ) );\n }\n\n chain.next();\n };\n};\n\nValidation.Rules.ceil = function(field, params, database, alias, message)\n{\n return function(value, model, chain)\n {\n value = tryParseFloat( value );\n\n if ( isNumber( value ) )\n {\n chain.update( Math.ceil( value ) );\n }\n\n chain.next();\n };\n};\n\nValidation.Rules.endOfDay = function(field, params, database, alias, message)\n{\n return function(value, model, chain)\n {\n chain.update( endOfDay( value ) );\n \n chain.next();\n };\n};\n\nValidation.Rules.filter = function(field, params, database, alias, message)\n{\n return function(value, model, chain)\n {\n if ( isArray( value ) )\n {\n for (var i = value.length - 1; i >= 0; i--)\n {\n if ( !isValue( value[ i ] ) )\n {\n value.splice( i, 1 );\n }\n }\n\n chain.update( value );\n }\n else if ( isObject( value ) )\n {\n for (var prop in value)\n {\n if ( !isValue( value[ prop ] ) )\n {\n delete value[ prop ];\n }\n }\n\n chain.update( value );\n }\n\n chain.next();\n };\n};\n\nValidation.Rules.floor = function(field, params, database, alias, message)\n{\n return function(value, model, chain)\n {\n value = tryParseFloat( value );\n\n if ( isNumber( value ) )\n {\n chain.update( Math.floor( value ) );\n }\n\n chain.next();\n };\n};\n\nValidation.Rules.mod = function(field, params, database, alias, message)\n{\n var number = tryParseFloat( params );\n\n if ( !isNumber( number ) )\n {\n throw '\"' + number + '\" is not a valid number for the mod rule.';\n }\n\n return function(value, model, chain)\n {\n value = tryParseFloat( value );\n\n if ( isNumber( value ) )\n {\n chain.update( value % number );\n }\n\n chain.next();\n };\n};\n\nValidation.Rules.null = function(field, params, database, alias, message)\n{\n return function(value, model, chain)\n {\n model.$set( field, null );\n\n chain.update( null );\n\n chain.next();\n };\n};\n\nValidation.Rules.round = function(field, params, database, alias, message)\n{\n return function(value, model, chain)\n {\n value = tryParseFloat( value );\n\n if ( isNumber( value ) )\n {\n chain.update( Math.round( value ) );\n }\n\n chain.next();\n };\n};\n\nValidation.Rules.startOfDay = function(field, params, database, alias, message)\n{\n return function(value, model, chain)\n {\n chain.update( startOfDay( value ) );\n\n chain.next();\n };\n};\n\nValidation.Rules.stripEnts = function(field, params, database, alias, message)\n{\n return function(value, model, chain)\n {\n if ( isString( value ) )\n {\n chain.update( value.replace( /&[a-z]+;/gi, '' ) );\n }\n\n chain.next();\n };\n};\n\nValidation.Rules.stripTags = function(field, params, database, alias, message)\n{\n return function(value, model, chain)\n {\n if ( isString( value ) )\n {\n chain.update( value.replace( /<(?:.|\\n)*?>/gm, '' ) );\n }\n\n chain.next();\n };\n};\n\nValidation.Rules.trim = function(field, params, database, alias, message)\n{\n var trim = (function()\n {\n if ( String.prototype.trim )\n {\n return function(x) {\n return x.trim();\n };\n }\n\n var regex = /^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g;\n\n return function(x)\n {\n return x.replace( regex, '' );\n };\n\n })();\n\n return function(value, model, chain)\n {\n if ( isString( value ) )\n {\n chain.update( trim( value ) );\n }\n\n chain.next();\n };\n};\n\nValidation.Rules.unbase64 = function(field, params, database, alias, message)\n{\n return function(value, model, chain)\n {\n if ( global.atob )\n {\n chain.update( global.atob( value ) );\n }\n\n chain.next();\n };\n};\n\n\n Rekord.Validation = Validation;\n\n Rekord.ruleGenerator = ruleGenerator;\n Rekord.rangeRuleGenerator = rangeRuleGenerator;\n Rekord.collectionRuleGenerator = collectionRuleGenerator;\n Rekord.dateRuleGenerator = dateRuleGenerator;\n Rekord.fieldListRuleGenerator = fieldListRuleGenerator;\n Rekord.fieldsRuleGenerator = fieldsRuleGenerator;\n Rekord.foreignRuleGenerator = foreignRuleGenerator;\n Rekord.subRuleGenerator = subRuleGenerator;\n Rekord.listRuleGenerator = listRuleGenerator;\n Rekord.regexRuleGenerator = regexRuleGenerator;\n Rekord.sizeRuleGenerator = sizeRuleGenerator;\n\n Rekord.joinFriendly = joinFriendly;\n Rekord.tryParseFloat = tryParseFloat;\n Rekord.tryParseInt = tryParseInt;\n Rekord.startOfDay = startOfDay;\n Rekord.endOfDay = endOfDay;\n Rekord.determineMessage = determineMessage;\n Rekord.mapFromArray = mapFromArray;\n Rekord.checkNoParams = checkNoParams;\n Rekord.generateMessage = generateMessage;\n\n return Rekord;\n\n}));\n"],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/package.json b/package.json index cfb5d03..d80176c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rekord-validation", - "version": "1.4.4", + "version": "1.5.0", "description": "Advanced validation rules for rekord", "author": "Philip Diffenderfer", "license": "MIT", @@ -32,6 +32,6 @@ "merge-stream": "^1.0.0" }, "dependencies": { - "rekord": "~1.4.0" + "rekord": "~1.5.0" } } diff --git a/src/header.js b/src/header.js index 6fb27df..d5419bd 100644 --- a/src/header.js +++ b/src/header.js @@ -28,6 +28,7 @@ var Promise = Rekord.Promise; var Collection = Rekord.Collection; var ModelCollection = Rekord.ModelCollection; + var Class = Rekord.Class; var isEmpty = Rekord.isEmpty; var isString = Rekord.isString; @@ -53,6 +54,3 @@ var format = Rekord.format; var parseDate = Rekord.parseDate; - - var addMethod = Rekord.addMethod; - var replaceMethod = Rekord.replaceMethod; diff --git a/src/lib/ValidationChain.js b/src/lib/ValidationChain.js new file mode 100644 index 0000000..aef5d94 --- /dev/null +++ b/src/lib/ValidationChain.js @@ -0,0 +1,81 @@ + +function ValidationChain(model, field, validations, onEnd) +{ + this.model = model; + this.field = field; + this.validations = validations; + this.onEnd = onEnd; +} + +Class.create( ValidationChain, +{ + + reset: function(value) + { + this.value = value !== undefined ? value : this.model.$get( this.field ); + this.updated = false; + this.valid = true; + this.message = ''; + this.linkIndex = 0; + }, + + start: function(value) + { + this.reset( value ); + this.call(); + }, + + call: function() + { + this.validations[ this.linkIndex ]( this.value, this.model, this ); + }, + + update: function(newValue) + { + this.value = newValue; + this.updated = true; + + return this; + }, + + next: function() + { + var n = this.validations.length; + + this.linkIndex++; + + if (this.linkIndex === n) + { + this.onEnd( this ); + } + else if (this.linkIndex < n) + { + this.call(); + } + + return this; + }, + + stop: function() + { + var n = this.validations.length; + + if (this.linkIndex < n) + { + this.linkIndex = n - 1; + this.next(); + } + + return this; + }, + + invalid: function(message) + { + this.message = message; + this.valid = false; + this.stop(); + + return this; + } + +}); diff --git a/src/lib/rules/accepted.js b/src/lib/rules/accepted.js index 6464c98..0311e8d 100644 --- a/src/lib/rules/accepted.js +++ b/src/lib/rules/accepted.js @@ -6,17 +6,19 @@ Validation.Rules.accepted = function(field, params, database, getAlias, message) var messageTemplate = determineMessage( 'accepted', message ); var acceptable = Validation.Rules.accepted.acceptable; - return function(value, model, setMessage) + return function(value, model, chain) { var valueString = (value + '').toLowerCase(); var accepted = acceptable[ valueString ]; if ( !accepted ) { - setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate ) ); + chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate ) ); + } + else + { + chain.next(); } - - return value; }; }; diff --git a/src/lib/rules/collection.js b/src/lib/rules/collection.js index e2ef6de..eda3231 100644 --- a/src/lib/rules/collection.js +++ b/src/lib/rules/collection.js @@ -75,14 +75,16 @@ function collectionRuleGenerator(ruleName, defaultMessage, isInvalid) $matchValue: matchValue }; - return function(value, model, setMessage) + return function(value, model, chain) { if ( isInvalid( value, model, matchField, matchValue, equality ) ) { - setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) ); + chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) ); + } + else + { + chain.next(); } - - return value; }; }; @@ -95,7 +97,7 @@ Validation.Rules.validate = function(field, params, database, getAlias, message) var messageOption = params || 'message'; var messageTemplate = determineMessage( 'validate', message ); - return function(value, model, setMessage) + return function(value, model, chain) { if ( isArray( value ) ) { @@ -116,19 +118,25 @@ Validation.Rules.validate = function(field, params, database, getAlias, message) switch (messageOption) { case 'models': - setMessage( invalid ); + chain.invalid( invalid ); break; case 'validations': - setMessage( invalid.pluck( '$validations', '$$key' ) ); + chain.invalid( invalid.pluck( '$validations', '$$key' ) ); break; default: // message - setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate ) ); + chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate ) ); break; } } + else + { + chain.next(); + } + } + else + { + chain.next(); } - - return value; }; }; diff --git a/src/lib/rules/dates.js b/src/lib/rules/dates.js index 7980d1c..b82167a 100644 --- a/src/lib/rules/dates.js +++ b/src/lib/rules/dates.js @@ -33,11 +33,11 @@ dateRuleGenerator('before_on', // date ruleGenerator('date_like', '{$alias} must be a valid date.', - function isInvalid(value, model, setValue) { + function isInvalid(value, model, chain) { var parsed = parseDate( value ); var invalid = parsed === false; if ( !invalid ) { - setValue( parsed.getTime() ); + chain.update( parsed.getTime() ); } return invalid; } @@ -87,7 +87,7 @@ function dateRuleGenerator(ruleName, defaultMessage, isInvalid) $date: params }; - return function(value, model, setMessage) + return function(value, model, chain) { var parsed = parseDate( value ); @@ -99,11 +99,17 @@ function dateRuleGenerator(ruleName, defaultMessage, isInvalid) if ( isNumber( date ) && isInvalid( value, date ) ) { - setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) ); + chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) ); + } + else + { + chain.next(); } } - - return value; + else + { + chain.next(); + } }; }; diff --git a/src/lib/rules/field_list.js b/src/lib/rules/field_list.js index d35e390..2098694 100644 --- a/src/lib/rules/field_list.js +++ b/src/lib/rules/field_list.js @@ -63,14 +63,16 @@ function fieldListRuleGenerator(ruleName, defaultMessage, isInvalid) }; var map = mapFromArray( matchValues, true ); - return function(value, model, setMessage) + return function(value, model, chain) { if ( isInvalid( value, model, matchField, matchValues, map ) ) { - setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) ); + chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) ); + } + else + { + chain.next(); } - - return value; }; }; diff --git a/src/lib/rules/fields.js b/src/lib/rules/fields.js index bd8058e..006bef2 100644 --- a/src/lib/rules/fields.js +++ b/src/lib/rules/fields.js @@ -1,7 +1,7 @@ // confirmed:X fieldsRuleGenerator('confirmed', '{$alias} must match {$fieldAliases}.', - function isInvalid(value, model, fields, setValue) { + function isInvalid(value, model, fields, chain) { var confirmed = true; for (var i = 0; i < fields.length; i++) @@ -19,7 +19,7 @@ fieldsRuleGenerator('confirmed', // different:X fieldsRuleGenerator('different', '{$alias} must not match {$fieldAliases}.', - function isInvalid(value, model, fields, setValue) { + function isInvalid(value, model, fields, chain) { var different = false; for (var i = 0; i < fields.length; i++) @@ -37,7 +37,7 @@ fieldsRuleGenerator('different', // if_valid:X fieldsRuleGenerator('if_valid', '', - function isInvalid(value, model, fields, setValue) { + function isInvalid(value, model, fields, chain) { var valid = true; for (var i = 0; i < fields.length && valid; i++) @@ -50,7 +50,7 @@ fieldsRuleGenerator('if_valid', if ( !valid ) { - setValue( Validation.Stop ); + chain.stop(); } return false; @@ -61,7 +61,7 @@ fieldsRuleGenerator('if_valid', // required_with:X,Y,... fieldsRuleGenerator('required_with', '{$alias} is required.', - function isInvalid(value, model, fields, setValue) { + function isInvalid(value, model, fields, chain) { var required = false; for (var i = 0; i < fields.length && !required; i++) @@ -80,7 +80,7 @@ fieldsRuleGenerator('required_with', // required_with_all:X,Y,... fieldsRuleGenerator('required_with_all', '{$alias} is required.', - function isInvalid(value, model, fields, setValue) { + function isInvalid(value, model, fields, chain) { var required = true; for (var i = 0; i < fields.length && required; i++) @@ -99,7 +99,7 @@ fieldsRuleGenerator('required_with_all', // required_without:X,Y,... fieldsRuleGenerator('required_without', '{$alias} is required.', - function isInvalid(value, model, fields, setValue) { + function isInvalid(value, model, fields, chain) { var required = false; for (var i = 0; i < fields.length && !required; i++) @@ -118,7 +118,7 @@ fieldsRuleGenerator('required_without', // required_without_all:X,Y,... fieldsRuleGenerator('required_without_all', '{$alias} is required.', - function isInvalid(value, model, fields, setValue) { + function isInvalid(value, model, fields, chain) { var required = true; for (var i = 0; i < fields.length && required; i++) @@ -160,19 +160,16 @@ function fieldsRuleGenerator(ruleName, defaultMessage, isInvalid) $fieldAliases: fieldAliases }; - return function(value, model, setMessage) + return function(value, model, chain) { - function setValue( newValue ) + if ( isInvalid( value, model, fields, chain ) ) { - value = newValue; + chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) ); } - - if ( isInvalid( value, model, fields, setValue ) ) + else { - setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) ); + chain.next(); } - - return value; }; }; diff --git a/src/lib/rules/foreign.js b/src/lib/rules/foreign.js index fa4c4cb..0b18e20 100644 --- a/src/lib/rules/foreign.js +++ b/src/lib/rules/foreign.js @@ -91,17 +91,23 @@ function foreignRuleGenerator(ruleName, defaultMessage, isInvalid) $matchAlias: getAlias( fieldName ) }; - return function(value, model, setMessage) + return function(value, model, chain) { if ( models && isValue( value ) ) { if ( isInvalid( value, model, models, fieldName ) ) { - setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) ); + chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) ); + } + else + { + chain.next(); } } - - return value; + else + { + chain.next(); + } }; }; diff --git a/src/lib/rules/if.js b/src/lib/rules/if.js index 0cc0a3f..42fb618 100644 --- a/src/lib/rules/if.js +++ b/src/lib/rules/if.js @@ -69,26 +69,39 @@ function subRuleGenerator(ruleName, isInvalid) var validators = Validation.parseRules( otherRules, otherField, database, getAlias ); - return function(value, model, setMessage) + return function(value, model, chain) { var invalids = 0; + var chainCount = 0; - var setInvalid = function(message) + var onChainEnd = function(innerChain) { - if ( message ) + if (!innerChain.valid) { invalids++; } + + if (++chainCount === validators.length) + { + if ( isInvalid( invalids, chainCount ) ) + { + chain.stop(); + } + else + { + chain.next(); + } + } }; var testValue = otherField === field ? value : model.$get( otherField ); for (var i = 0; i < validators.length; i++) { - validators[ i ]( testValue, model, setInvalid ); - } + var innerChain = new ValidationChain( model, otherField, [validators[ i ]], onChainEnd ); - return isInvalid( invalids, validators.length ) ? Validation.Stop : value; + innerChain.start( testValue ); + } }; }; } diff --git a/src/lib/rules/list.js b/src/lib/rules/list.js index 9c8da32..69e39e3 100644 --- a/src/lib/rules/list.js +++ b/src/lib/rules/list.js @@ -72,14 +72,16 @@ function listRuleGenerator(ruleName, defaultMessage, isInvalid) $list: list }; - return function(value, model, setMessage) + return function(value, model, chain) { if ( isInvalid( value, model, inList ) ) { - setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) ); + chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate, extra ) ); + } + else + { + chain.next(); } - - return value; }; }; diff --git a/src/lib/rules/range.js b/src/lib/rules/range.js index 8903186..aef1b77 100644 --- a/src/lib/rules/range.js +++ b/src/lib/rules/range.js @@ -69,7 +69,7 @@ function rangeRuleGenerator(ruleName, defaultMessages, isInvalid) $end: end }; - return function(value, model, setMessage) + return function(value, model, chain) { var size = sizeof( value ); var type = typeof( value ); @@ -79,10 +79,12 @@ function rangeRuleGenerator(ruleName, defaultMessages, isInvalid) { extra.$size = size; - setMessage( generateMessage( field, getAlias( field ), value, model, typeMessage, extra ) ); + chain.invalid( generateMessage( field, getAlias( field ), value, model, typeMessage, extra ) ); + } + else + { + chain.next(); } - - return value; }; }; diff --git a/src/lib/rules/regex.js b/src/lib/rules/regex.js index f81620e..12941a5 100644 --- a/src/lib/rules/regex.js +++ b/src/lib/rules/regex.js @@ -43,11 +43,15 @@ function regexRuleGenerator(ruleName, defaultMessage, regex) var messageTemplate = determineMessage( ruleName, message ); - return function(value, model, setMessage) + return function(value, model, chain) { if ( !regex.test( value ) ) { - setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate ) ); + chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate ) ); + } + else + { + chain.next(); } return value; @@ -82,14 +86,16 @@ Validation.Rules.regex = function(field, params, database, getAlias, message) var messageTemplate = determineMessage( 'regex', message ); - return function(value, model, setMessage) + return function(value, model, chain) { if ( !regex.test( value ) ) { - setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate ) ); + chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate ) ); + } + else + { + chain.next(); } - - return value; }; }; diff --git a/src/lib/rules/sizes.js b/src/lib/rules/sizes.js index ffc7171..e95d7de 100644 --- a/src/lib/rules/sizes.js +++ b/src/lib/rules/sizes.js @@ -98,7 +98,7 @@ function sizeRuleGenerator(ruleName, defaultMessages, isInvalid) $number: params }; - return function(value, model, setMessage) + return function(value, model, chain) { var size = sizeof( value ); var type = typeof( value ); @@ -108,10 +108,12 @@ function sizeRuleGenerator(ruleName, defaultMessages, isInvalid) { extra.$size = size; - setMessage( generateMessage( field, getAlias( field ), value, model, typeMessage, extra ) ); + chain.invalid( generateMessage( field, getAlias( field ), value, model, typeMessage, extra ) ); + } + else + { + chain.next(); } - - return value; }; }; diff --git a/src/lib/rules/types.js b/src/lib/rules/types.js index 93de498..8461b21 100644 --- a/src/lib/rules/types.js +++ b/src/lib/rules/types.js @@ -43,14 +43,14 @@ ruleGenerator('model', ruleGenerator('whole', '{$alias} must be a whole number.', - function isInvalid(value, model, setValue) { + function isInvalid(value, model, chain) { var parsed = tryParseInt( value ); var numeric = parseFloat( value ); var invalid = !isNumber( parsed ); if ( !invalid ) { invalid = Math.floor( parsed ) !== numeric; if ( !invalid ) { - setValue( parsed ); + chain.update( parsed ); } } return invalid; @@ -59,11 +59,11 @@ ruleGenerator('whole', ruleGenerator('numeric', '{$alias} must be numeric.', - function isInvalid(value, model, setValue) { + function isInvalid(value, model, chain) { var parsed = tryParseFloat( value ); var invalid = !isNumber( parsed ); if ( !invalid ) { - setValue( parsed ); + chain.update( parsed ); } return invalid; } @@ -71,11 +71,11 @@ ruleGenerator('numeric', ruleGenerator('yesno', '{$alias} must be a yes or no.', - function isInvalid(value, model, setValue) { + function isInvalid(value, model, chain) { var mapped = Validation.Rules.yesno.map[ value ]; var invalid = !isBoolean( mapped ); if ( !invalid ) { - setValue( mapped ); + chain.update( mapped ); } return invalid; } diff --git a/src/lib/transforms/abs.js b/src/lib/transforms/abs.js index 20b56aa..c79245b 100644 --- a/src/lib/transforms/abs.js +++ b/src/lib/transforms/abs.js @@ -1,14 +1,14 @@ Validation.Rules.abs = function(field, params, database, alias, message) { - return function(value, model, setMessage) + return function(value, model, chain) { value = tryParseFloat( value ); if ( isNumber( value ) ) { - value = Math.abs( value ); + chain.update( Math.abs( value ) ); } - return value; + chain.next(); }; }; diff --git a/src/lib/transforms/apply.js b/src/lib/transforms/apply.js index 5a8dd72..66fded3 100644 --- a/src/lib/transforms/apply.js +++ b/src/lib/transforms/apply.js @@ -1,9 +1,9 @@ Validation.Rules.apply = function(field, params, database, alias, message) { - return function(value, model, setMessage) + return function(value, model, chain) { model.$set( field, value ); - - return value; + + chain.next(); }; }; diff --git a/src/lib/transforms/base64.js b/src/lib/transforms/base64.js index b173582..08eb74c 100644 --- a/src/lib/transforms/base64.js +++ b/src/lib/transforms/base64.js @@ -1,12 +1,12 @@ Validation.Rules.base64 = function(field, params, database, alias, message) { - return function(value, model, setMessage) + return function(value, model, chain) { if ( global.btoa ) { - value = global.btoa( value ); + chain.update( global.btoa( value ) ); } - return value; + chain.next(); }; }; diff --git a/src/lib/transforms/ceil.js b/src/lib/transforms/ceil.js index f74324e..64cda45 100644 --- a/src/lib/transforms/ceil.js +++ b/src/lib/transforms/ceil.js @@ -1,14 +1,14 @@ Validation.Rules.ceil = function(field, params, database, alias, message) { - return function(value, model, setMessage) + return function(value, model, chain) { value = tryParseFloat( value ); - + if ( isNumber( value ) ) { - value = Math.ceil( value ); + chain.update( Math.ceil( value ) ); } - return value; + chain.next(); }; }; diff --git a/src/lib/transforms/endOfDay.js b/src/lib/transforms/endOfDay.js index 0afca54..592d8c7 100644 --- a/src/lib/transforms/endOfDay.js +++ b/src/lib/transforms/endOfDay.js @@ -1,7 +1,9 @@ Validation.Rules.endOfDay = function(field, params, database, alias, message) { - return function(value, model, setMessage) + return function(value, model, chain) { - return endOfDay( value ); + chain.update( endOfDay( value ) ); + + chain.next(); }; }; diff --git a/src/lib/transforms/filter.js b/src/lib/transforms/filter.js index 52db75e..efa4d8b 100644 --- a/src/lib/transforms/filter.js +++ b/src/lib/transforms/filter.js @@ -1,6 +1,6 @@ Validation.Rules.filter = function(field, params, database, alias, message) { - return function(value, model, setMessage) + return function(value, model, chain) { if ( isArray( value ) ) { @@ -11,6 +11,8 @@ Validation.Rules.filter = function(field, params, database, alias, message) value.splice( i, 1 ); } } + + chain.update( value ); } else if ( isObject( value ) ) { @@ -21,8 +23,10 @@ Validation.Rules.filter = function(field, params, database, alias, message) delete value[ prop ]; } } + + chain.update( value ); } - return value; + chain.next(); }; }; diff --git a/src/lib/transforms/floor.js b/src/lib/transforms/floor.js index a978d21..793ab54 100644 --- a/src/lib/transforms/floor.js +++ b/src/lib/transforms/floor.js @@ -1,14 +1,14 @@ Validation.Rules.floor = function(field, params, database, alias, message) { - return function(value, model, setMessage) + return function(value, model, chain) { value = tryParseFloat( value ); - + if ( isNumber( value ) ) { - value = Math.floor( value ); + chain.update( Math.floor( value ) ); } - return value; + chain.next(); }; }; diff --git a/src/lib/transforms/mod.js b/src/lib/transforms/mod.js index 19d7d76..d086194 100644 --- a/src/lib/transforms/mod.js +++ b/src/lib/transforms/mod.js @@ -7,15 +7,15 @@ Validation.Rules.mod = function(field, params, database, alias, message) throw '"' + number + '" is not a valid number for the mod rule.'; } - return function(value, model, setMessage) + return function(value, model, chain) { value = tryParseFloat( value ); if ( isNumber( value ) ) { - value = value % number; + chain.update( value % number ); } - return value; + chain.next(); }; }; diff --git a/src/lib/transforms/null.js b/src/lib/transforms/null.js index 16e86fd..85739f2 100644 --- a/src/lib/transforms/null.js +++ b/src/lib/transforms/null.js @@ -1,9 +1,11 @@ Validation.Rules.null = function(field, params, database, alias, message) { - return function(value, model, setMessage) + return function(value, model, chain) { model.$set( field, null ); - return null; + chain.update( null ); + + chain.next(); }; }; diff --git a/src/lib/transforms/round.js b/src/lib/transforms/round.js index b5ecf56..d341266 100644 --- a/src/lib/transforms/round.js +++ b/src/lib/transforms/round.js @@ -1,14 +1,14 @@ Validation.Rules.round = function(field, params, database, alias, message) { - return function(value, model, setMessage) + return function(value, model, chain) { value = tryParseFloat( value ); if ( isNumber( value ) ) { - value = Math.round( value ); + chain.update( Math.round( value ) ); } - return value; + chain.next(); }; }; diff --git a/src/lib/transforms/startOfDay.js b/src/lib/transforms/startOfDay.js index 560ff61..b7aa805 100644 --- a/src/lib/transforms/startOfDay.js +++ b/src/lib/transforms/startOfDay.js @@ -1,7 +1,9 @@ Validation.Rules.startOfDay = function(field, params, database, alias, message) { - return function(value, model, setMessage) + return function(value, model, chain) { - return startOfDay( value ); + chain.update( startOfDay( value ) ); + + chain.next(); }; }; diff --git a/src/lib/transforms/stripEnts.js b/src/lib/transforms/stripEnts.js new file mode 100644 index 0000000..a6bfbbd --- /dev/null +++ b/src/lib/transforms/stripEnts.js @@ -0,0 +1,12 @@ +Validation.Rules.stripEnts = function(field, params, database, alias, message) +{ + return function(value, model, chain) + { + if ( isString( value ) ) + { + chain.update( value.replace( /&[a-z]+;/gi, '' ) ); + } + + chain.next(); + }; +}; diff --git a/src/lib/transforms/stripTags.js b/src/lib/transforms/stripTags.js new file mode 100644 index 0000000..6da984e --- /dev/null +++ b/src/lib/transforms/stripTags.js @@ -0,0 +1,12 @@ +Validation.Rules.stripTags = function(field, params, database, alias, message) +{ + return function(value, model, chain) + { + if ( isString( value ) ) + { + chain.update( value.replace( /<(?:.|\n)*?>/gm, '' ) ); + } + + chain.next(); + }; +}; diff --git a/src/lib/transforms/trim.js b/src/lib/transforms/trim.js index 71ff337..3b24f81 100644 --- a/src/lib/transforms/trim.js +++ b/src/lib/transforms/trim.js @@ -18,13 +18,13 @@ Validation.Rules.trim = function(field, params, database, alias, message) })(); - return function(value, model, setMessage) + return function(value, model, chain) { if ( isString( value ) ) { - value = trim( value ); + chain.update( trim( value ) ); } - return value; + chain.next(); }; }; diff --git a/src/lib/transforms/unbase64.js b/src/lib/transforms/unbase64.js index cd28603..19aa4b3 100644 --- a/src/lib/transforms/unbase64.js +++ b/src/lib/transforms/unbase64.js @@ -1,12 +1,12 @@ Validation.Rules.unbase64 = function(field, params, database, alias, message) { - return function(value, model, setMessage) + return function(value, model, chain) { if ( global.atob ) { - value = global.atob( value ); + chain.update( global.atob( value ) ); } - return value; + chain.next(); }; }; diff --git a/src/lib/util.js b/src/lib/util.js index 1cb2cd6..20bfd0f 100644 --- a/src/lib/util.js +++ b/src/lib/util.js @@ -58,19 +58,16 @@ function ruleGenerator(ruleName, defaultMessage, isInvalid) var messageTemplate = determineMessage( ruleName, message ); - return function(value, model, setMessage) + return function(value, model, chain) { - function setValue( newValue ) + if ( isInvalid( value, model, chain ) ) { - value = newValue; + chain.invalid( generateMessage( field, getAlias( field ), value, model, messageTemplate ) ); } - - if ( isInvalid( value, model, setValue ) ) + else { - setMessage( generateMessage( field, getAlias( field ), value, model, messageTemplate ) ); + chain.next(); } - - return value; }; }; diff --git a/src/lib/validation.js b/src/lib/validation.js index 8f6cfee..62d5163 100644 --- a/src/lib/validation.js +++ b/src/lib/validation.js @@ -1,4 +1,4 @@ -Rekord.on( Rekord.Events.Plugins, function(model, db, options) +Rekord.addPlugin(function(model, db, options) { var validation = options.validation || Database.Defaults.validation; @@ -24,47 +24,56 @@ Rekord.on( Rekord.Events.Plugins, function(model, db, options) db.validations[ field ] = Validation.parseRules( rules[ field ], field, db, getAlias, messages[ field ] ); } - addMethod( model.prototype, '$validate', function() + Class.method( model, '$validate', function(callback, context) { - var $this = this; - this.$trigger( Model.Events.PreValidate, [this] ); this.$valid = true; this.$validations = {}; this.$validationMessages.length = 0; - for (var field in db.validations) + var chainEnds = 0; + var chains = []; + + var onChainEnd = function(chain) { - var chain = db.validations[ field ]; - var value = this.$get( field ); - var fieldValid = true; + var model = chain.model; - var setMessage = function(message) // jshint ignore:line + if (!chain.valid) { - // Only accept for the first valid message - if ( message && fieldValid ) - { - fieldValid = false; - - $this.$validations[ field ] = message; - $this.$validationMessages.push( message ); - $this.$valid = false; - } - }; + model.$validations[ chain.field ] = chain.message; + model.$validationMessages.push( chain.message ); + model.$valid = false; + } - for (var i = 0; i < chain.length && fieldValid && value !== Validation.Stop; i++) + if (++chainEnds === chains.length) { - value = chain[ i ]( value, this, setMessage ); + model.$trigger( model.$valid ? Model.Events.ValidatePass : Model.Events.ValidateFail, [model] ); + + if ( isFunction( callback ) ) + { + callback.call( context || model, model.$valid ); + } } + }; + + for (var field in db.validations) + { + var validations = db.validations[ field ]; + var chain = new ValidationChain( this, field, validations, onChainEnd ); + + chains.push( chain ); } - this.$trigger( this.$valid ? Model.Events.ValidatePass : Model.Events.ValidateFail, [this] ); + for (var i = 0; i < chains.length; i++) + { + chains[ i ].start(); + } return this.$valid; }); - replaceMethod( model.prototype, '$init', function($init) + Class.replace( model, '$init', function($init) { return function() { @@ -78,7 +87,7 @@ Rekord.on( Rekord.Events.Plugins, function(model, db, options) if ( required ) { - replaceMethod( model.prototype, '$save', function($save) + Class.replace( model, '$save', function($save) { return function() { @@ -89,12 +98,25 @@ Rekord.on( Rekord.Events.Plugins, function(model, db, options) return Promise.resolve( this ); } - if ( !this.$validate() ) - { - return Promise.resolve( this ); - } + var promise = new Rekord.Promise(); + var modelInstance = this; + var args = arguments; - return $save.apply( this, arguments ); + this.$validate(function(valid) + { + if (!valid) + { + promise.reject( modelInstance ); + } + else + { + var saving = $save.apply( modelInstance, args ); + + saving.then( promise.resolve, promise.reject, promise.noline, promise.cancel, promise ); + } + }); + + return promise; }; }); } @@ -197,16 +219,18 @@ var Validation = customValidator: function(functionName, field, database, getAlias, message) { - return function(value, model, setMessage) + return function(value, model, chain) { - var result = model[ functionName ]( value, getAlias, message ); + var result = model[ functionName ]( value, getAlias, message, chain ); if ( isString( result ) ) { - setMessage( result ); + chain.invalid( result ); + } + else if ( result !== false ) + { + chain.next(); } - - return value; }; } }; diff --git a/test/base.js b/test/base.js index d2267db..a1d691d 100644 --- a/test/base.js +++ b/test/base.js @@ -406,7 +406,7 @@ function TestRest() this.status = 200; this.returnValue = false; this.delay = 0; - this.lastModel = this.lastRecord = this.lastOperation = null; + this.lastModel = this.lastRecord = this.lastOperation = this.lastOptions = null; } TestRest.prototype = @@ -444,10 +444,11 @@ TestRest.prototype = if ( failure ) failure( returnedValue, status ); } }, - get: function(model, success, failure) + get: function(model, options, success, failure) { this.lastOperation = 'get'; this.lastModel = model; + this.lastOptions = options; var map = this.map; function onGet() @@ -462,11 +463,12 @@ TestRest.prototype = this.finishDelayed( onGet, failure, null ); }, - create: function(model, encoded, success, failure) + create: function(model, encoded, options, success, failure) { this.lastOperation = 'create'; this.lastModel = model; this.lastRecord = encoded; + this.lastOptions = options; var map = this.map; function onCreate() @@ -477,11 +479,12 @@ TestRest.prototype = this.finishDelayed( onCreate, failure, {} ); }, - update: function(model, encoded, success, failure) + update: function(model, encoded, options, success, failure) { this.lastOperation = 'update'; this.lastModel = model; this.lastRecord = encoded; + this.lastOptions = options; var map = this.map; function onUpdate() @@ -493,10 +496,11 @@ TestRest.prototype = this.finishDelayed( onUpdate, failure, {} ); }, - remove: function(model, success, failure) + remove: function(model, options, success, failure) { this.lastOperation = 'remove'; this.lastModel = model; + this.lastOptions = options; var map = this.map; function onRemove() @@ -507,15 +511,19 @@ TestRest.prototype = this.finishDelayed( onRemove, failure, {} ); }, - all: function(success, failure) + all: function(options, success, failure) { this.lastOperation = 'all'; + this.lastOptions = options; + this.finishDelayed( success, failure, this.map.values ); }, - query: function(url, data, success, failure) + query: function(url, data, options, success, failure) { this.lastOperation = 'query'; this.lastRecord = data; + this.lastOptions = options; + this.finishDelayed( success, failure, this.queries.get( url ) ); } }; diff --git a/test/cases/rekord-validation.js b/test/cases/rekord-validation.js index 16f6df1..e416387 100644 --- a/test/cases/rekord-validation.js +++ b/test/cases/rekord-validation.js @@ -372,46 +372,56 @@ test( 'rule after missing date', function(assert) { var prefix = 'rule_after_missing_date_'; - throws(function() { - Rekord({ - name: prefix + 'no_colon', - fields: ['due_date'], - validation: { - rules: { - due_date: 'after' - } + expect(2); + + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, 'after validation rule requires a date expression argument' ); + }); + + Rekord({ + name: prefix + 'no_colon', + fields: ['due_date'], + validation: { + rules: { + due_date: 'after' } - }); - }, 'after validation rule requires a date expression argument'); + } + }); - throws(function() { - Rekord({ - name: prefix + 'colon', - fields: ['due_date'], - validation: { - rules: { - due_date: 'after:' - } + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, 'after validation rule requires a date expression argument' ); + }); + + Rekord({ + name: prefix + 'colon', + fields: ['due_date'], + validation: { + rules: { + due_date: 'after:' } - }); - }, 'after validation rule requires a date expression argument'); + } + }); }); test( 'rule after invalid date', function(assert) { var prefix = 'rule_after_invalid_date_'; - throws(function() { - Rekord({ - name: prefix, - fields: ['due_date'], - validation: { - rules: { - due_date: 'after:NOTDATE' - } + expect(1); + + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, 'NOTDATE is not a valid date expression for the after rule' ); + }); + + Rekord({ + name: prefix, + fields: ['due_date'], + validation: { + rules: { + due_date: 'after:NOTDATE' } - }); - }, 'NOTDATE is not a valid date expression for the after rule'); + } + }); }); test( 'rule after_on custom message', function(assert) @@ -604,46 +614,56 @@ test( 'rule after_on missing date', function(assert) { var prefix = 'rule_after_on_missing_date_'; - throws(function() { - Rekord({ - name: prefix + 'no_colon', - fields: ['due_date'], - validation: { - rules: { - due_date: 'after_on' - } + expect(2); + + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, 'after_on validation rule requires a date expression argument' ); + }); + + Rekord({ + name: prefix + 'no_colon', + fields: ['due_date'], + validation: { + rules: { + due_date: 'after_on' } - }); - }, 'after_on validation rule requires a date expression argument'); + } + }); - throws(function() { - Rekord({ - name: prefix + 'colon', - fields: ['due_date'], - validation: { - rules: { - due_date: 'after_on:' - } + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, 'after_on validation rule requires a date expression argument' ); + }); + + Rekord({ + name: prefix + 'colon', + fields: ['due_date'], + validation: { + rules: { + due_date: 'after_on:' } - }); - }, 'after_on validation rule requires a date expression argument'); + } + }); }); test( 'rule after_on invalid date', function(assert) { var prefix = 'rule_after_on_invalid_date_'; - throws(function() { - Rekord({ - name: prefix, - fields: ['due_date'], - validation: { - rules: { - due_date: 'after_on:NOTDATE' - } + expect(1); + + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, 'NOTDATE is not a valid date expression for the after_on rule' ); + }); + + Rekord({ + name: prefix, + fields: ['due_date'], + validation: { + rules: { + due_date: 'after_on:NOTDATE' } - }); - }, 'NOTDATE is not a valid date expression for the after_on rule'); + } + }); }); test( 'rule before custom message', function(assert) @@ -836,46 +856,56 @@ test( 'rule before missing date', function(assert) { var prefix = 'rule_before_missing_date_'; - throws(function() { - Rekord({ - name: prefix + 'no_colon', - fields: ['due_date'], - validation: { - rules: { - due_date: 'before' - } + expect(2); + + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, 'before validation rule requires a date expression argument' ); + }); + + Rekord({ + name: prefix + 'no_colon', + fields: ['due_date'], + validation: { + rules: { + due_date: 'before' } - }); - }, 'before validation rule requires a date expression argument'); + } + }); - throws(function() { - Rekord({ - name: prefix + 'colon', - fields: ['due_date'], - validation: { - rules: { - due_date: 'before:' - } + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, 'before validation rule requires a date expression argument' ); + }); + + Rekord({ + name: prefix + 'colon', + fields: ['due_date'], + validation: { + rules: { + due_date: 'before:' } - }); - }, 'before validation rule requires a date expression argument'); + } + }); }); test( 'rule before invalid date', function(assert) { var prefix = 'rule_before_invalid_date_'; - throws(function() { - Rekord({ - name: prefix, - fields: ['due_date'], - validation: { - rules: { - due_date: 'before:NOTDATE' - } + expect(1); + + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, 'NOTDATE is not a valid date expression for the before rule' ); + }); + + Rekord({ + name: prefix, + fields: ['due_date'], + validation: { + rules: { + due_date: 'before:NOTDATE' } - }); - }, 'NOTDATE is not a valid date expression for the before rule'); + } + }); }); test( 'rule before_on custom message', function(assert) @@ -1068,46 +1098,56 @@ test( 'rule before_on missing date', function(assert) { var prefix = 'rule_before_on_missing_date_'; - throws(function() { - Rekord({ - name: prefix + 'no_colon', - fields: ['due_date'], - validation: { - rules: { - due_date: 'before_on' - } + expect(2); + + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, 'before_on validation rule requires a date expression argument' ); + }); + + Rekord({ + name: prefix + 'no_colon', + fields: ['due_date'], + validation: { + rules: { + due_date: 'before_on' } - }); - }, 'before_on validation rule requires a date expression argument'); + } + }); - throws(function() { - Rekord({ - name: prefix + 'colon', - fields: ['due_date'], - validation: { - rules: { - due_date: 'before_on:' - } + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, 'before_on validation rule requires a date expression argument' ); + }); + + Rekord({ + name: prefix + 'colon', + fields: ['due_date'], + validation: { + rules: { + due_date: 'before_on:' } - }); - }, 'before_on validation rule requires a date expression argument'); + } + }); }); test( 'rule before_on invalid date', function(assert) { var prefix = 'rule_before_on_invalid_date_'; - throws(function() { - Rekord({ - name: prefix, - fields: ['due_date'], - validation: { - rules: { - due_date: 'before_on:NOTDATE' - } + expect(1); + + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, 'NOTDATE is not a valid date expression for the before_on rule' ); + }); + + Rekord({ + name: prefix, + fields: ['due_date'], + validation: { + rules: { + due_date: 'before_on:NOTDATE' } - }); - }, 'NOTDATE is not a valid date expression for the before_on rule'); + } + }); }); test( 'rule required custom message', function(assert) @@ -1376,41 +1416,49 @@ test( 'rule min invalid arguments', function(assert) { var prefix = 'rule_min_invalid_arguments_'; - throws(function() { - Rekord({ - name: prefix + 'no_colon', - fields: ['count'], - validation: { - rules: { - count: 'min' - } + expect(3); + + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, '"undefined" is not a valid number for the min rule' ); + }); + + Rekord({ + name: prefix + 'no_colon', + fields: ['count'], + validation: { + rules: { + count: 'min' } - }); - }, 'min validation rule requires a number argument'); + } + }); - throws(function() { - Rekord({ - name: prefix + 'colon', - fields: ['count'], - validation: { - rules: { - count: 'min:' - } + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, '"" is not a valid number for the min rule' ); + }); + + Rekord({ + name: prefix + 'colon', + fields: ['count'], + validation: { + rules: { + count: 'min:' } - }); - }, 'min validation rule requires a number argument'); + } + }); - throws(function() { - Rekord({ - name: prefix + 'nan', - fields: ['count'], - validation: { - rules: { - count: 'min:X' - } + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, '"X" is not a valid number for the min rule' ); + }); + + Rekord({ + name: prefix + 'nan', + fields: ['count'], + validation: { + rules: { + count: 'min:X' } - }); - }, 'X is not a valid number for the min rule'); + } + }); }); // rule min custom message @@ -1514,41 +1562,49 @@ test( 'rule greater_than invalid arguments', function(assert) { var prefix = 'rule_greater_than_invalid_arguments_'; - throws(function() { - Rekord({ - name: prefix + 'no_colon', - fields: ['count'], - validation: { - rules: { - count: 'greater_than' - } + expect(3); + + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, '"undefined" is not a valid number for the greater_than rule' ); + }); + + Rekord({ + name: prefix + 'no_colon', + fields: ['count'], + validation: { + rules: { + count: 'greater_than' } - }); - }, 'greater_than validation rule requires a number argument'); + } + }); - throws(function() { - Rekord({ - name: prefix + 'colon', - fields: ['count'], - validation: { - rules: { - count: 'greater_than:' - } + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, '"" is not a valid number for the greater_than rule' ); + }); + + Rekord({ + name: prefix + 'colon', + fields: ['count'], + validation: { + rules: { + count: 'greater_than:' } - }); - }, 'greater_than validation rule requires a number argument'); + } + }); - throws(function() { - Rekord({ - name: prefix + 'nan', - fields: ['count'], - validation: { - rules: { - count: 'greater_than:X' - } + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, '"X" is not a valid number for the greater_than rule' ); + }); + + Rekord({ + name: prefix + 'nan', + fields: ['count'], + validation: { + rules: { + count: 'greater_than:X' } - }); - }, 'X is not a valid number for the greater_than rule'); + } + }); }); // rule greater_than custom message @@ -1653,41 +1709,49 @@ test( 'rule max invalid arguments', function(assert) { var prefix = 'rule_max_invalid_arguments_'; - throws(function() { - Rekord({ - name: prefix + 'no_colon', - fields: ['count'], - validation: { - rules: { - count: 'max' - } + expect(3); + + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, '"undefined" is not a valid number for the max rule' ); + }); + + Rekord({ + name: prefix + 'no_colon', + fields: ['count'], + validation: { + rules: { + count: 'max' } - }); - }, 'max validation rule requires a number argument'); + } + }); - throws(function() { - Rekord({ - name: prefix + 'colon', - fields: ['count'], - validation: { - rules: { - count: 'max:' - } + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, '"" is not a valid number for the max rule' ); + }); + + Rekord({ + name: prefix + 'colon', + fields: ['count'], + validation: { + rules: { + count: 'max:' } - }); - }, 'max validation rule requires a number argument'); + } + }); - throws(function() { - Rekord({ - name: prefix + 'nan', - fields: ['count'], - validation: { - rules: { - count: 'max:X' - } + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, '"X" is not a valid number for the max rule' ); + }); + + Rekord({ + name: prefix + 'nan', + fields: ['count'], + validation: { + rules: { + count: 'max:X' } - }); - }, 'X is not a valid number for the max rule'); + } + }); }); // rule max custom message @@ -1792,41 +1856,49 @@ test( 'rule less_than invalid arguments', function(assert) { var prefix = 'rule_less_than_invalid_arguments_'; - throws(function() { - Rekord({ - name: prefix + 'no_colon', - fields: ['count'], - validation: { - rules: { - count: 'less_than' - } + expect(3); + + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, '"undefined" is not a valid number for the less_than rule' ); + }); + + Rekord({ + name: prefix + 'no_colon', + fields: ['count'], + validation: { + rules: { + count: 'less_than' } - }); - }, 'less_than validation rule requires a number argument'); + } + }); - throws(function() { - Rekord({ - name: prefix + 'colon', - fields: ['count'], - validation: { - rules: { - count: 'less_than:' - } + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, '"" is not a valid number for the less_than rule' ); + }); + + Rekord({ + name: prefix + 'colon', + fields: ['count'], + validation: { + rules: { + count: 'less_than:' } - }); - }, 'less_than validation rule requires a number argument'); + } + }); - throws(function() { - Rekord({ - name: prefix + 'nan', - fields: ['count'], - validation: { - rules: { - count: 'less_than:X' - } + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, '"X" is not a valid number for the less_than rule' ); + }); + + Rekord({ + name: prefix + 'nan', + fields: ['count'], + validation: { + rules: { + count: 'less_than:X' } - }); - }, 'X is not a valid number for the less_than rule'); + } + }); }); // rule less_than custom message @@ -1931,41 +2003,49 @@ test( 'rule equal invalid arguments', function(assert) { var prefix = 'rule_equal_invalid_arguments_'; - throws(function() { - Rekord({ - name: prefix + 'no_colon', - fields: ['count'], - validation: { - rules: { - count: 'equal' - } + expect(3); + + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, '"undefined" is not a valid number for the equal rule' ); + }); + + Rekord({ + name: prefix + 'no_colon', + fields: ['count'], + validation: { + rules: { + count: 'equal' } - }); - }, 'equal validation rule requires a number argument'); + } + }); - throws(function() { - Rekord({ - name: prefix + 'colon', - fields: ['count'], - validation: { - rules: { - count: 'equal:' - } + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, '"" is not a valid number for the equal rule' ); + }); + + Rekord({ + name: prefix + 'colon', + fields: ['count'], + validation: { + rules: { + count: 'equal:' } - }); - }, 'equal validation rule requires a number argument'); + } + }); - throws(function() { - Rekord({ - name: prefix + 'nan', - fields: ['count'], - validation: { - rules: { - count: 'equal:X' - } + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, '"X" is not a valid number for the equal rule' ); + }); + + Rekord({ + name: prefix + 'nan', + fields: ['count'], + validation: { + rules: { + count: 'equal:X' } - }); - }, 'X is not a valid number for the equal rule'); + } + }); }); // rule equal custom message @@ -2070,41 +2150,49 @@ test( 'rule not_equal invalid arguments', function(assert) { var prefix = 'rule_not_equal_invalid_arguments_'; - throws(function() { - Rekord({ - name: prefix + 'no_colon', - fields: ['count'], - validation: { - rules: { - count: 'not_equal' - } + expect(3); + + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, '"undefined" is not a valid number for the not_equal rule' ); + }); + + Rekord({ + name: prefix + 'no_colon', + fields: ['count'], + validation: { + rules: { + count: 'not_equal' } - }); - }, 'not_equal validation rule requires a number argument'); + } + }); - throws(function() { - Rekord({ - name: prefix + 'colon', - fields: ['count'], - validation: { - rules: { - count: 'not_equal:' - } + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, '"" is not a valid number for the not_equal rule' ); + }); + + Rekord({ + name: prefix + 'colon', + fields: ['count'], + validation: { + rules: { + count: 'not_equal:' } - }); - }, 'not_equal validation rule requires a number argument'); + } + }); - throws(function() { - Rekord({ - name: prefix + 'nan', - fields: ['count'], - validation: { - rules: { - count: 'not_equal:X' - } + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, '"X" is not a valid number for the not_equal rule' ); + }); + + Rekord({ + name: prefix + 'nan', + fields: ['count'], + validation: { + rules: { + count: 'not_equal:X' } - }); - }, 'X is not a valid number for the not_equal rule'); + } + }); }); // rule not_equal custom message @@ -2664,65 +2752,77 @@ test( 'rule between invalid arguments', function(assert) { var prefix = 'rule_between_invalid_arguments_'; - throws(function() { - Rekord({ - name: prefix + 'no_colon', - fields: ['count'], - validation: { - rules: { - count: 'between' - } + expect(5); + + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, 'between validation rule requires a range argument' ); + }); + + Rekord({ + name: prefix + 'no_colon', + fields: ['count'], + validation: { + rules: { + count: 'between' } - }); - }, 'between validation rule requires a range argument'); + } + }); - throws(function() { - Rekord({ - name: prefix + 'colon', - fields: ['count'], - validation: { - rules: { - count: 'between:' - } + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, 'between validation rule requires a range argument' ); + }); + + Rekord({ + name: prefix + 'colon', + fields: ['count'], + validation: { + rules: { + count: 'between:' } - }); - }, 'between validation rule requires a range argument'); + } + }); - throws(function() { - Rekord({ - name: prefix + 'nan,', - fields: ['count'], - validation: { - rules: { - count: 'between:X' - } + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, 'X is not a valid range of numbers for the between rule' ); + }); + + Rekord({ + name: prefix + 'nan_', + fields: ['count'], + validation: { + rules: { + count: 'between:X' } - }); - }, 'X is not a valid range of numbers for the between rule'); + } + }); - throws(function() { - Rekord({ - name: prefix + 'nan,num', - fields: ['count'], - validation: { - rules: { - count: 'between:X,3' - } + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, 'X,3 is not a valid range of numbers for the between rule' ); + }); + + Rekord({ + name: prefix + 'nan_num', + fields: ['count'], + validation: { + rules: { + count: 'between:X,3' } - }); - }, 'X,3 is not a valid range of numbers for the between rule'); + } + }); - throws(function() { - Rekord({ - name: prefix + 'num,nan', - fields: ['count'], - validation: { - rules: { - count: 'between:4,' - } + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, '4, is not a valid range of numbers for the between rule' ); + }); + + Rekord({ + name: prefix + 'num_nan', + fields: ['count'], + validation: { + rules: { + count: 'between:4,' } - }); - }, '4, is not a valid range of numbers for the between rule'); + } + }); }); // rule between custom message @@ -2962,65 +3062,77 @@ test( 'rule not_between invalid arguments', function(assert) { var prefix = 'rule_not_between_invalid_arguments_'; - throws(function() { - Rekord({ - name: prefix + 'no_colon', - fields: ['count'], - validation: { - rules: { - count: 'not_between' - } + expect(5); + + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, 'not_between validation rule requires a range argument' ); + }); + + Rekord({ + name: prefix + 'no_colon', + fields: ['count'], + validation: { + rules: { + count: 'not_between' } - }); - }, 'not_between validation rule requires a range argument'); + } + }); - throws(function() { - Rekord({ - name: prefix + 'colon', - fields: ['count'], - validation: { - rules: { - count: 'not_between:' - } + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, 'not_between validation rule requires a range argument' ); + }); + + Rekord({ + name: prefix + 'colon', + fields: ['count'], + validation: { + rules: { + count: 'not_between:' } - }); - }, 'not_between validation rule requires a range argument'); + } + }); - throws(function() { - Rekord({ - name: prefix + 'nan,', - fields: ['count'], - validation: { - rules: { - count: 'not_between:X' - } + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, 'X is not a valid range of numbers for the not_between rule' ); + }); + + Rekord({ + name: prefix + 'nan_', + fields: ['count'], + validation: { + rules: { + count: 'not_between:X' } - }); - }, 'X is not a valid range of numbers for the not_between rule'); + } + }); - throws(function() { - Rekord({ - name: prefix + 'nan,num', - fields: ['count'], - validation: { - rules: { - count: 'not_between:X,3' - } + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, 'X,3 is not a valid range of numbers for the not_between rule' ); + }); + + Rekord({ + name: prefix + 'nan_num', + fields: ['count'], + validation: { + rules: { + count: 'not_between:X,3' } - }); - }, 'X,3 is not a valid range of numbers for the not_between rule'); + } + }); - throws(function() { - Rekord({ - name: prefix + 'num,nan', - fields: ['count'], - validation: { - rules: { - count: 'not_between:4,' - } + Rekord.once( Rekord.Events.Error, function(ex) { + strictEqual( ex, '4, is not a valid range of numbers for the not_between rule' ); + }); + + Rekord({ + name: prefix + 'num_nan', + fields: ['count'], + validation: { + rules: { + count: 'not_between:4,' } - }); - }, '4, is not a valid range of numbers for the not_between rule'); + } + }); }); // rule not_between custom message @@ -6761,5 +6873,211 @@ test( 'transform null', function(assert) strictEqual( a.value, null ); }); +test( 'async validation valid', function(assert) +{ + var done = assert.async(); + var prefix = 'async_validation_valid_'; + + var Task = Rekord({ + name: prefix + 'task', + fields: ['value'], + validation: { + rules: { + value: '$isValid' + } + }, + methods: { + $isValid: function(value, getAlias, message, chain) { + setTimeout(function() { + ok(true); + chain.next(); + }, 10); + return false; // we'll handle calling next/invalid ourself + } + } + }); + + expect(3); + + var a = Task.create({value: 'notnull'}); + + ok( a.$validate(function(valid) { + ok( valid ); + done(); + })); +}); + +test( 'async validation invalid', function(assert) +{ + var done = assert.async(); + var prefix = 'async_validation_invalid_'; + + var Task = Rekord({ + name: prefix + 'task', + fields: ['value'], + validation: { + rules: { + value: '$isValid' + } + }, + methods: { + $isValid: function(value, getAlias, message, chain) { + setTimeout(function() { + ok(true); + chain.invalid('NOPE'); + }, 10); + return false; // we'll handle calling next/invalid ourself + } + } + }); + + expect(4); + + var a = Task.create({value: 'notnull'}); + + ok( a.$validate(function(valid) { + notOk( valid ); + strictEqual( a.$validations.value, 'NOPE' ); + done(); + })); +}); + +test( 'async validation save', function(assert) +{ + var done = assert.async(); + var prefix = 'async_validation_save_'; + + var Task = Rekord({ + name: prefix + 'task', + fields: ['value'], + validation: { + required: true, + rules: { + value: '$isValid' + } + }, + methods: { + $isValid: function(value, getAlias, message, chain) { + setTimeout(function() { + chain.next(); + }, 10); + return false; // we'll handle calling next/invalid ourself + } + } + }); + + expect(3); + + var t0 = new Task({value: 'meow'}); + + var p0 = t0.$save(); + + ok( p0.isPending() ); + + p0.success(function() + { + ok( t0.$isSaved() ); + strictEqual( t0.$saved.value, 'meow' ); + done(); + }); +}); + +test( 'async validation save fail', function(assert) +{ + var done = assert.async(); + var prefix = 'async_validation_save_fail_'; + + var Task = Rekord({ + name: prefix + 'task', + fields: ['value'], + validation: { + required: true, + rules: { + value: '$isValid' + } + }, + methods: { + $isValid: function(value, getAlias, message, chain) { + setTimeout(function() { + chain.invalid(); + }, 10); + return false; // we'll handle calling next/invalid ourself + } + } + }); + + expect(3); + + var t0 = new Task({value: 'meow'}); + + var p0 = t0.$save(); + + ok( p0.isPending() ); + + p0.failure(function() + { + notOk( t0.$isSaved() ); + notOk( t0.$valid ); + done(); + }); +}); + +test( 'stripTags', function(assert) +{ + var prefix = 'stripTags_'; -// transform null + var Task = Rekord({ + name: prefix + 'task', + fields: ['value'], + validation: { + required: true, + rules: { + value: 'stripTags|apply' + } + } + }); + + var t0 = Task.create({value: 'Hello World!'}); + + strictEqual( t0.value, 'Hello World!' ); + + var t1 = Task.create({value: '> Meow!'}); + + strictEqual( t1.value, '> Meow!' ); + + var t2 = Task.create({value: ' yo

'}); + + strictEqual( t2.value, ' yo ' ); + + var t3 = Task.create({value: '

'}); + + strictEqual( t3.value, '' ); + + var t4 = Task.create({value: 'yerp'}); + + strictEqual( t4.value, 'yerp' ); +}); + +test( 'stripEnts', function(assert) +{ + var prefix = 'stripEnts_'; + + var Task = Rekord({ + name: prefix + 'task', + fields: ['value'], + validation: { + required: true, + rules: { + value: 'stripEnts|apply' + } + } + }); + + var t0 = Task.create({value: 'Hello World!>'}); + + strictEqual( t0.value, 'Hello World!' ); + + var t1 = Task.create({value: 'Meow!  '}); + + strictEqual( t1.value, 'Meow! ' ); +}); diff --git a/test/index.html b/test/index.html index 667c9d8..3fd0fd2 100644 --- a/test/index.html +++ b/test/index.html @@ -3,8 +3,8 @@ - - + +