From c15e3dfaa660d8e414909eb79673900d934ace7a Mon Sep 17 00:00:00 2001 From: Lee Prosser Date: Thu, 12 May 2016 13:50:50 +0100 Subject: [PATCH] Simplified parts of the promise logic as well as renaming certain exposed methods to make more sense, have also added documentation to the build as well as adding a gulp step for documentation generation but currently it offers little over the github source code browser, will hopefully look to add examples and other things in going forward. --- build/paths.js | 1 + build/tasks/documentation.js | 18 + build/tasks/package.js | 26 +- dist/definitions/exposer.d.ts | 4 +- dist/definitions/index.d.ts | 2 +- dist/treacherous.all.js | 12 +- dist/treacherous.browser.js | 12 +- dist/treacherous.js | 445 ++------ dist/treacherous.minimal.js | 1328 ----------------------- docs/breaking-changes.md | 9 + docs/creating-rulesets.md | 141 +++ docs/custom-rules.md | 78 ++ docs/installing.md | 35 + docs/readme.md | 14 + docs/tips.md | 3 + docs/validation-groups.md | 159 +++ docs/validation-process.md | 89 ++ package.json | 6 +- readme.md | 204 +--- src/exposer.ts | 2 - src/index.ts | 2 +- tests/specs/treacherous-sanity-tests.js | 6 +- 22 files changed, 729 insertions(+), 1867 deletions(-) create mode 100644 build/tasks/documentation.js delete mode 100644 dist/treacherous.minimal.js create mode 100644 docs/breaking-changes.md create mode 100644 docs/creating-rulesets.md create mode 100644 docs/custom-rules.md create mode 100644 docs/installing.md create mode 100644 docs/readme.md create mode 100644 docs/tips.md create mode 100644 docs/validation-groups.md create mode 100644 docs/validation-process.md diff --git a/build/paths.js b/build/paths.js index 2ef391e..973d07e 100644 --- a/build/paths.js +++ b/build/paths.js @@ -5,6 +5,7 @@ module.exports = { source: "src/**/*.ts", typings: "typings/**/*.ts", dist: "dist", + docs: "docs", reports: "reports", tests: "tests", output: "_output" diff --git a/build/tasks/documentation.js b/build/tasks/documentation.js new file mode 100644 index 0000000..7ccdac7 --- /dev/null +++ b/build/tasks/documentation.js @@ -0,0 +1,18 @@ +var paths = require('../paths'); +var gulp = require("gulp"); +var typedoc = require("gulp-typedoc"); + +gulp.task("generate:documentation", function() { + return gulp + .src([ + paths.source, paths.typings, + "!src/exposer.ts", + "!src/index.ts" + ]) + .pipe(typedoc({ + module: "commonjs", + target: "es5", + out: paths.docs + "/technical", + name: "Treacherous" + })); +}); \ No newline at end of file diff --git a/build/tasks/package.js b/build/tasks/package.js index f224749..69ac91d 100644 --- a/build/tasks/package.js +++ b/build/tasks/package.js @@ -2,26 +2,6 @@ var paths = require('../paths'); var gulp = require("gulp"); var webpack = require("webpack-stream"); -gulp.task('package:release', ["compile"], function () { - var webpackConfig = { - output: { - entry: "index.js", - filename: "treacherous.js", - library: "Treacherous", - libraryTarget: "umd" - }, - externals: [ - { - "bluebird": true - } - ] - }; - - return gulp.src([paths.output + "/index.js"]) - .pipe(webpack(webpackConfig)) - .pipe(gulp.dest(paths.dist)); -}); - gulp.task('package:all', ["compile"], function () { var webpackConfig = { output: { @@ -37,11 +17,11 @@ gulp.task('package:all', ["compile"], function () { .pipe(gulp.dest(paths.dist)); }); -gulp.task('package:minimal', ["compile"], function () { +gulp.task('package:release', ["compile"], function () { var webpackConfig = { output: { entry: "index.js", - filename: "treacherous.minimal.js", + filename: "treacherous.js", library: "Treacherous", libraryTarget: "umd" }, @@ -81,4 +61,4 @@ gulp.task('package:browser', ["compile"], function () { .pipe(gulp.dest(paths.dist)); }); -gulp.task('package', ["package:release", "package:all", "package:minimal", "package:browser"]); \ No newline at end of file +gulp.task('package', ["package:release", "package:all", "package:browser"]); \ No newline at end of file diff --git a/dist/definitions/exposer.d.ts b/dist/definitions/exposer.d.ts index 28e744f..18e897e 100644 --- a/dist/definitions/exposer.d.ts +++ b/dist/definitions/exposer.d.ts @@ -4,5 +4,5 @@ import { RulesetBuilder } from "./rulesets/ruleset-builder"; import { ValidationGroup } from "./validation-group"; export declare var ruleRegistry: RuleRegistry; export declare function createRuleset(): RulesetBuilder; -export declare function createWithRules(model: any, rulesCreator: (rulesetBuilder: RulesetBuilder) => Ruleset): ValidationGroup; -export declare function create(model: any, ruleset: Ruleset): ValidationGroup; +export declare function createGroupWithRules(model: any, rulesCreator: (rulesetBuilder: RulesetBuilder) => Ruleset): ValidationGroup; +export declare function createGroup(model: any, ruleset: Ruleset): ValidationGroup; diff --git a/dist/definitions/index.d.ts b/dist/definitions/index.d.ts index b3340ae..b9581f8 100644 --- a/dist/definitions/index.d.ts +++ b/dist/definitions/index.d.ts @@ -4,9 +4,9 @@ export * from "./validation-group"; export * from "./events/model-state-changed-event"; export * from "./events/property-changed-event"; export * from "./events/property-state-changed-event"; +export * from "./factories/validation-group-factory"; export * from "./helpers/comparer-helper"; export * from "./helpers/type-helper"; -export * from "./factories/validation-group-factory"; export * from "./rules/date-validation-rule"; export * from "./rules/decimal-validation-rule"; export * from "./rules/email-validation-rule"; diff --git a/dist/treacherous.all.js b/dist/treacherous.all.js index fe70b49..eefd953 100644 --- a/dist/treacherous.all.js +++ b/dist/treacherous.all.js @@ -63,9 +63,9 @@ return /******/ (function(modules) { // webpackBootstrap __export(__webpack_require__(12)); __export(__webpack_require__(39)); __export(__webpack_require__(11)); + __export(__webpack_require__(2)); __export(__webpack_require__(22)); __export(__webpack_require__(14)); - __export(__webpack_require__(2)); __export(__webpack_require__(18)); __export(__webpack_require__(19)); __export(__webpack_require__(20)); @@ -122,8 +122,6 @@ return /******/ (function(modules) { // webpackBootstrap var model_watcher_1 = __webpack_require__(37); var property_resolver_1 = __webpack_require__(7); var rule_resolver_1 = __webpack_require__(13); - //import {RuleResolver2} from "./rulesets/rule-resolver2"; - //export {RuleResolver2 as RuleResolver} from "./rulesets/rule-resolver2"; exports.ruleRegistry = new rule_registry_1.RuleRegistry(); exports.ruleRegistry.registerRule(new date_validation_rule_1.DateValidationRule()); exports.ruleRegistry.registerRule(new decimal_validation_rule_1.DecimalValidationRule()); @@ -148,15 +146,15 @@ return /******/ (function(modules) { // webpackBootstrap return new ruleset_builder_1.RulesetBuilder().create(); } exports.createRuleset = createRuleset; - function createWithRules(model, rulesCreator) { + function createGroupWithRules(model, rulesCreator) { var ruleset = rulesCreator(new ruleset_builder_1.RulesetBuilder()); return validationGroupFactory.createValidationGroup(model, ruleset); } - exports.createWithRules = createWithRules; - function create(model, ruleset) { + exports.createGroupWithRules = createGroupWithRules; + function createGroup(model, ruleset) { return validationGroupFactory.createValidationGroup(model, ruleset); } - exports.create = create; + exports.createGroup = createGroup; /***/ }, diff --git a/dist/treacherous.browser.js b/dist/treacherous.browser.js index 85207ca..6d88f40 100644 --- a/dist/treacherous.browser.js +++ b/dist/treacherous.browser.js @@ -63,9 +63,9 @@ return /******/ (function(modules) { // webpackBootstrap __export(__webpack_require__(8)); __export(__webpack_require__(35)); __export(__webpack_require__(7)); + __export(__webpack_require__(2)); __export(__webpack_require__(18)); __export(__webpack_require__(10)); - __export(__webpack_require__(2)); __export(__webpack_require__(14)); __export(__webpack_require__(15)); __export(__webpack_require__(16)); @@ -122,8 +122,6 @@ return /******/ (function(modules) { // webpackBootstrap var model_watcher_1 = __webpack_require__(33); var property_resolver_1 = __webpack_require__(5); var rule_resolver_1 = __webpack_require__(9); - //import {RuleResolver2} from "./rulesets/rule-resolver2"; - //export {RuleResolver2 as RuleResolver} from "./rulesets/rule-resolver2"; exports.ruleRegistry = new rule_registry_1.RuleRegistry(); exports.ruleRegistry.registerRule(new date_validation_rule_1.DateValidationRule()); exports.ruleRegistry.registerRule(new decimal_validation_rule_1.DecimalValidationRule()); @@ -148,15 +146,15 @@ return /******/ (function(modules) { // webpackBootstrap return new ruleset_builder_1.RulesetBuilder().create(); } exports.createRuleset = createRuleset; - function createWithRules(model, rulesCreator) { + function createGroupWithRules(model, rulesCreator) { var ruleset = rulesCreator(new ruleset_builder_1.RulesetBuilder()); return validationGroupFactory.createValidationGroup(model, ruleset); } - exports.createWithRules = createWithRules; - function create(model, ruleset) { + exports.createGroupWithRules = createGroupWithRules; + function createGroup(model, ruleset) { return validationGroupFactory.createValidationGroup(model, ruleset); } - exports.create = create; + exports.createGroup = createGroup; /***/ }, diff --git a/dist/treacherous.js b/dist/treacherous.js index b8344ff..f57398a 100644 --- a/dist/treacherous.js +++ b/dist/treacherous.js @@ -1,13 +1,13 @@ (function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') - module.exports = factory(require("bluebird")); + module.exports = factory(require("bluebird"), require("property-resolver"), require("event-js")); else if(typeof define === 'function' && define.amd) - define(["bluebird"], factory); + define(["bluebird", "property-resolver", "event-js"], factory); else if(typeof exports === 'object') - exports["Treacherous"] = factory(require("bluebird")); + exports["Treacherous"] = factory(require("bluebird"), require("property-resolver"), require("event-js")); else - root["Treacherous"] = factory(root["bluebird"]); -})(this, function(__WEBPACK_EXTERNAL_MODULE_4__) { + root["Treacherous"] = factory(root["bluebird"], root["property-resolver"], root["event-js"]); +})(this, function(__WEBPACK_EXTERNAL_MODULE_4__, __WEBPACK_EXTERNAL_MODULE_5__, __WEBPACK_EXTERNAL_MODULE_6__) { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; @@ -58,43 +58,43 @@ return /******/ (function(modules) { // webpackBootstrap for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; } __export(__webpack_require__(1)); - __export(__webpack_require__(38)); + __export(__webpack_require__(36)); __export(__webpack_require__(3)); - __export(__webpack_require__(10)); - __export(__webpack_require__(37)); - __export(__webpack_require__(9)); - __export(__webpack_require__(20)); - __export(__webpack_require__(12)); + __export(__webpack_require__(8)); + __export(__webpack_require__(35)); + __export(__webpack_require__(7)); __export(__webpack_require__(2)); + __export(__webpack_require__(18)); + __export(__webpack_require__(10)); + __export(__webpack_require__(14)); + __export(__webpack_require__(15)); __export(__webpack_require__(16)); __export(__webpack_require__(17)); - __export(__webpack_require__(18)); __export(__webpack_require__(19)); + __export(__webpack_require__(37)); + __export(__webpack_require__(20)); __export(__webpack_require__(21)); - __export(__webpack_require__(39)); __export(__webpack_require__(22)); __export(__webpack_require__(23)); __export(__webpack_require__(24)); __export(__webpack_require__(25)); __export(__webpack_require__(26)); __export(__webpack_require__(27)); + __export(__webpack_require__(13)); __export(__webpack_require__(28)); + __export(__webpack_require__(11)); + __export(__webpack_require__(12)); + __export(__webpack_require__(38)); + __export(__webpack_require__(39)); + __export(__webpack_require__(32)); + __export(__webpack_require__(40)); + __export(__webpack_require__(31)); + __export(__webpack_require__(9)); __export(__webpack_require__(29)); - __export(__webpack_require__(15)); __export(__webpack_require__(30)); - __export(__webpack_require__(13)); - __export(__webpack_require__(14)); - __export(__webpack_require__(40)); __export(__webpack_require__(41)); - __export(__webpack_require__(34)); - __export(__webpack_require__(42)); __export(__webpack_require__(33)); - __export(__webpack_require__(11)); - __export(__webpack_require__(31)); - __export(__webpack_require__(32)); - __export(__webpack_require__(43)); - __export(__webpack_require__(35)); - __export(__webpack_require__(36)); + __export(__webpack_require__(34)); /***/ }, @@ -102,28 +102,26 @@ return /******/ (function(modules) { // webpackBootstrap /***/ function(module, exports, __webpack_require__) { var validation_group_factory_1 = __webpack_require__(2); - var field_error_processor_1 = __webpack_require__(13); - var rule_registry_1 = __webpack_require__(15); - var date_validation_rule_1 = __webpack_require__(16); - var decimal_validation_rule_1 = __webpack_require__(17); - var email_validation_rule_1 = __webpack_require__(18); - var equal_validation_rule_1 = __webpack_require__(19); - var iso_date_validation_rule_1 = __webpack_require__(21); - var max_length_validation_rule_1 = __webpack_require__(22); - var max_value_validation_rule_1 = __webpack_require__(23); - var min_length_validation_rule_1 = __webpack_require__(24); - var min_value_validation_rule_1 = __webpack_require__(25); - var not_equal_validation_rule_1 = __webpack_require__(26); - var number_validation_rule_1 = __webpack_require__(27); - var regex_validation_rule_1 = __webpack_require__(28); - var required_validation_rule_1 = __webpack_require__(29); - var step_validation_rule_1 = __webpack_require__(30); - var ruleset_builder_1 = __webpack_require__(31); - var model_watcher_1 = __webpack_require__(35); + var field_error_processor_1 = __webpack_require__(11); + var rule_registry_1 = __webpack_require__(13); + var date_validation_rule_1 = __webpack_require__(14); + var decimal_validation_rule_1 = __webpack_require__(15); + var email_validation_rule_1 = __webpack_require__(16); + var equal_validation_rule_1 = __webpack_require__(17); + var iso_date_validation_rule_1 = __webpack_require__(19); + var max_length_validation_rule_1 = __webpack_require__(20); + var max_value_validation_rule_1 = __webpack_require__(21); + var min_length_validation_rule_1 = __webpack_require__(22); + var min_value_validation_rule_1 = __webpack_require__(23); + var not_equal_validation_rule_1 = __webpack_require__(24); + var number_validation_rule_1 = __webpack_require__(25); + var regex_validation_rule_1 = __webpack_require__(26); + var required_validation_rule_1 = __webpack_require__(27); + var step_validation_rule_1 = __webpack_require__(28); + var ruleset_builder_1 = __webpack_require__(29); + var model_watcher_1 = __webpack_require__(33); var property_resolver_1 = __webpack_require__(5); - var rule_resolver_1 = __webpack_require__(11); - //import {RuleResolver2} from "./rulesets/rule-resolver2"; - //export {RuleResolver2 as RuleResolver} from "./rulesets/rule-resolver2"; + var rule_resolver_1 = __webpack_require__(9); exports.ruleRegistry = new rule_registry_1.RuleRegistry(); exports.ruleRegistry.registerRule(new date_validation_rule_1.DateValidationRule()); exports.ruleRegistry.registerRule(new decimal_validation_rule_1.DecimalValidationRule()); @@ -148,15 +146,15 @@ return /******/ (function(modules) { // webpackBootstrap return new ruleset_builder_1.RulesetBuilder().create(); } exports.createRuleset = createRuleset; - function createWithRules(model, rulesCreator) { + function createGroupWithRules(model, rulesCreator) { var ruleset = rulesCreator(new ruleset_builder_1.RulesetBuilder()); return validationGroupFactory.createValidationGroup(model, ruleset); } - exports.createWithRules = createWithRules; - function create(model, ruleset) { + exports.createGroupWithRules = createGroupWithRules; + function createGroup(model, ruleset) { return validationGroupFactory.createValidationGroup(model, ruleset); } - exports.create = create; + exports.createGroup = createGroup; /***/ }, @@ -187,10 +185,10 @@ return /******/ (function(modules) { // webpackBootstrap var Promise = __webpack_require__(4); var property_resolver_1 = __webpack_require__(5); var event_js_1 = __webpack_require__(6); - var property_state_changed_event_1 = __webpack_require__(9); - var model_state_changed_event_1 = __webpack_require__(10); - var rule_resolver_1 = __webpack_require__(11); - var type_helper_1 = __webpack_require__(12); + var property_state_changed_event_1 = __webpack_require__(7); + var model_state_changed_event_1 = __webpack_require__(8); + var rule_resolver_1 = __webpack_require__(9); + var type_helper_1 = __webpack_require__(10); // TODO: This class is WAY to long, needs refactoring var ValidationGroup = (function () { function ValidationGroup(fieldErrorProcessor, modelWatcher, propertyResolver, ruleResolver, ruleset, model, refreshRate) { @@ -364,259 +362,18 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, /* 5 */ -/***/ function(module, exports, __webpack_require__) { +/***/ function(module, exports) { - (function webpackUniversalModuleDefinition(root, factory) { - if(true) - module.exports = factory(); - else if(typeof define === 'function' && define.amd) - define([], factory); - else { - var a = factory(); - for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i]; - } - })(this, function() { - return /******/ (function(modules) { // webpackBootstrap - /******/ // The module cache - /******/ var installedModules = {}; - - /******/ // The require function - /******/ function __webpack_require__(moduleId) { - - /******/ // Check if module is in cache - /******/ if(installedModules[moduleId]) - /******/ return installedModules[moduleId].exports; - - /******/ // Create a new module (and put it into the cache) - /******/ var module = installedModules[moduleId] = { - /******/ exports: {}, - /******/ id: moduleId, - /******/ loaded: false - /******/ }; - - /******/ // Execute the module function - /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); - - /******/ // Flag the module as loaded - /******/ module.loaded = true; - - /******/ // Return the exports of the module - /******/ return module.exports; - /******/ } - - - /******/ // expose the modules object (__webpack_modules__) - /******/ __webpack_require__.m = modules; - - /******/ // expose the module cache - /******/ __webpack_require__.c = installedModules; - - /******/ // __webpack_public_path__ - /******/ __webpack_require__.p = ""; - - /******/ // Load entry module and return exports - /******/ return __webpack_require__(0); - /******/ }) - /************************************************************************/ - /******/ ([ - /* 0 */ - /***/ function(module, exports) { - - var PropertyResolver = (function () { - function PropertyResolver() { - var _this = this; - this.indexRegex = /\[(\d)]/; - this.splitRegex = /\./; - this.resolveProperty = function (model, propertyChain) { - var check = null, chain = [], lastkey = ''; - if (typeof propertyChain !== 'string') { - throw new TypeError("propertyChain is not a string"); - } - var processChain = function (key) { - var arrayIndex = -1; - if (_this.indexRegex.test(key)) { - arrayIndex = _this.indexRegex.exec(key)[1]; - key = key.replace(_this.indexRegex, ""); - } - if (check) { - if (typeof check === 'object') { - if (arrayIndex >= 0) { - if (arrayIndex < check[key].length) { - chain.push(check = check[key][arrayIndex]); - lastkey = key[arrayIndex]; - } - else { - throw new TypeError('cannot find index "' + arrayIndex + '" in ' + lastkey); - } - } - else { - if (key in check) { - chain.push(check = check[key]); - lastkey = key; - } - else { - throw new TypeError('cannot resolve "' + key + '" in ' + lastkey); - } - } - } - else { - throw new TypeError('"' + check + '" ' + ' does not seem to be an object'); - } - } - else { - if (arrayIndex >= 0) { - if (key.length == 0) { - chain.push(check = model[arrayIndex]); - lastkey = arrayIndex; - } - else { - chain.push(check = model[key][arrayIndex]); - lastkey = key[arrayIndex]; - } - } - else { - lastkey = key; - chain.push(check = model[key]); - } - } - }; - var propertyRouteSections = propertyChain.split(_this.splitRegex); - propertyRouteSections.forEach(processChain); - return chain[chain.length - 1]; - }; - } - PropertyResolver.prototype.decomposePropertyRoute = function (propertyRoute) { - var routeComponents = []; - var arrayIndex; - var splitRoutes = propertyRoute.split(this.splitRegex); - for (var i = 0; i < splitRoutes.length; i++) { - if (this.indexRegex.test(splitRoutes[i])) { - arrayIndex = this.indexRegex.exec(splitRoutes[i])[1]; - routeComponents.push(splitRoutes[i].replace(this.indexRegex, "")); - routeComponents.push("[" + arrayIndex + "]"); - } - else { - routeComponents.push(splitRoutes[i]); - } - } - return routeComponents; - }; - PropertyResolver.prototype.getPropertyRouteSection = function (propertyRoute, sectionIndex) { - if (sectionIndex === void 0) { sectionIndex = 0; } - var routeComponents = this.decomposePropertyRoute(propertyRoute); - return routeComponents[sectionIndex]; - }; - PropertyResolver.prototype.buildPropertyRoute = function (propertySections) { - var propertyRoute = ""; - for (var i = 0; i < propertySections.length; i++) { - if (propertyRoute.length == 0) { - propertyRoute += propertySections[i]; - continue; - } - if (propertySections[i].indexOf("[") >= 0) { - propertyRoute += "" + propertySections[i]; - continue; - } - propertyRoute += "." + propertySections[i]; - } - return propertyRoute; - }; - return PropertyResolver; - })(); - exports.PropertyResolver = PropertyResolver; - - - /***/ } - /******/ ]) - }); - ; + module.exports = __WEBPACK_EXTERNAL_MODULE_5__; /***/ }, /* 6 */ -/***/ function(module, exports, __webpack_require__) { - - /* This is an auto-generated file by gulp-es6-exporter */ - function __export(m) { - for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; - } - __export(__webpack_require__(7)); - __export(__webpack_require__(8)); - - -/***/ }, -/* 7 */ -/***/ function(module, exports, __webpack_require__) { - - var event_listener_1 = __webpack_require__(8); - var EventHandler = (function () { - function EventHandler(sender) { - var _this = this; - this.sender = sender; - this.listeners = []; - this.subscribe = function (callback, predicate) { - _this.listeners.push(new event_listener_1.EventListener(callback, predicate)); - return function () { _this.unsubscribe(callback); }; - }; - this.unsubscribe = function (callback) { - for (var i = 0; i < _this.listeners.length; i++) { - if (_this.listeners[i].callback == callback) { - _this.listeners.splice(i, 1); - return; - } - } - }; - this.unsubscribeAll = function () { - _this.listeners = []; - }; - this.publish = function (args) { - _this.listeners.forEach(function (eventListener) { - if (eventListener.predicate) { - if (eventListener.predicate(args)) { - setTimeout(function () { eventListener.callback(args, _this.sender); }, 1); - } - } - else { - setTimeout(function () { eventListener.callback(args, _this.sender); }, 1); - } - }); - }; - this.publishSync = function (args) { - _this.listeners.forEach(function (eventListener) { - if (eventListener.predicate) { - if (eventListener.predicate(args)) { - eventListener.callback(args, _this.sender); - } - } - else { - eventListener.callback(args, _this.sender); - } - }); - }; - this.getSubscriptionCount = function () { - return _this.listeners.length; - }; - } - return EventHandler; - })(); - exports.EventHandler = EventHandler; - - -/***/ }, -/* 8 */ /***/ function(module, exports) { - var EventListener = (function () { - function EventListener(callback, predicate) { - this.callback = callback; - this.predicate = predicate; - } - return EventListener; - })(); - exports.EventListener = EventListener; - + module.exports = __WEBPACK_EXTERNAL_MODULE_6__; /***/ }, -/* 9 */ +/* 7 */ /***/ function(module, exports) { var PropertyStateChangedEvent = (function () { @@ -631,7 +388,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 10 */ +/* 8 */ /***/ function(module, exports) { var ModelStateChangedEvent = (function () { @@ -644,7 +401,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 11 */ +/* 9 */ /***/ function(module, exports, __webpack_require__) { var property_resolver_1 = __webpack_require__(5); @@ -740,7 +497,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 12 */ +/* 10 */ /***/ function(module, exports) { var TypeHelper = (function () { @@ -761,11 +518,11 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 13 */ +/* 11 */ /***/ function(module, exports, __webpack_require__) { var Promise = __webpack_require__(4); - var field_has_error_1 = __webpack_require__(14); + var field_has_error_1 = __webpack_require__(12); var FieldErrorProcessor = (function () { function FieldErrorProcessor(ruleRegistry) { this.ruleRegistry = ruleRegistry; @@ -801,7 +558,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 14 */ +/* 12 */ /***/ function(module, exports) { var __extends = (this && this.__extends) || function (d, b) { @@ -821,7 +578,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 15 */ +/* 13 */ /***/ function(module, exports) { var RuleRegistry = (function () { @@ -844,7 +601,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 16 */ +/* 14 */ /***/ function(module, exports, __webpack_require__) { var Promise = __webpack_require__(4); @@ -869,7 +626,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 17 */ +/* 15 */ /***/ function(module, exports, __webpack_require__) { var Promise = __webpack_require__(4); @@ -894,7 +651,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 18 */ +/* 16 */ /***/ function(module, exports, __webpack_require__) { var Promise = __webpack_require__(4); @@ -919,12 +676,12 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 19 */ +/* 17 */ /***/ function(module, exports, __webpack_require__) { var Promise = __webpack_require__(4); - var type_helper_1 = __webpack_require__(12); - var comparer_helper_1 = __webpack_require__(20); + var type_helper_1 = __webpack_require__(10); + var comparer_helper_1 = __webpack_require__(18); var EqualValidationRule = (function () { function EqualValidationRule() { this.ruleName = "equal"; @@ -953,7 +710,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 20 */ +/* 18 */ /***/ function(module, exports) { var ComparerHelper = (function () { @@ -972,7 +729,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 21 */ +/* 19 */ /***/ function(module, exports, __webpack_require__) { var Promise = __webpack_require__(4); @@ -997,7 +754,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 22 */ +/* 20 */ /***/ function(module, exports, __webpack_require__) { var Promise = __webpack_require__(4); @@ -1023,7 +780,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 23 */ +/* 21 */ /***/ function(module, exports, __webpack_require__) { var Promise = __webpack_require__(4); @@ -1049,7 +806,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 24 */ +/* 22 */ /***/ function(module, exports, __webpack_require__) { var Promise = __webpack_require__(4); @@ -1075,7 +832,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 25 */ +/* 23 */ /***/ function(module, exports, __webpack_require__) { var Promise = __webpack_require__(4); @@ -1101,12 +858,12 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 26 */ +/* 24 */ /***/ function(module, exports, __webpack_require__) { var Promise = __webpack_require__(4); - var type_helper_1 = __webpack_require__(12); - var comparer_helper_1 = __webpack_require__(20); + var type_helper_1 = __webpack_require__(10); + var comparer_helper_1 = __webpack_require__(18); var NotEqualValidationRule = (function () { function NotEqualValidationRule() { this.ruleName = "notEqual"; @@ -1135,7 +892,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 27 */ +/* 25 */ /***/ function(module, exports, __webpack_require__) { var Promise = __webpack_require__(4); @@ -1160,7 +917,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 28 */ +/* 26 */ /***/ function(module, exports, __webpack_require__) { var Promise = __webpack_require__(4); @@ -1184,7 +941,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 29 */ +/* 27 */ /***/ function(module, exports, __webpack_require__) { var Promise = __webpack_require__(4); @@ -1220,7 +977,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 30 */ +/* 28 */ /***/ function(module, exports, __webpack_require__) { var Promise = __webpack_require__(4); @@ -1245,12 +1002,12 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 31 */ +/* 29 */ /***/ function(module, exports, __webpack_require__) { - var ruleset_1 = __webpack_require__(32); - var rule_link_1 = __webpack_require__(33); - var for_each_rule_1 = __webpack_require__(34); + var ruleset_1 = __webpack_require__(30); + var rule_link_1 = __webpack_require__(31); + var for_each_rule_1 = __webpack_require__(32); var RulesetBuilder = (function () { function RulesetBuilder() { var _this = this; @@ -1302,7 +1059,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 32 */ +/* 30 */ /***/ function(module, exports) { var Ruleset = (function () { @@ -1330,7 +1087,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 33 */ +/* 31 */ /***/ function(module, exports) { var RuleLink = (function () { @@ -1344,7 +1101,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 34 */ +/* 32 */ /***/ function(module, exports) { var ForEachRule = (function () { @@ -1358,14 +1115,14 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 35 */ +/* 33 */ /***/ function(module, exports, __webpack_require__) { var property_resolver_1 = __webpack_require__(5); var event_js_1 = __webpack_require__(6); - var type_helper_1 = __webpack_require__(12); - var property_watcher_1 = __webpack_require__(36); - var property_changed_event_1 = __webpack_require__(37); + var type_helper_1 = __webpack_require__(10); + var property_watcher_1 = __webpack_require__(34); + var property_changed_event_1 = __webpack_require__(35); var ModelWatcher = (function () { function ModelWatcher(propertyResolver) { var _this = this; @@ -1486,7 +1243,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 36 */ +/* 34 */ /***/ function(module, exports) { var PropertyWatcher = (function () { @@ -1500,7 +1257,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 37 */ +/* 35 */ /***/ function(module, exports) { var PropertyChangedEvent = (function () { @@ -1515,28 +1272,28 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 38 */ +/* 36 */ /***/ function(module, exports) { /***/ }, -/* 39 */ +/* 37 */ /***/ function(module, exports) { /***/ }, -/* 40 */ +/* 38 */ /***/ function(module, exports) { /***/ }, -/* 41 */ +/* 39 */ /***/ function(module, exports) { var ValidationError = (function () { @@ -1550,14 +1307,14 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, -/* 42 */ +/* 40 */ /***/ function(module, exports) { /***/ }, -/* 43 */ +/* 41 */ /***/ function(module, exports) { diff --git a/dist/treacherous.minimal.js b/dist/treacherous.minimal.js deleted file mode 100644 index 2908bd6..0000000 --- a/dist/treacherous.minimal.js +++ /dev/null @@ -1,1328 +0,0 @@ -(function webpackUniversalModuleDefinition(root, factory) { - if(typeof exports === 'object' && typeof module === 'object') - module.exports = factory(require("bluebird"), require("property-resolver"), require("event-js")); - else if(typeof define === 'function' && define.amd) - define(["bluebird", "property-resolver", "event-js"], factory); - else if(typeof exports === 'object') - exports["Treacherous"] = factory(require("bluebird"), require("property-resolver"), require("event-js")); - else - root["Treacherous"] = factory(root["bluebird"], root["property-resolver"], root["event-js"]); -})(this, function(__WEBPACK_EXTERNAL_MODULE_4__, __WEBPACK_EXTERNAL_MODULE_5__, __WEBPACK_EXTERNAL_MODULE_6__) { -return /******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; - -/******/ // The require function -/******/ function __webpack_require__(moduleId) { - -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) -/******/ return installedModules[moduleId].exports; - -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ exports: {}, -/******/ id: moduleId, -/******/ loaded: false -/******/ }; - -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); - -/******/ // Flag the module as loaded -/******/ module.loaded = true; - -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } - - -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; - -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; - -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; - -/******/ // Load entry module and return exports -/******/ return __webpack_require__(0); -/******/ }) -/************************************************************************/ -/******/ ([ -/* 0 */ -/***/ function(module, exports, __webpack_require__) { - - function __export(m) { - for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; - } - __export(__webpack_require__(1)); - __export(__webpack_require__(36)); - __export(__webpack_require__(3)); - __export(__webpack_require__(8)); - __export(__webpack_require__(35)); - __export(__webpack_require__(7)); - __export(__webpack_require__(18)); - __export(__webpack_require__(10)); - __export(__webpack_require__(2)); - __export(__webpack_require__(14)); - __export(__webpack_require__(15)); - __export(__webpack_require__(16)); - __export(__webpack_require__(17)); - __export(__webpack_require__(19)); - __export(__webpack_require__(37)); - __export(__webpack_require__(20)); - __export(__webpack_require__(21)); - __export(__webpack_require__(22)); - __export(__webpack_require__(23)); - __export(__webpack_require__(24)); - __export(__webpack_require__(25)); - __export(__webpack_require__(26)); - __export(__webpack_require__(27)); - __export(__webpack_require__(13)); - __export(__webpack_require__(28)); - __export(__webpack_require__(11)); - __export(__webpack_require__(12)); - __export(__webpack_require__(38)); - __export(__webpack_require__(39)); - __export(__webpack_require__(32)); - __export(__webpack_require__(40)); - __export(__webpack_require__(31)); - __export(__webpack_require__(9)); - __export(__webpack_require__(29)); - __export(__webpack_require__(30)); - __export(__webpack_require__(41)); - __export(__webpack_require__(33)); - __export(__webpack_require__(34)); - - -/***/ }, -/* 1 */ -/***/ function(module, exports, __webpack_require__) { - - var validation_group_factory_1 = __webpack_require__(2); - var field_error_processor_1 = __webpack_require__(11); - var rule_registry_1 = __webpack_require__(13); - var date_validation_rule_1 = __webpack_require__(14); - var decimal_validation_rule_1 = __webpack_require__(15); - var email_validation_rule_1 = __webpack_require__(16); - var equal_validation_rule_1 = __webpack_require__(17); - var iso_date_validation_rule_1 = __webpack_require__(19); - var max_length_validation_rule_1 = __webpack_require__(20); - var max_value_validation_rule_1 = __webpack_require__(21); - var min_length_validation_rule_1 = __webpack_require__(22); - var min_value_validation_rule_1 = __webpack_require__(23); - var not_equal_validation_rule_1 = __webpack_require__(24); - var number_validation_rule_1 = __webpack_require__(25); - var regex_validation_rule_1 = __webpack_require__(26); - var required_validation_rule_1 = __webpack_require__(27); - var step_validation_rule_1 = __webpack_require__(28); - var ruleset_builder_1 = __webpack_require__(29); - var model_watcher_1 = __webpack_require__(33); - var property_resolver_1 = __webpack_require__(5); - var rule_resolver_1 = __webpack_require__(9); - //import {RuleResolver2} from "./rulesets/rule-resolver2"; - //export {RuleResolver2 as RuleResolver} from "./rulesets/rule-resolver2"; - exports.ruleRegistry = new rule_registry_1.RuleRegistry(); - exports.ruleRegistry.registerRule(new date_validation_rule_1.DateValidationRule()); - exports.ruleRegistry.registerRule(new decimal_validation_rule_1.DecimalValidationRule()); - exports.ruleRegistry.registerRule(new email_validation_rule_1.EmailValidationRule()); - exports.ruleRegistry.registerRule(new equal_validation_rule_1.EqualValidationRule()); - exports.ruleRegistry.registerRule(new iso_date_validation_rule_1.ISODateValidationRule()); - exports.ruleRegistry.registerRule(new max_length_validation_rule_1.MaxLengthValidationRule()); - exports.ruleRegistry.registerRule(new max_value_validation_rule_1.MaxValueValidationRule()); - exports.ruleRegistry.registerRule(new min_length_validation_rule_1.MinLengthValidationRule()); - exports.ruleRegistry.registerRule(new min_value_validation_rule_1.MinValueValidationRule()); - exports.ruleRegistry.registerRule(new not_equal_validation_rule_1.NotEqualValidationRule()); - exports.ruleRegistry.registerRule(new number_validation_rule_1.NumberValidationRule()); - exports.ruleRegistry.registerRule(new regex_validation_rule_1.RegexValidationRule()); - exports.ruleRegistry.registerRule(new required_validation_rule_1.RequiredValidaitonRule()); - exports.ruleRegistry.registerRule(new step_validation_rule_1.StepValidationRule()); - var fieldErrorProcessor = new field_error_processor_1.FieldErrorProcessor(exports.ruleRegistry); - var modelWatcher = new model_watcher_1.ModelWatcher(); - var propertyResolver = new property_resolver_1.PropertyResolver(); - var ruleResolver = new rule_resolver_1.RuleResolver(); - var validationGroupFactory = new validation_group_factory_1.ValidationGroupFactory(fieldErrorProcessor, modelWatcher, propertyResolver, ruleResolver); - function createRuleset() { - return new ruleset_builder_1.RulesetBuilder().create(); - } - exports.createRuleset = createRuleset; - function createWithRules(model, rulesCreator) { - var ruleset = rulesCreator(new ruleset_builder_1.RulesetBuilder()); - return validationGroupFactory.createValidationGroup(model, ruleset); - } - exports.createWithRules = createWithRules; - function create(model, ruleset) { - return validationGroupFactory.createValidationGroup(model, ruleset); - } - exports.create = create; - - -/***/ }, -/* 2 */ -/***/ function(module, exports, __webpack_require__) { - - var validation_group_1 = __webpack_require__(3); - var ValidationGroupFactory = (function () { - function ValidationGroupFactory(fieldErrorProcessor, modelWatcher, propertyResolver, ruleResolver) { - var _this = this; - this.fieldErrorProcessor = fieldErrorProcessor; - this.modelWatcher = modelWatcher; - this.propertyResolver = propertyResolver; - this.ruleResolver = ruleResolver; - this.createValidationGroup = function (model, ruleset) { - return new validation_group_1.ValidationGroup(_this.fieldErrorProcessor, _this.modelWatcher, _this.propertyResolver, _this.ruleResolver, ruleset, model); - }; - } - return ValidationGroupFactory; - })(); - exports.ValidationGroupFactory = ValidationGroupFactory; - - -/***/ }, -/* 3 */ -/***/ function(module, exports, __webpack_require__) { - - var Promise = __webpack_require__(4); - var property_resolver_1 = __webpack_require__(5); - var event_js_1 = __webpack_require__(6); - var property_state_changed_event_1 = __webpack_require__(7); - var model_state_changed_event_1 = __webpack_require__(8); - var rule_resolver_1 = __webpack_require__(9); - var type_helper_1 = __webpack_require__(10); - // TODO: This class is WAY to long, needs refactoring - var ValidationGroup = (function () { - function ValidationGroup(fieldErrorProcessor, modelWatcher, propertyResolver, ruleResolver, ruleset, model, refreshRate) { - var _this = this; - if (propertyResolver === void 0) { propertyResolver = new property_resolver_1.PropertyResolver(); } - if (ruleResolver === void 0) { ruleResolver = new rule_resolver_1.RuleResolver(); } - if (refreshRate === void 0) { refreshRate = 500; } - this.fieldErrorProcessor = fieldErrorProcessor; - this.modelWatcher = modelWatcher; - this.propertyResolver = propertyResolver; - this.ruleResolver = ruleResolver; - this.ruleset = ruleset; - this.model = model; - this.refreshRate = refreshRate; - this.propertyErrors = {}; - this.onModelChanged = function (eventArgs) { - _this.validateProperty(eventArgs.propertyPath); - }; - this.validatePropertyWithRuleLinks = function (propertyName, propertyRules) { - var handlePossibleError = function (possibleError) { - var hadErrors = _this.hasErrors(); - if (!possibleError) { - if (_this.propertyErrors[propertyName]) { - delete _this.propertyErrors[propertyName]; - var eventArgs = new property_state_changed_event_1.PropertyStateChangedEvent(propertyName, true); - _this.propertyStateChangedEvent.publish(eventArgs); - if (hadErrors) { - _this.modelStateChangedEvent.publish(new model_state_changed_event_1.ModelStateChangedEvent(true)); - } - } - return; - } - var previousError = _this.propertyErrors[propertyName]; - _this.propertyErrors[propertyName] = possibleError; - if (possibleError != previousError) { - var eventArgs = new property_state_changed_event_1.PropertyStateChangedEvent(propertyName, false, possibleError); - _this.propertyStateChangedEvent.publish(eventArgs); - if (!hadErrors) { - _this.modelStateChangedEvent.publish(new model_state_changed_event_1.ModelStateChangedEvent(false)); - } - } - }; - if (_this.activePromiseChain) { - _this.activePromiseChain = Promise.resolve(_this.activePromiseChain) - .then(function () { - var fieldValue = _this.propertyResolver.resolveProperty(_this.model, propertyName); - return _this.fieldErrorProcessor - .checkFieldForErrors(fieldValue, propertyRules) - .then(handlePossibleError); - }); - } - else { - var fieldValue = _this.propertyResolver.resolveProperty(_this.model, propertyName); - _this.activePromiseChain = _this.fieldErrorProcessor - .checkFieldForErrors(fieldValue, propertyRules) - .then(handlePossibleError); - } - }; - this.validatePropertyWithRuleSet = function (propertyName, ruleset) { - var promiseList = []; - var transformedPropertyName; - for (var childPropertyName in ruleset.rules) { - transformedPropertyName = propertyName + "." + childPropertyName; - promiseList.push(_this.validatePropertyWithRules(transformedPropertyName, ruleset.getRulesForProperty(childPropertyName))); - } - return Promise.all(promiseList); - }; - this.validatePropertyWithRules = function (propertyName, rules) { - var ruleLinks = []; - var ruleSets = []; - var validationPromises = []; - var routeEachRule = function (ruleLinkOrSet) { - if (_this.isForEach(ruleLinkOrSet)) { - var currentPropertyValue = _this.propertyResolver.resolveProperty(_this.model, propertyName); - var isCurrentlyAnArray = type_helper_1.TypeHelper.isArrayType(currentPropertyValue); - if (isCurrentlyAnArray) { - currentPropertyValue.forEach(function (element, index) { - var childPropertyName = propertyName + "[" + index + "]"; - var promise = _this.validatePropertyWithRules(childPropertyName, [ruleLinkOrSet.internalRule]); - validationPromises.push(promise); - }); - } - else { - if (_this.isRuleset(ruleLinkOrSet.internalRule)) { - ruleSets.push(ruleLinkOrSet.internalRule); - } - else { - ruleLinks.push(ruleLinkOrSet.internalRule); - } - } - } - else if (_this.isRuleset(ruleLinkOrSet)) { - ruleSets.push(ruleLinkOrSet); - } - else { - ruleLinks.push(ruleLinkOrSet); - } - }; - rules.forEach(routeEachRule); - validationPromises.push(_this.validatePropertyWithRuleLinks(propertyName, ruleLinks)); - ruleSets.forEach(function (ruleSet) { - validationPromises.push(_this.validatePropertyWithRuleSet(propertyName, ruleSet)); - }); - return Promise.all(validationPromises); - }; - this.validateProperty = function (propertyName) { - var rulesForProperty = _this.ruleResolver.resolvePropertyRules(propertyName, _this.ruleset); - if (!rulesForProperty) { - return; - } - if (_this.activePromiseChain && _this.activePromiseChain.isFulfilled()) { - _this.activePromiseChain = null; - } - return _this.validatePropertyWithRules(propertyName, rulesForProperty); - }; - this.validateModel = function () { - for (var parameterName in _this.ruleset.rules) { - _this.validateProperty(parameterName); - } - }; - this.hasErrors = function () { - return Object.keys(_this.propertyErrors).length > 0; - }; - this.isValid = function () { - return _this.waitForValidatorsToFinish() - .then(function () { return !_this.hasErrors(); }); - }; - this.getModelErrors = function () { - return _this.waitForValidatorsToFinish() - .then(function () { return _this.propertyErrors; }); - }; - this.getPropertyError = function (propertyRoute) { - return _this.waitForValidatorsToFinish() - .then(function () { return _this.propertyErrors[propertyRoute]; }); - }; - this.release = function () { - _this.modelWatcher.stopWatching(); - }; - this.waitForValidatorsToFinish = function () { - return new Promise(function (resolve, reject) { - var interval = setInterval(function () { - if (_this.activePromiseChain.isFulfilled()) { - clearInterval(interval); - resolve(); - } - }, 50); - }); - }; - this.propertyStateChangedEvent = new event_js_1.EventHandler(this); - this.modelStateChangedEvent = new event_js_1.EventHandler(this); - this.modelWatcher.setupWatcher(model, ruleset, refreshRate); - this.modelWatcher.onPropertyChanged.subscribe(this.onModelChanged); - this.validateModel(); - } - ValidationGroup.prototype.isRuleset = function (possibleRuleset) { - return (typeof (possibleRuleset.addRule) == "function"); - }; - ValidationGroup.prototype.isForEach = function (possibleForEach) { - return possibleForEach.isForEach; - }; - return ValidationGroup; - })(); - exports.ValidationGroup = ValidationGroup; - - -/***/ }, -/* 4 */ -/***/ function(module, exports) { - - module.exports = __WEBPACK_EXTERNAL_MODULE_4__; - -/***/ }, -/* 5 */ -/***/ function(module, exports) { - - module.exports = __WEBPACK_EXTERNAL_MODULE_5__; - -/***/ }, -/* 6 */ -/***/ function(module, exports) { - - module.exports = __WEBPACK_EXTERNAL_MODULE_6__; - -/***/ }, -/* 7 */ -/***/ function(module, exports) { - - var PropertyStateChangedEvent = (function () { - function PropertyStateChangedEvent(property, isValid, error) { - this.property = property; - this.isValid = isValid; - this.error = error; - } - return PropertyStateChangedEvent; - })(); - exports.PropertyStateChangedEvent = PropertyStateChangedEvent; - - -/***/ }, -/* 8 */ -/***/ function(module, exports) { - - var ModelStateChangedEvent = (function () { - function ModelStateChangedEvent(isValid) { - this.isValid = isValid; - } - return ModelStateChangedEvent; - })(); - exports.ModelStateChangedEvent = ModelStateChangedEvent; - - -/***/ }, -/* 9 */ -/***/ function(module, exports, __webpack_require__) { - - var property_resolver_1 = __webpack_require__(5); - var RuleResolver = (function () { - function RuleResolver(propertyResolver) { - var _this = this; - if (propertyResolver === void 0) { propertyResolver = new property_resolver_1.PropertyResolver(); } - this.propertyResolver = propertyResolver; - this.isPropertyRoute = function (possiblePropertyRoute) { - return possiblePropertyRoute.indexOf(".") >= 0; - }; - this.isIndexRoute = function (possibleIndexRoute) { - return possibleIndexRoute.indexOf("[") >= 0; - }; - this.resolvePropertyRules = function (propertyRoute, ruleset) { - var propertyRouteSections = _this.propertyResolver.decomposePropertyRoute(propertyRoute); - var finalProperty = propertyRouteSections[propertyRouteSections.length - 1]; - var matchingRules = _this.traverseRulesForRoutes(propertyRouteSections, ruleset); - if (!matchingRules) { - return null; - } - if (matchingRules.getRulesForProperty) { - var outputRules = matchingRules.getRulesForProperty(finalProperty); - return outputRules; - } - return matchingRules; - }; - this.getMatchingRuleForProperty = function (property, rules) { - var currentRule; - for (var i = 0; i < rules.length; i++) { - currentRule = rules[i]; - if (currentRule.isForEach) { - currentRule = currentRule.internalRule; - } - if (!currentRule.getRulesForProperty) { - continue; - } - if (currentRule.rules[property]) { - return currentRule; - } - } - }; - this.traverseRulesForRoutes = function (propertyRouteSections, ruleset) { - var currentProperty = propertyRouteSections.shift(); - var childRules = ruleset; - if (ruleset.rules) { - childRules = childRules.rules[currentProperty]; - } - if (!childRules) { - return null; - } - if (propertyRouteSections.length == 0) { - return childRules; - } - var nextProperty = propertyRouteSections[0]; - if (!nextProperty) { - return ruleset; - } - if (_this.isIndexRoute(nextProperty)) { - propertyRouteSections.shift(); - var applicableRules = []; - childRules.forEach(function (internalRules) { - if (internalRules.isForEach) { - applicableRules.push(internalRules.internalRule); - } - }); - if (propertyRouteSections.length > 0) { - var totalRules = []; - applicableRules.forEach(function (applicableRule) { - var currentRouteSection = propertyRouteSections.slice(); - var outputRules = _this.traverseRulesForRoutes(currentRouteSection, applicableRule); - outputRules.forEach(function (outputRule) { - totalRules.push(outputRule); - }); - }); - return totalRules; - } - return applicableRules; - } - if (propertyRouteSections.length == 0) { - return childRules; - } - var nextChildRule = _this.getMatchingRuleForProperty(nextProperty, childRules); - if (propertyRouteSections.length > 0) { - return _this.traverseRulesForRoutes(propertyRouteSections, nextChildRule); - } - return nextChildRule; - }; - } - return RuleResolver; - })(); - exports.RuleResolver = RuleResolver; - - -/***/ }, -/* 10 */ -/***/ function(module, exports) { - - var TypeHelper = (function () { - function TypeHelper() { - } - TypeHelper.isDateType = function (value) { - return (typeof value.getMonth === 'function'); - }; - TypeHelper.isSimpleType = function (value) { - return (typeof value == "string" || typeof value == "number"); - }; - TypeHelper.isArrayType = function (value) { - return Object.prototype.toString.call(value) === '[object Array]'; - }; - return TypeHelper; - })(); - exports.TypeHelper = TypeHelper; - - -/***/ }, -/* 11 */ -/***/ function(module, exports, __webpack_require__) { - - var Promise = __webpack_require__(4); - var field_has_error_1 = __webpack_require__(12); - var FieldErrorProcessor = (function () { - function FieldErrorProcessor(ruleRegistry) { - this.ruleRegistry = ruleRegistry; - } - FieldErrorProcessor.prototype.processRuleLink = function (fieldValue, ruleLink) { - var validator = this.ruleRegistry.getRuleNamed(ruleLink.ruleName); - var checkIfValid = function (isValid) { - if (!isValid) { - var error = validator.getMessage(fieldValue, ruleLink.ruleOptions); - throw new field_has_error_1.FieldHasError(error); - } - return null; - }; - return validator - .validate(fieldValue, ruleLink.ruleOptions) - .then(checkIfValid); - }; - FieldErrorProcessor.prototype.checkFieldForErrors = function (fieldValue, rules) { - var _this = this; - var ruleCheck = function (ruleLinkOrSet) { - return _this.processRuleLink(fieldValue, ruleLinkOrSet); - }; - return Promise.resolve(rules) - .each(ruleCheck) - .then(function () { return null; }) - .catch(field_has_error_1.FieldHasError, function (validationError) { - return validationError.message; - }); - }; - return FieldErrorProcessor; - })(); - exports.FieldErrorProcessor = FieldErrorProcessor; - - -/***/ }, -/* 12 */ -/***/ function(module, exports) { - - var __extends = (this && this.__extends) || function (d, b) { - for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; - var FieldHasError = (function (_super) { - __extends(FieldHasError, _super); - function FieldHasError(message) { - _super.call(this, message); - this.message = message; - } - return FieldHasError; - })(Error); - exports.FieldHasError = FieldHasError; - - -/***/ }, -/* 13 */ -/***/ function(module, exports) { - - var RuleRegistry = (function () { - function RuleRegistry() { - var _this = this; - this.rules = {}; - this.registerRule = function (validationRule) { - _this.rules[validationRule.ruleName] = validationRule; - }; - this.unregisterRule = function (validationRule) { - delete _this.rules[validationRule.ruleName]; - }; - this.getRuleNamed = function (ruleName) { - return _this.rules[ruleName]; - }; - } - return RuleRegistry; - })(); - exports.RuleRegistry = RuleRegistry; - - -/***/ }, -/* 14 */ -/***/ function(module, exports, __webpack_require__) { - - var Promise = __webpack_require__(4); - var DateValidationRule = (function () { - function DateValidationRule() { - this.ruleName = "date"; - this.invalidObjectRegex = /Invalid|NaN/; - } - DateValidationRule.prototype.validate = function (value) { - if (value === undefined || value === null) { - return Promise.resolve(true); - } - var matchesRegex = !this.invalidObjectRegex.test(new Date(value)); - return Promise.resolve(matchesRegex); - }; - DateValidationRule.prototype.getMessage = function (value) { - return "This field contains \"" + value + "\" which is not a valid date"; - }; - return DateValidationRule; - })(); - exports.DateValidationRule = DateValidationRule; - - -/***/ }, -/* 15 */ -/***/ function(module, exports, __webpack_require__) { - - var Promise = __webpack_require__(4); - var DecimalValidationRule = (function () { - function DecimalValidationRule() { - this.ruleName = "decimal"; - this.decimalRegex = /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/; - } - DecimalValidationRule.prototype.validate = function (value) { - if (value === undefined || value === null) { - return Promise.resolve(true); - } - var matchesRegex = this.decimalRegex.test(value); - return Promise.resolve(matchesRegex); - }; - DecimalValidationRule.prototype.getMessage = function (value) { - return "This field contains " + value + " which is not a decimal value"; - }; - return DecimalValidationRule; - })(); - exports.DecimalValidationRule = DecimalValidationRule; - - -/***/ }, -/* 16 */ -/***/ function(module, exports, __webpack_require__) { - - var Promise = __webpack_require__(4); - var EmailValidationRule = (function () { - function EmailValidationRule() { - this.ruleName = "email"; - this.emailRegex = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/; - } - EmailValidationRule.prototype.validate = function (value) { - if (value === undefined || value === null) { - return Promise.resolve(true); - } - var matchesRegex = this.emailRegex.test(value); - return Promise.resolve(matchesRegex); - }; - EmailValidationRule.prototype.getMessage = function (value) { - return "This field contains \"" + value + "\" which is not a valid email address"; - }; - return EmailValidationRule; - })(); - exports.EmailValidationRule = EmailValidationRule; - - -/***/ }, -/* 17 */ -/***/ function(module, exports, __webpack_require__) { - - var Promise = __webpack_require__(4); - var type_helper_1 = __webpack_require__(10); - var comparer_helper_1 = __webpack_require__(18); - var EqualValidationRule = (function () { - function EqualValidationRule() { - this.ruleName = "equal"; - } - EqualValidationRule.prototype.validate = function (value, optionsOrValue) { - if (value === undefined || value === null) { - return Promise.resolve(true); - } - var result; - var comparison = optionsOrValue.value || optionsOrValue; - var weakEquality = optionsOrValue.weakEquality || false; - if (type_helper_1.TypeHelper.isDateType(comparison)) { - result = comparer_helper_1.ComparerHelper.dateTimeCompararer(value, comparison); - } - else { - result = comparer_helper_1.ComparerHelper.simpleTypeComparer(value, comparison, weakEquality); - } - return Promise.resolve(result); - }; - EqualValidationRule.prototype.getMessage = function (value, optionsOrValue) { - return "This field is " + value + " but should be equal to " + (optionsOrValue.value || optionsOrValue); - }; - return EqualValidationRule; - })(); - exports.EqualValidationRule = EqualValidationRule; - - -/***/ }, -/* 18 */ -/***/ function(module, exports) { - - var ComparerHelper = (function () { - function ComparerHelper() { - } - ComparerHelper.simpleTypeComparer = function (value1, value2, isWeak) { - if (isWeak) { - return (value1 == value2); - } - return (value1 === value2); - }; - ComparerHelper.dateTimeCompararer = function (value1, value2) { return (value1.getTime() == value2.getTime()); }; - return ComparerHelper; - })(); - exports.ComparerHelper = ComparerHelper; - - -/***/ }, -/* 19 */ -/***/ function(module, exports, __webpack_require__) { - - var Promise = __webpack_require__(4); - var ISODateValidationRule = (function () { - function ISODateValidationRule() { - this.ruleName = "isoDate"; - this.isoDateRegex = /(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))/; - } - ISODateValidationRule.prototype.validate = function (value) { - if (value === undefined || value === null) { - return Promise.resolve(true); - } - var matchesRegex = this.isoDateRegex.test(value); - return Promise.resolve(matchesRegex); - }; - ISODateValidationRule.prototype.getMessage = function (value) { - return "This field contains \"" + value + "\" which is not a valid ISO date"; - }; - return ISODateValidationRule; - })(); - exports.ISODateValidationRule = ISODateValidationRule; - - -/***/ }, -/* 20 */ -/***/ function(module, exports, __webpack_require__) { - - var Promise = __webpack_require__(4); - var MaxLengthValidationRule = (function () { - function MaxLengthValidationRule() { - this.ruleName = "maxLength"; - } - MaxLengthValidationRule.prototype.validate = function (value, maxLength) { - if (value === undefined || value === null || value.length == 0) { - return Promise.resolve(true); - } - if (value.length <= maxLength) { - return Promise.resolve(true); - } - return Promise.resolve(false); - }; - MaxLengthValidationRule.prototype.getMessage = function (value, maxLength) { - return "This field has a length of " + value.length + " but should contain no more than " + maxLength; - }; - return MaxLengthValidationRule; - })(); - exports.MaxLengthValidationRule = MaxLengthValidationRule; - - -/***/ }, -/* 21 */ -/***/ function(module, exports, __webpack_require__) { - - var Promise = __webpack_require__(4); - var MaxValueValidationRule = (function () { - function MaxValueValidationRule() { - this.ruleName = "maxValue"; - } - MaxValueValidationRule.prototype.validate = function (value, maxValue) { - if (value === undefined || value === null || value.length == 0) { - return Promise.resolve(true); - } - if (value <= maxValue) { - return Promise.resolve(true); - } - return Promise.resolve(false); - }; - MaxValueValidationRule.prototype.getMessage = function (value, maxValue) { - return "This field has a value of " + value + " but should be less than or equal to " + maxValue; - }; - return MaxValueValidationRule; - })(); - exports.MaxValueValidationRule = MaxValueValidationRule; - - -/***/ }, -/* 22 */ -/***/ function(module, exports, __webpack_require__) { - - var Promise = __webpack_require__(4); - var MinLengthValidationRule = (function () { - function MinLengthValidationRule() { - this.ruleName = "minLength"; - } - MinLengthValidationRule.prototype.validate = function (value, minLength) { - if (value === undefined || value === null || value.length == 0) { - return Promise.resolve(true); - } - if (value.length >= minLength) { - return Promise.resolve(true); - } - return Promise.resolve(false); - }; - MinLengthValidationRule.prototype.getMessage = function (value, minLength) { - return "This field has a length of " + value.length + " but should more than " + minLength; - }; - return MinLengthValidationRule; - })(); - exports.MinLengthValidationRule = MinLengthValidationRule; - - -/***/ }, -/* 23 */ -/***/ function(module, exports, __webpack_require__) { - - var Promise = __webpack_require__(4); - var MinValueValidationRule = (function () { - function MinValueValidationRule() { - this.ruleName = "minValue"; - } - MinValueValidationRule.prototype.validate = function (value, minValue) { - if (value === undefined || value === null || value.length == 0) { - return Promise.resolve(true); - } - if (value >= minValue) { - return Promise.resolve(true); - } - return Promise.resolve(false); - }; - MinValueValidationRule.prototype.getMessage = function (value, minValue) { - return "This field has a value of " + value + " but should be greater than or equal to " + minValue; - }; - return MinValueValidationRule; - })(); - exports.MinValueValidationRule = MinValueValidationRule; - - -/***/ }, -/* 24 */ -/***/ function(module, exports, __webpack_require__) { - - var Promise = __webpack_require__(4); - var type_helper_1 = __webpack_require__(10); - var comparer_helper_1 = __webpack_require__(18); - var NotEqualValidationRule = (function () { - function NotEqualValidationRule() { - this.ruleName = "notEqual"; - } - NotEqualValidationRule.prototype.validate = function (value, optionsOrValue) { - if (value === undefined || value === null) { - return Promise.resolve(true); - } - var result; - var comparison = optionsOrValue.value || optionsOrValue; - var weakEquality = optionsOrValue.weakEquality || false; - if (type_helper_1.TypeHelper.isDateType(comparison)) { - result = !comparer_helper_1.ComparerHelper.dateTimeCompararer(value, comparison); - } - else { - result = !comparer_helper_1.ComparerHelper.simpleTypeComparer(value, comparison, weakEquality); - } - return Promise.resolve(result); - }; - NotEqualValidationRule.prototype.getMessage = function (value, optionsOrValue) { - return "This field is " + value + " but should not be equal to " + (optionsOrValue.value || optionsOrValue); - }; - return NotEqualValidationRule; - })(); - exports.NotEqualValidationRule = NotEqualValidationRule; - - -/***/ }, -/* 25 */ -/***/ function(module, exports, __webpack_require__) { - - var Promise = __webpack_require__(4); - var NumberValidationRule = (function () { - function NumberValidationRule() { - this.ruleName = "number"; - this.numberRegex = /^\d+$/; - } - NumberValidationRule.prototype.validate = function (value) { - if (value === undefined || value === null) { - return Promise.resolve(true); - } - var matchesRegex = this.numberRegex.test(value); - return Promise.resolve(matchesRegex); - }; - NumberValidationRule.prototype.getMessage = function (value) { - return "This field contains " + value + " which is not a numeric value"; - }; - return NumberValidationRule; - })(); - exports.NumberValidationRule = NumberValidationRule; - - -/***/ }, -/* 26 */ -/***/ function(module, exports, __webpack_require__) { - - var Promise = __webpack_require__(4); - var RegexValidationRule = (function () { - function RegexValidationRule() { - this.ruleName = "regex"; - } - RegexValidationRule.prototype.validate = function (value, regexPattern) { - if (value === undefined || value === null || value.length == 0) { - return Promise.resolve(true); - } - var matchesPattern = value.toString().match(regexPattern) !== null; - return Promise.resolve(matchesPattern); - }; - RegexValidationRule.prototype.getMessage = function (value, regexPattern) { - return "This field does not match the expected format"; - }; - return RegexValidationRule; - })(); - exports.RegexValidationRule = RegexValidationRule; - - -/***/ }, -/* 27 */ -/***/ function(module, exports, __webpack_require__) { - - var Promise = __webpack_require__(4); - var RequiredValidaitonRule = (function () { - function RequiredValidaitonRule() { - this.ruleName = "required"; - } - RequiredValidaitonRule.prototype.validate = function (value, isRequired) { - if (isRequired === void 0) { isRequired = true; } - if (value === undefined || value === null) { - return Promise.resolve(!isRequired); - } - var testValue = value; - if (typeof (testValue) === 'string') { - if (String.prototype.trim) { - testValue = value.trim(); - } - else { - testValue = value.replace(/^\s+|\s+$/g, ''); - } - } - if (!isRequired) { - return Promise.resolve(true); - } - return Promise.resolve((testValue + '').length > 0); - }; - RequiredValidaitonRule.prototype.getMessage = function (value, isRequired) { - return "This field is required"; - }; - return RequiredValidaitonRule; - })(); - exports.RequiredValidaitonRule = RequiredValidaitonRule; - - -/***/ }, -/* 28 */ -/***/ function(module, exports, __webpack_require__) { - - var Promise = __webpack_require__(4); - var StepValidationRule = (function () { - function StepValidationRule() { - this.ruleName = "step"; - } - StepValidationRule.prototype.validate = function (value, step) { - if (value === undefined || value === null) { - return Promise.resolve(true); - } - var dif = (value * 100) % (step * 100); - var matchesStep = Math.abs(dif) < 0.00001 || Math.abs(1 - dif) < 0.00001; - return Promise.resolve(matchesStep); - }; - StepValidationRule.prototype.getMessage = function (value, step) { - return "This field has a value of " + value + " and should be an increment of " + step; - }; - return StepValidationRule; - })(); - exports.StepValidationRule = StepValidationRule; - - -/***/ }, -/* 29 */ -/***/ function(module, exports, __webpack_require__) { - - var ruleset_1 = __webpack_require__(30); - var rule_link_1 = __webpack_require__(31); - var for_each_rule_1 = __webpack_require__(32); - var RulesetBuilder = (function () { - function RulesetBuilder() { - var _this = this; - this.create = function () { - _this.internalRuleset = new ruleset_1.Ruleset(); - _this.currentProperty = null; - return _this; - }; - this.forProperty = function (propertyName) { - _this.currentProperty = propertyName; - return _this; - }; - this.addRule = function (rule, ruleOptions) { - if (!_this.currentProperty) { - throw new Error("A property must precede any rule calls in the chain"); - } - _this.internalRuleset.addRule(_this.currentProperty, new rule_link_1.RuleLink(rule, ruleOptions)); - return _this; - }; - this.addRuleForEach = function (rule, ruleOptions) { - if (!_this.currentProperty) { - throw new Error("A property must precede any rule calls in the chain"); - } - var ruleLink = new rule_link_1.RuleLink(rule, ruleOptions); - _this.internalRuleset.addRule(_this.currentProperty, new for_each_rule_1.ForEachRule(ruleLink)); - return _this; - }; - this.addRuleset = function (ruleset) { - if (!_this.currentProperty) { - throw new Error("A property must precede any rule calls in the chain"); - } - _this.internalRuleset.addRuleset(_this.currentProperty, ruleset); - return _this; - }; - this.addRulesetForEach = function (ruleset) { - if (!_this.currentProperty) { - throw new Error("A property must precede any rule calls in the chain"); - } - _this.internalRuleset.addRuleset(_this.currentProperty, new for_each_rule_1.ForEachRule(ruleset)); - return _this; - }; - this.build = function () { - return _this.internalRuleset; - }; - } - return RulesetBuilder; - })(); - exports.RulesetBuilder = RulesetBuilder; - - -/***/ }, -/* 30 */ -/***/ function(module, exports) { - - var Ruleset = (function () { - function Ruleset() { - var _this = this; - this.rules = {}; - this.createPropertyEntryIfNeeded = function (property) { - if (!_this.rules[property]) { - _this.rules[property] = []; - } - }; - this.addRule = function (property, ruleLink) { - _this.createPropertyEntryIfNeeded(property); - _this.rules[property].push(ruleLink); - }; - this.addRuleset = function (property, ruleset) { - _this.createPropertyEntryIfNeeded(property); - _this.rules[property].push(ruleset); - }; - this.getRulesForProperty = function (property) { return _this.rules[property]; }; - } - return Ruleset; - })(); - exports.Ruleset = Ruleset; - - -/***/ }, -/* 31 */ -/***/ function(module, exports) { - - var RuleLink = (function () { - function RuleLink(ruleName, ruleOptions) { - this.ruleName = ruleName; - this.ruleOptions = ruleOptions; - } - return RuleLink; - })(); - exports.RuleLink = RuleLink; - - -/***/ }, -/* 32 */ -/***/ function(module, exports) { - - var ForEachRule = (function () { - function ForEachRule(internalRule) { - this.internalRule = internalRule; - this.isForEach = true; - } - return ForEachRule; - })(); - exports.ForEachRule = ForEachRule; - - -/***/ }, -/* 33 */ -/***/ function(module, exports, __webpack_require__) { - - var property_resolver_1 = __webpack_require__(5); - var event_js_1 = __webpack_require__(6); - var type_helper_1 = __webpack_require__(10); - var property_watcher_1 = __webpack_require__(34); - var property_changed_event_1 = __webpack_require__(35); - var ModelWatcher = (function () { - function ModelWatcher(propertyResolver) { - var _this = this; - if (propertyResolver === void 0) { propertyResolver = new property_resolver_1.PropertyResolver(); } - this.propertyResolver = propertyResolver; - this.watchCache = []; - this.watchCacheKeys = []; - this.watcherInterval = null; - this.setupWatcher = function (model, ruleset, scanInterval) { - if (scanInterval === void 0) { scanInterval = 500; } - _this.model = model; - _this.ruleset = ruleset; - _this.scanInterval = scanInterval; - _this.watchCache = []; - _this.watchCacheKeys = []; - _this.cacheWatchTargets("", _this.ruleset); - _this.scanProperties(); - _this.startWatching(); - }; - this.startWatching = function () { - _this.stopWatching(); - _this.watcherInterval = setInterval(_this.scanProperties, _this.scanInterval); - }; - this.stopWatching = function () { - if (_this.watcherInterval) { - clearInterval(_this.watcherInterval); - } - }; - this.updateAndNotifyDifferences = function () { - var previousKeyCache = _this.watchCacheKeys; - _this.watchCache = []; - _this.watchCacheKeys = []; - _this.cacheWatchTargets("", _this.ruleset); - _this.watchCacheKeys.forEach(function (key, index) { - if (previousKeyCache.indexOf(key) == -1) { - var previousValue = _this.watchCache[index].previousValue; - var propertyChangedArgs = new property_changed_event_1.PropertyChangedEvent(key, previousValue, null); - setTimeout(function () { _this.onPropertyChanged.publish(propertyChangedArgs); }, 1); - } - }); - }; - this.watchProperty = function (watchRoute, previousData) { - if (_this.watchCacheKeys.indexOf(watchRoute) == -1) { - var propertyWatcher = new property_watcher_1.PropertyWatcher(watchRoute, previousData); - _this.watchCache.push(propertyWatcher); - _this.watchCacheKeys.push(watchRoute); - } - }; - this.cacheWatchTargets = function (propertyStack, ruleset) { - var paramRoute, parameterRules; - for (var param in ruleset.rules) { - paramRoute = propertyStack ? propertyStack + "." + param : param; - parameterRules = ruleset.rules[param]; - parameterRules.forEach(function (rule) { - var currentValue = _this.propertyResolver.resolveProperty(_this.model, paramRoute); - var isArray = type_helper_1.TypeHelper.isArrayType(currentValue); - if (isArray) { - var cachedArrayInfo = { length: currentValue.length, isArray: true }; - _this.watchProperty(paramRoute, cachedArrayInfo); - } - if (rule.isForEach) { - // ruleset - if (rule.internalRule.getRulesForProperty) { - _this.model[param].forEach(function (element, index) { - _this.cacheWatchTargets(paramRoute + "[" + index + "]", rule.internalRule); - }); - } - else { - _this.model[param].forEach(function (element, index) { - _this.watchProperty(paramRoute + "[" + index + "]", _this.model[param][index]); - }); - } - } - else { - // ruleset - if (rule.getRulesForProperty) { - _this.cacheWatchTargets(paramRoute, rule); - } - else { - if (!isArray) { - _this.watchProperty(paramRoute, currentValue); - } - } - } - }); - } - }; - this.scanProperties = function () { - if (_this.onPropertyChanged.getSubscriptionCount() == 0) { - return; - } - if (_this.watchCache.length == 0) { - return; - } - var refreshOnNextCycle = false; - _this.watchCache.forEach(function (propertyWatcher) { - var currentValue = _this.propertyResolver.resolveProperty(_this.model, propertyWatcher.propertyPath); - if (currentValue && propertyWatcher.previousValue.isArray) { - if (currentValue.length != propertyWatcher.previousValue.length) { - refreshOnNextCycle = true; - } - } - else if (currentValue !== propertyWatcher.previousValue) { - var propertyChangedArgs = new property_changed_event_1.PropertyChangedEvent(propertyWatcher.propertyPath, currentValue, propertyWatcher.previousValue); - setTimeout(function () { _this.onPropertyChanged.publish(propertyChangedArgs); }, 1); - propertyWatcher.previousValue = currentValue; - } - }); - if (refreshOnNextCycle) { - setTimeout(_this.updateAndNotifyDifferences, 1); - } - }; - this.onPropertyChanged = new event_js_1.EventHandler(this); - } - return ModelWatcher; - })(); - exports.ModelWatcher = ModelWatcher; - - -/***/ }, -/* 34 */ -/***/ function(module, exports) { - - var PropertyWatcher = (function () { - function PropertyWatcher(propertyPath, previousValue) { - this.propertyPath = propertyPath; - this.previousValue = previousValue; - } - return PropertyWatcher; - })(); - exports.PropertyWatcher = PropertyWatcher; - - -/***/ }, -/* 35 */ -/***/ function(module, exports) { - - var PropertyChangedEvent = (function () { - function PropertyChangedEvent(propertyPath, newValue, oldValue) { - this.propertyPath = propertyPath; - this.newValue = newValue; - this.oldValue = oldValue; - } - return PropertyChangedEvent; - })(); - exports.PropertyChangedEvent = PropertyChangedEvent; - - -/***/ }, -/* 36 */ -/***/ function(module, exports) { - - - - -/***/ }, -/* 37 */ -/***/ function(module, exports) { - - - - -/***/ }, -/* 38 */ -/***/ function(module, exports) { - - - - -/***/ }, -/* 39 */ -/***/ function(module, exports) { - - var ValidationError = (function () { - function ValidationError(propertyName, message) { - this.propertyName = propertyName; - this.message = message; - } - return ValidationError; - })(); - exports.ValidationError = ValidationError; - - -/***/ }, -/* 40 */ -/***/ function(module, exports) { - - - - -/***/ }, -/* 41 */ -/***/ function(module, exports) { - - - - -/***/ } -/******/ ]) -}); -; \ No newline at end of file diff --git a/docs/breaking-changes.md b/docs/breaking-changes.md new file mode 100644 index 0000000..281408c --- /dev/null +++ b/docs/breaking-changes.md @@ -0,0 +1,9 @@ +# Breaking Changes + +Wherever possible breaking changes are avoided but in some cases its going to happen. + +## 0.6.0 + +- The `treacherous.minimal.js` dist file was removed +- The `create` method exposed was changed to `createGroup` to be more explicit of what it does +- The `createWithRules` method exposed was changed to `createGroupWithRules` to be more explicit of what it does \ No newline at end of file diff --git a/docs/creating-rulesets.md b/docs/creating-rulesets.md new file mode 100644 index 0000000..a8c379f --- /dev/null +++ b/docs/creating-rulesets.md @@ -0,0 +1,141 @@ +# Creating Rulesets + +Creating rulesets is really simple, and they are created without needing a model so you can make your rules without +having a class or object. The rulesets are glorified dictionaries which tie a property to a set of rules and +are very lightweight objects with no logic. + +--- + +## Available Methods + +When creating a ruleset there are the following methods: + +### forProperty(propertyName: string) + +This takes a string which should be the property name that you wish to apply rules to. + +### addRule(ruleType: string, ruleArgs?: any) + +This adds a rule to a previously specified property, it is mandatory for you to provide a rule type however +not all rule types require arguments. For example if you were to use `.addRule("required")` that would be +perfectly fine, however if you were to want to specify a max length you would need to provide it the +length you wish, so that would look like `.addRule("maxLength", 5);`. Each rule is different and you can +accept custom objects if you have complex rules, although its recommended you keep them as simple as possible. + +### addRuleset(ruleset: Ruleset) + +This much like `addRule` specifies that the ruleset provided should be used within the current property. This +is the primary way of composing rulesets for complex objects. + +### addRuleForEach(ruleType: string, ruleArgs?: any) + +This is for use with arrays and basically specifies a repeatable rule for every element within the array, so +for example if we were to have `.addRuleForEach("maxValue", 10);` we would be saying that all elements in the array +must be <= 10, although this is only really used when you have all value type elements and not complex objects in +the arrays. + +### addRulesetForEach(ruleset: Ruleset) + +This is for use with arrays and specifies that each element within the array should be subject to the ruleset +provided, so this allows for complex repeatable rules with very little effort. + +### build() + +This method builds the configured ruleset for use within the validation group object or other rulesets. Behind +the scenes there is a `RulesetBuilder` which is being used to build up the concerns and this returns the ruleset +rather than the chainable builder. + +### create() + +This method is rarely used as it is called for you via the `Treacherous.createRuleset()` method however if +you were to use the `RulesetBuilder` directly without going via the mentioned method you would need to +call `create` before building to make sure it resets all internal configuration state. + +--- + +## Simplest Example + +```js +var ruleset = Treacherous.createRuleset() + .forProperty("foo") + .addRule("maxLength", 5) // The array can only contain <= 5 elements + .build(); +``` + +So the above example is basically setting up a ruleset where it expects a property called `foo` and that property +should have a `maxLength` of 5. You can easily add multiple rules or nested rules to properties allowing for a +very flexible and composite approach to ruleset building and management. + +## More Complex Example + +```js +var ruleset = Treacherous.createRuleset() + .forProperty("foo") + .addRule("maxLength", 5) // The array can only contain <= 5 elements + .forProperty("bar") + .addRule("required") // The array can only contain <= 5 elements + .build(); +``` + +## Nested Example + +```js +var childRuleset = rulesetBuilder.create() + .forProperty("bar") + .addRule("maxLength", 5) + .build(); + +var ruleset = rulesetBuilder.create() + .forProperty("foo") + .addRuleset(childRuleset) // Reusing the other ruleset + .build(); +``` + +So as shown above we can easily nest rulesets by creating each layer in the validation as its own ruleset. +The first ruleset is saying that there should be a property called `bar` which has a `maxlength` of 5, then +there is the ruleset which expects a `foo` property which should adhere to the `childRuleset`. + +Just to be more explicit the above `ruleset` variable would be looking for a model like so: + +```js +{ + "bar": { + "foo": "valid" // This must have a length <= 5 + } +} +``` + +It is recommended that you basically have a ruleset per object type, so if you had an `Item` class and +an `Inventory` class it would make sense to re-use the `Item` rules within the context of the `Inventory`. + +## Validating With Arrays + +So building upon the previous example you can easily validate arrays, be they simple with singular values +or complex objects within them, by using the `*forEach` methods. + + +```js +var ruleset = rulesetBuilder.create() + .forProperty("someSimpleArray") + .addRuleForEach("maxLength", 5) + .build(); +``` + +This will tell it to loop through the `someSimpleArray` property and check that each element in the array +has a length <= 5. + +You can also set rulesets to be repeated like shown: + +```js +var childRuleset = rulesetBuilder.create() + .forProperty("bar") + .addRule("maxLength", 5) + .build(); + +var ruleset = rulesetBuilder.create() + .forProperty("someArray") + .addRulesetForEach(childRuleset) + .build(); +``` + +This will make sure that every element in `someArray` has a property named `bar` that has a length <= 5. diff --git a/docs/custom-rules.md b/docs/custom-rules.md new file mode 100644 index 0000000..2c6ec74 --- /dev/null +++ b/docs/custom-rules.md @@ -0,0 +1,78 @@ +# Custom Rules + +By default Treacherous sets up a `ruleRegistry` instance with all native rules, however you can easily make +your own rules and add them into the rule registry. + +## Creating a Rule + +There is an [IValidationRule](../src/rules/ivalidation-rule.ts) file which is the interface that all rules +should adhere to. Don't worry if you are not using Typescript it's fine you just need to make sure you have the +methods listed in there. + +So for example lets make a custom rule for checking if the value is "hello". + +### Typescript Example +``` +import * as Promise from "bluebird"; +import {IValidationRule} from "treacherous"; + +export class HelloValidationRule implements IValidationRule +{ + public ruleName = "hello"; + + public validate(value, optionsOrValue): Promise + { return Promise.resolve(value == "hello"); } + + public getMessage(value, optionsOrValue) + { return `This field is ${value} but should be hello`; } +} +``` + +### Javascript Example +```js +function HelloValidationRule() +{ + this.ruleName = "hello"; + + this.validate(value, optionsOrValue) + { return Promise.resolve(value == "hello"); } + + this.getMessage(value, optionsOrValue) + { return "This field is " + value + "but should be hello"; } +} +``` + +This rule now can be used within the Treacherous system by registering it within the `ruleRegistry`. + +### Extra Info + +To provide some more information on each property: + +* `ruleName` - The name of the rule to be used within the `addRule` statements +* `validate` - The validation method where you should verify the value and return a promise with true/false +* `getMessage` - The getMessage method should return a string providing information related to the validation error + +(It is also worth mentioning that at some point the message will be pulled out when internationalisation occurs) + +--- + +## Registering a Rule + +Once you have created a rule you then need to register it so the other components in the system are aware +of it. This is pretty easy to do, so assuming we have created the above rule we can do the following: + +```js +Treacherous.ruleRegistry.registerRule(new HelloValidationRule()); +``` + +This now means everything else in the system will be aware of the "hello" rule, so if you were to do the +following ruleset and use it in a validation group it would work. + +```js +var ruleset = Treacherous.createRuleset() + .forProperty("shouldBeHello") + .addRule("hello") // Notice how we use the ruleName here + .build(); +``` + +That is all that you need to do and your rules just work. \ No newline at end of file diff --git a/docs/installing.md b/docs/installing.md new file mode 100644 index 0000000..ef50730 --- /dev/null +++ b/docs/installing.md @@ -0,0 +1,35 @@ +# Installing + +This can be used in nodejs or the browser, through a module loader or without one. It is flexible in how it +is consumed as well as how it is exposed. + +## Via NPM + +Just do an `npm install treacherous` + +## In browser + +There are 3 flavours in the dist dir: + +* `treacherous.all.js` - Contains treacherous and all dependencies (mainly for testing) +* `treacherous.minimal.js` - Contains only treacherous and no dependencies (for module aware systems) +* `treacherous.browser.js` - Contains treacherous which works without modules for browser usage + +The reason there are 3 flavours is because some people will use this in a non-module aware +browser scenario, and `treacherous.all.js` will contain everything for it to just work, it is also +used by the unit tests in the project. + +`treacherous.js` is purely just the treacherous library without any dependencies, this is +the most modular version of the package and what the `package.json` defaults to. However there +are 2 dependencies to other libraries I have written which were originally part of this but +were split out for re-use, however I am aware most people will not use them outside of here, so +this is where the last version comes from. + +`treacherous.browser.js` is same as `treacherous.minimal.js` but it does not know of modules, so it +requires you to include `bluebird` and the other 2 packages in the page somewhere. +([event-js](https://github.com/grofit/eventjs), [property-resolver](https://github.com/grofit/property-resolver)), + +--- + +In the browser it will self register the `Treacherous` global, however in node or module aware +environments its up to you how you include it, but it will expose the same object for you. \ No newline at end of file diff --git a/docs/readme.md b/docs/readme.md new file mode 100644 index 0000000..14474ba --- /dev/null +++ b/docs/readme.md @@ -0,0 +1,14 @@ +# Welcome to the Docs + +Here you will find some more in depth information on the library and how to use it as well as some information +on how it all works under the hood if you wanted to mess with it or build upon it. + +Just click on the relevent files above to select the topic you are interested in. + +(Assuming you are viewing this folder in github) + +## Additional Technical Documentation + +There is technical documentation available however it is not really needed when you can access the source directly, +if you do however want to generate it you can clone and run `gulp generate:documentation`. This will then output the +`technical` folder into this directory which contains some technical documentation to go with the source code. \ No newline at end of file diff --git a/docs/tips.md b/docs/tips.md new file mode 100644 index 0000000..4c766eb --- /dev/null +++ b/docs/tips.md @@ -0,0 +1,3 @@ +# Tips + +This will hopefully get bigger as more users come up with common concerns or patterns. \ No newline at end of file diff --git a/docs/validation-groups.md b/docs/validation-groups.md new file mode 100644 index 0000000..29163b3 --- /dev/null +++ b/docs/validation-groups.md @@ -0,0 +1,159 @@ +# Validation Groups + +A validation group takes a model and ruleset and monitors the model to see if the validation concerns change +in any way and track errors as well as providing events to notify subscribers of validation changes. + +## Creating Validation Groups + +There are 2 main ways to create a validation group given the `Treacherous` object: + +### createGroup(model: any, ruleset: Ruleset) + +This takes the model and the ruleset to be applied to the model and returns the validation group. This is +the most common way of creating a validationGroup and should be seen as the preferred way of usage as this +enforces you to isolate your ruleset creation logic. + +```js +var ruleset = ...; +var model = ...; +var validationGroup = Treacherous.createGroup(model, ruleset); +``` + +### createGroupWithRules(model: any, rulesetGenerator: Function) + +This approach allows you to create a group for a model based upon a generator method you provide, this is +mainly for users who may have a slight deviation on the normal validation rules or just want to inline +their rules for creation of the group. + +```js +var model = ...; +var validationGroup = Treacherous.createGroupWithRules(model, function(rulesetBuilder){ + return rulesetBuilder + .forProperty("foo") + .addRule("required") + .build(); +}); +``` + +You can also separate out your ruleset logic into its own method and expose it like so: + +```js +function createModelRuleset(rulesetBuilder){ + return rulesetBuilder + .forProperty("foo") + .addRule("required") + .build(); +} + +var model = ...; +var validationGroup = Treacherous.createGroupWithRules(model, createModelRuleset); +``` + +--- + +## Using Validation Groups + +There are a few different ways to get validation information, you can subscribe to events and react +to validation concerns as they update, or you can just query the group for all outstanding errors. + +This satisfies the 2 main use cases of having a front end reacting to errors as they occur or having +a button which triggers validation to see what has changed. + +### Check current validity +```js +var validationGroup = Treacherous.createGroup(simpleModel, ruleset); + +validationGroup.isValid() + .then(function(isValid){ + // true is valid, false is invalid + )); +``` + +### Get current validation errors +```js +var validationGroup = Treacherous.createGroup(simpleModel, ruleset); + +validationGroup.getModelErrors() + .then(function(propertyErrors){ + /* + propertyErrors is a json object with the name of the property per error. + + So for example if you had a property called foo which was required and had failed + then you would be passed back the error structure: + + { + "foo": "some error message here" + } + + You will only get the most recent validation error as the library will stop processing + after the first failure. Also another thing to keep in mind is that the validation field + will contain the property route, not just the actual property. + + So for example if you had an object called foo, which contained the property bar which was + and array and the second element had failed validation you would get back the error structure: + + { + "foo.bar[1]": "some error message here" + } + + It is recommended that you process these errors in a for loop as you never know what + will be in there: + + for(var relatedPropertyName in propertyErrors) { ... } + */ + )); +``` + +### Get current validation errors for a property +```js +var validationGroup = Treacherous.createGroup(simpleModel, ruleset); + +validationGroup.getPropertyError("somePropertyName") + .then(function(propertyError){ + /* + propertyError is a either a string containing the error or undefined + */ + )); +``` + +### Subscribe to per property validation changes +```js +var validationGroup = Treacherous.createGroup(simpleModel, ruleset); + +validationGroup.propertyStateChangedEvent.subscribe(function(propertyValidationChangedEvent){ + /* + The propertyValidationChangedEvent is of type PropertyValidationChangedEvent + and contains the following fields: + + { + property: string, // The property/route which has failed, i.e 'foo' or 'foo.bar[2].woo' + isValid: boolean, // The validation state + error?: string // The error message if isValid is false + } + + The event is only raised when the validation state of a property changes, however if a property + is invalid and then it changes and is still invalid but for a different reason this event would + be triggered but the error string would be different. + */ +)); +``` + +### Subscribe to model validation changes +```js +var validationGroup = Treacherous.createGroup(simpleModel, ruleset); + +validationGroup.modelStateChangedEvent.subscribe(function(validationStateChangedEvent){ + /* + The validationStateChangedEvent is of type ValidationStateChangedEvent + and contains the following fields: + + { + isValid: boolean, // The validation state + } + + The event is only raised when the validation state of a model changes, so if a single + property has an error, then suddenly 20 properties have an error no event will be sent + until the model is all valid. + */ +)); +``` \ No newline at end of file diff --git a/docs/validation-process.md b/docs/validation-process.md new file mode 100644 index 0000000..9635fb7 --- /dev/null +++ b/docs/validation-process.md @@ -0,0 +1,89 @@ +# Validation Process + +The validation process in Treacherous is split up between registered rules, ruleset creation and model validation, +this is mainly to allow for composite rule generation, however behind the scenes there are some other +steps which you may or may not care about depending upon if you wish to customize how things work. + +For most instances all you will care about is [creating rulesets](creating-rulesets.md) and [validating models](validation-groups.md), +which is all exposed via the high level `Treacherous` object, this contains methods for creating rulesets/validation groups. +However if you want to know more or start tinkering with it more then there is a higher level process behind the scenes. + +--- + +## High Level Process + +- Create Rule Registry +- Register Rules +- Create Validation Group Factory +- Create Ruleset For Model +- Create Validation Group For Model + Ruleset + +So the first 3 steps (and some more) are done for you by Treacherous and can be seen in [exposer.ts](../src/exposer.ts), however you +can easily replace this with your own validation group factories or internal validation processors etc. + +One of the key things to think about is the 3 core aspects of the validation system: + +- Registered Rules +- Ruleset Creation +- Validation Group Creation + +### Registered Rules + +So as mentioned Treacherous by default creates a global `ruleRegistry` object and registers all the native rules +so you do not need to worry about handling that. In most cases you will just want to add to that when you have your +own [custom rules](custom-rules.md). + +### Ruleset Creation + +Treacherous internally has a `RulesetBuilder` which it uses to give a fluent style approach to building rulesets, +you do not have to use this though, as internally this just creates ruleset objects and applies rule links or rulesets +as applicable. For the most part though you will not even know you are using half of this stuff as it is all +exposed via the 3 main methods on the `Treacherous` object, but its worth knowing these bits exist. + +### Validation Group Creation + +Again in most cases you will just call `createGroup` with the model and rulesets you care about, behind the scenes +this is the validation group factory automatically satisfying a lot of the dependencies required for the `ValidationGroup` +to function. + +You can make your own implementation of `IValidationGroup` as long as it satisfies the interface and the tests pass +you can easily create your own ways of validating. In most cases though you do not really need to do this, as even with +other frameworks like knockout which has custom `ko.observable` properties and not basic value types, the logic to resolve +those properties live in a class which is outside of the validation group, so it is quite flexible and it may be worth looking +at how the other plugins for treacherous work in this area if you want to support weird and wonderful scenarios. + +--- + +## How The Validation Works + +So inside the validation group there are a few components which do some important things: + +- FieldErrorProcessor +- ModelWatcher +- PropertyResolver +- RuleResolver + +As mentioned previously this is the *DEFAULT* ValidationGroup implementation, you are free to make your own, +however in most cases you can just swap out the dependent components and solve your problems. + +### FieldErrorProcessor + +This basically takes a value and some rules and executes them all returning the promise containing the result. +For all intents and purposes this is the core validator as it will go through all rules it is given until it +fails and then stops processing. + +### ModelWatcher + +As the name implies this basically watches the model that is being validated and will notify whatever is listening +to tell it that a property has changed, this is mainly what drives the reactive element of it all. + +### PropertyResolver + +This is in charge of resolving a property value from a property path, so this will get all the values from +the model based upon the path to the property. This does incur some minor performance issues when lots of +validation is happening at once on large models, but generally it is deemed fast enough. + +### RuleResolver + +This is same sort of thing as the PropertyResolver, it gets all rules applicable to a propertyPath. This is +used to get the rules for each property which delegates through to the other components. \ No newline at end of file diff --git a/package.json b/package.json index f901a33..d4c2890 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,13 @@ { "name": "treacherous", - "version": "0.5.1", + "version": "0.6.0", "license": "MIT", "dependencies": { "bluebird": "^3.3.4", "property-resolver": "^0.0.6", "event-js": "^0.1.0" }, - "main": "dist/treacherous.minimal.js", + "main": "dist/treacherous.js", "bugs": { "url": "https://github.com/grofit/treacherous/issues" }, @@ -30,8 +30,10 @@ "gulp-es6-exporter": "^0.0.5", "gulp-uglify": "latest", "gulp-rename": "latest", + "gulp-typedoc": "^2.0.0", "webpack-stream": "^3.1.0", "gulp-bump": "latest", + "typedoc": "^0.3.12", "rimraf": "latest", "mkdirp": "latest", "require-dir": "^0.1.0", diff --git a/readme.md b/readme.md index 5c2372e..1bbab48 100644 --- a/readme.md +++ b/readme.md @@ -1,18 +1,16 @@ # Treacherous -There are LOTS of validation frameworks out currently, some synchronous, some are async, -some work off the DOM some work off models. Some of them are tied to specific front end frameworks, -others are mainly for the node world. +A modern async validation system to be used on the server or in the browser as well as with or without +view frameworks. -However in most cases you will be hard pressed to find a easy to use async based one which is -agnostic of frameworks/platforms but can be plugged into them. This was the idea behind Treacherous, -a simple validation framework which could be shared between browser and server as well as -being able to expose MVVM style subscriptions for other frameworks to hook into. +It is an attempt to bring some consistency to validation in the javascript world you can write your +validation rules in a single way a single time and re-use it anywhere you want without worrying about +each framework/platforms many different validation paradigms or libraries. ## Features / Benefits - Fully async validation`*` -- Rule based validation for reusable validation concerns +- Separation of rules and validation allowing composable rulesets - Supports nested complex objects/arrays - Outside in validation, does not augment your models in any way - Can be integrated with any front end framework`**` @@ -20,7 +18,9 @@ being able to expose MVVM style subscriptions for other frameworks to hook into. `*` = Currently has a hard dependency on bluebird (looking to refactor out going forward), and incorrect validation does not reject promises (this was a design decision but is open to discussion.) -`**` = Existing plugins for [knockout](https://github.com/grofit/treacherous-knockout), and [aurelia] (https://github.com/grofit/treacherous-aurelia) one coming soon. +`**` = Currently supports [knockout](https://github.com/grofit/treacherous-knockout), [aurelia] (https://github.com/grofit/treacherous-aurelia), others coming soon. + +--- ## Installing @@ -30,36 +30,19 @@ Just do an `npm install treacherous` ### In browser -There are 4 flavours in the dist dir: - -* `treacherous.all.js` - Contains treacherous and all dependencies (including bluebird) -* `treacherous.minimal.js` - Contains only treacherous and no dependencies -* `treacherous.js` - Contains treacherous dependencies without bluebird -* `treacherous.browser.js` - Contains treacherous which works without modules for browser usage +There are a few different flavours in the dist directory for the browser ([read more here](docs/installing.md)). -The reason there are 4 flavours is because some people will use this in a non-module aware -browser scenario, and `treacherous.all.js` will contain everything for it to just work, it is also -used by the unit tests in the project. +- If you have a module aware framework in your browser use `treacheous.js` +- If you are running without modules then include either `treacherous.browser.js` or `treacherous.all.js` -`treacherous.minimal.js` is purely just the treacherous library without any dependencies, this is -the most modular version of the package and what the `package.json` defaults to. However there -are 2 dependencies to other libraries I have written which were originally part of this but -were split out for re-use, however I am aware most people will not use them outside of here, so -this is where the last version comes from. +The difference between the two latter ones is that one requires you to include ([event-js](https://github.com/grofit/eventjs) and [property-resolver](https://github.com/grofit/property-resolver)), +however the `all` one has those bundled in, for most instances use the browser one and include the other 2 files. -`treacherous.js` contains the 2 custom modules that are required for this library -([event-js](https://github.com/grofit/eventjs), [property-resolver](https://github.com/grofit/property-resolver)), -without you needing to include them, as chances are if you are using this you already have bluebird -or some other promise library included so dont want it bundled with this. +In non module aware scenarios it will self register the `Treacherous` global var for you. -`treacherous.browser.js` is same as `treacherous.minimal.js` but it does not know of modules, so it -requires you to include `bluebird` and the other 2 packages in the page somewhere. -([event-js](https://github.com/grofit/eventjs), [property-resolver](https://github.com/grofit/property-resolver)), +--- -## How do I use it - -In the browser it will self register the `Treacherous` global, however in node or module aware -environments its up to you how you include it, but it will expose the same object for you. +## Simple Examples ### Validating simple models @@ -77,7 +60,7 @@ var ruleset = Treacherous.createRuleset() .addRule("maxLength", 5) // The property neds a length <= 5 .build(); -var validationGroup = Treacherous.create(simpleModel, ruleset); +var validationGroup = Treacherous.createGroup(simpleModel, ruleset); validationGroup.isValid() .then(function(isValid){ @@ -98,7 +81,7 @@ var ruleset = Treacherous.createRuleset() .addRuleForEach("maxValue", 20) // Each element needs a value <= 20 .build(); -var validationGroup = Treacherous.create(simpleModel, ruleset); +var validationGroup = Treacherous.createGroup(simpleModel, ruleset); validationGroup.getModelErrors() .then(function(errors){ @@ -106,16 +89,18 @@ validationGroup.getModelErrors() }); ``` -## Validation State +--- + +## Creating Validation Groups -You can manually call the validation group to confirm the validation state, you can also subscribe -to be notified when validation changes occur, allowing you to reactively handle validation changes. +The validation group is the object which manages validation state, you can find out a lot more +information on this within the [docs](docs/validation-groups.md). -For the examples below imagine the model and ruleset are already set. +Here are a few simple examples to save you trawling the docs. ### Check current validity ```js -var validationGroup = Treacherous.create(simpleModel, ruleset); +var validationGroup = Treacherous.createGroup(simpleModel, ruleset); validationGroup.isValid() .then(function(isValid){ @@ -123,94 +108,22 @@ validationGroup.isValid() )); ``` -### Get current validation errors +### Get all errors ```js -var validationGroup = Treacherous.create(simpleModel, ruleset); +var validationGroup = Treacherous.createGroup(...); validationGroup.getModelErrors() - .then(function(propertyErrors){ - /* - propertyErrors is a json object with the name of the property per error. - - So for example if you had a property called foo which was required and had failed - then you would be passed back the error structure: - - { - "foo": "some error message here" - } - - You will only get the most recent validation error as the library will stop processing - after the first failure. Also another thing to keep in mind is that the validation field - will contain the property route, not just the actual property. - - So for example if you had an object called foo, which contained the property bar which was - and array and the second element had failed validation you would get back the error structure: - - { - "foo.bar[1]": "some error message here" - } - - It is recommended that you process these errors in a for loop as you never know what - will be in there: - - for(var relatedPropertyName in propertyErrors) { ... } - */ - )); + .then(function(propertyErrors){...)); ``` -### Get current validation errors for a property +### Subscribe validation changes ```js -var validationGroup = Treacherous.create(simpleModel, ruleset); +var validationGroup = Treacherous.createGroup(...); -validationGroup.getPropertyError("somePropertyName") - .then(function(propertyError){ - /* - propertyError is a either a string containing the error or undefined - */ - )); +validationGroup.propertyStateChangedEvent.subscribe(function(propertyValidationChangedEvent){...)); ``` -### Subscribe to per property validation changes -```js -var validationGroup = Treacherous.create(simpleModel, ruleset); - -validationGroup.propertyStateChangedEvent.subscribe(function(propertyValidationChangedEvent){ - /* - The propertyValidationChangedEvent is of type PropertyValidationChangedEvent - and contains the following fields: - - { - property: string, // The property/route which has failed, i.e 'foo' or 'foo.bar[2].woo' - isValid: boolean, // The validation state - error?: string // The error message if isValid is false - } - - The event is only raised when the validation state of a property changes, however if a property - is invalid and then it changes and is still invalid but for a different reason this event would - be triggered but the error string would be different. - */ -)); -``` - -### Subscribe to model validation changes -```js -var validationGroup = Treacherous.create(simpleModel, ruleset); - -validationGroup.modelStateChangedEvent.subscribe(function(validationStateChangedEvent){ - /* - The validationStateChangedEvent is of type ValidationStateChangedEvent - and contains the following fields: - - { - isValid: boolean, // The validation state - } - - The event is only raised when the validation state of a model changes, so if a single - property has an error, then suddenly 20 properties have an error no event will be sent - until the model is all valid. - */ -)); -``` +--- ## Validation rules @@ -234,38 +147,18 @@ The framework comes with built in validators for the following: ### Creating custom rules So if you want to make your own rule you need to do 2 things, one is create the rule handler class -which should conform to the `IValidationRule` interface, which in raw JS would conform to this: +which should conform to the `IValidationRule` interface, once you have done the validator you then need to register it so the validation system is aware of it, +to do that you need to call `Treacherous.ruleRegistry.registerRule(new SomeCustomValidator());`. -``` -function SomeCustomValidator() -{ - this.ruleName = "someRuleName"; - - this.validate = function(value, options) - { - return Promise.resolve(true); - } - - this.getMessage = function(value, options) { - return `some message`; - } -} -``` +To find out more read the [Custom Rules](docs/custom-rules.md) docs. -To provide some more information on each property: +--- -* `ruleName` - The name of the rule to be used within the `addRule` statements -* `validate` - The validation method where you should verify the value and return a promise with true/false -* `getMessage` - The getMessage method should return a string providing information related to the validation error +## Documentation -Once you have done the validator you then need to register it so the validation system is aware of it, -to do that you need to call `Treacherous.ruleRegistry.registerRule(new SomeCustomValidator());`. +Just look in the `docs` folder for more documentation on certain scenarios or subject matters. -### Translations - -This is a todo, if needed this will probably be implemented by having a class representing the -validation message for a given language linked via the `ruleName` for now all messages are in -english but feel free to raise this if you need this functionality sooner rather than later. +--- ## Developing @@ -275,8 +168,25 @@ output files, we don't minify by default. You can also run `gulp run-tests` which will run the tests to make sure everythign works as expected. +### Translations + +This is a todo, if needed this will probably be implemented by having a class representing the +validation message for a given language linked via the `ruleName` for now all messages are in +english but feel free to raise this if you need this functionality sooner rather than later. + +--- + ## Why should I use it? +There are LOTS of validation frameworks out currently, some synchronous, some are async, +some work off the DOM some work off models. Some of them are tied to specific front end frameworks, +others are mainly for the node world. + +However in most cases you will be hard pressed to find a easy to use async based one which is +agnostic of frameworks/platforms but can be plugged into them. This was the idea behind Treacherous, +a simple validation framework which could be shared between browser and server as well as +being able to expose MVVM style subscriptions for other frameworks to hook into. + If you share code between multiple worlds (i.e nodejs, browser, mobile) then you may have had issues before where you are tied into a specific framework for your front ends and want to re-use your models in the back end as a contractual layer or something. In these cases diff --git a/src/exposer.ts b/src/exposer.ts index 1e1ef66..09da801 100644 --- a/src/exposer.ts +++ b/src/exposer.ts @@ -22,8 +22,6 @@ import {ValidationGroup} from "./validation-group"; import {ModelWatcher} from "./watcher/model-watcher"; import {PropertyResolver} from "property-resolver"; import {RuleResolver} from "./rulesets/rule-resolver"; -//import {RuleResolver2} from "./rulesets/rule-resolver2"; -//export {RuleResolver2 as RuleResolver} from "./rulesets/rule-resolver2"; export var ruleRegistry = new RuleRegistry(); ruleRegistry.registerRule(new DateValidationRule()); diff --git a/src/index.ts b/src/index.ts index 2a5e02b..7ecfb26 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,9 +4,9 @@ export * from "./validation-group" export * from "./events/model-state-changed-event" export * from "./events/property-changed-event" export * from "./events/property-state-changed-event" +export * from "./factories/validation-group-factory" export * from "./helpers/comparer-helper" export * from "./helpers/type-helper" -export * from "./factories/validation-group-factory" export * from "./rules/date-validation-rule" export * from "./rules/decimal-validation-rule" export * from "./rules/email-validation-rule" diff --git a/tests/specs/treacherous-sanity-tests.js b/tests/specs/treacherous-sanity-tests.js index a58f641..f6cfea4 100644 --- a/tests/specs/treacherous-sanity-tests.js +++ b/tests/specs/treacherous-sanity-tests.js @@ -8,11 +8,11 @@ describe('Treacherous Sanity Checks', function () { expect(ruleBuilder).is.not.null; expect(ruleBuilder.create).to.be.a("function"); - var validationGroup = Treacherous.create({}, new Treacherous.Ruleset()); + var validationGroup = Treacherous.createGroup({}, new Treacherous.Ruleset()); expect(validationGroup).is.not.null; expect(validationGroup.getModelErrors).to.be.a("function"); - var validationGroupExplicitRules = Treacherous.createWithRules({}, function(rulesetBuilder){ + var validationGroupExplicitRules = Treacherous.createGroupWithRules({}, function(rulesetBuilder){ return rulesetBuilder.create().build(); }); expect(validationGroupExplicitRules).is.not.null; @@ -46,7 +46,7 @@ describe('Treacherous Sanity Checks', function () { .addRuleForEach("maxValue", 19) .build(); - var validationGroup = Treacherous.create(dummyModel, ruleset); + var validationGroup = Treacherous.createGroup(dummyModel, ruleset); validationGroup.getModelErrors() .then(function(errors){