diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000..e4e8dd03 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,45 @@ +version: 2.1 + +map-1: &filter_only_develop + filters: + branches: + only: develop + +map-2: &filter_only_tag + filters: + branches: + ignore: /.*/ + tags: + only: /^v?[0-9]+(\.[0-9]+)*$/ + +orbs: + v1: aurelia/v1@volatile + +workflows: + main: + jobs: + - v1/build_test + - v1/build_merge: + <<: *filter_only_develop + requires: + - v1/build_test + - v1/npm_publish: + <<: *filter_only_tag + name: npm_publish_dry + args: "--dry-run" + - request_publish_latest: + <<: *filter_only_tag + type: approval + requires: + - npm_publish_dry + - v1/npm_publish: + <<: *filter_only_tag + name: npm_publish + context: Aurelia + requires: + - request_publish_latest + - v1/merge_back: + <<: *filter_only_tag + requires: + - npm_publish + diff --git a/.gitignore b/.gitignore index ff9600cc..06f15a38 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ npm-debug.log* .rollupcache dist/doc-temp dist/test +dist diff --git a/bower.json b/bower.json index 00e09534..31bbd96a 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "aurelia-validation", - "version": "1.3.3", + "version": "1.4.0", "description": "Validation for Aurelia applications", "keywords": [ "aurelia", diff --git a/circle.yml b/circle.yml deleted file mode 100644 index b36067ae..00000000 --- a/circle.yml +++ /dev/null @@ -1,11 +0,0 @@ - -##### -# Circle CI -# -# For running docker images on circle ci, see: https://circleci.com/docs/docker -# For circle.yml explanation, see: https://circleci.com/docs/manually -##### - -machine: - node: - version: 6.4.0 diff --git a/dist/amd/aurelia-validation.js b/dist/amd/aurelia-validation.js deleted file mode 100644 index 0c3c6d23..00000000 --- a/dist/amd/aurelia-validation.js +++ /dev/null @@ -1,1811 +0,0 @@ -define('aurelia-validation', ['exports', 'aurelia-pal', 'aurelia-binding', 'aurelia-dependency-injection', 'aurelia-task-queue', 'aurelia-templating', 'aurelia-logging'], function (exports, aureliaPal, aureliaBinding, aureliaDependencyInjection, aureliaTaskQueue, aureliaTemplating, LogManager) { 'use strict'; - - /** - * Gets the DOM element associated with the data-binding. Most of the time it's - * the binding.target but sometimes binding.target is an aurelia custom element, - * or custom attribute which is a javascript "class" instance, so we need to use - * the controller's container to retrieve the actual DOM element. - */ - function getTargetDOMElement(binding, view) { - var target = binding.target; - // DOM element - if (target instanceof Element) { - return target; - } - // custom element or custom attribute - // tslint:disable-next-line:prefer-const - for (var i = 0, ii = view.controllers.length; i < ii; i++) { - var controller = view.controllers[i]; - if (controller.viewModel === target) { - var element = controller.container.get(aureliaPal.DOM.Element); - if (element) { - return element; - } - throw new Error("Unable to locate target element for \"" + binding.sourceExpression + "\"."); - } - } - throw new Error("Unable to locate target element for \"" + binding.sourceExpression + "\"."); - } - - function getObject(expression, objectExpression, source) { - var value = objectExpression.evaluate(source, null); - if (value === null || value === undefined || value instanceof Object) { - return value; - } - // tslint:disable-next-line:max-line-length - throw new Error("The '" + objectExpression + "' part of '" + expression + "' evaluates to " + value + " instead of an object, null or undefined."); - } - /** - * Retrieves the object and property name for the specified expression. - * @param expression The expression - * @param source The scope - */ - function getPropertyInfo(expression, source) { - var originalExpression = expression; - while (expression instanceof aureliaBinding.BindingBehavior || expression instanceof aureliaBinding.ValueConverter) { - expression = expression.expression; - } - var object; - var propertyName; - if (expression instanceof aureliaBinding.AccessScope) { - object = aureliaBinding.getContextFor(expression.name, source, expression.ancestor); - propertyName = expression.name; - } - else if (expression instanceof aureliaBinding.AccessMember) { - object = getObject(originalExpression, expression.object, source); - propertyName = expression.name; - } - else if (expression instanceof aureliaBinding.AccessKeyed) { - object = getObject(originalExpression, expression.object, source); - propertyName = expression.key.evaluate(source); - } - else { - throw new Error("Expression '" + originalExpression + "' is not compatible with the validate binding-behavior."); - } - if (object === null || object === undefined) { - return null; - } - return { object: object, propertyName: propertyName }; - } - - function isString(value) { - return Object.prototype.toString.call(value) === '[object String]'; - } - function isNumber(value) { - return Object.prototype.toString.call(value) === '[object Number]'; - } - - var PropertyAccessorParser = /** @class */ (function () { - function PropertyAccessorParser(parser) { - this.parser = parser; - } - PropertyAccessorParser.prototype.parse = function (property) { - if (isString(property) || isNumber(property)) { - return property; - } - var accessorText = getAccessorExpression(property.toString()); - var accessor = this.parser.parse(accessorText); - if (accessor instanceof aureliaBinding.AccessScope - || accessor instanceof aureliaBinding.AccessMember && accessor.object instanceof aureliaBinding.AccessScope) { - return accessor.name; - } - throw new Error("Invalid property expression: \"" + accessor + "\""); - }; - PropertyAccessorParser.inject = [aureliaBinding.Parser]; - return PropertyAccessorParser; - }()); - function getAccessorExpression(fn) { - /* tslint:disable:max-line-length */ - var classic = /^function\s*\([$_\w\d]+\)\s*\{(?:\s*"use strict";)?\s*(?:[$_\w\d.['"\]+;]+)?\s*return\s+[$_\w\d]+\.([$_\w\d]+)\s*;?\s*\}$/; - /* tslint:enable:max-line-length */ - var arrow = /^\(?[$_\w\d]+\)?\s*=>\s*[$_\w\d]+\.([$_\w\d]+)$/; - var match = classic.exec(fn) || arrow.exec(fn); - if (match === null) { - throw new Error("Unable to parse accessor function:\n" + fn); - } - return match[1]; - } - - /*! ***************************************************************************** - Copyright (c) Microsoft Corporation. All rights reserved. - Licensed under the Apache License, Version 2.0 (the "License"); you may not use - this file except in compliance with the License. You may obtain a copy of the - License at http://www.apache.org/licenses/LICENSE-2.0 - - THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED - WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, - MERCHANTABLITY OR NON-INFRINGEMENT. - - See the Apache Version 2.0 License for specific language governing permissions - and limitations under the License. - ***************************************************************************** */ - /* global Reflect, Promise */ - - var extendStatics = function(d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - - function __extends(d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - } - - function __decorate(decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; - } - - /** - * Validation triggers. - */ - (function (validateTrigger) { - /** - * Manual validation. Use the controller's `validate()` and `reset()` methods - * to validate all bindings. - */ - validateTrigger[validateTrigger["manual"] = 0] = "manual"; - /** - * Validate the binding when the binding's target element fires a DOM "blur" event. - */ - validateTrigger[validateTrigger["blur"] = 1] = "blur"; - /** - * Validate the binding when it updates the model due to a change in the view. - */ - validateTrigger[validateTrigger["change"] = 2] = "change"; - /** - * Validate the binding when the binding's target element fires a DOM "blur" event and - * when it updates the model due to a change in the view. - */ - validateTrigger[validateTrigger["changeOrBlur"] = 3] = "changeOrBlur"; - })(exports.validateTrigger || (exports.validateTrigger = {})); - - /** - * Validates objects and properties. - */ - var Validator = /** @class */ (function () { - function Validator() { - } - return Validator; - }()); - - /** - * The result of validating an individual validation rule. - */ - var ValidateResult = /** @class */ (function () { - /** - * @param rule The rule associated with the result. Validator implementation specific. - * @param object The object that was validated. - * @param propertyName The name of the property that was validated. - * @param error The error, if the result is a validation error. - */ - function ValidateResult(rule, object, propertyName, valid, message) { - if (message === void 0) { message = null; } - this.rule = rule; - this.object = object; - this.propertyName = propertyName; - this.valid = valid; - this.message = message; - this.id = ValidateResult.nextId++; - } - ValidateResult.prototype.toString = function () { - return this.valid ? 'Valid.' : this.message; - }; - ValidateResult.nextId = 0; - return ValidateResult; - }()); - - var ValidateEvent = /** @class */ (function () { - function ValidateEvent( - /** - * The type of validate event. Either "validate" or "reset". - */ - type, - /** - * The controller's current array of errors. For an array containing both - * failed rules and passed rules, use the "results" property. - */ - errors, - /** - * The controller's current array of validate results. This - * includes both passed rules and failed rules. For an array of only failed rules, - * use the "errors" property. - */ - results, - /** - * The instruction passed to the "validate" or "reset" event. Will be null when - * the controller's validate/reset method was called with no instruction argument. - */ - instruction, - /** - * In events with type === "validate", this property will contain the result - * of validating the instruction (see "instruction" property). Use the controllerValidateResult - * to access the validate results specific to the call to "validate" - * (as opposed to using the "results" and "errors" properties to access the controller's entire - * set of results/errors). - */ - controllerValidateResult) { - this.type = type; - this.errors = errors; - this.results = results; - this.instruction = instruction; - this.controllerValidateResult = controllerValidateResult; - } - return ValidateEvent; - }()); - - /** - * Orchestrates validation. - * Manages a set of bindings, renderers and objects. - * Exposes the current list of validation results for binding purposes. - */ - var ValidationController = /** @class */ (function () { - function ValidationController(validator, propertyParser) { - this.validator = validator; - this.propertyParser = propertyParser; - // Registered bindings (via the validate binding behavior) - this.bindings = new Map(); - // Renderers that have been added to the controller instance. - this.renderers = []; - /** - * Validation results that have been rendered by the controller. - */ - this.results = []; - /** - * Validation errors that have been rendered by the controller. - */ - this.errors = []; - /** - * Whether the controller is currently validating. - */ - this.validating = false; - // Elements related to validation results that have been rendered. - this.elements = new Map(); - // Objects that have been added to the controller instance (entity-style validation). - this.objects = new Map(); - /** - * The trigger that will invoke automatic validation of a property used in a binding. - */ - this.validateTrigger = exports.validateTrigger.blur; - // Promise that resolves when validation has completed. - this.finishValidating = Promise.resolve(); - this.eventCallbacks = []; - } - /** - * Subscribe to controller validate and reset events. These events occur when the - * controller's "validate"" and "reset" methods are called. - * @param callback The callback to be invoked when the controller validates or resets. - */ - ValidationController.prototype.subscribe = function (callback) { - var _this = this; - this.eventCallbacks.push(callback); - return { - dispose: function () { - var index = _this.eventCallbacks.indexOf(callback); - if (index === -1) { - return; - } - _this.eventCallbacks.splice(index, 1); - } - }; - }; - /** - * Adds an object to the set of objects that should be validated when validate is called. - * @param object The object. - * @param rules Optional. The rules. If rules aren't supplied the Validator implementation will lookup the rules. - */ - ValidationController.prototype.addObject = function (object, rules) { - this.objects.set(object, rules); - }; - /** - * Removes an object from the set of objects that should be validated when validate is called. - * @param object The object. - */ - ValidationController.prototype.removeObject = function (object) { - this.objects.delete(object); - this.processResultDelta('reset', this.results.filter(function (result) { return result.object === object; }), []); - }; - /** - * Adds and renders an error. - */ - ValidationController.prototype.addError = function (message, object, propertyName) { - if (propertyName === void 0) { propertyName = null; } - var resolvedPropertyName; - if (propertyName === null) { - resolvedPropertyName = propertyName; - } - else { - resolvedPropertyName = this.propertyParser.parse(propertyName); - } - var result = new ValidateResult({ __manuallyAdded__: true }, object, resolvedPropertyName, false, message); - this.processResultDelta('validate', [], [result]); - return result; - }; - /** - * Removes and unrenders an error. - */ - ValidationController.prototype.removeError = function (result) { - if (this.results.indexOf(result) !== -1) { - this.processResultDelta('reset', [result], []); - } - }; - /** - * Adds a renderer. - * @param renderer The renderer. - */ - ValidationController.prototype.addRenderer = function (renderer) { - var _this = this; - this.renderers.push(renderer); - renderer.render({ - kind: 'validate', - render: this.results.map(function (result) { return ({ result: result, elements: _this.elements.get(result) }); }), - unrender: [] - }); - }; - /** - * Removes a renderer. - * @param renderer The renderer. - */ - ValidationController.prototype.removeRenderer = function (renderer) { - var _this = this; - this.renderers.splice(this.renderers.indexOf(renderer), 1); - renderer.render({ - kind: 'reset', - render: [], - unrender: this.results.map(function (result) { return ({ result: result, elements: _this.elements.get(result) }); }) - }); - }; - /** - * Registers a binding with the controller. - * @param binding The binding instance. - * @param target The DOM element. - * @param rules (optional) rules associated with the binding. Validator implementation specific. - */ - ValidationController.prototype.registerBinding = function (binding, target, rules) { - this.bindings.set(binding, { target: target, rules: rules, propertyInfo: null }); - }; - /** - * Unregisters a binding with the controller. - * @param binding The binding instance. - */ - ValidationController.prototype.unregisterBinding = function (binding) { - this.resetBinding(binding); - this.bindings.delete(binding); - }; - /** - * Interprets the instruction and returns a predicate that will identify - * relevant results in the list of rendered validation results. - */ - ValidationController.prototype.getInstructionPredicate = function (instruction) { - var _this = this; - if (instruction) { - var object_1 = instruction.object, propertyName_1 = instruction.propertyName, rules_1 = instruction.rules; - var predicate_1; - if (instruction.propertyName) { - predicate_1 = function (x) { return x.object === object_1 && x.propertyName === propertyName_1; }; - } - else { - predicate_1 = function (x) { return x.object === object_1; }; - } - if (rules_1) { - return function (x) { return predicate_1(x) && _this.validator.ruleExists(rules_1, x.rule); }; - } - return predicate_1; - } - else { - return function () { return true; }; - } - }; - /** - * Validates and renders results. - * @param instruction Optional. Instructions on what to validate. If undefined, all - * objects and bindings will be validated. - */ - ValidationController.prototype.validate = function (instruction) { - var _this = this; - // Get a function that will process the validation instruction. - var execute; - if (instruction) { - // tslint:disable-next-line:prefer-const - var object_2 = instruction.object, propertyName_2 = instruction.propertyName, rules_2 = instruction.rules; - // if rules were not specified, check the object map. - rules_2 = rules_2 || this.objects.get(object_2); - // property specified? - if (instruction.propertyName === undefined) { - // validate the specified object. - execute = function () { return _this.validator.validateObject(object_2, rules_2); }; - } - else { - // validate the specified property. - execute = function () { return _this.validator.validateProperty(object_2, propertyName_2, rules_2); }; - } - } - else { - // validate all objects and bindings. - execute = function () { - var promises = []; - for (var _i = 0, _a = Array.from(_this.objects); _i < _a.length; _i++) { - var _b = _a[_i], object = _b[0], rules = _b[1]; - promises.push(_this.validator.validateObject(object, rules)); - } - for (var _c = 0, _d = Array.from(_this.bindings); _c < _d.length; _c++) { - var _e = _d[_c], binding = _e[0], rules = _e[1].rules; - var propertyInfo = getPropertyInfo(binding.sourceExpression, binding.source); - if (!propertyInfo || _this.objects.has(propertyInfo.object)) { - continue; - } - promises.push(_this.validator.validateProperty(propertyInfo.object, propertyInfo.propertyName, rules)); - } - return Promise.all(promises).then(function (resultSets) { return resultSets.reduce(function (a, b) { return a.concat(b); }, []); }); - }; - } - // Wait for any existing validation to finish, execute the instruction, render the results. - this.validating = true; - var returnPromise = this.finishValidating - .then(execute) - .then(function (newResults) { - var predicate = _this.getInstructionPredicate(instruction); - var oldResults = _this.results.filter(predicate); - _this.processResultDelta('validate', oldResults, newResults); - if (returnPromise === _this.finishValidating) { - _this.validating = false; - } - var result = { - instruction: instruction, - valid: newResults.find(function (x) { return !x.valid; }) === undefined, - results: newResults - }; - _this.invokeCallbacks(instruction, result); - return result; - }) - .catch(function (exception) { - // recover, to enable subsequent calls to validate() - _this.validating = false; - _this.finishValidating = Promise.resolve(); - return Promise.reject(exception); - }); - this.finishValidating = returnPromise; - return returnPromise; - }; - /** - * Resets any rendered validation results (unrenders). - * @param instruction Optional. Instructions on what to reset. If unspecified all rendered results - * will be unrendered. - */ - ValidationController.prototype.reset = function (instruction) { - var predicate = this.getInstructionPredicate(instruction); - var oldResults = this.results.filter(predicate); - this.processResultDelta('reset', oldResults, []); - this.invokeCallbacks(instruction, null); - }; - /** - * Gets the elements associated with an object and propertyName (if any). - */ - ValidationController.prototype.getAssociatedElements = function (_a) { - var object = _a.object, propertyName = _a.propertyName; - var elements = []; - for (var _i = 0, _b = Array.from(this.bindings); _i < _b.length; _i++) { - var _c = _b[_i], binding = _c[0], target = _c[1].target; - var propertyInfo = getPropertyInfo(binding.sourceExpression, binding.source); - if (propertyInfo && propertyInfo.object === object && propertyInfo.propertyName === propertyName) { - elements.push(target); - } - } - return elements; - }; - ValidationController.prototype.processResultDelta = function (kind, oldResults, newResults) { - // prepare the instruction. - var instruction = { - kind: kind, - render: [], - unrender: [] - }; - // create a shallow copy of newResults so we can mutate it without causing side-effects. - newResults = newResults.slice(0); - var _loop_1 = function (oldResult) { - // get the elements associated with the old result. - var elements = this_1.elements.get(oldResult); - // remove the old result from the element map. - this_1.elements.delete(oldResult); - // create the unrender instruction. - instruction.unrender.push({ result: oldResult, elements: elements }); - // determine if there's a corresponding new result for the old result we are unrendering. - var newResultIndex = newResults.findIndex(function (x) { return x.rule === oldResult.rule && x.object === oldResult.object && x.propertyName === oldResult.propertyName; }); - if (newResultIndex === -1) { - // no corresponding new result... simple remove. - this_1.results.splice(this_1.results.indexOf(oldResult), 1); - if (!oldResult.valid) { - this_1.errors.splice(this_1.errors.indexOf(oldResult), 1); - } - } - else { - // there is a corresponding new result... - var newResult = newResults.splice(newResultIndex, 1)[0]; - // get the elements that are associated with the new result. - var elements_1 = this_1.getAssociatedElements(newResult); - this_1.elements.set(newResult, elements_1); - // create a render instruction for the new result. - instruction.render.push({ result: newResult, elements: elements_1 }); - // do an in-place replacement of the old result with the new result. - // this ensures any repeats bound to this.results will not thrash. - this_1.results.splice(this_1.results.indexOf(oldResult), 1, newResult); - if (!oldResult.valid && newResult.valid) { - this_1.errors.splice(this_1.errors.indexOf(oldResult), 1); - } - else if (!oldResult.valid && !newResult.valid) { - this_1.errors.splice(this_1.errors.indexOf(oldResult), 1, newResult); - } - else if (!newResult.valid) { - this_1.errors.push(newResult); - } - } - }; - var this_1 = this; - // create unrender instructions from the old results. - for (var _i = 0, oldResults_1 = oldResults; _i < oldResults_1.length; _i++) { - var oldResult = oldResults_1[_i]; - _loop_1(oldResult); - } - // create render instructions from the remaining new results. - for (var _a = 0, newResults_1 = newResults; _a < newResults_1.length; _a++) { - var result = newResults_1[_a]; - var elements = this.getAssociatedElements(result); - instruction.render.push({ result: result, elements: elements }); - this.elements.set(result, elements); - this.results.push(result); - if (!result.valid) { - this.errors.push(result); - } - } - // render. - for (var _b = 0, _c = this.renderers; _b < _c.length; _b++) { - var renderer = _c[_b]; - renderer.render(instruction); - } - }; - /** - * Validates the property associated with a binding. - */ - ValidationController.prototype.validateBinding = function (binding) { - if (!binding.isBound) { - return; - } - var propertyInfo = getPropertyInfo(binding.sourceExpression, binding.source); - var rules; - var registeredBinding = this.bindings.get(binding); - if (registeredBinding) { - rules = registeredBinding.rules; - registeredBinding.propertyInfo = propertyInfo; - } - if (!propertyInfo) { - return; - } - var object = propertyInfo.object, propertyName = propertyInfo.propertyName; - this.validate({ object: object, propertyName: propertyName, rules: rules }); - }; - /** - * Resets the results for a property associated with a binding. - */ - ValidationController.prototype.resetBinding = function (binding) { - var registeredBinding = this.bindings.get(binding); - var propertyInfo = getPropertyInfo(binding.sourceExpression, binding.source); - if (!propertyInfo && registeredBinding) { - propertyInfo = registeredBinding.propertyInfo; - } - if (registeredBinding) { - registeredBinding.propertyInfo = null; - } - if (!propertyInfo) { - return; - } - var object = propertyInfo.object, propertyName = propertyInfo.propertyName; - this.reset({ object: object, propertyName: propertyName }); - }; - /** - * Changes the controller's validateTrigger. - * @param newTrigger The new validateTrigger - */ - ValidationController.prototype.changeTrigger = function (newTrigger) { - this.validateTrigger = newTrigger; - var bindings = Array.from(this.bindings.keys()); - for (var _i = 0, bindings_1 = bindings; _i < bindings_1.length; _i++) { - var binding = bindings_1[_i]; - var source = binding.source; - binding.unbind(); - binding.bind(source); - } - }; - /** - * Revalidates the controller's current set of errors. - */ - ValidationController.prototype.revalidateErrors = function () { - for (var _i = 0, _a = this.errors; _i < _a.length; _i++) { - var _b = _a[_i], object = _b.object, propertyName = _b.propertyName, rule = _b.rule; - if (rule.__manuallyAdded__) { - continue; - } - var rules = [[rule]]; - this.validate({ object: object, propertyName: propertyName, rules: rules }); - } - }; - ValidationController.prototype.invokeCallbacks = function (instruction, result) { - if (this.eventCallbacks.length === 0) { - return; - } - var event = new ValidateEvent(result ? 'validate' : 'reset', this.errors, this.results, instruction || null, result); - for (var i = 0; i < this.eventCallbacks.length; i++) { - this.eventCallbacks[i](event); - } - }; - ValidationController.inject = [Validator, PropertyAccessorParser]; - return ValidationController; - }()); - - /** - * Binding behavior. Indicates the bound property should be validated. - */ - var ValidateBindingBehaviorBase = /** @class */ (function () { - function ValidateBindingBehaviorBase(taskQueue) { - this.taskQueue = taskQueue; - } - ValidateBindingBehaviorBase.prototype.bind = function (binding, source, rulesOrController, rules) { - var _this = this; - // identify the target element. - var target = getTargetDOMElement(binding, source); - // locate the controller. - var controller; - if (rulesOrController instanceof ValidationController) { - controller = rulesOrController; - } - else { - controller = source.container.get(aureliaDependencyInjection.Optional.of(ValidationController)); - rules = rulesOrController; - } - if (controller === null) { - throw new Error("A ValidationController has not been registered."); - } - controller.registerBinding(binding, target, rules); - binding.validationController = controller; - var trigger = this.getValidateTrigger(controller); - // tslint:disable-next-line:no-bitwise - if (trigger & exports.validateTrigger.change) { - binding.vbbUpdateSource = binding.updateSource; - // tslint:disable-next-line:only-arrow-functions - // tslint:disable-next-line:space-before-function-paren - binding.updateSource = function (value) { - this.vbbUpdateSource(value); - this.validationController.validateBinding(this); - }; - } - // tslint:disable-next-line:no-bitwise - if (trigger & exports.validateTrigger.blur) { - binding.validateBlurHandler = function () { - _this.taskQueue.queueMicroTask(function () { return controller.validateBinding(binding); }); - }; - binding.validateTarget = target; - target.addEventListener('blur', binding.validateBlurHandler); - } - if (trigger !== exports.validateTrigger.manual) { - binding.standardUpdateTarget = binding.updateTarget; - // tslint:disable-next-line:only-arrow-functions - // tslint:disable-next-line:space-before-function-paren - binding.updateTarget = function (value) { - this.standardUpdateTarget(value); - this.validationController.resetBinding(this); - }; - } - }; - ValidateBindingBehaviorBase.prototype.unbind = function (binding) { - // reset the binding to it's original state. - if (binding.vbbUpdateSource) { - binding.updateSource = binding.vbbUpdateSource; - binding.vbbUpdateSource = null; - } - if (binding.standardUpdateTarget) { - binding.updateTarget = binding.standardUpdateTarget; - binding.standardUpdateTarget = null; - } - if (binding.validateBlurHandler) { - binding.validateTarget.removeEventListener('blur', binding.validateBlurHandler); - binding.validateBlurHandler = null; - binding.validateTarget = null; - } - binding.validationController.unregisterBinding(binding); - binding.validationController = null; - }; - return ValidateBindingBehaviorBase; - }()); - - /** - * Binding behavior. Indicates the bound property should be validated - * when the validate trigger specified by the associated controller's - * validateTrigger property occurs. - */ - var ValidateBindingBehavior = /** @class */ (function (_super) { - __extends(ValidateBindingBehavior, _super); - function ValidateBindingBehavior() { - return _super !== null && _super.apply(this, arguments) || this; - } - ValidateBindingBehavior.prototype.getValidateTrigger = function (controller) { - return controller.validateTrigger; - }; - ValidateBindingBehavior.inject = [aureliaTaskQueue.TaskQueue]; - ValidateBindingBehavior = __decorate([ - aureliaBinding.bindingBehavior('validate') - ], ValidateBindingBehavior); - return ValidateBindingBehavior; - }(ValidateBindingBehaviorBase)); - /** - * Binding behavior. Indicates the bound property will be validated - * manually, by calling controller.validate(). No automatic validation - * triggered by data-entry or blur will occur. - */ - var ValidateManuallyBindingBehavior = /** @class */ (function (_super) { - __extends(ValidateManuallyBindingBehavior, _super); - function ValidateManuallyBindingBehavior() { - return _super !== null && _super.apply(this, arguments) || this; - } - ValidateManuallyBindingBehavior.prototype.getValidateTrigger = function () { - return exports.validateTrigger.manual; - }; - ValidateManuallyBindingBehavior.inject = [aureliaTaskQueue.TaskQueue]; - ValidateManuallyBindingBehavior = __decorate([ - aureliaBinding.bindingBehavior('validateManually') - ], ValidateManuallyBindingBehavior); - return ValidateManuallyBindingBehavior; - }(ValidateBindingBehaviorBase)); - /** - * Binding behavior. Indicates the bound property should be validated - * when the associated element blurs. - */ - var ValidateOnBlurBindingBehavior = /** @class */ (function (_super) { - __extends(ValidateOnBlurBindingBehavior, _super); - function ValidateOnBlurBindingBehavior() { - return _super !== null && _super.apply(this, arguments) || this; - } - ValidateOnBlurBindingBehavior.prototype.getValidateTrigger = function () { - return exports.validateTrigger.blur; - }; - ValidateOnBlurBindingBehavior.inject = [aureliaTaskQueue.TaskQueue]; - ValidateOnBlurBindingBehavior = __decorate([ - aureliaBinding.bindingBehavior('validateOnBlur') - ], ValidateOnBlurBindingBehavior); - return ValidateOnBlurBindingBehavior; - }(ValidateBindingBehaviorBase)); - /** - * Binding behavior. Indicates the bound property should be validated - * when the associated element is changed by the user, causing a change - * to the model. - */ - var ValidateOnChangeBindingBehavior = /** @class */ (function (_super) { - __extends(ValidateOnChangeBindingBehavior, _super); - function ValidateOnChangeBindingBehavior() { - return _super !== null && _super.apply(this, arguments) || this; - } - ValidateOnChangeBindingBehavior.prototype.getValidateTrigger = function () { - return exports.validateTrigger.change; - }; - ValidateOnChangeBindingBehavior.inject = [aureliaTaskQueue.TaskQueue]; - ValidateOnChangeBindingBehavior = __decorate([ - aureliaBinding.bindingBehavior('validateOnChange') - ], ValidateOnChangeBindingBehavior); - return ValidateOnChangeBindingBehavior; - }(ValidateBindingBehaviorBase)); - /** - * Binding behavior. Indicates the bound property should be validated - * when the associated element blurs or is changed by the user, causing - * a change to the model. - */ - var ValidateOnChangeOrBlurBindingBehavior = /** @class */ (function (_super) { - __extends(ValidateOnChangeOrBlurBindingBehavior, _super); - function ValidateOnChangeOrBlurBindingBehavior() { - return _super !== null && _super.apply(this, arguments) || this; - } - ValidateOnChangeOrBlurBindingBehavior.prototype.getValidateTrigger = function () { - return exports.validateTrigger.changeOrBlur; - }; - ValidateOnChangeOrBlurBindingBehavior.inject = [aureliaTaskQueue.TaskQueue]; - ValidateOnChangeOrBlurBindingBehavior = __decorate([ - aureliaBinding.bindingBehavior('validateOnChangeOrBlur') - ], ValidateOnChangeOrBlurBindingBehavior); - return ValidateOnChangeOrBlurBindingBehavior; - }(ValidateBindingBehaviorBase)); - - /** - * Creates ValidationController instances. - */ - var ValidationControllerFactory = /** @class */ (function () { - function ValidationControllerFactory(container) { - this.container = container; - } - ValidationControllerFactory.get = function (container) { - return new ValidationControllerFactory(container); - }; - /** - * Creates a new controller instance. - */ - ValidationControllerFactory.prototype.create = function (validator) { - if (!validator) { - validator = this.container.get(Validator); - } - var propertyParser = this.container.get(PropertyAccessorParser); - return new ValidationController(validator, propertyParser); - }; - /** - * Creates a new controller and registers it in the current element's container so that it's - * available to the validate binding behavior and renderers. - */ - ValidationControllerFactory.prototype.createForCurrentScope = function (validator) { - var controller = this.create(validator); - this.container.registerInstance(ValidationController, controller); - return controller; - }; - return ValidationControllerFactory; - }()); - ValidationControllerFactory['protocol:aurelia:resolver'] = true; - - var ValidationErrorsCustomAttribute = /** @class */ (function () { - function ValidationErrorsCustomAttribute(boundaryElement, controllerAccessor) { - this.boundaryElement = boundaryElement; - this.controllerAccessor = controllerAccessor; - this.controller = null; - this.errors = []; - this.errorsInternal = []; - } - ValidationErrorsCustomAttribute.inject = function () { - return [aureliaPal.DOM.Element, aureliaDependencyInjection.Lazy.of(ValidationController)]; - }; - ValidationErrorsCustomAttribute.prototype.sort = function () { - this.errorsInternal.sort(function (a, b) { - if (a.targets[0] === b.targets[0]) { - return 0; - } - // tslint:disable-next-line:no-bitwise - return a.targets[0].compareDocumentPosition(b.targets[0]) & 2 ? 1 : -1; - }); - }; - ValidationErrorsCustomAttribute.prototype.interestingElements = function (elements) { - var _this = this; - return elements.filter(function (e) { return _this.boundaryElement.contains(e); }); - }; - ValidationErrorsCustomAttribute.prototype.render = function (instruction) { - var _loop_1 = function (result) { - var index = this_1.errorsInternal.findIndex(function (x) { return x.error === result; }); - if (index !== -1) { - this_1.errorsInternal.splice(index, 1); - } - }; - var this_1 = this; - for (var _i = 0, _a = instruction.unrender; _i < _a.length; _i++) { - var result = _a[_i].result; - _loop_1(result); - } - for (var _b = 0, _c = instruction.render; _b < _c.length; _b++) { - var _d = _c[_b], result = _d.result, elements = _d.elements; - if (result.valid) { - continue; - } - var targets = this.interestingElements(elements); - if (targets.length) { - this.errorsInternal.push({ error: result, targets: targets }); - } - } - this.sort(); - this.errors = this.errorsInternal; - }; - ValidationErrorsCustomAttribute.prototype.bind = function () { - if (!this.controller) { - this.controller = this.controllerAccessor(); - } - // this will call render() with the side-effect of updating this.errors - this.controller.addRenderer(this); - }; - ValidationErrorsCustomAttribute.prototype.unbind = function () { - if (this.controller) { - this.controller.removeRenderer(this); - } - }; - __decorate([ - aureliaTemplating.bindable({ defaultBindingMode: aureliaBinding.bindingMode.oneWay }) - ], ValidationErrorsCustomAttribute.prototype, "controller", void 0); - __decorate([ - aureliaTemplating.bindable({ primaryProperty: true, defaultBindingMode: aureliaBinding.bindingMode.twoWay }) - ], ValidationErrorsCustomAttribute.prototype, "errors", void 0); - ValidationErrorsCustomAttribute = __decorate([ - aureliaTemplating.customAttribute('validation-errors') - ], ValidationErrorsCustomAttribute); - return ValidationErrorsCustomAttribute; - }()); - - var ValidationRendererCustomAttribute = /** @class */ (function () { - function ValidationRendererCustomAttribute() { - } - ValidationRendererCustomAttribute.prototype.created = function (view) { - this.container = view.container; - }; - ValidationRendererCustomAttribute.prototype.bind = function () { - this.controller = this.container.get(ValidationController); - this.renderer = this.container.get(this.value); - this.controller.addRenderer(this.renderer); - }; - ValidationRendererCustomAttribute.prototype.unbind = function () { - this.controller.removeRenderer(this.renderer); - this.controller = null; - this.renderer = null; - }; - ValidationRendererCustomAttribute = __decorate([ - aureliaTemplating.customAttribute('validation-renderer') - ], ValidationRendererCustomAttribute); - return ValidationRendererCustomAttribute; - }()); - - /** - * Sets, unsets and retrieves rules on an object or constructor function. - */ - var Rules = /** @class */ (function () { - function Rules() { - } - /** - * Applies the rules to a target. - */ - Rules.set = function (target, rules) { - if (target instanceof Function) { - target = target.prototype; - } - Object.defineProperty(target, Rules.key, { enumerable: false, configurable: false, writable: true, value: rules }); - }; - /** - * Removes rules from a target. - */ - Rules.unset = function (target) { - if (target instanceof Function) { - target = target.prototype; - } - target[Rules.key] = null; - }; - /** - * Retrieves the target's rules. - */ - Rules.get = function (target) { - return target[Rules.key] || null; - }; - /** - * The name of the property that stores the rules. - */ - Rules.key = '__rules__'; - return Rules; - }()); - - // tslint:disable:no-empty - var ExpressionVisitor = /** @class */ (function () { - function ExpressionVisitor() { - } - ExpressionVisitor.prototype.visitChain = function (chain) { - this.visitArgs(chain.expressions); - }; - ExpressionVisitor.prototype.visitBindingBehavior = function (behavior) { - behavior.expression.accept(this); - this.visitArgs(behavior.args); - }; - ExpressionVisitor.prototype.visitValueConverter = function (converter) { - converter.expression.accept(this); - this.visitArgs(converter.args); - }; - ExpressionVisitor.prototype.visitAssign = function (assign) { - assign.target.accept(this); - assign.value.accept(this); - }; - ExpressionVisitor.prototype.visitConditional = function (conditional) { - conditional.condition.accept(this); - conditional.yes.accept(this); - conditional.no.accept(this); - }; - ExpressionVisitor.prototype.visitAccessThis = function (access) { - access.ancestor = access.ancestor; - }; - ExpressionVisitor.prototype.visitAccessScope = function (access) { - access.name = access.name; - }; - ExpressionVisitor.prototype.visitAccessMember = function (access) { - access.object.accept(this); - }; - ExpressionVisitor.prototype.visitAccessKeyed = function (access) { - access.object.accept(this); - access.key.accept(this); - }; - ExpressionVisitor.prototype.visitCallScope = function (call) { - this.visitArgs(call.args); - }; - ExpressionVisitor.prototype.visitCallFunction = function (call) { - call.func.accept(this); - this.visitArgs(call.args); - }; - ExpressionVisitor.prototype.visitCallMember = function (call) { - call.object.accept(this); - this.visitArgs(call.args); - }; - ExpressionVisitor.prototype.visitPrefix = function (prefix) { - prefix.expression.accept(this); - }; - ExpressionVisitor.prototype.visitBinary = function (binary) { - binary.left.accept(this); - binary.right.accept(this); - }; - ExpressionVisitor.prototype.visitLiteralPrimitive = function (literal) { - literal.value = literal.value; - }; - ExpressionVisitor.prototype.visitLiteralArray = function (literal) { - this.visitArgs(literal.elements); - }; - ExpressionVisitor.prototype.visitLiteralObject = function (literal) { - this.visitArgs(literal.values); - }; - ExpressionVisitor.prototype.visitLiteralString = function (literal) { - literal.value = literal.value; - }; - ExpressionVisitor.prototype.visitArgs = function (args) { - for (var i = 0; i < args.length; i++) { - args[i].accept(this); - } - }; - return ExpressionVisitor; - }()); - - var ValidationMessageParser = /** @class */ (function () { - function ValidationMessageParser(bindinqLanguage) { - this.bindinqLanguage = bindinqLanguage; - this.emptyStringExpression = new aureliaBinding.LiteralString(''); - this.nullExpression = new aureliaBinding.LiteralPrimitive(null); - this.undefinedExpression = new aureliaBinding.LiteralPrimitive(undefined); - this.cache = {}; - } - ValidationMessageParser.prototype.parse = function (message) { - if (this.cache[message] !== undefined) { - return this.cache[message]; - } - var parts = this.bindinqLanguage.parseInterpolation(null, message); - if (parts === null) { - return new aureliaBinding.LiteralString(message); - } - var expression = new aureliaBinding.LiteralString(parts[0]); - for (var i = 1; i < parts.length; i += 2) { - expression = new aureliaBinding.Binary('+', expression, new aureliaBinding.Binary('+', this.coalesce(parts[i]), new aureliaBinding.LiteralString(parts[i + 1]))); - } - MessageExpressionValidator.validate(expression, message); - this.cache[message] = expression; - return expression; - }; - ValidationMessageParser.prototype.coalesce = function (part) { - // part === null || part === undefined ? '' : part - return new aureliaBinding.Conditional(new aureliaBinding.Binary('||', new aureliaBinding.Binary('===', part, this.nullExpression), new aureliaBinding.Binary('===', part, this.undefinedExpression)), this.emptyStringExpression, new aureliaBinding.CallMember(part, 'toString', [])); - }; - ValidationMessageParser.inject = [aureliaTemplating.BindingLanguage]; - return ValidationMessageParser; - }()); - var MessageExpressionValidator = /** @class */ (function (_super) { - __extends(MessageExpressionValidator, _super); - function MessageExpressionValidator(originalMessage) { - var _this = _super.call(this) || this; - _this.originalMessage = originalMessage; - return _this; - } - MessageExpressionValidator.validate = function (expression, originalMessage) { - var visitor = new MessageExpressionValidator(originalMessage); - expression.accept(visitor); - }; - MessageExpressionValidator.prototype.visitAccessScope = function (access) { - if (access.ancestor !== 0) { - throw new Error('$parent is not permitted in validation message expressions.'); - } - if (['displayName', 'propertyName', 'value', 'object', 'config', 'getDisplayName'].indexOf(access.name) !== -1) { - LogManager.getLogger('aurelia-validation') - // tslint:disable-next-line:max-line-length - .warn("Did you mean to use \"$" + access.name + "\" instead of \"" + access.name + "\" in this validation message template: \"" + this.originalMessage + "\"?"); - } - }; - return MessageExpressionValidator; - }(ExpressionVisitor)); - - /** - * Dictionary of validation messages. [messageKey]: messageExpression - */ - var validationMessages = { - /** - * The default validation message. Used with rules that have no standard message. - */ - default: "${$displayName} is invalid.", - required: "${$displayName} is required.", - matches: "${$displayName} is not correctly formatted.", - email: "${$displayName} is not a valid email.", - minLength: "${$displayName} must be at least ${$config.length} character${$config.length === 1 ? '' : 's'}.", - maxLength: "${$displayName} cannot be longer than ${$config.length} character${$config.length === 1 ? '' : 's'}.", - minItems: "${$displayName} must contain at least ${$config.count} item${$config.count === 1 ? '' : 's'}.", - maxItems: "${$displayName} cannot contain more than ${$config.count} item${$config.count === 1 ? '' : 's'}.", - equals: "${$displayName} must be ${$config.expectedValue}.", - }; - /** - * Retrieves validation messages and property display names. - */ - var ValidationMessageProvider = /** @class */ (function () { - function ValidationMessageProvider(parser) { - this.parser = parser; - } - /** - * Returns a message binding expression that corresponds to the key. - * @param key The message key. - */ - ValidationMessageProvider.prototype.getMessage = function (key) { - var message; - if (key in validationMessages) { - message = validationMessages[key]; - } - else { - message = validationMessages['default']; - } - return this.parser.parse(message); - }; - /** - * Formulates a property display name using the property name and the configured - * displayName (if provided). - * Override this with your own custom logic. - * @param propertyName The property name. - */ - ValidationMessageProvider.prototype.getDisplayName = function (propertyName, displayName) { - if (displayName !== null && displayName !== undefined) { - return (displayName instanceof Function) ? displayName() : displayName; - } - // split on upper-case letters. - var words = propertyName.toString().split(/(?=[A-Z])/).join(' '); - // capitalize first letter. - return words.charAt(0).toUpperCase() + words.slice(1); - }; - ValidationMessageProvider.inject = [ValidationMessageParser]; - return ValidationMessageProvider; - }()); - - /** - * Validates. - * Responsible for validating objects and properties. - */ - var StandardValidator = /** @class */ (function (_super) { - __extends(StandardValidator, _super); - function StandardValidator(messageProvider, resources) { - var _this = _super.call(this) || this; - _this.messageProvider = messageProvider; - _this.lookupFunctions = resources.lookupFunctions; - _this.getDisplayName = messageProvider.getDisplayName.bind(messageProvider); - return _this; - } - /** - * Validates the specified property. - * @param object The object to validate. - * @param propertyName The name of the property to validate. - * @param rules Optional. If unspecified, the rules will be looked up using the metadata - * for the object created by ValidationRules....on(class/object) - */ - StandardValidator.prototype.validateProperty = function (object, propertyName, rules) { - return this.validate(object, propertyName, rules || null); - }; - /** - * Validates all rules for specified object and it's properties. - * @param object The object to validate. - * @param rules Optional. If unspecified, the rules will be looked up using the metadata - * for the object created by ValidationRules....on(class/object) - */ - StandardValidator.prototype.validateObject = function (object, rules) { - return this.validate(object, null, rules || null); - }; - /** - * Determines whether a rule exists in a set of rules. - * @param rules The rules to search. - * @parem rule The rule to find. - */ - StandardValidator.prototype.ruleExists = function (rules, rule) { - var i = rules.length; - while (i--) { - if (rules[i].indexOf(rule) !== -1) { - return true; - } - } - return false; - }; - StandardValidator.prototype.getMessage = function (rule, object, value) { - var expression = rule.message || this.messageProvider.getMessage(rule.messageKey); - // tslint:disable-next-line:prefer-const - var _a = rule.property, propertyName = _a.name, displayName = _a.displayName; - if (propertyName !== null) { - displayName = this.messageProvider.getDisplayName(propertyName, displayName); - } - var overrideContext = { - $displayName: displayName, - $propertyName: propertyName, - $value: value, - $object: object, - $config: rule.config, - // returns the name of a given property, given just the property name (irrespective of the property's displayName) - // split on capital letters, first letter ensured to be capitalized - $getDisplayName: this.getDisplayName - }; - return expression.evaluate({ bindingContext: object, overrideContext: overrideContext }, this.lookupFunctions); - }; - StandardValidator.prototype.validateRuleSequence = function (object, propertyName, ruleSequence, sequence, results) { - var _this = this; - // are we validating all properties or a single property? - var validateAllProperties = propertyName === null || propertyName === undefined; - var rules = ruleSequence[sequence]; - var allValid = true; - // validate each rule. - var promises = []; - var _loop_1 = function (i) { - var rule = rules[i]; - // is the rule related to the property we're validating. - // tslint:disable-next-line:triple-equals | Use loose equality for property keys - if (!validateAllProperties && rule.property.name != propertyName) { - return "continue"; - } - // is this a conditional rule? is the condition met? - if (rule.when && !rule.when(object)) { - return "continue"; - } - // validate. - var value = rule.property.name === null ? object : object[rule.property.name]; - var promiseOrBoolean = rule.condition(value, object); - if (!(promiseOrBoolean instanceof Promise)) { - promiseOrBoolean = Promise.resolve(promiseOrBoolean); - } - promises.push(promiseOrBoolean.then(function (valid) { - var message = valid ? null : _this.getMessage(rule, object, value); - results.push(new ValidateResult(rule, object, rule.property.name, valid, message)); - allValid = allValid && valid; - return valid; - })); - }; - for (var i = 0; i < rules.length; i++) { - _loop_1(i); - } - return Promise.all(promises) - .then(function () { - sequence++; - if (allValid && sequence < ruleSequence.length) { - return _this.validateRuleSequence(object, propertyName, ruleSequence, sequence, results); - } - return results; - }); - }; - StandardValidator.prototype.validate = function (object, propertyName, rules) { - // rules specified? - if (!rules) { - // no. attempt to locate the rules. - rules = Rules.get(object); - } - // any rules? - if (!rules || rules.length === 0) { - return Promise.resolve([]); - } - return this.validateRuleSequence(object, propertyName, rules, 0, []); - }; - StandardValidator.inject = [ValidationMessageProvider, aureliaTemplating.ViewResources]; - return StandardValidator; - }(Validator)); - - /** - * Part of the fluent rule API. Enables customizing property rules. - */ - var FluentRuleCustomizer = /** @class */ (function () { - function FluentRuleCustomizer(property, condition, config, fluentEnsure, fluentRules, parsers) { - if (config === void 0) { config = {}; } - this.fluentEnsure = fluentEnsure; - this.fluentRules = fluentRules; - this.parsers = parsers; - this.rule = { - property: property, - condition: condition, - config: config, - when: null, - messageKey: 'default', - message: null, - sequence: fluentRules.sequence - }; - this.fluentEnsure._addRule(this.rule); - } - /** - * Validate subsequent rules after previously declared rules have - * been validated successfully. Use to postpone validation of costly - * rules until less expensive rules pass validation. - */ - FluentRuleCustomizer.prototype.then = function () { - this.fluentRules.sequence++; - return this; - }; - /** - * Specifies the key to use when looking up the rule's validation message. - */ - FluentRuleCustomizer.prototype.withMessageKey = function (key) { - this.rule.messageKey = key; - this.rule.message = null; - return this; - }; - /** - * Specifies rule's validation message. - */ - FluentRuleCustomizer.prototype.withMessage = function (message) { - this.rule.messageKey = 'custom'; - this.rule.message = this.parsers.message.parse(message); - return this; - }; - /** - * Specifies a condition that must be met before attempting to validate the rule. - * @param condition A function that accepts the object as a parameter and returns true - * or false whether the rule should be evaluated. - */ - FluentRuleCustomizer.prototype.when = function (condition) { - this.rule.when = condition; - return this; - }; - /** - * Tags the rule instance, enabling the rule to be found easily - * using ValidationRules.taggedRules(rules, tag) - */ - FluentRuleCustomizer.prototype.tag = function (tag) { - this.rule.tag = tag; - return this; - }; - ///// FluentEnsure APIs ///// - /** - * Target a property with validation rules. - * @param property The property to target. Can be the property name or a property accessor function. - */ - FluentRuleCustomizer.prototype.ensure = function (subject) { - return this.fluentEnsure.ensure(subject); - }; - /** - * Targets an object with validation rules. - */ - FluentRuleCustomizer.prototype.ensureObject = function () { - return this.fluentEnsure.ensureObject(); - }; - Object.defineProperty(FluentRuleCustomizer.prototype, "rules", { - /** - * Rules that have been defined using the fluent API. - */ - get: function () { - return this.fluentEnsure.rules; - }, - enumerable: true, - configurable: true - }); - /** - * Applies the rules to a class or object, making them discoverable by the StandardValidator. - * @param target A class or object. - */ - FluentRuleCustomizer.prototype.on = function (target) { - return this.fluentEnsure.on(target); - }; - ///////// FluentRules APIs ///////// - /** - * Applies an ad-hoc rule function to the ensured property or object. - * @param condition The function to validate the rule. - * Will be called with two arguments, the property value and the object. - * Should return a boolean or a Promise that resolves to a boolean. - */ - FluentRuleCustomizer.prototype.satisfies = function (condition, config) { - return this.fluentRules.satisfies(condition, config); - }; - /** - * Applies a rule by name. - * @param name The name of the custom or standard rule. - * @param args The rule's arguments. - */ - FluentRuleCustomizer.prototype.satisfiesRule = function (name) { - var args = []; - for (var _i = 1; _i < arguments.length; _i++) { - args[_i - 1] = arguments[_i]; - } - var _a; - return (_a = this.fluentRules).satisfiesRule.apply(_a, [name].concat(args)); - }; - /** - * Applies the "required" rule to the property. - * The value cannot be null, undefined or whitespace. - */ - FluentRuleCustomizer.prototype.required = function () { - return this.fluentRules.required(); - }; - /** - * Applies the "matches" rule to the property. - * Value must match the specified regular expression. - * null, undefined and empty-string values are considered valid. - */ - FluentRuleCustomizer.prototype.matches = function (regex) { - return this.fluentRules.matches(regex); - }; - /** - * Applies the "email" rule to the property. - * null, undefined and empty-string values are considered valid. - */ - FluentRuleCustomizer.prototype.email = function () { - return this.fluentRules.email(); - }; - /** - * Applies the "minLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - FluentRuleCustomizer.prototype.minLength = function (length) { - return this.fluentRules.minLength(length); - }; - /** - * Applies the "maxLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - FluentRuleCustomizer.prototype.maxLength = function (length) { - return this.fluentRules.maxLength(length); - }; - /** - * Applies the "minItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - FluentRuleCustomizer.prototype.minItems = function (count) { - return this.fluentRules.minItems(count); - }; - /** - * Applies the "maxItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - FluentRuleCustomizer.prototype.maxItems = function (count) { - return this.fluentRules.maxItems(count); - }; - /** - * Applies the "equals" validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - FluentRuleCustomizer.prototype.equals = function (expectedValue) { - return this.fluentRules.equals(expectedValue); - }; - return FluentRuleCustomizer; - }()); - /** - * Part of the fluent rule API. Enables applying rules to properties and objects. - */ - var FluentRules = /** @class */ (function () { - function FluentRules(fluentEnsure, parsers, property) { - this.fluentEnsure = fluentEnsure; - this.parsers = parsers; - this.property = property; - /** - * Current rule sequence number. Used to postpone evaluation of rules until rules - * with lower sequence number have successfully validated. The "then" fluent API method - * manages this property, there's usually no need to set it directly. - */ - this.sequence = 0; - } - /** - * Sets the display name of the ensured property. - */ - FluentRules.prototype.displayName = function (name) { - this.property.displayName = name; - return this; - }; - /** - * Applies an ad-hoc rule function to the ensured property or object. - * @param condition The function to validate the rule. - * Will be called with two arguments, the property value and the object. - * Should return a boolean or a Promise that resolves to a boolean. - */ - FluentRules.prototype.satisfies = function (condition, config) { - return new FluentRuleCustomizer(this.property, condition, config, this.fluentEnsure, this, this.parsers); - }; - /** - * Applies a rule by name. - * @param name The name of the custom or standard rule. - * @param args The rule's arguments. - */ - FluentRules.prototype.satisfiesRule = function (name) { - var _this = this; - var args = []; - for (var _i = 1; _i < arguments.length; _i++) { - args[_i - 1] = arguments[_i]; - } - var rule = FluentRules.customRules[name]; - if (!rule) { - // standard rule? - rule = this[name]; - if (rule instanceof Function) { - return rule.call.apply(rule, [this].concat(args)); - } - throw new Error("Rule with name \"" + name + "\" does not exist."); - } - var config = rule.argsToConfig ? rule.argsToConfig.apply(rule, args) : undefined; - return this.satisfies(function (value, obj) { - var _a; - return (_a = rule.condition).call.apply(_a, [_this, value, obj].concat(args)); - }, config) - .withMessageKey(name); - }; - /** - * Applies the "required" rule to the property. - * The value cannot be null, undefined or whitespace. - */ - FluentRules.prototype.required = function () { - return this.satisfies(function (value) { - return value !== null - && value !== undefined - && !(isString(value) && !/\S/.test(value)); - }).withMessageKey('required'); - }; - /** - * Applies the "matches" rule to the property. - * Value must match the specified regular expression. - * null, undefined and empty-string values are considered valid. - */ - FluentRules.prototype.matches = function (regex) { - return this.satisfies(function (value) { return value === null || value === undefined || value.length === 0 || regex.test(value); }) - .withMessageKey('matches'); - }; - /** - * Applies the "email" rule to the property. - * null, undefined and empty-string values are considered valid. - */ - FluentRules.prototype.email = function () { - // regex from https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address - /* tslint:disable:max-line-length */ - return this.matches(/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/) - /* tslint:enable:max-line-length */ - .withMessageKey('email'); - }; - /** - * Applies the "minLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - FluentRules.prototype.minLength = function (length) { - return this.satisfies(function (value) { return value === null || value === undefined || value.length === 0 || value.length >= length; }, { length: length }) - .withMessageKey('minLength'); - }; - /** - * Applies the "maxLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - FluentRules.prototype.maxLength = function (length) { - return this.satisfies(function (value) { return value === null || value === undefined || value.length === 0 || value.length <= length; }, { length: length }) - .withMessageKey('maxLength'); - }; - /** - * Applies the "minItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - FluentRules.prototype.minItems = function (count) { - return this.satisfies(function (value) { return value === null || value === undefined || value.length >= count; }, { count: count }) - .withMessageKey('minItems'); - }; - /** - * Applies the "maxItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - FluentRules.prototype.maxItems = function (count) { - return this.satisfies(function (value) { return value === null || value === undefined || value.length <= count; }, { count: count }) - .withMessageKey('maxItems'); - }; - /** - * Applies the "equals" validation rule to the property. - * null and undefined values are considered valid. - */ - FluentRules.prototype.equals = function (expectedValue) { - return this.satisfies(function (value) { return value === null || value === undefined || value === '' || value === expectedValue; }, { expectedValue: expectedValue }) - .withMessageKey('equals'); - }; - FluentRules.customRules = {}; - return FluentRules; - }()); - /** - * Part of the fluent rule API. Enables targeting properties and objects with rules. - */ - var FluentEnsure = /** @class */ (function () { - function FluentEnsure(parsers) { - this.parsers = parsers; - /** - * Rules that have been defined using the fluent API. - */ - this.rules = []; - } - /** - * Target a property with validation rules. - * @param property The property to target. Can be the property name or a property accessor - * function. - */ - FluentEnsure.prototype.ensure = function (property) { - this.assertInitialized(); - var name = this.parsers.property.parse(property); - var fluentRules = new FluentRules(this, this.parsers, { name: name, displayName: null }); - return this.mergeRules(fluentRules, name); - }; - /** - * Targets an object with validation rules. - */ - FluentEnsure.prototype.ensureObject = function () { - this.assertInitialized(); - var fluentRules = new FluentRules(this, this.parsers, { name: null, displayName: null }); - return this.mergeRules(fluentRules, null); - }; - /** - * Applies the rules to a class or object, making them discoverable by the StandardValidator. - * @param target A class or object. - */ - FluentEnsure.prototype.on = function (target) { - Rules.set(target, this.rules); - return this; - }; - /** - * Adds a rule definition to the sequenced ruleset. - * @internal - */ - FluentEnsure.prototype._addRule = function (rule) { - while (this.rules.length < rule.sequence + 1) { - this.rules.push([]); - } - this.rules[rule.sequence].push(rule); - }; - FluentEnsure.prototype.assertInitialized = function () { - if (this.parsers) { - return; - } - throw new Error("Did you forget to add \".plugin('aurelia-validation')\" to your main.js?"); - }; - FluentEnsure.prototype.mergeRules = function (fluentRules, propertyName) { - // tslint:disable-next-line:triple-equals | Use loose equality for property keys - var existingRules = this.rules.find(function (r) { return r.length > 0 && r[0].property.name == propertyName; }); - if (existingRules) { - var rule = existingRules[existingRules.length - 1]; - fluentRules.sequence = rule.sequence; - if (rule.property.displayName !== null) { - fluentRules = fluentRules.displayName(rule.property.displayName); - } - } - return fluentRules; - }; - return FluentEnsure; - }()); - /** - * Fluent rule definition API. - */ - var ValidationRules = /** @class */ (function () { - function ValidationRules() { - } - ValidationRules.initialize = function (messageParser, propertyParser) { - this.parsers = { - message: messageParser, - property: propertyParser - }; - }; - /** - * Target a property with validation rules. - * @param property The property to target. Can be the property name or a property accessor function. - */ - ValidationRules.ensure = function (property) { - return new FluentEnsure(ValidationRules.parsers).ensure(property); - }; - /** - * Targets an object with validation rules. - */ - ValidationRules.ensureObject = function () { - return new FluentEnsure(ValidationRules.parsers).ensureObject(); - }; - /** - * Defines a custom rule. - * @param name The name of the custom rule. Also serves as the message key. - * @param condition The rule function. - * @param message The message expression - * @param argsToConfig A function that maps the rule's arguments to a "config" - * object that can be used when evaluating the message expression. - */ - ValidationRules.customRule = function (name, condition, message, argsToConfig) { - validationMessages[name] = message; - FluentRules.customRules[name] = { condition: condition, argsToConfig: argsToConfig }; - }; - /** - * Returns rules with the matching tag. - * @param rules The rules to search. - * @param tag The tag to search for. - */ - ValidationRules.taggedRules = function (rules, tag) { - return rules.map(function (x) { return x.filter(function (r) { return r.tag === tag; }); }); - }; - /** - * Returns rules that have no tag. - * @param rules The rules to search. - */ - ValidationRules.untaggedRules = function (rules) { - return rules.map(function (x) { return x.filter(function (r) { return r.tag === undefined; }); }); - }; - /** - * Removes the rules from a class or object. - * @param target A class or object. - */ - ValidationRules.off = function (target) { - Rules.unset(target); - }; - return ValidationRules; - }()); - - // Exports - /** - * Aurelia Validation Configuration API - */ - var AureliaValidationConfiguration = /** @class */ (function () { - function AureliaValidationConfiguration() { - this.validatorType = StandardValidator; - } - /** - * Use a custom Validator implementation. - */ - AureliaValidationConfiguration.prototype.customValidator = function (type) { - this.validatorType = type; - }; - /** - * Applies the configuration. - */ - AureliaValidationConfiguration.prototype.apply = function (container) { - var validator = container.get(this.validatorType); - container.registerInstance(Validator, validator); - }; - return AureliaValidationConfiguration; - }()); - /** - * Configures the plugin. - */ - function configure( - // tslint:disable-next-line:ban-types - frameworkConfig, callback) { - // the fluent rule definition API needs the parser to translate messages - // to interpolation expressions. - var messageParser = frameworkConfig.container.get(ValidationMessageParser); - var propertyParser = frameworkConfig.container.get(PropertyAccessorParser); - ValidationRules.initialize(messageParser, propertyParser); - // configure... - var config = new AureliaValidationConfiguration(); - if (callback instanceof Function) { - callback(config); - } - config.apply(frameworkConfig.container); - // globalize the behaviors. - if (frameworkConfig.globalResources) { - frameworkConfig.globalResources(ValidateBindingBehavior, ValidateManuallyBindingBehavior, ValidateOnBlurBindingBehavior, ValidateOnChangeBindingBehavior, ValidateOnChangeOrBlurBindingBehavior, ValidationErrorsCustomAttribute, ValidationRendererCustomAttribute); - } - } - - exports.AureliaValidationConfiguration = AureliaValidationConfiguration; - exports.configure = configure; - exports.getTargetDOMElement = getTargetDOMElement; - exports.getPropertyInfo = getPropertyInfo; - exports.PropertyAccessorParser = PropertyAccessorParser; - exports.getAccessorExpression = getAccessorExpression; - exports.ValidateBindingBehavior = ValidateBindingBehavior; - exports.ValidateManuallyBindingBehavior = ValidateManuallyBindingBehavior; - exports.ValidateOnBlurBindingBehavior = ValidateOnBlurBindingBehavior; - exports.ValidateOnChangeBindingBehavior = ValidateOnChangeBindingBehavior; - exports.ValidateOnChangeOrBlurBindingBehavior = ValidateOnChangeOrBlurBindingBehavior; - exports.ValidateEvent = ValidateEvent; - exports.ValidateResult = ValidateResult; - exports.ValidationController = ValidationController; - exports.ValidationControllerFactory = ValidationControllerFactory; - exports.ValidationErrorsCustomAttribute = ValidationErrorsCustomAttribute; - exports.ValidationRendererCustomAttribute = ValidationRendererCustomAttribute; - exports.Validator = Validator; - exports.Rules = Rules; - exports.StandardValidator = StandardValidator; - exports.validationMessages = validationMessages; - exports.ValidationMessageProvider = ValidationMessageProvider; - exports.ValidationMessageParser = ValidationMessageParser; - exports.MessageExpressionValidator = MessageExpressionValidator; - exports.FluentRuleCustomizer = FluentRuleCustomizer; - exports.FluentRules = FluentRules; - exports.FluentEnsure = FluentEnsure; - exports.ValidationRules = ValidationRules; - - Object.defineProperty(exports, '__esModule', { value: true }); - -}); diff --git a/dist/aurelia-validation.d.ts b/dist/aurelia-validation.d.ts deleted file mode 100644 index 317724d8..00000000 --- a/dist/aurelia-validation.d.ts +++ /dev/null @@ -1,896 +0,0 @@ -import { AccessKeyed, AccessMember, AccessScope, Binary, Binding, BindingBehavior, CallMember, Conditional, Expression, Parser, Scope, ValueConverter } from 'aurelia-binding'; -import { Container, Lazy } from 'aurelia-dependency-injection'; -import { TaskQueue } from 'aurelia-task-queue'; -import { BindingLanguage, ViewResources } from 'aurelia-templating'; - -/** - * The result of validating an individual validation rule. - */ -export declare class ValidateResult { - rule: any; - object: any; - propertyName: string | number | null; - valid: boolean; - message: string | null; - private static nextId; - /** - * A number that uniquely identifies the result instance. - */ - id: number; - /** - * @param rule The rule associated with the result. Validator implementation specific. - * @param object The object that was validated. - * @param propertyName The name of the property that was validated. - * @param error The error, if the result is a validation error. - */ - constructor(rule: any, object: any, propertyName: string | number | null, valid: boolean, message?: string | null); - toString(): string | null; -} -/** - * Instructions for the validation controller's validate method. - */ -export interface ValidateInstruction { - /** - * The object to validate. - */ - object: any; - /** - * The property to validate. Optional. - */ - propertyName?: any; - /** - * The rules to validate. Optional. - */ - rules?: any; -} -/** - * The result of a call to the validation controller's validate method. - */ -export interface ControllerValidateResult { - /** - * Whether validation passed. - */ - valid: boolean; - /** - * The validation result of every rule that was evaluated. - */ - results: ValidateResult[]; - /** - * The instruction passed to the controller's validate method. - */ - instruction?: ValidateInstruction; -} -/** - * Gets the DOM element associated with the data-binding. Most of the time it's - * the binding.target but sometimes binding.target is an aurelia custom element, - * or custom attribute which is a javascript "class" instance, so we need to use - * the controller's container to retrieve the actual DOM element. - */ -export declare function getTargetDOMElement(binding: any, view: any): Element; -/** - * Retrieves the object and property name for the specified expression. - * @param expression The expression - * @param source The scope - */ -export declare function getPropertyInfo(expression: Expression, source: Scope): { - object: object; - propertyName: string; -} | null; -export declare type PropertyAccessor = (object: TObject) => TValue; -export declare class PropertyAccessorParser { - private parser; - static inject: (typeof Parser)[]; - constructor(parser: Parser); - parse(property: string | number | PropertyAccessor): string | number; -} -export declare function getAccessorExpression(fn: string): string; -/** - * Validates objects and properties. - */ -export declare abstract class Validator { - /** - * Validates the specified property. - * @param object The object to validate. - * @param propertyName The name of the property to validate. - * @param rules Optional. If unspecified, the implementation should lookup the rules for the - * specified object. This may not be possible for all implementations of this interface. - */ - abstract validateProperty(object: any, propertyName: string, rules?: any): Promise; - /** - * Validates all rules for specified object and it's properties. - * @param object The object to validate. - * @param rules Optional. If unspecified, the implementation should lookup the rules for the - * specified object. This may not be possible for all implementations of this interface. - */ - abstract validateObject(object: any, rules?: any): Promise; - /** - * Determines whether a rule exists in a set of rules. - * @param rules The rules to search. - * @parem rule The rule to find. - */ - abstract ruleExists(rules: any, rule: any): boolean; -} -/** - * Validation triggers. - */ -export declare enum validateTrigger { - /** - * Manual validation. Use the controller's `validate()` and `reset()` methods - * to validate all bindings. - */ - manual = 0, - /** - * Validate the binding when the binding's target element fires a DOM "blur" event. - */ - blur = 1, - /** - * Validate the binding when it updates the model due to a change in the view. - */ - change = 2, - /** - * Validate the binding when the binding's target element fires a DOM "blur" event and - * when it updates the model due to a change in the view. - */ - changeOrBlur = 3 -} -/** - * A result to render (or unrender) and the associated elements (if any) - */ -export interface ResultInstruction { - /** - * The validation result. - */ - result: ValidateResult; - /** - * The associated elements (if any). - */ - elements: Element[]; -} -/** - * Defines which validation results to render and which validation results to unrender. - */ -export interface RenderInstruction { - /** - * The "kind" of render instruction. Either 'validate' or 'reset'. - */ - kind: 'validate' | 'reset'; - /** - * The results to render. - */ - render: ResultInstruction[]; - /** - * The results to unrender. - */ - unrender: ResultInstruction[]; -} -/** - * Renders validation results. - */ -export interface ValidationRenderer { - /** - * Render the validation results. - * @param instruction The render instruction. Defines which results to render and which - * results to unrender. - */ - render(instruction: RenderInstruction): void; -} -export declare class ValidateEvent { - /** - * The type of validate event. Either "validate" or "reset". - */ - readonly type: 'validate' | 'reset'; - /** - * The controller's current array of errors. For an array containing both - * failed rules and passed rules, use the "results" property. - */ - readonly errors: ValidateResult[]; - /** - * The controller's current array of validate results. This - * includes both passed rules and failed rules. For an array of only failed rules, - * use the "errors" property. - */ - readonly results: ValidateResult[]; - /** - * The instruction passed to the "validate" or "reset" event. Will be null when - * the controller's validate/reset method was called with no instruction argument. - */ - readonly instruction: ValidateInstruction | null; - /** - * In events with type === "validate", this property will contain the result - * of validating the instruction (see "instruction" property). Use the controllerValidateResult - * to access the validate results specific to the call to "validate" - * (as opposed to using the "results" and "errors" properties to access the controller's entire - * set of results/errors). - */ - readonly controllerValidateResult: ControllerValidateResult | null; - constructor( - /** - * The type of validate event. Either "validate" or "reset". - */ - type: 'validate' | 'reset', - /** - * The controller's current array of errors. For an array containing both - * failed rules and passed rules, use the "results" property. - */ - errors: ValidateResult[], - /** - * The controller's current array of validate results. This - * includes both passed rules and failed rules. For an array of only failed rules, - * use the "errors" property. - */ - results: ValidateResult[], - /** - * The instruction passed to the "validate" or "reset" event. Will be null when - * the controller's validate/reset method was called with no instruction argument. - */ - instruction: ValidateInstruction | null, - /** - * In events with type === "validate", this property will contain the result - * of validating the instruction (see "instruction" property). Use the controllerValidateResult - * to access the validate results specific to the call to "validate" - * (as opposed to using the "results" and "errors" properties to access the controller's entire - * set of results/errors). - */ - controllerValidateResult: ControllerValidateResult | null); -} -/** - * Orchestrates validation. - * Manages a set of bindings, renderers and objects. - * Exposes the current list of validation results for binding purposes. - */ -export declare class ValidationController { - private validator; - private propertyParser; - static inject: (typeof PropertyAccessorParser | typeof Validator)[]; - private bindings; - private renderers; - /** - * Validation results that have been rendered by the controller. - */ - private results; - /** - * Validation errors that have been rendered by the controller. - */ - errors: ValidateResult[]; - /** - * Whether the controller is currently validating. - */ - validating: boolean; - private elements; - private objects; - /** - * The trigger that will invoke automatic validation of a property used in a binding. - */ - validateTrigger: validateTrigger; - private finishValidating; - private eventCallbacks; - constructor(validator: Validator, propertyParser: PropertyAccessorParser); - /** - * Subscribe to controller validate and reset events. These events occur when the - * controller's "validate"" and "reset" methods are called. - * @param callback The callback to be invoked when the controller validates or resets. - */ - subscribe(callback: (event: ValidateEvent) => void): { - dispose: () => void; - }; - /** - * Adds an object to the set of objects that should be validated when validate is called. - * @param object The object. - * @param rules Optional. The rules. If rules aren't supplied the Validator implementation will lookup the rules. - */ - addObject(object: any, rules?: any): void; - /** - * Removes an object from the set of objects that should be validated when validate is called. - * @param object The object. - */ - removeObject(object: any): void; - /** - * Adds and renders an error. - */ - addError(message: string, object: TObject, propertyName?: string | PropertyAccessor | null): ValidateResult; - /** - * Removes and unrenders an error. - */ - removeError(result: ValidateResult): void; - /** - * Adds a renderer. - * @param renderer The renderer. - */ - addRenderer(renderer: ValidationRenderer): void; - /** - * Removes a renderer. - * @param renderer The renderer. - */ - removeRenderer(renderer: ValidationRenderer): void; - /** - * Registers a binding with the controller. - * @param binding The binding instance. - * @param target The DOM element. - * @param rules (optional) rules associated with the binding. Validator implementation specific. - */ - registerBinding(binding: Binding, target: Element, rules?: any): void; - /** - * Unregisters a binding with the controller. - * @param binding The binding instance. - */ - unregisterBinding(binding: Binding): void; - /** - * Interprets the instruction and returns a predicate that will identify - * relevant results in the list of rendered validation results. - */ - private getInstructionPredicate; - /** - * Validates and renders results. - * @param instruction Optional. Instructions on what to validate. If undefined, all - * objects and bindings will be validated. - */ - validate(instruction?: ValidateInstruction): Promise; - /** - * Resets any rendered validation results (unrenders). - * @param instruction Optional. Instructions on what to reset. If unspecified all rendered results - * will be unrendered. - */ - reset(instruction?: ValidateInstruction): void; - /** - * Gets the elements associated with an object and propertyName (if any). - */ - private getAssociatedElements; - private processResultDelta; - /** - * Validates the property associated with a binding. - */ - validateBinding(binding: Binding): void; - /** - * Resets the results for a property associated with a binding. - */ - resetBinding(binding: Binding): void; - /** - * Changes the controller's validateTrigger. - * @param newTrigger The new validateTrigger - */ - changeTrigger(newTrigger: validateTrigger): void; - /** - * Revalidates the controller's current set of errors. - */ - revalidateErrors(): void; - private invokeCallbacks; -} -declare abstract class ValidateBindingBehaviorBase { - private taskQueue; - constructor(taskQueue: TaskQueue); - protected abstract getValidateTrigger(controller: ValidationController): validateTrigger; - bind(binding: any, source: any, rulesOrController?: ValidationController | any, rules?: any): void; - unbind(binding: any): void; -} -/** - * Binding behavior. Indicates the bound property should be validated - * when the validate trigger specified by the associated controller's - * validateTrigger property occurs. - */ -export declare class ValidateBindingBehavior extends ValidateBindingBehaviorBase { - static inject: (typeof TaskQueue)[]; - getValidateTrigger(controller: ValidationController): validateTrigger; -} -/** - * Binding behavior. Indicates the bound property will be validated - * manually, by calling controller.validate(). No automatic validation - * triggered by data-entry or blur will occur. - */ -export declare class ValidateManuallyBindingBehavior extends ValidateBindingBehaviorBase { - static inject: (typeof TaskQueue)[]; - getValidateTrigger(): validateTrigger; -} -/** - * Binding behavior. Indicates the bound property should be validated - * when the associated element blurs. - */ -export declare class ValidateOnBlurBindingBehavior extends ValidateBindingBehaviorBase { - static inject: (typeof TaskQueue)[]; - getValidateTrigger(): validateTrigger; -} -/** - * Binding behavior. Indicates the bound property should be validated - * when the associated element is changed by the user, causing a change - * to the model. - */ -export declare class ValidateOnChangeBindingBehavior extends ValidateBindingBehaviorBase { - static inject: (typeof TaskQueue)[]; - getValidateTrigger(): validateTrigger; -} -/** - * Binding behavior. Indicates the bound property should be validated - * when the associated element blurs or is changed by the user, causing - * a change to the model. - */ -export declare class ValidateOnChangeOrBlurBindingBehavior extends ValidateBindingBehaviorBase { - static inject: (typeof TaskQueue)[]; - getValidateTrigger(): validateTrigger; -} -/** - * Creates ValidationController instances. - */ -export declare class ValidationControllerFactory { - private container; - static get(container: Container): ValidationControllerFactory; - constructor(container: Container); - /** - * Creates a new controller instance. - */ - create(validator?: Validator): ValidationController; - /** - * Creates a new controller and registers it in the current element's container so that it's - * available to the validate binding behavior and renderers. - */ - createForCurrentScope(validator?: Validator): ValidationController; -} -export interface RenderedError { - error: ValidateResult; - targets: Element[]; -} -export declare class ValidationErrorsCustomAttribute implements ValidationRenderer { - private boundaryElement; - private controllerAccessor; - static inject(): ({ - new (): Element; - prototype: Element; - } | Lazy)[]; - controller: ValidationController | null; - errors: RenderedError[]; - private errorsInternal; - constructor(boundaryElement: Element, controllerAccessor: () => ValidationController); - sort(): void; - interestingElements(elements: Element[]): Element[]; - render(instruction: RenderInstruction): void; - bind(): void; - unbind(): void; -} -export declare class ValidationRendererCustomAttribute { - private container; - private controller; - private value; - private renderer; - created(view: any): void; - bind(): void; - unbind(): void; -} -export declare type ValidationDisplayNameAccessor = () => string; -/** - * Information related to a property that is the subject of validation. - */ -export interface RuleProperty { - /** - * The property name. null indicates the rule targets the object itself. - */ - name: string | number | null; - /** - * The displayName of the property (or object). - */ - displayName: string | ValidationDisplayNameAccessor | null; -} -/** - * A rule definition. Associations a rule with a property or object. - */ -export interface Rule { - property: RuleProperty; - condition: (value: TValue, object?: TObject) => boolean | Promise; - config: object; - when: ((object: TObject) => boolean) | null; - messageKey: string; - message: Expression | null; - sequence: number; - tag?: string; -} -/** - * Sets, unsets and retrieves rules on an object or constructor function. - */ -export declare class Rules { - /** - * The name of the property that stores the rules. - */ - private static key; - /** - * Applies the rules to a target. - */ - static set(target: any, rules: Rule[][]): void; - /** - * Removes rules from a target. - */ - static unset(target: any): void; - /** - * Retrieves the target's rules. - */ - static get(target: any): Rule[][] | null; -} -export declare type Chain = any; -export declare type Assign = any; -export declare type AccessThis = any; -export declare type AccessScope = any; -export declare type CallScope = any; -export declare type CallFunction = any; -export declare type PrefixNot = any; -export declare type LiteralPrimitive = any; -export declare type LiteralArray = any; -export declare type LiteralObject = any; -export declare type LiteralString = any; -declare class ExpressionVisitor { - visitChain(chain: Chain): void; - visitBindingBehavior(behavior: BindingBehavior): void; - visitValueConverter(converter: ValueConverter): void; - visitAssign(assign: Assign): void; - visitConditional(conditional: Conditional): void; - visitAccessThis(access: AccessThis): void; - visitAccessScope(access: AccessScope): void; - visitAccessMember(access: AccessMember): void; - visitAccessKeyed(access: AccessKeyed): void; - visitCallScope(call: CallScope): void; - visitCallFunction(call: CallFunction): void; - visitCallMember(call: CallMember): void; - visitPrefix(prefix: PrefixNot): void; - visitBinary(binary: Binary): void; - visitLiteralPrimitive(literal: LiteralPrimitive): void; - visitLiteralArray(literal: LiteralArray): void; - visitLiteralObject(literal: LiteralObject): void; - visitLiteralString(literal: LiteralString): void; - private visitArgs; -} -export declare class ValidationMessageParser { - private bindinqLanguage; - static inject: (typeof BindingLanguage)[]; - private emptyStringExpression; - private nullExpression; - private undefinedExpression; - private cache; - constructor(bindinqLanguage: BindingLanguage); - parse(message: string): Expression; - private coalesce; -} -export declare class MessageExpressionValidator extends ExpressionVisitor { - private originalMessage; - static validate(expression: Expression, originalMessage: string): void; - constructor(originalMessage: string); - visitAccessScope(access: AccessScope): void; -} -export interface ValidationMessages { - [key: string]: string; -} -/** - * Dictionary of validation messages. [messageKey]: messageExpression - */ -export declare const validationMessages: ValidationMessages; -/** - * Retrieves validation messages and property display names. - */ -export declare class ValidationMessageProvider { - parser: ValidationMessageParser; - static inject: (typeof ValidationMessageParser)[]; - constructor(parser: ValidationMessageParser); - /** - * Returns a message binding expression that corresponds to the key. - * @param key The message key. - */ - getMessage(key: string): Expression; - /** - * Formulates a property display name using the property name and the configured - * displayName (if provided). - * Override this with your own custom logic. - * @param propertyName The property name. - */ - getDisplayName(propertyName: string | number, displayName?: string | null | (() => string)): string; -} -/** - * Validates. - * Responsible for validating objects and properties. - */ -export declare class StandardValidator extends Validator { - static inject: (typeof ViewResources | typeof ValidationMessageProvider)[]; - private messageProvider; - private lookupFunctions; - private getDisplayName; - constructor(messageProvider: ValidationMessageProvider, resources: ViewResources); - /** - * Validates the specified property. - * @param object The object to validate. - * @param propertyName The name of the property to validate. - * @param rules Optional. If unspecified, the rules will be looked up using the metadata - * for the object created by ValidationRules....on(class/object) - */ - validateProperty(object: any, propertyName: string | number, rules?: any): Promise; - /** - * Validates all rules for specified object and it's properties. - * @param object The object to validate. - * @param rules Optional. If unspecified, the rules will be looked up using the metadata - * for the object created by ValidationRules....on(class/object) - */ - validateObject(object: any, rules?: any): Promise; - /** - * Determines whether a rule exists in a set of rules. - * @param rules The rules to search. - * @parem rule The rule to find. - */ - ruleExists(rules: Rule[][], rule: Rule): boolean; - private getMessage; - private validateRuleSequence; - private validate; -} -/** - * Part of the fluent rule API. Enables customizing property rules. - */ -export declare class FluentRuleCustomizer { - private fluentEnsure; - private fluentRules; - private parsers; - private rule; - constructor(property: RuleProperty, condition: (value: TValue, object?: TObject) => boolean | Promise, config: object | undefined, fluentEnsure: FluentEnsure, fluentRules: FluentRules, parsers: Parsers); - /** - * Validate subsequent rules after previously declared rules have - * been validated successfully. Use to postpone validation of costly - * rules until less expensive rules pass validation. - */ - then(): this; - /** - * Specifies the key to use when looking up the rule's validation message. - */ - withMessageKey(key: string): this; - /** - * Specifies rule's validation message. - */ - withMessage(message: string): this; - /** - * Specifies a condition that must be met before attempting to validate the rule. - * @param condition A function that accepts the object as a parameter and returns true - * or false whether the rule should be evaluated. - */ - when(condition: (object: TObject) => boolean): this; - /** - * Tags the rule instance, enabling the rule to be found easily - * using ValidationRules.taggedRules(rules, tag) - */ - tag(tag: string): this; - /** - * Target a property with validation rules. - * @param property The property to target. Can be the property name or a property accessor function. - */ - ensure(subject: string | ((model: TObject) => TValue2)): FluentRules; - /** - * Targets an object with validation rules. - */ - ensureObject(): FluentRules; - /** - * Rules that have been defined using the fluent API. - */ - readonly rules: Rule[][]; - /** - * Applies the rules to a class or object, making them discoverable by the StandardValidator. - * @param target A class or object. - */ - on(target: any): FluentEnsure; - /** - * Applies an ad-hoc rule function to the ensured property or object. - * @param condition The function to validate the rule. - * Will be called with two arguments, the property value and the object. - * Should return a boolean or a Promise that resolves to a boolean. - */ - satisfies(condition: (value: TValue, object: TObject) => boolean | Promise, config?: object): FluentRuleCustomizer; - /** - * Applies a rule by name. - * @param name The name of the custom or standard rule. - * @param args The rule's arguments. - */ - satisfiesRule(name: string, ...args: any[]): FluentRuleCustomizer; - /** - * Applies the "required" rule to the property. - * The value cannot be null, undefined or whitespace. - */ - required(): FluentRuleCustomizer; - /** - * Applies the "matches" rule to the property. - * Value must match the specified regular expression. - * null, undefined and empty-string values are considered valid. - */ - matches(regex: RegExp): FluentRuleCustomizer; - /** - * Applies the "email" rule to the property. - * null, undefined and empty-string values are considered valid. - */ - email(): FluentRuleCustomizer; - /** - * Applies the "minLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - minLength(length: number): FluentRuleCustomizer; - /** - * Applies the "maxLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - maxLength(length: number): FluentRuleCustomizer; - /** - * Applies the "minItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - minItems(count: number): FluentRuleCustomizer; - /** - * Applies the "maxItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - maxItems(count: number): FluentRuleCustomizer; - /** - * Applies the "equals" validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - equals(expectedValue: TValue): FluentRuleCustomizer; -} -/** - * Part of the fluent rule API. Enables applying rules to properties and objects. - */ -export declare class FluentRules { - private fluentEnsure; - private parsers; - private property; - static customRules: { - [name: string]: { - condition: (value: any, object?: any, ...fluentArgs: any[]) => boolean | Promise; - argsToConfig?: (...args: any[]) => any; - }; - }; - /** - * Current rule sequence number. Used to postpone evaluation of rules until rules - * with lower sequence number have successfully validated. The "then" fluent API method - * manages this property, there's usually no need to set it directly. - */ - sequence: number; - constructor(fluentEnsure: FluentEnsure, parsers: Parsers, property: RuleProperty); - /** - * Sets the display name of the ensured property. - */ - displayName(name: string | ValidationDisplayNameAccessor | null): this; - /** - * Applies an ad-hoc rule function to the ensured property or object. - * @param condition The function to validate the rule. - * Will be called with two arguments, the property value and the object. - * Should return a boolean or a Promise that resolves to a boolean. - */ - satisfies(condition: (value: TValue, object?: TObject) => boolean | Promise, config?: object): FluentRuleCustomizer; - /** - * Applies a rule by name. - * @param name The name of the custom or standard rule. - * @param args The rule's arguments. - */ - satisfiesRule(name: string, ...args: any[]): FluentRuleCustomizer; - /** - * Applies the "required" rule to the property. - * The value cannot be null, undefined or whitespace. - */ - required(): FluentRuleCustomizer; - /** - * Applies the "matches" rule to the property. - * Value must match the specified regular expression. - * null, undefined and empty-string values are considered valid. - */ - matches(regex: RegExp): FluentRuleCustomizer; - /** - * Applies the "email" rule to the property. - * null, undefined and empty-string values are considered valid. - */ - email(): FluentRuleCustomizer; - /** - * Applies the "minLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - minLength(length: number): FluentRuleCustomizer; - /** - * Applies the "maxLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - maxLength(length: number): FluentRuleCustomizer; - /** - * Applies the "minItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - minItems(count: number): FluentRuleCustomizer; - /** - * Applies the "maxItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - maxItems(count: number): FluentRuleCustomizer; - /** - * Applies the "equals" validation rule to the property. - * null and undefined values are considered valid. - */ - equals(expectedValue: TValue): FluentRuleCustomizer; -} -/** - * Part of the fluent rule API. Enables targeting properties and objects with rules. - */ -export declare class FluentEnsure { - private parsers; - /** - * Rules that have been defined using the fluent API. - */ - rules: Rule[][]; - constructor(parsers: Parsers); - /** - * Target a property with validation rules. - * @param property The property to target. Can be the property name or a property accessor - * function. - */ - ensure(property: string | number | PropertyAccessor): FluentRules; - /** - * Targets an object with validation rules. - */ - ensureObject(): FluentRules; - /** - * Applies the rules to a class or object, making them discoverable by the StandardValidator. - * @param target A class or object. - */ - on(target: any): this; - private assertInitialized; - private mergeRules; -} -/** - * Fluent rule definition API. - */ -export declare class ValidationRules { - private static parsers; - static initialize(messageParser: ValidationMessageParser, propertyParser: PropertyAccessorParser): void; - /** - * Target a property with validation rules. - * @param property The property to target. Can be the property name or a property accessor function. - */ - static ensure(property: string | number | PropertyAccessor): FluentRules; - /** - * Targets an object with validation rules. - */ - static ensureObject(): FluentRules; - /** - * Defines a custom rule. - * @param name The name of the custom rule. Also serves as the message key. - * @param condition The rule function. - * @param message The message expression - * @param argsToConfig A function that maps the rule's arguments to a "config" - * object that can be used when evaluating the message expression. - */ - static customRule(name: string, condition: (value: any, object?: any, ...args: any[]) => boolean | Promise, message: string, argsToConfig?: (...args: any[]) => any): void; - /** - * Returns rules with the matching tag. - * @param rules The rules to search. - * @param tag The tag to search for. - */ - static taggedRules(rules: Rule[][], tag: string): Rule[][]; - /** - * Returns rules that have no tag. - * @param rules The rules to search. - */ - static untaggedRules(rules: Rule[][]): Rule[][]; - /** - * Removes the rules from a class or object. - * @param target A class or object. - */ - static off(target: any): void; -} -export interface Parsers { - message: ValidationMessageParser; - property: PropertyAccessorParser; -} -/** - * Aurelia Validation Configuration API - */ -export declare class AureliaValidationConfiguration { - private validatorType; - /** - * Use a custom Validator implementation. - */ - customValidator(type: { - new (...args: any[]): Validator; - }): void; - /** - * Applies the configuration. - */ - apply(container: Container): void; -} -/** - * Configures the plugin. - */ -export declare function configure(frameworkConfig: { - container: Container; - globalResources?: (...resources: any[]) => any; -}, callback?: (config: AureliaValidationConfiguration) => void): void; \ No newline at end of file diff --git a/dist/commonjs/aurelia-validation.js b/dist/commonjs/aurelia-validation.js deleted file mode 100644 index d13b7640..00000000 --- a/dist/commonjs/aurelia-validation.js +++ /dev/null @@ -1,1816 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, '__esModule', { value: true }); - -var aureliaPal = require('aurelia-pal'); -var aureliaBinding = require('aurelia-binding'); -var aureliaDependencyInjection = require('aurelia-dependency-injection'); -var aureliaTaskQueue = require('aurelia-task-queue'); -var aureliaTemplating = require('aurelia-templating'); -var LogManager = require('aurelia-logging'); - -/** - * Gets the DOM element associated with the data-binding. Most of the time it's - * the binding.target but sometimes binding.target is an aurelia custom element, - * or custom attribute which is a javascript "class" instance, so we need to use - * the controller's container to retrieve the actual DOM element. - */ -function getTargetDOMElement(binding, view) { - var target = binding.target; - // DOM element - if (target instanceof Element) { - return target; - } - // custom element or custom attribute - // tslint:disable-next-line:prefer-const - for (var i = 0, ii = view.controllers.length; i < ii; i++) { - var controller = view.controllers[i]; - if (controller.viewModel === target) { - var element = controller.container.get(aureliaPal.DOM.Element); - if (element) { - return element; - } - throw new Error("Unable to locate target element for \"" + binding.sourceExpression + "\"."); - } - } - throw new Error("Unable to locate target element for \"" + binding.sourceExpression + "\"."); -} - -function getObject(expression, objectExpression, source) { - var value = objectExpression.evaluate(source, null); - if (value === null || value === undefined || value instanceof Object) { - return value; - } - // tslint:disable-next-line:max-line-length - throw new Error("The '" + objectExpression + "' part of '" + expression + "' evaluates to " + value + " instead of an object, null or undefined."); -} -/** - * Retrieves the object and property name for the specified expression. - * @param expression The expression - * @param source The scope - */ -function getPropertyInfo(expression, source) { - var originalExpression = expression; - while (expression instanceof aureliaBinding.BindingBehavior || expression instanceof aureliaBinding.ValueConverter) { - expression = expression.expression; - } - var object; - var propertyName; - if (expression instanceof aureliaBinding.AccessScope) { - object = aureliaBinding.getContextFor(expression.name, source, expression.ancestor); - propertyName = expression.name; - } - else if (expression instanceof aureliaBinding.AccessMember) { - object = getObject(originalExpression, expression.object, source); - propertyName = expression.name; - } - else if (expression instanceof aureliaBinding.AccessKeyed) { - object = getObject(originalExpression, expression.object, source); - propertyName = expression.key.evaluate(source); - } - else { - throw new Error("Expression '" + originalExpression + "' is not compatible with the validate binding-behavior."); - } - if (object === null || object === undefined) { - return null; - } - return { object: object, propertyName: propertyName }; -} - -function isString(value) { - return Object.prototype.toString.call(value) === '[object String]'; -} -function isNumber(value) { - return Object.prototype.toString.call(value) === '[object Number]'; -} - -var PropertyAccessorParser = /** @class */ (function () { - function PropertyAccessorParser(parser) { - this.parser = parser; - } - PropertyAccessorParser.prototype.parse = function (property) { - if (isString(property) || isNumber(property)) { - return property; - } - var accessorText = getAccessorExpression(property.toString()); - var accessor = this.parser.parse(accessorText); - if (accessor instanceof aureliaBinding.AccessScope - || accessor instanceof aureliaBinding.AccessMember && accessor.object instanceof aureliaBinding.AccessScope) { - return accessor.name; - } - throw new Error("Invalid property expression: \"" + accessor + "\""); - }; - PropertyAccessorParser.inject = [aureliaBinding.Parser]; - return PropertyAccessorParser; -}()); -function getAccessorExpression(fn) { - /* tslint:disable:max-line-length */ - var classic = /^function\s*\([$_\w\d]+\)\s*\{(?:\s*"use strict";)?\s*(?:[$_\w\d.['"\]+;]+)?\s*return\s+[$_\w\d]+\.([$_\w\d]+)\s*;?\s*\}$/; - /* tslint:enable:max-line-length */ - var arrow = /^\(?[$_\w\d]+\)?\s*=>\s*[$_\w\d]+\.([$_\w\d]+)$/; - var match = classic.exec(fn) || arrow.exec(fn); - if (match === null) { - throw new Error("Unable to parse accessor function:\n" + fn); - } - return match[1]; -} - -/*! ***************************************************************************** -Copyright (c) Microsoft Corporation. All rights reserved. -Licensed under the Apache License, Version 2.0 (the "License"); you may not use -this file except in compliance with the License. You may obtain a copy of the -License at http://www.apache.org/licenses/LICENSE-2.0 - -THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED -WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, -MERCHANTABLITY OR NON-INFRINGEMENT. - -See the Apache Version 2.0 License for specific language governing permissions -and limitations under the License. -***************************************************************************** */ -/* global Reflect, Promise */ - -var extendStatics = function(d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); -}; - -function __extends(d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); -} - -function __decorate(decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -} - -/** - * Validation triggers. - */ -(function (validateTrigger) { - /** - * Manual validation. Use the controller's `validate()` and `reset()` methods - * to validate all bindings. - */ - validateTrigger[validateTrigger["manual"] = 0] = "manual"; - /** - * Validate the binding when the binding's target element fires a DOM "blur" event. - */ - validateTrigger[validateTrigger["blur"] = 1] = "blur"; - /** - * Validate the binding when it updates the model due to a change in the view. - */ - validateTrigger[validateTrigger["change"] = 2] = "change"; - /** - * Validate the binding when the binding's target element fires a DOM "blur" event and - * when it updates the model due to a change in the view. - */ - validateTrigger[validateTrigger["changeOrBlur"] = 3] = "changeOrBlur"; -})(exports.validateTrigger || (exports.validateTrigger = {})); - -/** - * Validates objects and properties. - */ -var Validator = /** @class */ (function () { - function Validator() { - } - return Validator; -}()); - -/** - * The result of validating an individual validation rule. - */ -var ValidateResult = /** @class */ (function () { - /** - * @param rule The rule associated with the result. Validator implementation specific. - * @param object The object that was validated. - * @param propertyName The name of the property that was validated. - * @param error The error, if the result is a validation error. - */ - function ValidateResult(rule, object, propertyName, valid, message) { - if (message === void 0) { message = null; } - this.rule = rule; - this.object = object; - this.propertyName = propertyName; - this.valid = valid; - this.message = message; - this.id = ValidateResult.nextId++; - } - ValidateResult.prototype.toString = function () { - return this.valid ? 'Valid.' : this.message; - }; - ValidateResult.nextId = 0; - return ValidateResult; -}()); - -var ValidateEvent = /** @class */ (function () { - function ValidateEvent( - /** - * The type of validate event. Either "validate" or "reset". - */ - type, - /** - * The controller's current array of errors. For an array containing both - * failed rules and passed rules, use the "results" property. - */ - errors, - /** - * The controller's current array of validate results. This - * includes both passed rules and failed rules. For an array of only failed rules, - * use the "errors" property. - */ - results, - /** - * The instruction passed to the "validate" or "reset" event. Will be null when - * the controller's validate/reset method was called with no instruction argument. - */ - instruction, - /** - * In events with type === "validate", this property will contain the result - * of validating the instruction (see "instruction" property). Use the controllerValidateResult - * to access the validate results specific to the call to "validate" - * (as opposed to using the "results" and "errors" properties to access the controller's entire - * set of results/errors). - */ - controllerValidateResult) { - this.type = type; - this.errors = errors; - this.results = results; - this.instruction = instruction; - this.controllerValidateResult = controllerValidateResult; - } - return ValidateEvent; -}()); - -/** - * Orchestrates validation. - * Manages a set of bindings, renderers and objects. - * Exposes the current list of validation results for binding purposes. - */ -var ValidationController = /** @class */ (function () { - function ValidationController(validator, propertyParser) { - this.validator = validator; - this.propertyParser = propertyParser; - // Registered bindings (via the validate binding behavior) - this.bindings = new Map(); - // Renderers that have been added to the controller instance. - this.renderers = []; - /** - * Validation results that have been rendered by the controller. - */ - this.results = []; - /** - * Validation errors that have been rendered by the controller. - */ - this.errors = []; - /** - * Whether the controller is currently validating. - */ - this.validating = false; - // Elements related to validation results that have been rendered. - this.elements = new Map(); - // Objects that have been added to the controller instance (entity-style validation). - this.objects = new Map(); - /** - * The trigger that will invoke automatic validation of a property used in a binding. - */ - this.validateTrigger = exports.validateTrigger.blur; - // Promise that resolves when validation has completed. - this.finishValidating = Promise.resolve(); - this.eventCallbacks = []; - } - /** - * Subscribe to controller validate and reset events. These events occur when the - * controller's "validate"" and "reset" methods are called. - * @param callback The callback to be invoked when the controller validates or resets. - */ - ValidationController.prototype.subscribe = function (callback) { - var _this = this; - this.eventCallbacks.push(callback); - return { - dispose: function () { - var index = _this.eventCallbacks.indexOf(callback); - if (index === -1) { - return; - } - _this.eventCallbacks.splice(index, 1); - } - }; - }; - /** - * Adds an object to the set of objects that should be validated when validate is called. - * @param object The object. - * @param rules Optional. The rules. If rules aren't supplied the Validator implementation will lookup the rules. - */ - ValidationController.prototype.addObject = function (object, rules) { - this.objects.set(object, rules); - }; - /** - * Removes an object from the set of objects that should be validated when validate is called. - * @param object The object. - */ - ValidationController.prototype.removeObject = function (object) { - this.objects.delete(object); - this.processResultDelta('reset', this.results.filter(function (result) { return result.object === object; }), []); - }; - /** - * Adds and renders an error. - */ - ValidationController.prototype.addError = function (message, object, propertyName) { - if (propertyName === void 0) { propertyName = null; } - var resolvedPropertyName; - if (propertyName === null) { - resolvedPropertyName = propertyName; - } - else { - resolvedPropertyName = this.propertyParser.parse(propertyName); - } - var result = new ValidateResult({ __manuallyAdded__: true }, object, resolvedPropertyName, false, message); - this.processResultDelta('validate', [], [result]); - return result; - }; - /** - * Removes and unrenders an error. - */ - ValidationController.prototype.removeError = function (result) { - if (this.results.indexOf(result) !== -1) { - this.processResultDelta('reset', [result], []); - } - }; - /** - * Adds a renderer. - * @param renderer The renderer. - */ - ValidationController.prototype.addRenderer = function (renderer) { - var _this = this; - this.renderers.push(renderer); - renderer.render({ - kind: 'validate', - render: this.results.map(function (result) { return ({ result: result, elements: _this.elements.get(result) }); }), - unrender: [] - }); - }; - /** - * Removes a renderer. - * @param renderer The renderer. - */ - ValidationController.prototype.removeRenderer = function (renderer) { - var _this = this; - this.renderers.splice(this.renderers.indexOf(renderer), 1); - renderer.render({ - kind: 'reset', - render: [], - unrender: this.results.map(function (result) { return ({ result: result, elements: _this.elements.get(result) }); }) - }); - }; - /** - * Registers a binding with the controller. - * @param binding The binding instance. - * @param target The DOM element. - * @param rules (optional) rules associated with the binding. Validator implementation specific. - */ - ValidationController.prototype.registerBinding = function (binding, target, rules) { - this.bindings.set(binding, { target: target, rules: rules, propertyInfo: null }); - }; - /** - * Unregisters a binding with the controller. - * @param binding The binding instance. - */ - ValidationController.prototype.unregisterBinding = function (binding) { - this.resetBinding(binding); - this.bindings.delete(binding); - }; - /** - * Interprets the instruction and returns a predicate that will identify - * relevant results in the list of rendered validation results. - */ - ValidationController.prototype.getInstructionPredicate = function (instruction) { - var _this = this; - if (instruction) { - var object_1 = instruction.object, propertyName_1 = instruction.propertyName, rules_1 = instruction.rules; - var predicate_1; - if (instruction.propertyName) { - predicate_1 = function (x) { return x.object === object_1 && x.propertyName === propertyName_1; }; - } - else { - predicate_1 = function (x) { return x.object === object_1; }; - } - if (rules_1) { - return function (x) { return predicate_1(x) && _this.validator.ruleExists(rules_1, x.rule); }; - } - return predicate_1; - } - else { - return function () { return true; }; - } - }; - /** - * Validates and renders results. - * @param instruction Optional. Instructions on what to validate. If undefined, all - * objects and bindings will be validated. - */ - ValidationController.prototype.validate = function (instruction) { - var _this = this; - // Get a function that will process the validation instruction. - var execute; - if (instruction) { - // tslint:disable-next-line:prefer-const - var object_2 = instruction.object, propertyName_2 = instruction.propertyName, rules_2 = instruction.rules; - // if rules were not specified, check the object map. - rules_2 = rules_2 || this.objects.get(object_2); - // property specified? - if (instruction.propertyName === undefined) { - // validate the specified object. - execute = function () { return _this.validator.validateObject(object_2, rules_2); }; - } - else { - // validate the specified property. - execute = function () { return _this.validator.validateProperty(object_2, propertyName_2, rules_2); }; - } - } - else { - // validate all objects and bindings. - execute = function () { - var promises = []; - for (var _i = 0, _a = Array.from(_this.objects); _i < _a.length; _i++) { - var _b = _a[_i], object = _b[0], rules = _b[1]; - promises.push(_this.validator.validateObject(object, rules)); - } - for (var _c = 0, _d = Array.from(_this.bindings); _c < _d.length; _c++) { - var _e = _d[_c], binding = _e[0], rules = _e[1].rules; - var propertyInfo = getPropertyInfo(binding.sourceExpression, binding.source); - if (!propertyInfo || _this.objects.has(propertyInfo.object)) { - continue; - } - promises.push(_this.validator.validateProperty(propertyInfo.object, propertyInfo.propertyName, rules)); - } - return Promise.all(promises).then(function (resultSets) { return resultSets.reduce(function (a, b) { return a.concat(b); }, []); }); - }; - } - // Wait for any existing validation to finish, execute the instruction, render the results. - this.validating = true; - var returnPromise = this.finishValidating - .then(execute) - .then(function (newResults) { - var predicate = _this.getInstructionPredicate(instruction); - var oldResults = _this.results.filter(predicate); - _this.processResultDelta('validate', oldResults, newResults); - if (returnPromise === _this.finishValidating) { - _this.validating = false; - } - var result = { - instruction: instruction, - valid: newResults.find(function (x) { return !x.valid; }) === undefined, - results: newResults - }; - _this.invokeCallbacks(instruction, result); - return result; - }) - .catch(function (exception) { - // recover, to enable subsequent calls to validate() - _this.validating = false; - _this.finishValidating = Promise.resolve(); - return Promise.reject(exception); - }); - this.finishValidating = returnPromise; - return returnPromise; - }; - /** - * Resets any rendered validation results (unrenders). - * @param instruction Optional. Instructions on what to reset. If unspecified all rendered results - * will be unrendered. - */ - ValidationController.prototype.reset = function (instruction) { - var predicate = this.getInstructionPredicate(instruction); - var oldResults = this.results.filter(predicate); - this.processResultDelta('reset', oldResults, []); - this.invokeCallbacks(instruction, null); - }; - /** - * Gets the elements associated with an object and propertyName (if any). - */ - ValidationController.prototype.getAssociatedElements = function (_a) { - var object = _a.object, propertyName = _a.propertyName; - var elements = []; - for (var _i = 0, _b = Array.from(this.bindings); _i < _b.length; _i++) { - var _c = _b[_i], binding = _c[0], target = _c[1].target; - var propertyInfo = getPropertyInfo(binding.sourceExpression, binding.source); - if (propertyInfo && propertyInfo.object === object && propertyInfo.propertyName === propertyName) { - elements.push(target); - } - } - return elements; - }; - ValidationController.prototype.processResultDelta = function (kind, oldResults, newResults) { - // prepare the instruction. - var instruction = { - kind: kind, - render: [], - unrender: [] - }; - // create a shallow copy of newResults so we can mutate it without causing side-effects. - newResults = newResults.slice(0); - var _loop_1 = function (oldResult) { - // get the elements associated with the old result. - var elements = this_1.elements.get(oldResult); - // remove the old result from the element map. - this_1.elements.delete(oldResult); - // create the unrender instruction. - instruction.unrender.push({ result: oldResult, elements: elements }); - // determine if there's a corresponding new result for the old result we are unrendering. - var newResultIndex = newResults.findIndex(function (x) { return x.rule === oldResult.rule && x.object === oldResult.object && x.propertyName === oldResult.propertyName; }); - if (newResultIndex === -1) { - // no corresponding new result... simple remove. - this_1.results.splice(this_1.results.indexOf(oldResult), 1); - if (!oldResult.valid) { - this_1.errors.splice(this_1.errors.indexOf(oldResult), 1); - } - } - else { - // there is a corresponding new result... - var newResult = newResults.splice(newResultIndex, 1)[0]; - // get the elements that are associated with the new result. - var elements_1 = this_1.getAssociatedElements(newResult); - this_1.elements.set(newResult, elements_1); - // create a render instruction for the new result. - instruction.render.push({ result: newResult, elements: elements_1 }); - // do an in-place replacement of the old result with the new result. - // this ensures any repeats bound to this.results will not thrash. - this_1.results.splice(this_1.results.indexOf(oldResult), 1, newResult); - if (!oldResult.valid && newResult.valid) { - this_1.errors.splice(this_1.errors.indexOf(oldResult), 1); - } - else if (!oldResult.valid && !newResult.valid) { - this_1.errors.splice(this_1.errors.indexOf(oldResult), 1, newResult); - } - else if (!newResult.valid) { - this_1.errors.push(newResult); - } - } - }; - var this_1 = this; - // create unrender instructions from the old results. - for (var _i = 0, oldResults_1 = oldResults; _i < oldResults_1.length; _i++) { - var oldResult = oldResults_1[_i]; - _loop_1(oldResult); - } - // create render instructions from the remaining new results. - for (var _a = 0, newResults_1 = newResults; _a < newResults_1.length; _a++) { - var result = newResults_1[_a]; - var elements = this.getAssociatedElements(result); - instruction.render.push({ result: result, elements: elements }); - this.elements.set(result, elements); - this.results.push(result); - if (!result.valid) { - this.errors.push(result); - } - } - // render. - for (var _b = 0, _c = this.renderers; _b < _c.length; _b++) { - var renderer = _c[_b]; - renderer.render(instruction); - } - }; - /** - * Validates the property associated with a binding. - */ - ValidationController.prototype.validateBinding = function (binding) { - if (!binding.isBound) { - return; - } - var propertyInfo = getPropertyInfo(binding.sourceExpression, binding.source); - var rules; - var registeredBinding = this.bindings.get(binding); - if (registeredBinding) { - rules = registeredBinding.rules; - registeredBinding.propertyInfo = propertyInfo; - } - if (!propertyInfo) { - return; - } - var object = propertyInfo.object, propertyName = propertyInfo.propertyName; - this.validate({ object: object, propertyName: propertyName, rules: rules }); - }; - /** - * Resets the results for a property associated with a binding. - */ - ValidationController.prototype.resetBinding = function (binding) { - var registeredBinding = this.bindings.get(binding); - var propertyInfo = getPropertyInfo(binding.sourceExpression, binding.source); - if (!propertyInfo && registeredBinding) { - propertyInfo = registeredBinding.propertyInfo; - } - if (registeredBinding) { - registeredBinding.propertyInfo = null; - } - if (!propertyInfo) { - return; - } - var object = propertyInfo.object, propertyName = propertyInfo.propertyName; - this.reset({ object: object, propertyName: propertyName }); - }; - /** - * Changes the controller's validateTrigger. - * @param newTrigger The new validateTrigger - */ - ValidationController.prototype.changeTrigger = function (newTrigger) { - this.validateTrigger = newTrigger; - var bindings = Array.from(this.bindings.keys()); - for (var _i = 0, bindings_1 = bindings; _i < bindings_1.length; _i++) { - var binding = bindings_1[_i]; - var source = binding.source; - binding.unbind(); - binding.bind(source); - } - }; - /** - * Revalidates the controller's current set of errors. - */ - ValidationController.prototype.revalidateErrors = function () { - for (var _i = 0, _a = this.errors; _i < _a.length; _i++) { - var _b = _a[_i], object = _b.object, propertyName = _b.propertyName, rule = _b.rule; - if (rule.__manuallyAdded__) { - continue; - } - var rules = [[rule]]; - this.validate({ object: object, propertyName: propertyName, rules: rules }); - } - }; - ValidationController.prototype.invokeCallbacks = function (instruction, result) { - if (this.eventCallbacks.length === 0) { - return; - } - var event = new ValidateEvent(result ? 'validate' : 'reset', this.errors, this.results, instruction || null, result); - for (var i = 0; i < this.eventCallbacks.length; i++) { - this.eventCallbacks[i](event); - } - }; - ValidationController.inject = [Validator, PropertyAccessorParser]; - return ValidationController; -}()); - -/** - * Binding behavior. Indicates the bound property should be validated. - */ -var ValidateBindingBehaviorBase = /** @class */ (function () { - function ValidateBindingBehaviorBase(taskQueue) { - this.taskQueue = taskQueue; - } - ValidateBindingBehaviorBase.prototype.bind = function (binding, source, rulesOrController, rules) { - var _this = this; - // identify the target element. - var target = getTargetDOMElement(binding, source); - // locate the controller. - var controller; - if (rulesOrController instanceof ValidationController) { - controller = rulesOrController; - } - else { - controller = source.container.get(aureliaDependencyInjection.Optional.of(ValidationController)); - rules = rulesOrController; - } - if (controller === null) { - throw new Error("A ValidationController has not been registered."); - } - controller.registerBinding(binding, target, rules); - binding.validationController = controller; - var trigger = this.getValidateTrigger(controller); - // tslint:disable-next-line:no-bitwise - if (trigger & exports.validateTrigger.change) { - binding.vbbUpdateSource = binding.updateSource; - // tslint:disable-next-line:only-arrow-functions - // tslint:disable-next-line:space-before-function-paren - binding.updateSource = function (value) { - this.vbbUpdateSource(value); - this.validationController.validateBinding(this); - }; - } - // tslint:disable-next-line:no-bitwise - if (trigger & exports.validateTrigger.blur) { - binding.validateBlurHandler = function () { - _this.taskQueue.queueMicroTask(function () { return controller.validateBinding(binding); }); - }; - binding.validateTarget = target; - target.addEventListener('blur', binding.validateBlurHandler); - } - if (trigger !== exports.validateTrigger.manual) { - binding.standardUpdateTarget = binding.updateTarget; - // tslint:disable-next-line:only-arrow-functions - // tslint:disable-next-line:space-before-function-paren - binding.updateTarget = function (value) { - this.standardUpdateTarget(value); - this.validationController.resetBinding(this); - }; - } - }; - ValidateBindingBehaviorBase.prototype.unbind = function (binding) { - // reset the binding to it's original state. - if (binding.vbbUpdateSource) { - binding.updateSource = binding.vbbUpdateSource; - binding.vbbUpdateSource = null; - } - if (binding.standardUpdateTarget) { - binding.updateTarget = binding.standardUpdateTarget; - binding.standardUpdateTarget = null; - } - if (binding.validateBlurHandler) { - binding.validateTarget.removeEventListener('blur', binding.validateBlurHandler); - binding.validateBlurHandler = null; - binding.validateTarget = null; - } - binding.validationController.unregisterBinding(binding); - binding.validationController = null; - }; - return ValidateBindingBehaviorBase; -}()); - -/** - * Binding behavior. Indicates the bound property should be validated - * when the validate trigger specified by the associated controller's - * validateTrigger property occurs. - */ -var ValidateBindingBehavior = /** @class */ (function (_super) { - __extends(ValidateBindingBehavior, _super); - function ValidateBindingBehavior() { - return _super !== null && _super.apply(this, arguments) || this; - } - ValidateBindingBehavior.prototype.getValidateTrigger = function (controller) { - return controller.validateTrigger; - }; - ValidateBindingBehavior.inject = [aureliaTaskQueue.TaskQueue]; - ValidateBindingBehavior = __decorate([ - aureliaBinding.bindingBehavior('validate') - ], ValidateBindingBehavior); - return ValidateBindingBehavior; -}(ValidateBindingBehaviorBase)); -/** - * Binding behavior. Indicates the bound property will be validated - * manually, by calling controller.validate(). No automatic validation - * triggered by data-entry or blur will occur. - */ -var ValidateManuallyBindingBehavior = /** @class */ (function (_super) { - __extends(ValidateManuallyBindingBehavior, _super); - function ValidateManuallyBindingBehavior() { - return _super !== null && _super.apply(this, arguments) || this; - } - ValidateManuallyBindingBehavior.prototype.getValidateTrigger = function () { - return exports.validateTrigger.manual; - }; - ValidateManuallyBindingBehavior.inject = [aureliaTaskQueue.TaskQueue]; - ValidateManuallyBindingBehavior = __decorate([ - aureliaBinding.bindingBehavior('validateManually') - ], ValidateManuallyBindingBehavior); - return ValidateManuallyBindingBehavior; -}(ValidateBindingBehaviorBase)); -/** - * Binding behavior. Indicates the bound property should be validated - * when the associated element blurs. - */ -var ValidateOnBlurBindingBehavior = /** @class */ (function (_super) { - __extends(ValidateOnBlurBindingBehavior, _super); - function ValidateOnBlurBindingBehavior() { - return _super !== null && _super.apply(this, arguments) || this; - } - ValidateOnBlurBindingBehavior.prototype.getValidateTrigger = function () { - return exports.validateTrigger.blur; - }; - ValidateOnBlurBindingBehavior.inject = [aureliaTaskQueue.TaskQueue]; - ValidateOnBlurBindingBehavior = __decorate([ - aureliaBinding.bindingBehavior('validateOnBlur') - ], ValidateOnBlurBindingBehavior); - return ValidateOnBlurBindingBehavior; -}(ValidateBindingBehaviorBase)); -/** - * Binding behavior. Indicates the bound property should be validated - * when the associated element is changed by the user, causing a change - * to the model. - */ -var ValidateOnChangeBindingBehavior = /** @class */ (function (_super) { - __extends(ValidateOnChangeBindingBehavior, _super); - function ValidateOnChangeBindingBehavior() { - return _super !== null && _super.apply(this, arguments) || this; - } - ValidateOnChangeBindingBehavior.prototype.getValidateTrigger = function () { - return exports.validateTrigger.change; - }; - ValidateOnChangeBindingBehavior.inject = [aureliaTaskQueue.TaskQueue]; - ValidateOnChangeBindingBehavior = __decorate([ - aureliaBinding.bindingBehavior('validateOnChange') - ], ValidateOnChangeBindingBehavior); - return ValidateOnChangeBindingBehavior; -}(ValidateBindingBehaviorBase)); -/** - * Binding behavior. Indicates the bound property should be validated - * when the associated element blurs or is changed by the user, causing - * a change to the model. - */ -var ValidateOnChangeOrBlurBindingBehavior = /** @class */ (function (_super) { - __extends(ValidateOnChangeOrBlurBindingBehavior, _super); - function ValidateOnChangeOrBlurBindingBehavior() { - return _super !== null && _super.apply(this, arguments) || this; - } - ValidateOnChangeOrBlurBindingBehavior.prototype.getValidateTrigger = function () { - return exports.validateTrigger.changeOrBlur; - }; - ValidateOnChangeOrBlurBindingBehavior.inject = [aureliaTaskQueue.TaskQueue]; - ValidateOnChangeOrBlurBindingBehavior = __decorate([ - aureliaBinding.bindingBehavior('validateOnChangeOrBlur') - ], ValidateOnChangeOrBlurBindingBehavior); - return ValidateOnChangeOrBlurBindingBehavior; -}(ValidateBindingBehaviorBase)); - -/** - * Creates ValidationController instances. - */ -var ValidationControllerFactory = /** @class */ (function () { - function ValidationControllerFactory(container) { - this.container = container; - } - ValidationControllerFactory.get = function (container) { - return new ValidationControllerFactory(container); - }; - /** - * Creates a new controller instance. - */ - ValidationControllerFactory.prototype.create = function (validator) { - if (!validator) { - validator = this.container.get(Validator); - } - var propertyParser = this.container.get(PropertyAccessorParser); - return new ValidationController(validator, propertyParser); - }; - /** - * Creates a new controller and registers it in the current element's container so that it's - * available to the validate binding behavior and renderers. - */ - ValidationControllerFactory.prototype.createForCurrentScope = function (validator) { - var controller = this.create(validator); - this.container.registerInstance(ValidationController, controller); - return controller; - }; - return ValidationControllerFactory; -}()); -ValidationControllerFactory['protocol:aurelia:resolver'] = true; - -var ValidationErrorsCustomAttribute = /** @class */ (function () { - function ValidationErrorsCustomAttribute(boundaryElement, controllerAccessor) { - this.boundaryElement = boundaryElement; - this.controllerAccessor = controllerAccessor; - this.controller = null; - this.errors = []; - this.errorsInternal = []; - } - ValidationErrorsCustomAttribute.inject = function () { - return [aureliaPal.DOM.Element, aureliaDependencyInjection.Lazy.of(ValidationController)]; - }; - ValidationErrorsCustomAttribute.prototype.sort = function () { - this.errorsInternal.sort(function (a, b) { - if (a.targets[0] === b.targets[0]) { - return 0; - } - // tslint:disable-next-line:no-bitwise - return a.targets[0].compareDocumentPosition(b.targets[0]) & 2 ? 1 : -1; - }); - }; - ValidationErrorsCustomAttribute.prototype.interestingElements = function (elements) { - var _this = this; - return elements.filter(function (e) { return _this.boundaryElement.contains(e); }); - }; - ValidationErrorsCustomAttribute.prototype.render = function (instruction) { - var _loop_1 = function (result) { - var index = this_1.errorsInternal.findIndex(function (x) { return x.error === result; }); - if (index !== -1) { - this_1.errorsInternal.splice(index, 1); - } - }; - var this_1 = this; - for (var _i = 0, _a = instruction.unrender; _i < _a.length; _i++) { - var result = _a[_i].result; - _loop_1(result); - } - for (var _b = 0, _c = instruction.render; _b < _c.length; _b++) { - var _d = _c[_b], result = _d.result, elements = _d.elements; - if (result.valid) { - continue; - } - var targets = this.interestingElements(elements); - if (targets.length) { - this.errorsInternal.push({ error: result, targets: targets }); - } - } - this.sort(); - this.errors = this.errorsInternal; - }; - ValidationErrorsCustomAttribute.prototype.bind = function () { - if (!this.controller) { - this.controller = this.controllerAccessor(); - } - // this will call render() with the side-effect of updating this.errors - this.controller.addRenderer(this); - }; - ValidationErrorsCustomAttribute.prototype.unbind = function () { - if (this.controller) { - this.controller.removeRenderer(this); - } - }; - __decorate([ - aureliaTemplating.bindable({ defaultBindingMode: aureliaBinding.bindingMode.oneWay }) - ], ValidationErrorsCustomAttribute.prototype, "controller", void 0); - __decorate([ - aureliaTemplating.bindable({ primaryProperty: true, defaultBindingMode: aureliaBinding.bindingMode.twoWay }) - ], ValidationErrorsCustomAttribute.prototype, "errors", void 0); - ValidationErrorsCustomAttribute = __decorate([ - aureliaTemplating.customAttribute('validation-errors') - ], ValidationErrorsCustomAttribute); - return ValidationErrorsCustomAttribute; -}()); - -var ValidationRendererCustomAttribute = /** @class */ (function () { - function ValidationRendererCustomAttribute() { - } - ValidationRendererCustomAttribute.prototype.created = function (view) { - this.container = view.container; - }; - ValidationRendererCustomAttribute.prototype.bind = function () { - this.controller = this.container.get(ValidationController); - this.renderer = this.container.get(this.value); - this.controller.addRenderer(this.renderer); - }; - ValidationRendererCustomAttribute.prototype.unbind = function () { - this.controller.removeRenderer(this.renderer); - this.controller = null; - this.renderer = null; - }; - ValidationRendererCustomAttribute = __decorate([ - aureliaTemplating.customAttribute('validation-renderer') - ], ValidationRendererCustomAttribute); - return ValidationRendererCustomAttribute; -}()); - -/** - * Sets, unsets and retrieves rules on an object or constructor function. - */ -var Rules = /** @class */ (function () { - function Rules() { - } - /** - * Applies the rules to a target. - */ - Rules.set = function (target, rules) { - if (target instanceof Function) { - target = target.prototype; - } - Object.defineProperty(target, Rules.key, { enumerable: false, configurable: false, writable: true, value: rules }); - }; - /** - * Removes rules from a target. - */ - Rules.unset = function (target) { - if (target instanceof Function) { - target = target.prototype; - } - target[Rules.key] = null; - }; - /** - * Retrieves the target's rules. - */ - Rules.get = function (target) { - return target[Rules.key] || null; - }; - /** - * The name of the property that stores the rules. - */ - Rules.key = '__rules__'; - return Rules; -}()); - -// tslint:disable:no-empty -var ExpressionVisitor = /** @class */ (function () { - function ExpressionVisitor() { - } - ExpressionVisitor.prototype.visitChain = function (chain) { - this.visitArgs(chain.expressions); - }; - ExpressionVisitor.prototype.visitBindingBehavior = function (behavior) { - behavior.expression.accept(this); - this.visitArgs(behavior.args); - }; - ExpressionVisitor.prototype.visitValueConverter = function (converter) { - converter.expression.accept(this); - this.visitArgs(converter.args); - }; - ExpressionVisitor.prototype.visitAssign = function (assign) { - assign.target.accept(this); - assign.value.accept(this); - }; - ExpressionVisitor.prototype.visitConditional = function (conditional) { - conditional.condition.accept(this); - conditional.yes.accept(this); - conditional.no.accept(this); - }; - ExpressionVisitor.prototype.visitAccessThis = function (access) { - access.ancestor = access.ancestor; - }; - ExpressionVisitor.prototype.visitAccessScope = function (access) { - access.name = access.name; - }; - ExpressionVisitor.prototype.visitAccessMember = function (access) { - access.object.accept(this); - }; - ExpressionVisitor.prototype.visitAccessKeyed = function (access) { - access.object.accept(this); - access.key.accept(this); - }; - ExpressionVisitor.prototype.visitCallScope = function (call) { - this.visitArgs(call.args); - }; - ExpressionVisitor.prototype.visitCallFunction = function (call) { - call.func.accept(this); - this.visitArgs(call.args); - }; - ExpressionVisitor.prototype.visitCallMember = function (call) { - call.object.accept(this); - this.visitArgs(call.args); - }; - ExpressionVisitor.prototype.visitPrefix = function (prefix) { - prefix.expression.accept(this); - }; - ExpressionVisitor.prototype.visitBinary = function (binary) { - binary.left.accept(this); - binary.right.accept(this); - }; - ExpressionVisitor.prototype.visitLiteralPrimitive = function (literal) { - literal.value = literal.value; - }; - ExpressionVisitor.prototype.visitLiteralArray = function (literal) { - this.visitArgs(literal.elements); - }; - ExpressionVisitor.prototype.visitLiteralObject = function (literal) { - this.visitArgs(literal.values); - }; - ExpressionVisitor.prototype.visitLiteralString = function (literal) { - literal.value = literal.value; - }; - ExpressionVisitor.prototype.visitArgs = function (args) { - for (var i = 0; i < args.length; i++) { - args[i].accept(this); - } - }; - return ExpressionVisitor; -}()); - -var ValidationMessageParser = /** @class */ (function () { - function ValidationMessageParser(bindinqLanguage) { - this.bindinqLanguage = bindinqLanguage; - this.emptyStringExpression = new aureliaBinding.LiteralString(''); - this.nullExpression = new aureliaBinding.LiteralPrimitive(null); - this.undefinedExpression = new aureliaBinding.LiteralPrimitive(undefined); - this.cache = {}; - } - ValidationMessageParser.prototype.parse = function (message) { - if (this.cache[message] !== undefined) { - return this.cache[message]; - } - var parts = this.bindinqLanguage.parseInterpolation(null, message); - if (parts === null) { - return new aureliaBinding.LiteralString(message); - } - var expression = new aureliaBinding.LiteralString(parts[0]); - for (var i = 1; i < parts.length; i += 2) { - expression = new aureliaBinding.Binary('+', expression, new aureliaBinding.Binary('+', this.coalesce(parts[i]), new aureliaBinding.LiteralString(parts[i + 1]))); - } - MessageExpressionValidator.validate(expression, message); - this.cache[message] = expression; - return expression; - }; - ValidationMessageParser.prototype.coalesce = function (part) { - // part === null || part === undefined ? '' : part - return new aureliaBinding.Conditional(new aureliaBinding.Binary('||', new aureliaBinding.Binary('===', part, this.nullExpression), new aureliaBinding.Binary('===', part, this.undefinedExpression)), this.emptyStringExpression, new aureliaBinding.CallMember(part, 'toString', [])); - }; - ValidationMessageParser.inject = [aureliaTemplating.BindingLanguage]; - return ValidationMessageParser; -}()); -var MessageExpressionValidator = /** @class */ (function (_super) { - __extends(MessageExpressionValidator, _super); - function MessageExpressionValidator(originalMessage) { - var _this = _super.call(this) || this; - _this.originalMessage = originalMessage; - return _this; - } - MessageExpressionValidator.validate = function (expression, originalMessage) { - var visitor = new MessageExpressionValidator(originalMessage); - expression.accept(visitor); - }; - MessageExpressionValidator.prototype.visitAccessScope = function (access) { - if (access.ancestor !== 0) { - throw new Error('$parent is not permitted in validation message expressions.'); - } - if (['displayName', 'propertyName', 'value', 'object', 'config', 'getDisplayName'].indexOf(access.name) !== -1) { - LogManager.getLogger('aurelia-validation') - // tslint:disable-next-line:max-line-length - .warn("Did you mean to use \"$" + access.name + "\" instead of \"" + access.name + "\" in this validation message template: \"" + this.originalMessage + "\"?"); - } - }; - return MessageExpressionValidator; -}(ExpressionVisitor)); - -/** - * Dictionary of validation messages. [messageKey]: messageExpression - */ -var validationMessages = { - /** - * The default validation message. Used with rules that have no standard message. - */ - default: "${$displayName} is invalid.", - required: "${$displayName} is required.", - matches: "${$displayName} is not correctly formatted.", - email: "${$displayName} is not a valid email.", - minLength: "${$displayName} must be at least ${$config.length} character${$config.length === 1 ? '' : 's'}.", - maxLength: "${$displayName} cannot be longer than ${$config.length} character${$config.length === 1 ? '' : 's'}.", - minItems: "${$displayName} must contain at least ${$config.count} item${$config.count === 1 ? '' : 's'}.", - maxItems: "${$displayName} cannot contain more than ${$config.count} item${$config.count === 1 ? '' : 's'}.", - equals: "${$displayName} must be ${$config.expectedValue}.", -}; -/** - * Retrieves validation messages and property display names. - */ -var ValidationMessageProvider = /** @class */ (function () { - function ValidationMessageProvider(parser) { - this.parser = parser; - } - /** - * Returns a message binding expression that corresponds to the key. - * @param key The message key. - */ - ValidationMessageProvider.prototype.getMessage = function (key) { - var message; - if (key in validationMessages) { - message = validationMessages[key]; - } - else { - message = validationMessages['default']; - } - return this.parser.parse(message); - }; - /** - * Formulates a property display name using the property name and the configured - * displayName (if provided). - * Override this with your own custom logic. - * @param propertyName The property name. - */ - ValidationMessageProvider.prototype.getDisplayName = function (propertyName, displayName) { - if (displayName !== null && displayName !== undefined) { - return (displayName instanceof Function) ? displayName() : displayName; - } - // split on upper-case letters. - var words = propertyName.toString().split(/(?=[A-Z])/).join(' '); - // capitalize first letter. - return words.charAt(0).toUpperCase() + words.slice(1); - }; - ValidationMessageProvider.inject = [ValidationMessageParser]; - return ValidationMessageProvider; -}()); - -/** - * Validates. - * Responsible for validating objects and properties. - */ -var StandardValidator = /** @class */ (function (_super) { - __extends(StandardValidator, _super); - function StandardValidator(messageProvider, resources) { - var _this = _super.call(this) || this; - _this.messageProvider = messageProvider; - _this.lookupFunctions = resources.lookupFunctions; - _this.getDisplayName = messageProvider.getDisplayName.bind(messageProvider); - return _this; - } - /** - * Validates the specified property. - * @param object The object to validate. - * @param propertyName The name of the property to validate. - * @param rules Optional. If unspecified, the rules will be looked up using the metadata - * for the object created by ValidationRules....on(class/object) - */ - StandardValidator.prototype.validateProperty = function (object, propertyName, rules) { - return this.validate(object, propertyName, rules || null); - }; - /** - * Validates all rules for specified object and it's properties. - * @param object The object to validate. - * @param rules Optional. If unspecified, the rules will be looked up using the metadata - * for the object created by ValidationRules....on(class/object) - */ - StandardValidator.prototype.validateObject = function (object, rules) { - return this.validate(object, null, rules || null); - }; - /** - * Determines whether a rule exists in a set of rules. - * @param rules The rules to search. - * @parem rule The rule to find. - */ - StandardValidator.prototype.ruleExists = function (rules, rule) { - var i = rules.length; - while (i--) { - if (rules[i].indexOf(rule) !== -1) { - return true; - } - } - return false; - }; - StandardValidator.prototype.getMessage = function (rule, object, value) { - var expression = rule.message || this.messageProvider.getMessage(rule.messageKey); - // tslint:disable-next-line:prefer-const - var _a = rule.property, propertyName = _a.name, displayName = _a.displayName; - if (propertyName !== null) { - displayName = this.messageProvider.getDisplayName(propertyName, displayName); - } - var overrideContext = { - $displayName: displayName, - $propertyName: propertyName, - $value: value, - $object: object, - $config: rule.config, - // returns the name of a given property, given just the property name (irrespective of the property's displayName) - // split on capital letters, first letter ensured to be capitalized - $getDisplayName: this.getDisplayName - }; - return expression.evaluate({ bindingContext: object, overrideContext: overrideContext }, this.lookupFunctions); - }; - StandardValidator.prototype.validateRuleSequence = function (object, propertyName, ruleSequence, sequence, results) { - var _this = this; - // are we validating all properties or a single property? - var validateAllProperties = propertyName === null || propertyName === undefined; - var rules = ruleSequence[sequence]; - var allValid = true; - // validate each rule. - var promises = []; - var _loop_1 = function (i) { - var rule = rules[i]; - // is the rule related to the property we're validating. - // tslint:disable-next-line:triple-equals | Use loose equality for property keys - if (!validateAllProperties && rule.property.name != propertyName) { - return "continue"; - } - // is this a conditional rule? is the condition met? - if (rule.when && !rule.when(object)) { - return "continue"; - } - // validate. - var value = rule.property.name === null ? object : object[rule.property.name]; - var promiseOrBoolean = rule.condition(value, object); - if (!(promiseOrBoolean instanceof Promise)) { - promiseOrBoolean = Promise.resolve(promiseOrBoolean); - } - promises.push(promiseOrBoolean.then(function (valid) { - var message = valid ? null : _this.getMessage(rule, object, value); - results.push(new ValidateResult(rule, object, rule.property.name, valid, message)); - allValid = allValid && valid; - return valid; - })); - }; - for (var i = 0; i < rules.length; i++) { - _loop_1(i); - } - return Promise.all(promises) - .then(function () { - sequence++; - if (allValid && sequence < ruleSequence.length) { - return _this.validateRuleSequence(object, propertyName, ruleSequence, sequence, results); - } - return results; - }); - }; - StandardValidator.prototype.validate = function (object, propertyName, rules) { - // rules specified? - if (!rules) { - // no. attempt to locate the rules. - rules = Rules.get(object); - } - // any rules? - if (!rules || rules.length === 0) { - return Promise.resolve([]); - } - return this.validateRuleSequence(object, propertyName, rules, 0, []); - }; - StandardValidator.inject = [ValidationMessageProvider, aureliaTemplating.ViewResources]; - return StandardValidator; -}(Validator)); - -/** - * Part of the fluent rule API. Enables customizing property rules. - */ -var FluentRuleCustomizer = /** @class */ (function () { - function FluentRuleCustomizer(property, condition, config, fluentEnsure, fluentRules, parsers) { - if (config === void 0) { config = {}; } - this.fluentEnsure = fluentEnsure; - this.fluentRules = fluentRules; - this.parsers = parsers; - this.rule = { - property: property, - condition: condition, - config: config, - when: null, - messageKey: 'default', - message: null, - sequence: fluentRules.sequence - }; - this.fluentEnsure._addRule(this.rule); - } - /** - * Validate subsequent rules after previously declared rules have - * been validated successfully. Use to postpone validation of costly - * rules until less expensive rules pass validation. - */ - FluentRuleCustomizer.prototype.then = function () { - this.fluentRules.sequence++; - return this; - }; - /** - * Specifies the key to use when looking up the rule's validation message. - */ - FluentRuleCustomizer.prototype.withMessageKey = function (key) { - this.rule.messageKey = key; - this.rule.message = null; - return this; - }; - /** - * Specifies rule's validation message. - */ - FluentRuleCustomizer.prototype.withMessage = function (message) { - this.rule.messageKey = 'custom'; - this.rule.message = this.parsers.message.parse(message); - return this; - }; - /** - * Specifies a condition that must be met before attempting to validate the rule. - * @param condition A function that accepts the object as a parameter and returns true - * or false whether the rule should be evaluated. - */ - FluentRuleCustomizer.prototype.when = function (condition) { - this.rule.when = condition; - return this; - }; - /** - * Tags the rule instance, enabling the rule to be found easily - * using ValidationRules.taggedRules(rules, tag) - */ - FluentRuleCustomizer.prototype.tag = function (tag) { - this.rule.tag = tag; - return this; - }; - ///// FluentEnsure APIs ///// - /** - * Target a property with validation rules. - * @param property The property to target. Can be the property name or a property accessor function. - */ - FluentRuleCustomizer.prototype.ensure = function (subject) { - return this.fluentEnsure.ensure(subject); - }; - /** - * Targets an object with validation rules. - */ - FluentRuleCustomizer.prototype.ensureObject = function () { - return this.fluentEnsure.ensureObject(); - }; - Object.defineProperty(FluentRuleCustomizer.prototype, "rules", { - /** - * Rules that have been defined using the fluent API. - */ - get: function () { - return this.fluentEnsure.rules; - }, - enumerable: true, - configurable: true - }); - /** - * Applies the rules to a class or object, making them discoverable by the StandardValidator. - * @param target A class or object. - */ - FluentRuleCustomizer.prototype.on = function (target) { - return this.fluentEnsure.on(target); - }; - ///////// FluentRules APIs ///////// - /** - * Applies an ad-hoc rule function to the ensured property or object. - * @param condition The function to validate the rule. - * Will be called with two arguments, the property value and the object. - * Should return a boolean or a Promise that resolves to a boolean. - */ - FluentRuleCustomizer.prototype.satisfies = function (condition, config) { - return this.fluentRules.satisfies(condition, config); - }; - /** - * Applies a rule by name. - * @param name The name of the custom or standard rule. - * @param args The rule's arguments. - */ - FluentRuleCustomizer.prototype.satisfiesRule = function (name) { - var args = []; - for (var _i = 1; _i < arguments.length; _i++) { - args[_i - 1] = arguments[_i]; - } - var _a; - return (_a = this.fluentRules).satisfiesRule.apply(_a, [name].concat(args)); - }; - /** - * Applies the "required" rule to the property. - * The value cannot be null, undefined or whitespace. - */ - FluentRuleCustomizer.prototype.required = function () { - return this.fluentRules.required(); - }; - /** - * Applies the "matches" rule to the property. - * Value must match the specified regular expression. - * null, undefined and empty-string values are considered valid. - */ - FluentRuleCustomizer.prototype.matches = function (regex) { - return this.fluentRules.matches(regex); - }; - /** - * Applies the "email" rule to the property. - * null, undefined and empty-string values are considered valid. - */ - FluentRuleCustomizer.prototype.email = function () { - return this.fluentRules.email(); - }; - /** - * Applies the "minLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - FluentRuleCustomizer.prototype.minLength = function (length) { - return this.fluentRules.minLength(length); - }; - /** - * Applies the "maxLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - FluentRuleCustomizer.prototype.maxLength = function (length) { - return this.fluentRules.maxLength(length); - }; - /** - * Applies the "minItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - FluentRuleCustomizer.prototype.minItems = function (count) { - return this.fluentRules.minItems(count); - }; - /** - * Applies the "maxItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - FluentRuleCustomizer.prototype.maxItems = function (count) { - return this.fluentRules.maxItems(count); - }; - /** - * Applies the "equals" validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - FluentRuleCustomizer.prototype.equals = function (expectedValue) { - return this.fluentRules.equals(expectedValue); - }; - return FluentRuleCustomizer; -}()); -/** - * Part of the fluent rule API. Enables applying rules to properties and objects. - */ -var FluentRules = /** @class */ (function () { - function FluentRules(fluentEnsure, parsers, property) { - this.fluentEnsure = fluentEnsure; - this.parsers = parsers; - this.property = property; - /** - * Current rule sequence number. Used to postpone evaluation of rules until rules - * with lower sequence number have successfully validated. The "then" fluent API method - * manages this property, there's usually no need to set it directly. - */ - this.sequence = 0; - } - /** - * Sets the display name of the ensured property. - */ - FluentRules.prototype.displayName = function (name) { - this.property.displayName = name; - return this; - }; - /** - * Applies an ad-hoc rule function to the ensured property or object. - * @param condition The function to validate the rule. - * Will be called with two arguments, the property value and the object. - * Should return a boolean or a Promise that resolves to a boolean. - */ - FluentRules.prototype.satisfies = function (condition, config) { - return new FluentRuleCustomizer(this.property, condition, config, this.fluentEnsure, this, this.parsers); - }; - /** - * Applies a rule by name. - * @param name The name of the custom or standard rule. - * @param args The rule's arguments. - */ - FluentRules.prototype.satisfiesRule = function (name) { - var _this = this; - var args = []; - for (var _i = 1; _i < arguments.length; _i++) { - args[_i - 1] = arguments[_i]; - } - var rule = FluentRules.customRules[name]; - if (!rule) { - // standard rule? - rule = this[name]; - if (rule instanceof Function) { - return rule.call.apply(rule, [this].concat(args)); - } - throw new Error("Rule with name \"" + name + "\" does not exist."); - } - var config = rule.argsToConfig ? rule.argsToConfig.apply(rule, args) : undefined; - return this.satisfies(function (value, obj) { - var _a; - return (_a = rule.condition).call.apply(_a, [_this, value, obj].concat(args)); - }, config) - .withMessageKey(name); - }; - /** - * Applies the "required" rule to the property. - * The value cannot be null, undefined or whitespace. - */ - FluentRules.prototype.required = function () { - return this.satisfies(function (value) { - return value !== null - && value !== undefined - && !(isString(value) && !/\S/.test(value)); - }).withMessageKey('required'); - }; - /** - * Applies the "matches" rule to the property. - * Value must match the specified regular expression. - * null, undefined and empty-string values are considered valid. - */ - FluentRules.prototype.matches = function (regex) { - return this.satisfies(function (value) { return value === null || value === undefined || value.length === 0 || regex.test(value); }) - .withMessageKey('matches'); - }; - /** - * Applies the "email" rule to the property. - * null, undefined and empty-string values are considered valid. - */ - FluentRules.prototype.email = function () { - // regex from https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address - /* tslint:disable:max-line-length */ - return this.matches(/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/) - /* tslint:enable:max-line-length */ - .withMessageKey('email'); - }; - /** - * Applies the "minLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - FluentRules.prototype.minLength = function (length) { - return this.satisfies(function (value) { return value === null || value === undefined || value.length === 0 || value.length >= length; }, { length: length }) - .withMessageKey('minLength'); - }; - /** - * Applies the "maxLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - FluentRules.prototype.maxLength = function (length) { - return this.satisfies(function (value) { return value === null || value === undefined || value.length === 0 || value.length <= length; }, { length: length }) - .withMessageKey('maxLength'); - }; - /** - * Applies the "minItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - FluentRules.prototype.minItems = function (count) { - return this.satisfies(function (value) { return value === null || value === undefined || value.length >= count; }, { count: count }) - .withMessageKey('minItems'); - }; - /** - * Applies the "maxItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - FluentRules.prototype.maxItems = function (count) { - return this.satisfies(function (value) { return value === null || value === undefined || value.length <= count; }, { count: count }) - .withMessageKey('maxItems'); - }; - /** - * Applies the "equals" validation rule to the property. - * null and undefined values are considered valid. - */ - FluentRules.prototype.equals = function (expectedValue) { - return this.satisfies(function (value) { return value === null || value === undefined || value === '' || value === expectedValue; }, { expectedValue: expectedValue }) - .withMessageKey('equals'); - }; - FluentRules.customRules = {}; - return FluentRules; -}()); -/** - * Part of the fluent rule API. Enables targeting properties and objects with rules. - */ -var FluentEnsure = /** @class */ (function () { - function FluentEnsure(parsers) { - this.parsers = parsers; - /** - * Rules that have been defined using the fluent API. - */ - this.rules = []; - } - /** - * Target a property with validation rules. - * @param property The property to target. Can be the property name or a property accessor - * function. - */ - FluentEnsure.prototype.ensure = function (property) { - this.assertInitialized(); - var name = this.parsers.property.parse(property); - var fluentRules = new FluentRules(this, this.parsers, { name: name, displayName: null }); - return this.mergeRules(fluentRules, name); - }; - /** - * Targets an object with validation rules. - */ - FluentEnsure.prototype.ensureObject = function () { - this.assertInitialized(); - var fluentRules = new FluentRules(this, this.parsers, { name: null, displayName: null }); - return this.mergeRules(fluentRules, null); - }; - /** - * Applies the rules to a class or object, making them discoverable by the StandardValidator. - * @param target A class or object. - */ - FluentEnsure.prototype.on = function (target) { - Rules.set(target, this.rules); - return this; - }; - /** - * Adds a rule definition to the sequenced ruleset. - * @internal - */ - FluentEnsure.prototype._addRule = function (rule) { - while (this.rules.length < rule.sequence + 1) { - this.rules.push([]); - } - this.rules[rule.sequence].push(rule); - }; - FluentEnsure.prototype.assertInitialized = function () { - if (this.parsers) { - return; - } - throw new Error("Did you forget to add \".plugin('aurelia-validation')\" to your main.js?"); - }; - FluentEnsure.prototype.mergeRules = function (fluentRules, propertyName) { - // tslint:disable-next-line:triple-equals | Use loose equality for property keys - var existingRules = this.rules.find(function (r) { return r.length > 0 && r[0].property.name == propertyName; }); - if (existingRules) { - var rule = existingRules[existingRules.length - 1]; - fluentRules.sequence = rule.sequence; - if (rule.property.displayName !== null) { - fluentRules = fluentRules.displayName(rule.property.displayName); - } - } - return fluentRules; - }; - return FluentEnsure; -}()); -/** - * Fluent rule definition API. - */ -var ValidationRules = /** @class */ (function () { - function ValidationRules() { - } - ValidationRules.initialize = function (messageParser, propertyParser) { - this.parsers = { - message: messageParser, - property: propertyParser - }; - }; - /** - * Target a property with validation rules. - * @param property The property to target. Can be the property name or a property accessor function. - */ - ValidationRules.ensure = function (property) { - return new FluentEnsure(ValidationRules.parsers).ensure(property); - }; - /** - * Targets an object with validation rules. - */ - ValidationRules.ensureObject = function () { - return new FluentEnsure(ValidationRules.parsers).ensureObject(); - }; - /** - * Defines a custom rule. - * @param name The name of the custom rule. Also serves as the message key. - * @param condition The rule function. - * @param message The message expression - * @param argsToConfig A function that maps the rule's arguments to a "config" - * object that can be used when evaluating the message expression. - */ - ValidationRules.customRule = function (name, condition, message, argsToConfig) { - validationMessages[name] = message; - FluentRules.customRules[name] = { condition: condition, argsToConfig: argsToConfig }; - }; - /** - * Returns rules with the matching tag. - * @param rules The rules to search. - * @param tag The tag to search for. - */ - ValidationRules.taggedRules = function (rules, tag) { - return rules.map(function (x) { return x.filter(function (r) { return r.tag === tag; }); }); - }; - /** - * Returns rules that have no tag. - * @param rules The rules to search. - */ - ValidationRules.untaggedRules = function (rules) { - return rules.map(function (x) { return x.filter(function (r) { return r.tag === undefined; }); }); - }; - /** - * Removes the rules from a class or object. - * @param target A class or object. - */ - ValidationRules.off = function (target) { - Rules.unset(target); - }; - return ValidationRules; -}()); - -// Exports -/** - * Aurelia Validation Configuration API - */ -var AureliaValidationConfiguration = /** @class */ (function () { - function AureliaValidationConfiguration() { - this.validatorType = StandardValidator; - } - /** - * Use a custom Validator implementation. - */ - AureliaValidationConfiguration.prototype.customValidator = function (type) { - this.validatorType = type; - }; - /** - * Applies the configuration. - */ - AureliaValidationConfiguration.prototype.apply = function (container) { - var validator = container.get(this.validatorType); - container.registerInstance(Validator, validator); - }; - return AureliaValidationConfiguration; -}()); -/** - * Configures the plugin. - */ -function configure( -// tslint:disable-next-line:ban-types -frameworkConfig, callback) { - // the fluent rule definition API needs the parser to translate messages - // to interpolation expressions. - var messageParser = frameworkConfig.container.get(ValidationMessageParser); - var propertyParser = frameworkConfig.container.get(PropertyAccessorParser); - ValidationRules.initialize(messageParser, propertyParser); - // configure... - var config = new AureliaValidationConfiguration(); - if (callback instanceof Function) { - callback(config); - } - config.apply(frameworkConfig.container); - // globalize the behaviors. - if (frameworkConfig.globalResources) { - frameworkConfig.globalResources(ValidateBindingBehavior, ValidateManuallyBindingBehavior, ValidateOnBlurBindingBehavior, ValidateOnChangeBindingBehavior, ValidateOnChangeOrBlurBindingBehavior, ValidationErrorsCustomAttribute, ValidationRendererCustomAttribute); - } -} - -exports.AureliaValidationConfiguration = AureliaValidationConfiguration; -exports.configure = configure; -exports.getTargetDOMElement = getTargetDOMElement; -exports.getPropertyInfo = getPropertyInfo; -exports.PropertyAccessorParser = PropertyAccessorParser; -exports.getAccessorExpression = getAccessorExpression; -exports.ValidateBindingBehavior = ValidateBindingBehavior; -exports.ValidateManuallyBindingBehavior = ValidateManuallyBindingBehavior; -exports.ValidateOnBlurBindingBehavior = ValidateOnBlurBindingBehavior; -exports.ValidateOnChangeBindingBehavior = ValidateOnChangeBindingBehavior; -exports.ValidateOnChangeOrBlurBindingBehavior = ValidateOnChangeOrBlurBindingBehavior; -exports.ValidateEvent = ValidateEvent; -exports.ValidateResult = ValidateResult; -exports.ValidationController = ValidationController; -exports.ValidationControllerFactory = ValidationControllerFactory; -exports.ValidationErrorsCustomAttribute = ValidationErrorsCustomAttribute; -exports.ValidationRendererCustomAttribute = ValidationRendererCustomAttribute; -exports.Validator = Validator; -exports.Rules = Rules; -exports.StandardValidator = StandardValidator; -exports.validationMessages = validationMessages; -exports.ValidationMessageProvider = ValidationMessageProvider; -exports.ValidationMessageParser = ValidationMessageParser; -exports.MessageExpressionValidator = MessageExpressionValidator; -exports.FluentRuleCustomizer = FluentRuleCustomizer; -exports.FluentRules = FluentRules; -exports.FluentEnsure = FluentEnsure; -exports.ValidationRules = ValidationRules; diff --git a/dist/es2015/aurelia-validation.js b/dist/es2015/aurelia-validation.js deleted file mode 100644 index 4c67ca36..00000000 --- a/dist/es2015/aurelia-validation.js +++ /dev/null @@ -1,1661 +0,0 @@ -import { DOM } from 'aurelia-pal'; -import { AccessMember, AccessScope, AccessKeyed, BindingBehavior, ValueConverter, getContextFor, Parser, bindingBehavior, bindingMode, LiteralString, Binary, Conditional, LiteralPrimitive, CallMember } from 'aurelia-binding'; -import { Optional, Lazy } from 'aurelia-dependency-injection'; -import { TaskQueue } from 'aurelia-task-queue'; -import { customAttribute, bindable, BindingLanguage, ViewResources } from 'aurelia-templating'; -import { getLogger } from 'aurelia-logging'; - -/** - * Gets the DOM element associated with the data-binding. Most of the time it's - * the binding.target but sometimes binding.target is an aurelia custom element, - * or custom attribute which is a javascript "class" instance, so we need to use - * the controller's container to retrieve the actual DOM element. - */ -function getTargetDOMElement(binding, view) { - const target = binding.target; - // DOM element - if (target instanceof Element) { - return target; - } - // custom element or custom attribute - // tslint:disable-next-line:prefer-const - for (let i = 0, ii = view.controllers.length; i < ii; i++) { - const controller = view.controllers[i]; - if (controller.viewModel === target) { - const element = controller.container.get(DOM.Element); - if (element) { - return element; - } - throw new Error(`Unable to locate target element for "${binding.sourceExpression}".`); - } - } - throw new Error(`Unable to locate target element for "${binding.sourceExpression}".`); -} - -function getObject(expression, objectExpression, source) { - const value = objectExpression.evaluate(source, null); - if (value === null || value === undefined || value instanceof Object) { - return value; - } - // tslint:disable-next-line:max-line-length - throw new Error(`The '${objectExpression}' part of '${expression}' evaluates to ${value} instead of an object, null or undefined.`); -} -/** - * Retrieves the object and property name for the specified expression. - * @param expression The expression - * @param source The scope - */ -function getPropertyInfo(expression, source) { - const originalExpression = expression; - while (expression instanceof BindingBehavior || expression instanceof ValueConverter) { - expression = expression.expression; - } - let object; - let propertyName; - if (expression instanceof AccessScope) { - object = getContextFor(expression.name, source, expression.ancestor); - propertyName = expression.name; - } - else if (expression instanceof AccessMember) { - object = getObject(originalExpression, expression.object, source); - propertyName = expression.name; - } - else if (expression instanceof AccessKeyed) { - object = getObject(originalExpression, expression.object, source); - propertyName = expression.key.evaluate(source); - } - else { - throw new Error(`Expression '${originalExpression}' is not compatible with the validate binding-behavior.`); - } - if (object === null || object === undefined) { - return null; - } - return { object, propertyName }; -} - -function isString(value) { - return Object.prototype.toString.call(value) === '[object String]'; -} -function isNumber(value) { - return Object.prototype.toString.call(value) === '[object Number]'; -} - -class PropertyAccessorParser { - constructor(parser) { - this.parser = parser; - } - parse(property) { - if (isString(property) || isNumber(property)) { - return property; - } - const accessorText = getAccessorExpression(property.toString()); - const accessor = this.parser.parse(accessorText); - if (accessor instanceof AccessScope - || accessor instanceof AccessMember && accessor.object instanceof AccessScope) { - return accessor.name; - } - throw new Error(`Invalid property expression: "${accessor}"`); - } -} -PropertyAccessorParser.inject = [Parser]; -function getAccessorExpression(fn) { - /* tslint:disable:max-line-length */ - const classic = /^function\s*\([$_\w\d]+\)\s*\{(?:\s*"use strict";)?\s*(?:[$_\w\d.['"\]+;]+)?\s*return\s+[$_\w\d]+\.([$_\w\d]+)\s*;?\s*\}$/; - /* tslint:enable:max-line-length */ - const arrow = /^\(?[$_\w\d]+\)?\s*=>\s*[$_\w\d]+\.([$_\w\d]+)$/; - const match = classic.exec(fn) || arrow.exec(fn); - if (match === null) { - throw new Error(`Unable to parse accessor function:\n${fn}`); - } - return match[1]; -} - -/*! ***************************************************************************** -Copyright (c) Microsoft Corporation. All rights reserved. -Licensed under the Apache License, Version 2.0 (the "License"); you may not use -this file except in compliance with the License. You may obtain a copy of the -License at http://www.apache.org/licenses/LICENSE-2.0 - -THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED -WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, -MERCHANTABLITY OR NON-INFRINGEMENT. - -See the Apache Version 2.0 License for specific language governing permissions -and limitations under the License. -***************************************************************************** */ - -function __decorate(decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -} - -/** - * Validation triggers. - */ -var validateTrigger; -(function (validateTrigger) { - /** - * Manual validation. Use the controller's `validate()` and `reset()` methods - * to validate all bindings. - */ - validateTrigger[validateTrigger["manual"] = 0] = "manual"; - /** - * Validate the binding when the binding's target element fires a DOM "blur" event. - */ - validateTrigger[validateTrigger["blur"] = 1] = "blur"; - /** - * Validate the binding when it updates the model due to a change in the view. - */ - validateTrigger[validateTrigger["change"] = 2] = "change"; - /** - * Validate the binding when the binding's target element fires a DOM "blur" event and - * when it updates the model due to a change in the view. - */ - validateTrigger[validateTrigger["changeOrBlur"] = 3] = "changeOrBlur"; -})(validateTrigger || (validateTrigger = {})); - -/** - * Validates objects and properties. - */ -class Validator { -} - -/** - * The result of validating an individual validation rule. - */ -class ValidateResult { - /** - * @param rule The rule associated with the result. Validator implementation specific. - * @param object The object that was validated. - * @param propertyName The name of the property that was validated. - * @param error The error, if the result is a validation error. - */ - constructor(rule, object, propertyName, valid, message = null) { - this.rule = rule; - this.object = object; - this.propertyName = propertyName; - this.valid = valid; - this.message = message; - this.id = ValidateResult.nextId++; - } - toString() { - return this.valid ? 'Valid.' : this.message; - } -} -ValidateResult.nextId = 0; - -class ValidateEvent { - constructor( - /** - * The type of validate event. Either "validate" or "reset". - */ - type, - /** - * The controller's current array of errors. For an array containing both - * failed rules and passed rules, use the "results" property. - */ - errors, - /** - * The controller's current array of validate results. This - * includes both passed rules and failed rules. For an array of only failed rules, - * use the "errors" property. - */ - results, - /** - * The instruction passed to the "validate" or "reset" event. Will be null when - * the controller's validate/reset method was called with no instruction argument. - */ - instruction, - /** - * In events with type === "validate", this property will contain the result - * of validating the instruction (see "instruction" property). Use the controllerValidateResult - * to access the validate results specific to the call to "validate" - * (as opposed to using the "results" and "errors" properties to access the controller's entire - * set of results/errors). - */ - controllerValidateResult) { - this.type = type; - this.errors = errors; - this.results = results; - this.instruction = instruction; - this.controllerValidateResult = controllerValidateResult; - } -} - -/** - * Orchestrates validation. - * Manages a set of bindings, renderers and objects. - * Exposes the current list of validation results for binding purposes. - */ -class ValidationController { - constructor(validator, propertyParser) { - this.validator = validator; - this.propertyParser = propertyParser; - // Registered bindings (via the validate binding behavior) - this.bindings = new Map(); - // Renderers that have been added to the controller instance. - this.renderers = []; - /** - * Validation results that have been rendered by the controller. - */ - this.results = []; - /** - * Validation errors that have been rendered by the controller. - */ - this.errors = []; - /** - * Whether the controller is currently validating. - */ - this.validating = false; - // Elements related to validation results that have been rendered. - this.elements = new Map(); - // Objects that have been added to the controller instance (entity-style validation). - this.objects = new Map(); - /** - * The trigger that will invoke automatic validation of a property used in a binding. - */ - this.validateTrigger = validateTrigger.blur; - // Promise that resolves when validation has completed. - this.finishValidating = Promise.resolve(); - this.eventCallbacks = []; - } - /** - * Subscribe to controller validate and reset events. These events occur when the - * controller's "validate"" and "reset" methods are called. - * @param callback The callback to be invoked when the controller validates or resets. - */ - subscribe(callback) { - this.eventCallbacks.push(callback); - return { - dispose: () => { - const index = this.eventCallbacks.indexOf(callback); - if (index === -1) { - return; - } - this.eventCallbacks.splice(index, 1); - } - }; - } - /** - * Adds an object to the set of objects that should be validated when validate is called. - * @param object The object. - * @param rules Optional. The rules. If rules aren't supplied the Validator implementation will lookup the rules. - */ - addObject(object, rules) { - this.objects.set(object, rules); - } - /** - * Removes an object from the set of objects that should be validated when validate is called. - * @param object The object. - */ - removeObject(object) { - this.objects.delete(object); - this.processResultDelta('reset', this.results.filter(result => result.object === object), []); - } - /** - * Adds and renders an error. - */ - addError(message, object, propertyName = null) { - let resolvedPropertyName; - if (propertyName === null) { - resolvedPropertyName = propertyName; - } - else { - resolvedPropertyName = this.propertyParser.parse(propertyName); - } - const result = new ValidateResult({ __manuallyAdded__: true }, object, resolvedPropertyName, false, message); - this.processResultDelta('validate', [], [result]); - return result; - } - /** - * Removes and unrenders an error. - */ - removeError(result) { - if (this.results.indexOf(result) !== -1) { - this.processResultDelta('reset', [result], []); - } - } - /** - * Adds a renderer. - * @param renderer The renderer. - */ - addRenderer(renderer) { - this.renderers.push(renderer); - renderer.render({ - kind: 'validate', - render: this.results.map(result => ({ result, elements: this.elements.get(result) })), - unrender: [] - }); - } - /** - * Removes a renderer. - * @param renderer The renderer. - */ - removeRenderer(renderer) { - this.renderers.splice(this.renderers.indexOf(renderer), 1); - renderer.render({ - kind: 'reset', - render: [], - unrender: this.results.map(result => ({ result, elements: this.elements.get(result) })) - }); - } - /** - * Registers a binding with the controller. - * @param binding The binding instance. - * @param target The DOM element. - * @param rules (optional) rules associated with the binding. Validator implementation specific. - */ - registerBinding(binding, target, rules) { - this.bindings.set(binding, { target, rules, propertyInfo: null }); - } - /** - * Unregisters a binding with the controller. - * @param binding The binding instance. - */ - unregisterBinding(binding) { - this.resetBinding(binding); - this.bindings.delete(binding); - } - /** - * Interprets the instruction and returns a predicate that will identify - * relevant results in the list of rendered validation results. - */ - getInstructionPredicate(instruction) { - if (instruction) { - const { object, propertyName, rules } = instruction; - let predicate; - if (instruction.propertyName) { - predicate = x => x.object === object && x.propertyName === propertyName; - } - else { - predicate = x => x.object === object; - } - if (rules) { - return x => predicate(x) && this.validator.ruleExists(rules, x.rule); - } - return predicate; - } - else { - return () => true; - } - } - /** - * Validates and renders results. - * @param instruction Optional. Instructions on what to validate. If undefined, all - * objects and bindings will be validated. - */ - validate(instruction) { - // Get a function that will process the validation instruction. - let execute; - if (instruction) { - // tslint:disable-next-line:prefer-const - let { object, propertyName, rules } = instruction; - // if rules were not specified, check the object map. - rules = rules || this.objects.get(object); - // property specified? - if (instruction.propertyName === undefined) { - // validate the specified object. - execute = () => this.validator.validateObject(object, rules); - } - else { - // validate the specified property. - execute = () => this.validator.validateProperty(object, propertyName, rules); - } - } - else { - // validate all objects and bindings. - execute = () => { - const promises = []; - for (const [object, rules] of Array.from(this.objects)) { - promises.push(this.validator.validateObject(object, rules)); - } - for (const [binding, { rules }] of Array.from(this.bindings)) { - const propertyInfo = getPropertyInfo(binding.sourceExpression, binding.source); - if (!propertyInfo || this.objects.has(propertyInfo.object)) { - continue; - } - promises.push(this.validator.validateProperty(propertyInfo.object, propertyInfo.propertyName, rules)); - } - return Promise.all(promises).then(resultSets => resultSets.reduce((a, b) => a.concat(b), [])); - }; - } - // Wait for any existing validation to finish, execute the instruction, render the results. - this.validating = true; - const returnPromise = this.finishValidating - .then(execute) - .then((newResults) => { - const predicate = this.getInstructionPredicate(instruction); - const oldResults = this.results.filter(predicate); - this.processResultDelta('validate', oldResults, newResults); - if (returnPromise === this.finishValidating) { - this.validating = false; - } - const result = { - instruction, - valid: newResults.find(x => !x.valid) === undefined, - results: newResults - }; - this.invokeCallbacks(instruction, result); - return result; - }) - .catch(exception => { - // recover, to enable subsequent calls to validate() - this.validating = false; - this.finishValidating = Promise.resolve(); - return Promise.reject(exception); - }); - this.finishValidating = returnPromise; - return returnPromise; - } - /** - * Resets any rendered validation results (unrenders). - * @param instruction Optional. Instructions on what to reset. If unspecified all rendered results - * will be unrendered. - */ - reset(instruction) { - const predicate = this.getInstructionPredicate(instruction); - const oldResults = this.results.filter(predicate); - this.processResultDelta('reset', oldResults, []); - this.invokeCallbacks(instruction, null); - } - /** - * Gets the elements associated with an object and propertyName (if any). - */ - getAssociatedElements({ object, propertyName }) { - const elements = []; - for (const [binding, { target }] of Array.from(this.bindings)) { - const propertyInfo = getPropertyInfo(binding.sourceExpression, binding.source); - if (propertyInfo && propertyInfo.object === object && propertyInfo.propertyName === propertyName) { - elements.push(target); - } - } - return elements; - } - processResultDelta(kind, oldResults, newResults) { - // prepare the instruction. - const instruction = { - kind, - render: [], - unrender: [] - }; - // create a shallow copy of newResults so we can mutate it without causing side-effects. - newResults = newResults.slice(0); - // create unrender instructions from the old results. - for (const oldResult of oldResults) { - // get the elements associated with the old result. - const elements = this.elements.get(oldResult); - // remove the old result from the element map. - this.elements.delete(oldResult); - // create the unrender instruction. - instruction.unrender.push({ result: oldResult, elements }); - // determine if there's a corresponding new result for the old result we are unrendering. - const newResultIndex = newResults.findIndex(x => x.rule === oldResult.rule && x.object === oldResult.object && x.propertyName === oldResult.propertyName); - if (newResultIndex === -1) { - // no corresponding new result... simple remove. - this.results.splice(this.results.indexOf(oldResult), 1); - if (!oldResult.valid) { - this.errors.splice(this.errors.indexOf(oldResult), 1); - } - } - else { - // there is a corresponding new result... - const newResult = newResults.splice(newResultIndex, 1)[0]; - // get the elements that are associated with the new result. - const elements = this.getAssociatedElements(newResult); - this.elements.set(newResult, elements); - // create a render instruction for the new result. - instruction.render.push({ result: newResult, elements }); - // do an in-place replacement of the old result with the new result. - // this ensures any repeats bound to this.results will not thrash. - this.results.splice(this.results.indexOf(oldResult), 1, newResult); - if (!oldResult.valid && newResult.valid) { - this.errors.splice(this.errors.indexOf(oldResult), 1); - } - else if (!oldResult.valid && !newResult.valid) { - this.errors.splice(this.errors.indexOf(oldResult), 1, newResult); - } - else if (!newResult.valid) { - this.errors.push(newResult); - } - } - } - // create render instructions from the remaining new results. - for (const result of newResults) { - const elements = this.getAssociatedElements(result); - instruction.render.push({ result, elements }); - this.elements.set(result, elements); - this.results.push(result); - if (!result.valid) { - this.errors.push(result); - } - } - // render. - for (const renderer of this.renderers) { - renderer.render(instruction); - } - } - /** - * Validates the property associated with a binding. - */ - validateBinding(binding) { - if (!binding.isBound) { - return; - } - const propertyInfo = getPropertyInfo(binding.sourceExpression, binding.source); - let rules; - const registeredBinding = this.bindings.get(binding); - if (registeredBinding) { - rules = registeredBinding.rules; - registeredBinding.propertyInfo = propertyInfo; - } - if (!propertyInfo) { - return; - } - const { object, propertyName } = propertyInfo; - this.validate({ object, propertyName, rules }); - } - /** - * Resets the results for a property associated with a binding. - */ - resetBinding(binding) { - const registeredBinding = this.bindings.get(binding); - let propertyInfo = getPropertyInfo(binding.sourceExpression, binding.source); - if (!propertyInfo && registeredBinding) { - propertyInfo = registeredBinding.propertyInfo; - } - if (registeredBinding) { - registeredBinding.propertyInfo = null; - } - if (!propertyInfo) { - return; - } - const { object, propertyName } = propertyInfo; - this.reset({ object, propertyName }); - } - /** - * Changes the controller's validateTrigger. - * @param newTrigger The new validateTrigger - */ - changeTrigger(newTrigger) { - this.validateTrigger = newTrigger; - const bindings = Array.from(this.bindings.keys()); - for (const binding of bindings) { - const source = binding.source; - binding.unbind(); - binding.bind(source); - } - } - /** - * Revalidates the controller's current set of errors. - */ - revalidateErrors() { - for (const { object, propertyName, rule } of this.errors) { - if (rule.__manuallyAdded__) { - continue; - } - const rules = [[rule]]; - this.validate({ object, propertyName, rules }); - } - } - invokeCallbacks(instruction, result) { - if (this.eventCallbacks.length === 0) { - return; - } - const event = new ValidateEvent(result ? 'validate' : 'reset', this.errors, this.results, instruction || null, result); - for (let i = 0; i < this.eventCallbacks.length; i++) { - this.eventCallbacks[i](event); - } - } -} -ValidationController.inject = [Validator, PropertyAccessorParser]; - -/** - * Binding behavior. Indicates the bound property should be validated. - */ -class ValidateBindingBehaviorBase { - constructor(taskQueue) { - this.taskQueue = taskQueue; - } - bind(binding, source, rulesOrController, rules) { - // identify the target element. - const target = getTargetDOMElement(binding, source); - // locate the controller. - let controller; - if (rulesOrController instanceof ValidationController) { - controller = rulesOrController; - } - else { - controller = source.container.get(Optional.of(ValidationController)); - rules = rulesOrController; - } - if (controller === null) { - throw new Error(`A ValidationController has not been registered.`); - } - controller.registerBinding(binding, target, rules); - binding.validationController = controller; - const trigger = this.getValidateTrigger(controller); - // tslint:disable-next-line:no-bitwise - if (trigger & validateTrigger.change) { - binding.vbbUpdateSource = binding.updateSource; - // tslint:disable-next-line:only-arrow-functions - // tslint:disable-next-line:space-before-function-paren - binding.updateSource = function (value) { - this.vbbUpdateSource(value); - this.validationController.validateBinding(this); - }; - } - // tslint:disable-next-line:no-bitwise - if (trigger & validateTrigger.blur) { - binding.validateBlurHandler = () => { - this.taskQueue.queueMicroTask(() => controller.validateBinding(binding)); - }; - binding.validateTarget = target; - target.addEventListener('blur', binding.validateBlurHandler); - } - if (trigger !== validateTrigger.manual) { - binding.standardUpdateTarget = binding.updateTarget; - // tslint:disable-next-line:only-arrow-functions - // tslint:disable-next-line:space-before-function-paren - binding.updateTarget = function (value) { - this.standardUpdateTarget(value); - this.validationController.resetBinding(this); - }; - } - } - unbind(binding) { - // reset the binding to it's original state. - if (binding.vbbUpdateSource) { - binding.updateSource = binding.vbbUpdateSource; - binding.vbbUpdateSource = null; - } - if (binding.standardUpdateTarget) { - binding.updateTarget = binding.standardUpdateTarget; - binding.standardUpdateTarget = null; - } - if (binding.validateBlurHandler) { - binding.validateTarget.removeEventListener('blur', binding.validateBlurHandler); - binding.validateBlurHandler = null; - binding.validateTarget = null; - } - binding.validationController.unregisterBinding(binding); - binding.validationController = null; - } -} - -/** - * Binding behavior. Indicates the bound property should be validated - * when the validate trigger specified by the associated controller's - * validateTrigger property occurs. - */ -let ValidateBindingBehavior = class ValidateBindingBehavior extends ValidateBindingBehaviorBase { - getValidateTrigger(controller) { - return controller.validateTrigger; - } -}; -ValidateBindingBehavior.inject = [TaskQueue]; -ValidateBindingBehavior = __decorate([ - bindingBehavior('validate') -], ValidateBindingBehavior); -/** - * Binding behavior. Indicates the bound property will be validated - * manually, by calling controller.validate(). No automatic validation - * triggered by data-entry or blur will occur. - */ -let ValidateManuallyBindingBehavior = class ValidateManuallyBindingBehavior extends ValidateBindingBehaviorBase { - getValidateTrigger() { - return validateTrigger.manual; - } -}; -ValidateManuallyBindingBehavior.inject = [TaskQueue]; -ValidateManuallyBindingBehavior = __decorate([ - bindingBehavior('validateManually') -], ValidateManuallyBindingBehavior); -/** - * Binding behavior. Indicates the bound property should be validated - * when the associated element blurs. - */ -let ValidateOnBlurBindingBehavior = class ValidateOnBlurBindingBehavior extends ValidateBindingBehaviorBase { - getValidateTrigger() { - return validateTrigger.blur; - } -}; -ValidateOnBlurBindingBehavior.inject = [TaskQueue]; -ValidateOnBlurBindingBehavior = __decorate([ - bindingBehavior('validateOnBlur') -], ValidateOnBlurBindingBehavior); -/** - * Binding behavior. Indicates the bound property should be validated - * when the associated element is changed by the user, causing a change - * to the model. - */ -let ValidateOnChangeBindingBehavior = class ValidateOnChangeBindingBehavior extends ValidateBindingBehaviorBase { - getValidateTrigger() { - return validateTrigger.change; - } -}; -ValidateOnChangeBindingBehavior.inject = [TaskQueue]; -ValidateOnChangeBindingBehavior = __decorate([ - bindingBehavior('validateOnChange') -], ValidateOnChangeBindingBehavior); -/** - * Binding behavior. Indicates the bound property should be validated - * when the associated element blurs or is changed by the user, causing - * a change to the model. - */ -let ValidateOnChangeOrBlurBindingBehavior = class ValidateOnChangeOrBlurBindingBehavior extends ValidateBindingBehaviorBase { - getValidateTrigger() { - return validateTrigger.changeOrBlur; - } -}; -ValidateOnChangeOrBlurBindingBehavior.inject = [TaskQueue]; -ValidateOnChangeOrBlurBindingBehavior = __decorate([ - bindingBehavior('validateOnChangeOrBlur') -], ValidateOnChangeOrBlurBindingBehavior); - -/** - * Creates ValidationController instances. - */ -class ValidationControllerFactory { - constructor(container) { - this.container = container; - } - static get(container) { - return new ValidationControllerFactory(container); - } - /** - * Creates a new controller instance. - */ - create(validator) { - if (!validator) { - validator = this.container.get(Validator); - } - const propertyParser = this.container.get(PropertyAccessorParser); - return new ValidationController(validator, propertyParser); - } - /** - * Creates a new controller and registers it in the current element's container so that it's - * available to the validate binding behavior and renderers. - */ - createForCurrentScope(validator) { - const controller = this.create(validator); - this.container.registerInstance(ValidationController, controller); - return controller; - } -} -ValidationControllerFactory['protocol:aurelia:resolver'] = true; - -let ValidationErrorsCustomAttribute = class ValidationErrorsCustomAttribute { - constructor(boundaryElement, controllerAccessor) { - this.boundaryElement = boundaryElement; - this.controllerAccessor = controllerAccessor; - this.controller = null; - this.errors = []; - this.errorsInternal = []; - } - static inject() { - return [DOM.Element, Lazy.of(ValidationController)]; - } - sort() { - this.errorsInternal.sort((a, b) => { - if (a.targets[0] === b.targets[0]) { - return 0; - } - // tslint:disable-next-line:no-bitwise - return a.targets[0].compareDocumentPosition(b.targets[0]) & 2 ? 1 : -1; - }); - } - interestingElements(elements) { - return elements.filter(e => this.boundaryElement.contains(e)); - } - render(instruction) { - for (const { result } of instruction.unrender) { - const index = this.errorsInternal.findIndex(x => x.error === result); - if (index !== -1) { - this.errorsInternal.splice(index, 1); - } - } - for (const { result, elements } of instruction.render) { - if (result.valid) { - continue; - } - const targets = this.interestingElements(elements); - if (targets.length) { - this.errorsInternal.push({ error: result, targets }); - } - } - this.sort(); - this.errors = this.errorsInternal; - } - bind() { - if (!this.controller) { - this.controller = this.controllerAccessor(); - } - // this will call render() with the side-effect of updating this.errors - this.controller.addRenderer(this); - } - unbind() { - if (this.controller) { - this.controller.removeRenderer(this); - } - } -}; -__decorate([ - bindable({ defaultBindingMode: bindingMode.oneWay }) -], ValidationErrorsCustomAttribute.prototype, "controller", void 0); -__decorate([ - bindable({ primaryProperty: true, defaultBindingMode: bindingMode.twoWay }) -], ValidationErrorsCustomAttribute.prototype, "errors", void 0); -ValidationErrorsCustomAttribute = __decorate([ - customAttribute('validation-errors') -], ValidationErrorsCustomAttribute); - -let ValidationRendererCustomAttribute = class ValidationRendererCustomAttribute { - created(view) { - this.container = view.container; - } - bind() { - this.controller = this.container.get(ValidationController); - this.renderer = this.container.get(this.value); - this.controller.addRenderer(this.renderer); - } - unbind() { - this.controller.removeRenderer(this.renderer); - this.controller = null; - this.renderer = null; - } -}; -ValidationRendererCustomAttribute = __decorate([ - customAttribute('validation-renderer') -], ValidationRendererCustomAttribute); - -/** - * Sets, unsets and retrieves rules on an object or constructor function. - */ -class Rules { - /** - * Applies the rules to a target. - */ - static set(target, rules) { - if (target instanceof Function) { - target = target.prototype; - } - Object.defineProperty(target, Rules.key, { enumerable: false, configurable: false, writable: true, value: rules }); - } - /** - * Removes rules from a target. - */ - static unset(target) { - if (target instanceof Function) { - target = target.prototype; - } - target[Rules.key] = null; - } - /** - * Retrieves the target's rules. - */ - static get(target) { - return target[Rules.key] || null; - } -} -/** - * The name of the property that stores the rules. - */ -Rules.key = '__rules__'; - -// tslint:disable:no-empty -class ExpressionVisitor { - visitChain(chain) { - this.visitArgs(chain.expressions); - } - visitBindingBehavior(behavior) { - behavior.expression.accept(this); - this.visitArgs(behavior.args); - } - visitValueConverter(converter) { - converter.expression.accept(this); - this.visitArgs(converter.args); - } - visitAssign(assign) { - assign.target.accept(this); - assign.value.accept(this); - } - visitConditional(conditional) { - conditional.condition.accept(this); - conditional.yes.accept(this); - conditional.no.accept(this); - } - visitAccessThis(access) { - access.ancestor = access.ancestor; - } - visitAccessScope(access) { - access.name = access.name; - } - visitAccessMember(access) { - access.object.accept(this); - } - visitAccessKeyed(access) { - access.object.accept(this); - access.key.accept(this); - } - visitCallScope(call) { - this.visitArgs(call.args); - } - visitCallFunction(call) { - call.func.accept(this); - this.visitArgs(call.args); - } - visitCallMember(call) { - call.object.accept(this); - this.visitArgs(call.args); - } - visitPrefix(prefix) { - prefix.expression.accept(this); - } - visitBinary(binary) { - binary.left.accept(this); - binary.right.accept(this); - } - visitLiteralPrimitive(literal) { - literal.value = literal.value; - } - visitLiteralArray(literal) { - this.visitArgs(literal.elements); - } - visitLiteralObject(literal) { - this.visitArgs(literal.values); - } - visitLiteralString(literal) { - literal.value = literal.value; - } - visitArgs(args) { - for (let i = 0; i < args.length; i++) { - args[i].accept(this); - } - } -} - -class ValidationMessageParser { - constructor(bindinqLanguage) { - this.bindinqLanguage = bindinqLanguage; - this.emptyStringExpression = new LiteralString(''); - this.nullExpression = new LiteralPrimitive(null); - this.undefinedExpression = new LiteralPrimitive(undefined); - this.cache = {}; - } - parse(message) { - if (this.cache[message] !== undefined) { - return this.cache[message]; - } - const parts = this.bindinqLanguage.parseInterpolation(null, message); - if (parts === null) { - return new LiteralString(message); - } - let expression = new LiteralString(parts[0]); - for (let i = 1; i < parts.length; i += 2) { - expression = new Binary('+', expression, new Binary('+', this.coalesce(parts[i]), new LiteralString(parts[i + 1]))); - } - MessageExpressionValidator.validate(expression, message); - this.cache[message] = expression; - return expression; - } - coalesce(part) { - // part === null || part === undefined ? '' : part - return new Conditional(new Binary('||', new Binary('===', part, this.nullExpression), new Binary('===', part, this.undefinedExpression)), this.emptyStringExpression, new CallMember(part, 'toString', [])); - } -} -ValidationMessageParser.inject = [BindingLanguage]; -class MessageExpressionValidator extends ExpressionVisitor { - constructor(originalMessage) { - super(); - this.originalMessage = originalMessage; - } - static validate(expression, originalMessage) { - const visitor = new MessageExpressionValidator(originalMessage); - expression.accept(visitor); - } - visitAccessScope(access) { - if (access.ancestor !== 0) { - throw new Error('$parent is not permitted in validation message expressions.'); - } - if (['displayName', 'propertyName', 'value', 'object', 'config', 'getDisplayName'].indexOf(access.name) !== -1) { - getLogger('aurelia-validation') - // tslint:disable-next-line:max-line-length - .warn(`Did you mean to use "$${access.name}" instead of "${access.name}" in this validation message template: "${this.originalMessage}"?`); - } - } -} - -/** - * Dictionary of validation messages. [messageKey]: messageExpression - */ -const validationMessages = { - /** - * The default validation message. Used with rules that have no standard message. - */ - default: `\${$displayName} is invalid.`, - required: `\${$displayName} is required.`, - matches: `\${$displayName} is not correctly formatted.`, - email: `\${$displayName} is not a valid email.`, - minLength: `\${$displayName} must be at least \${$config.length} character\${$config.length === 1 ? '' : 's'}.`, - maxLength: `\${$displayName} cannot be longer than \${$config.length} character\${$config.length === 1 ? '' : 's'}.`, - minItems: `\${$displayName} must contain at least \${$config.count} item\${$config.count === 1 ? '' : 's'}.`, - maxItems: `\${$displayName} cannot contain more than \${$config.count} item\${$config.count === 1 ? '' : 's'}.`, - equals: `\${$displayName} must be \${$config.expectedValue}.`, -}; -/** - * Retrieves validation messages and property display names. - */ -class ValidationMessageProvider { - constructor(parser) { - this.parser = parser; - } - /** - * Returns a message binding expression that corresponds to the key. - * @param key The message key. - */ - getMessage(key) { - let message; - if (key in validationMessages) { - message = validationMessages[key]; - } - else { - message = validationMessages['default']; - } - return this.parser.parse(message); - } - /** - * Formulates a property display name using the property name and the configured - * displayName (if provided). - * Override this with your own custom logic. - * @param propertyName The property name. - */ - getDisplayName(propertyName, displayName) { - if (displayName !== null && displayName !== undefined) { - return (displayName instanceof Function) ? displayName() : displayName; - } - // split on upper-case letters. - const words = propertyName.toString().split(/(?=[A-Z])/).join(' '); - // capitalize first letter. - return words.charAt(0).toUpperCase() + words.slice(1); - } -} -ValidationMessageProvider.inject = [ValidationMessageParser]; - -/** - * Validates. - * Responsible for validating objects and properties. - */ -class StandardValidator extends Validator { - constructor(messageProvider, resources) { - super(); - this.messageProvider = messageProvider; - this.lookupFunctions = resources.lookupFunctions; - this.getDisplayName = messageProvider.getDisplayName.bind(messageProvider); - } - /** - * Validates the specified property. - * @param object The object to validate. - * @param propertyName The name of the property to validate. - * @param rules Optional. If unspecified, the rules will be looked up using the metadata - * for the object created by ValidationRules....on(class/object) - */ - validateProperty(object, propertyName, rules) { - return this.validate(object, propertyName, rules || null); - } - /** - * Validates all rules for specified object and it's properties. - * @param object The object to validate. - * @param rules Optional. If unspecified, the rules will be looked up using the metadata - * for the object created by ValidationRules....on(class/object) - */ - validateObject(object, rules) { - return this.validate(object, null, rules || null); - } - /** - * Determines whether a rule exists in a set of rules. - * @param rules The rules to search. - * @parem rule The rule to find. - */ - ruleExists(rules, rule) { - let i = rules.length; - while (i--) { - if (rules[i].indexOf(rule) !== -1) { - return true; - } - } - return false; - } - getMessage(rule, object, value) { - const expression = rule.message || this.messageProvider.getMessage(rule.messageKey); - // tslint:disable-next-line:prefer-const - let { name: propertyName, displayName } = rule.property; - if (propertyName !== null) { - displayName = this.messageProvider.getDisplayName(propertyName, displayName); - } - const overrideContext = { - $displayName: displayName, - $propertyName: propertyName, - $value: value, - $object: object, - $config: rule.config, - // returns the name of a given property, given just the property name (irrespective of the property's displayName) - // split on capital letters, first letter ensured to be capitalized - $getDisplayName: this.getDisplayName - }; - return expression.evaluate({ bindingContext: object, overrideContext }, this.lookupFunctions); - } - validateRuleSequence(object, propertyName, ruleSequence, sequence, results) { - // are we validating all properties or a single property? - const validateAllProperties = propertyName === null || propertyName === undefined; - const rules = ruleSequence[sequence]; - let allValid = true; - // validate each rule. - const promises = []; - for (let i = 0; i < rules.length; i++) { - const rule = rules[i]; - // is the rule related to the property we're validating. - // tslint:disable-next-line:triple-equals | Use loose equality for property keys - if (!validateAllProperties && rule.property.name != propertyName) { - continue; - } - // is this a conditional rule? is the condition met? - if (rule.when && !rule.when(object)) { - continue; - } - // validate. - const value = rule.property.name === null ? object : object[rule.property.name]; - let promiseOrBoolean = rule.condition(value, object); - if (!(promiseOrBoolean instanceof Promise)) { - promiseOrBoolean = Promise.resolve(promiseOrBoolean); - } - promises.push(promiseOrBoolean.then(valid => { - const message = valid ? null : this.getMessage(rule, object, value); - results.push(new ValidateResult(rule, object, rule.property.name, valid, message)); - allValid = allValid && valid; - return valid; - })); - } - return Promise.all(promises) - .then(() => { - sequence++; - if (allValid && sequence < ruleSequence.length) { - return this.validateRuleSequence(object, propertyName, ruleSequence, sequence, results); - } - return results; - }); - } - validate(object, propertyName, rules) { - // rules specified? - if (!rules) { - // no. attempt to locate the rules. - rules = Rules.get(object); - } - // any rules? - if (!rules || rules.length === 0) { - return Promise.resolve([]); - } - return this.validateRuleSequence(object, propertyName, rules, 0, []); - } -} -StandardValidator.inject = [ValidationMessageProvider, ViewResources]; - -/** - * Part of the fluent rule API. Enables customizing property rules. - */ -class FluentRuleCustomizer { - constructor(property, condition, config = {}, fluentEnsure, fluentRules, parsers) { - this.fluentEnsure = fluentEnsure; - this.fluentRules = fluentRules; - this.parsers = parsers; - this.rule = { - property, - condition, - config, - when: null, - messageKey: 'default', - message: null, - sequence: fluentRules.sequence - }; - this.fluentEnsure._addRule(this.rule); - } - /** - * Validate subsequent rules after previously declared rules have - * been validated successfully. Use to postpone validation of costly - * rules until less expensive rules pass validation. - */ - then() { - this.fluentRules.sequence++; - return this; - } - /** - * Specifies the key to use when looking up the rule's validation message. - */ - withMessageKey(key) { - this.rule.messageKey = key; - this.rule.message = null; - return this; - } - /** - * Specifies rule's validation message. - */ - withMessage(message) { - this.rule.messageKey = 'custom'; - this.rule.message = this.parsers.message.parse(message); - return this; - } - /** - * Specifies a condition that must be met before attempting to validate the rule. - * @param condition A function that accepts the object as a parameter and returns true - * or false whether the rule should be evaluated. - */ - when(condition) { - this.rule.when = condition; - return this; - } - /** - * Tags the rule instance, enabling the rule to be found easily - * using ValidationRules.taggedRules(rules, tag) - */ - tag(tag) { - this.rule.tag = tag; - return this; - } - ///// FluentEnsure APIs ///// - /** - * Target a property with validation rules. - * @param property The property to target. Can be the property name or a property accessor function. - */ - ensure(subject) { - return this.fluentEnsure.ensure(subject); - } - /** - * Targets an object with validation rules. - */ - ensureObject() { - return this.fluentEnsure.ensureObject(); - } - /** - * Rules that have been defined using the fluent API. - */ - get rules() { - return this.fluentEnsure.rules; - } - /** - * Applies the rules to a class or object, making them discoverable by the StandardValidator. - * @param target A class or object. - */ - on(target) { - return this.fluentEnsure.on(target); - } - ///////// FluentRules APIs ///////// - /** - * Applies an ad-hoc rule function to the ensured property or object. - * @param condition The function to validate the rule. - * Will be called with two arguments, the property value and the object. - * Should return a boolean or a Promise that resolves to a boolean. - */ - satisfies(condition, config) { - return this.fluentRules.satisfies(condition, config); - } - /** - * Applies a rule by name. - * @param name The name of the custom or standard rule. - * @param args The rule's arguments. - */ - satisfiesRule(name, ...args) { - return this.fluentRules.satisfiesRule(name, ...args); - } - /** - * Applies the "required" rule to the property. - * The value cannot be null, undefined or whitespace. - */ - required() { - return this.fluentRules.required(); - } - /** - * Applies the "matches" rule to the property. - * Value must match the specified regular expression. - * null, undefined and empty-string values are considered valid. - */ - matches(regex) { - return this.fluentRules.matches(regex); - } - /** - * Applies the "email" rule to the property. - * null, undefined and empty-string values are considered valid. - */ - email() { - return this.fluentRules.email(); - } - /** - * Applies the "minLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - minLength(length) { - return this.fluentRules.minLength(length); - } - /** - * Applies the "maxLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - maxLength(length) { - return this.fluentRules.maxLength(length); - } - /** - * Applies the "minItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - minItems(count) { - return this.fluentRules.minItems(count); - } - /** - * Applies the "maxItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - maxItems(count) { - return this.fluentRules.maxItems(count); - } - /** - * Applies the "equals" validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - equals(expectedValue) { - return this.fluentRules.equals(expectedValue); - } -} -/** - * Part of the fluent rule API. Enables applying rules to properties and objects. - */ -class FluentRules { - constructor(fluentEnsure, parsers, property) { - this.fluentEnsure = fluentEnsure; - this.parsers = parsers; - this.property = property; - /** - * Current rule sequence number. Used to postpone evaluation of rules until rules - * with lower sequence number have successfully validated. The "then" fluent API method - * manages this property, there's usually no need to set it directly. - */ - this.sequence = 0; - } - /** - * Sets the display name of the ensured property. - */ - displayName(name) { - this.property.displayName = name; - return this; - } - /** - * Applies an ad-hoc rule function to the ensured property or object. - * @param condition The function to validate the rule. - * Will be called with two arguments, the property value and the object. - * Should return a boolean or a Promise that resolves to a boolean. - */ - satisfies(condition, config) { - return new FluentRuleCustomizer(this.property, condition, config, this.fluentEnsure, this, this.parsers); - } - /** - * Applies a rule by name. - * @param name The name of the custom or standard rule. - * @param args The rule's arguments. - */ - satisfiesRule(name, ...args) { - let rule = FluentRules.customRules[name]; - if (!rule) { - // standard rule? - rule = this[name]; - if (rule instanceof Function) { - return rule.call(this, ...args); - } - throw new Error(`Rule with name "${name}" does not exist.`); - } - const config = rule.argsToConfig ? rule.argsToConfig(...args) : undefined; - return this.satisfies((value, obj) => rule.condition.call(this, value, obj, ...args), config) - .withMessageKey(name); - } - /** - * Applies the "required" rule to the property. - * The value cannot be null, undefined or whitespace. - */ - required() { - return this.satisfies(value => value !== null - && value !== undefined - && !(isString(value) && !/\S/.test(value))).withMessageKey('required'); - } - /** - * Applies the "matches" rule to the property. - * Value must match the specified regular expression. - * null, undefined and empty-string values are considered valid. - */ - matches(regex) { - return this.satisfies(value => value === null || value === undefined || value.length === 0 || regex.test(value)) - .withMessageKey('matches'); - } - /** - * Applies the "email" rule to the property. - * null, undefined and empty-string values are considered valid. - */ - email() { - // regex from https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address - /* tslint:disable:max-line-length */ - return this.matches(/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/) - /* tslint:enable:max-line-length */ - .withMessageKey('email'); - } - /** - * Applies the "minLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - minLength(length) { - return this.satisfies((value) => value === null || value === undefined || value.length === 0 || value.length >= length, { length }) - .withMessageKey('minLength'); - } - /** - * Applies the "maxLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - maxLength(length) { - return this.satisfies((value) => value === null || value === undefined || value.length === 0 || value.length <= length, { length }) - .withMessageKey('maxLength'); - } - /** - * Applies the "minItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - minItems(count) { - return this.satisfies((value) => value === null || value === undefined || value.length >= count, { count }) - .withMessageKey('minItems'); - } - /** - * Applies the "maxItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - maxItems(count) { - return this.satisfies((value) => value === null || value === undefined || value.length <= count, { count }) - .withMessageKey('maxItems'); - } - /** - * Applies the "equals" validation rule to the property. - * null and undefined values are considered valid. - */ - equals(expectedValue) { - return this.satisfies(value => value === null || value === undefined || value === '' || value === expectedValue, { expectedValue }) - .withMessageKey('equals'); - } -} -FluentRules.customRules = {}; -/** - * Part of the fluent rule API. Enables targeting properties and objects with rules. - */ -class FluentEnsure { - constructor(parsers) { - this.parsers = parsers; - /** - * Rules that have been defined using the fluent API. - */ - this.rules = []; - } - /** - * Target a property with validation rules. - * @param property The property to target. Can be the property name or a property accessor - * function. - */ - ensure(property) { - this.assertInitialized(); - const name = this.parsers.property.parse(property); - const fluentRules = new FluentRules(this, this.parsers, { name, displayName: null }); - return this.mergeRules(fluentRules, name); - } - /** - * Targets an object with validation rules. - */ - ensureObject() { - this.assertInitialized(); - const fluentRules = new FluentRules(this, this.parsers, { name: null, displayName: null }); - return this.mergeRules(fluentRules, null); - } - /** - * Applies the rules to a class or object, making them discoverable by the StandardValidator. - * @param target A class or object. - */ - on(target) { - Rules.set(target, this.rules); - return this; - } - /** - * Adds a rule definition to the sequenced ruleset. - * @internal - */ - _addRule(rule) { - while (this.rules.length < rule.sequence + 1) { - this.rules.push([]); - } - this.rules[rule.sequence].push(rule); - } - assertInitialized() { - if (this.parsers) { - return; - } - throw new Error(`Did you forget to add ".plugin('aurelia-validation')" to your main.js?`); - } - mergeRules(fluentRules, propertyName) { - // tslint:disable-next-line:triple-equals | Use loose equality for property keys - const existingRules = this.rules.find(r => r.length > 0 && r[0].property.name == propertyName); - if (existingRules) { - const rule = existingRules[existingRules.length - 1]; - fluentRules.sequence = rule.sequence; - if (rule.property.displayName !== null) { - fluentRules = fluentRules.displayName(rule.property.displayName); - } - } - return fluentRules; - } -} -/** - * Fluent rule definition API. - */ -class ValidationRules { - static initialize(messageParser, propertyParser) { - this.parsers = { - message: messageParser, - property: propertyParser - }; - } - /** - * Target a property with validation rules. - * @param property The property to target. Can be the property name or a property accessor function. - */ - static ensure(property) { - return new FluentEnsure(ValidationRules.parsers).ensure(property); - } - /** - * Targets an object with validation rules. - */ - static ensureObject() { - return new FluentEnsure(ValidationRules.parsers).ensureObject(); - } - /** - * Defines a custom rule. - * @param name The name of the custom rule. Also serves as the message key. - * @param condition The rule function. - * @param message The message expression - * @param argsToConfig A function that maps the rule's arguments to a "config" - * object that can be used when evaluating the message expression. - */ - static customRule(name, condition, message, argsToConfig) { - validationMessages[name] = message; - FluentRules.customRules[name] = { condition, argsToConfig }; - } - /** - * Returns rules with the matching tag. - * @param rules The rules to search. - * @param tag The tag to search for. - */ - static taggedRules(rules, tag) { - return rules.map(x => x.filter(r => r.tag === tag)); - } - /** - * Returns rules that have no tag. - * @param rules The rules to search. - */ - static untaggedRules(rules) { - return rules.map(x => x.filter(r => r.tag === undefined)); - } - /** - * Removes the rules from a class or object. - * @param target A class or object. - */ - static off(target) { - Rules.unset(target); - } -} - -// Exports -/** - * Aurelia Validation Configuration API - */ -class AureliaValidationConfiguration { - constructor() { - this.validatorType = StandardValidator; - } - /** - * Use a custom Validator implementation. - */ - customValidator(type) { - this.validatorType = type; - } - /** - * Applies the configuration. - */ - apply(container) { - const validator = container.get(this.validatorType); - container.registerInstance(Validator, validator); - } -} -/** - * Configures the plugin. - */ -function configure( -// tslint:disable-next-line:ban-types -frameworkConfig, callback) { - // the fluent rule definition API needs the parser to translate messages - // to interpolation expressions. - const messageParser = frameworkConfig.container.get(ValidationMessageParser); - const propertyParser = frameworkConfig.container.get(PropertyAccessorParser); - ValidationRules.initialize(messageParser, propertyParser); - // configure... - const config = new AureliaValidationConfiguration(); - if (callback instanceof Function) { - callback(config); - } - config.apply(frameworkConfig.container); - // globalize the behaviors. - if (frameworkConfig.globalResources) { - frameworkConfig.globalResources(ValidateBindingBehavior, ValidateManuallyBindingBehavior, ValidateOnBlurBindingBehavior, ValidateOnChangeBindingBehavior, ValidateOnChangeOrBlurBindingBehavior, ValidationErrorsCustomAttribute, ValidationRendererCustomAttribute); - } -} - -export { AureliaValidationConfiguration, configure, getTargetDOMElement, getPropertyInfo, PropertyAccessorParser, getAccessorExpression, ValidateBindingBehavior, ValidateManuallyBindingBehavior, ValidateOnBlurBindingBehavior, ValidateOnChangeBindingBehavior, ValidateOnChangeOrBlurBindingBehavior, ValidateEvent, ValidateResult, validateTrigger, ValidationController, ValidationControllerFactory, ValidationErrorsCustomAttribute, ValidationRendererCustomAttribute, Validator, Rules, StandardValidator, validationMessages, ValidationMessageProvider, ValidationMessageParser, MessageExpressionValidator, FluentRuleCustomizer, FluentRules, FluentEnsure, ValidationRules }; diff --git a/dist/es2017/aurelia-validation.js b/dist/es2017/aurelia-validation.js deleted file mode 100644 index 4c67ca36..00000000 --- a/dist/es2017/aurelia-validation.js +++ /dev/null @@ -1,1661 +0,0 @@ -import { DOM } from 'aurelia-pal'; -import { AccessMember, AccessScope, AccessKeyed, BindingBehavior, ValueConverter, getContextFor, Parser, bindingBehavior, bindingMode, LiteralString, Binary, Conditional, LiteralPrimitive, CallMember } from 'aurelia-binding'; -import { Optional, Lazy } from 'aurelia-dependency-injection'; -import { TaskQueue } from 'aurelia-task-queue'; -import { customAttribute, bindable, BindingLanguage, ViewResources } from 'aurelia-templating'; -import { getLogger } from 'aurelia-logging'; - -/** - * Gets the DOM element associated with the data-binding. Most of the time it's - * the binding.target but sometimes binding.target is an aurelia custom element, - * or custom attribute which is a javascript "class" instance, so we need to use - * the controller's container to retrieve the actual DOM element. - */ -function getTargetDOMElement(binding, view) { - const target = binding.target; - // DOM element - if (target instanceof Element) { - return target; - } - // custom element or custom attribute - // tslint:disable-next-line:prefer-const - for (let i = 0, ii = view.controllers.length; i < ii; i++) { - const controller = view.controllers[i]; - if (controller.viewModel === target) { - const element = controller.container.get(DOM.Element); - if (element) { - return element; - } - throw new Error(`Unable to locate target element for "${binding.sourceExpression}".`); - } - } - throw new Error(`Unable to locate target element for "${binding.sourceExpression}".`); -} - -function getObject(expression, objectExpression, source) { - const value = objectExpression.evaluate(source, null); - if (value === null || value === undefined || value instanceof Object) { - return value; - } - // tslint:disable-next-line:max-line-length - throw new Error(`The '${objectExpression}' part of '${expression}' evaluates to ${value} instead of an object, null or undefined.`); -} -/** - * Retrieves the object and property name for the specified expression. - * @param expression The expression - * @param source The scope - */ -function getPropertyInfo(expression, source) { - const originalExpression = expression; - while (expression instanceof BindingBehavior || expression instanceof ValueConverter) { - expression = expression.expression; - } - let object; - let propertyName; - if (expression instanceof AccessScope) { - object = getContextFor(expression.name, source, expression.ancestor); - propertyName = expression.name; - } - else if (expression instanceof AccessMember) { - object = getObject(originalExpression, expression.object, source); - propertyName = expression.name; - } - else if (expression instanceof AccessKeyed) { - object = getObject(originalExpression, expression.object, source); - propertyName = expression.key.evaluate(source); - } - else { - throw new Error(`Expression '${originalExpression}' is not compatible with the validate binding-behavior.`); - } - if (object === null || object === undefined) { - return null; - } - return { object, propertyName }; -} - -function isString(value) { - return Object.prototype.toString.call(value) === '[object String]'; -} -function isNumber(value) { - return Object.prototype.toString.call(value) === '[object Number]'; -} - -class PropertyAccessorParser { - constructor(parser) { - this.parser = parser; - } - parse(property) { - if (isString(property) || isNumber(property)) { - return property; - } - const accessorText = getAccessorExpression(property.toString()); - const accessor = this.parser.parse(accessorText); - if (accessor instanceof AccessScope - || accessor instanceof AccessMember && accessor.object instanceof AccessScope) { - return accessor.name; - } - throw new Error(`Invalid property expression: "${accessor}"`); - } -} -PropertyAccessorParser.inject = [Parser]; -function getAccessorExpression(fn) { - /* tslint:disable:max-line-length */ - const classic = /^function\s*\([$_\w\d]+\)\s*\{(?:\s*"use strict";)?\s*(?:[$_\w\d.['"\]+;]+)?\s*return\s+[$_\w\d]+\.([$_\w\d]+)\s*;?\s*\}$/; - /* tslint:enable:max-line-length */ - const arrow = /^\(?[$_\w\d]+\)?\s*=>\s*[$_\w\d]+\.([$_\w\d]+)$/; - const match = classic.exec(fn) || arrow.exec(fn); - if (match === null) { - throw new Error(`Unable to parse accessor function:\n${fn}`); - } - return match[1]; -} - -/*! ***************************************************************************** -Copyright (c) Microsoft Corporation. All rights reserved. -Licensed under the Apache License, Version 2.0 (the "License"); you may not use -this file except in compliance with the License. You may obtain a copy of the -License at http://www.apache.org/licenses/LICENSE-2.0 - -THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED -WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, -MERCHANTABLITY OR NON-INFRINGEMENT. - -See the Apache Version 2.0 License for specific language governing permissions -and limitations under the License. -***************************************************************************** */ - -function __decorate(decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -} - -/** - * Validation triggers. - */ -var validateTrigger; -(function (validateTrigger) { - /** - * Manual validation. Use the controller's `validate()` and `reset()` methods - * to validate all bindings. - */ - validateTrigger[validateTrigger["manual"] = 0] = "manual"; - /** - * Validate the binding when the binding's target element fires a DOM "blur" event. - */ - validateTrigger[validateTrigger["blur"] = 1] = "blur"; - /** - * Validate the binding when it updates the model due to a change in the view. - */ - validateTrigger[validateTrigger["change"] = 2] = "change"; - /** - * Validate the binding when the binding's target element fires a DOM "blur" event and - * when it updates the model due to a change in the view. - */ - validateTrigger[validateTrigger["changeOrBlur"] = 3] = "changeOrBlur"; -})(validateTrigger || (validateTrigger = {})); - -/** - * Validates objects and properties. - */ -class Validator { -} - -/** - * The result of validating an individual validation rule. - */ -class ValidateResult { - /** - * @param rule The rule associated with the result. Validator implementation specific. - * @param object The object that was validated. - * @param propertyName The name of the property that was validated. - * @param error The error, if the result is a validation error. - */ - constructor(rule, object, propertyName, valid, message = null) { - this.rule = rule; - this.object = object; - this.propertyName = propertyName; - this.valid = valid; - this.message = message; - this.id = ValidateResult.nextId++; - } - toString() { - return this.valid ? 'Valid.' : this.message; - } -} -ValidateResult.nextId = 0; - -class ValidateEvent { - constructor( - /** - * The type of validate event. Either "validate" or "reset". - */ - type, - /** - * The controller's current array of errors. For an array containing both - * failed rules and passed rules, use the "results" property. - */ - errors, - /** - * The controller's current array of validate results. This - * includes both passed rules and failed rules. For an array of only failed rules, - * use the "errors" property. - */ - results, - /** - * The instruction passed to the "validate" or "reset" event. Will be null when - * the controller's validate/reset method was called with no instruction argument. - */ - instruction, - /** - * In events with type === "validate", this property will contain the result - * of validating the instruction (see "instruction" property). Use the controllerValidateResult - * to access the validate results specific to the call to "validate" - * (as opposed to using the "results" and "errors" properties to access the controller's entire - * set of results/errors). - */ - controllerValidateResult) { - this.type = type; - this.errors = errors; - this.results = results; - this.instruction = instruction; - this.controllerValidateResult = controllerValidateResult; - } -} - -/** - * Orchestrates validation. - * Manages a set of bindings, renderers and objects. - * Exposes the current list of validation results for binding purposes. - */ -class ValidationController { - constructor(validator, propertyParser) { - this.validator = validator; - this.propertyParser = propertyParser; - // Registered bindings (via the validate binding behavior) - this.bindings = new Map(); - // Renderers that have been added to the controller instance. - this.renderers = []; - /** - * Validation results that have been rendered by the controller. - */ - this.results = []; - /** - * Validation errors that have been rendered by the controller. - */ - this.errors = []; - /** - * Whether the controller is currently validating. - */ - this.validating = false; - // Elements related to validation results that have been rendered. - this.elements = new Map(); - // Objects that have been added to the controller instance (entity-style validation). - this.objects = new Map(); - /** - * The trigger that will invoke automatic validation of a property used in a binding. - */ - this.validateTrigger = validateTrigger.blur; - // Promise that resolves when validation has completed. - this.finishValidating = Promise.resolve(); - this.eventCallbacks = []; - } - /** - * Subscribe to controller validate and reset events. These events occur when the - * controller's "validate"" and "reset" methods are called. - * @param callback The callback to be invoked when the controller validates or resets. - */ - subscribe(callback) { - this.eventCallbacks.push(callback); - return { - dispose: () => { - const index = this.eventCallbacks.indexOf(callback); - if (index === -1) { - return; - } - this.eventCallbacks.splice(index, 1); - } - }; - } - /** - * Adds an object to the set of objects that should be validated when validate is called. - * @param object The object. - * @param rules Optional. The rules. If rules aren't supplied the Validator implementation will lookup the rules. - */ - addObject(object, rules) { - this.objects.set(object, rules); - } - /** - * Removes an object from the set of objects that should be validated when validate is called. - * @param object The object. - */ - removeObject(object) { - this.objects.delete(object); - this.processResultDelta('reset', this.results.filter(result => result.object === object), []); - } - /** - * Adds and renders an error. - */ - addError(message, object, propertyName = null) { - let resolvedPropertyName; - if (propertyName === null) { - resolvedPropertyName = propertyName; - } - else { - resolvedPropertyName = this.propertyParser.parse(propertyName); - } - const result = new ValidateResult({ __manuallyAdded__: true }, object, resolvedPropertyName, false, message); - this.processResultDelta('validate', [], [result]); - return result; - } - /** - * Removes and unrenders an error. - */ - removeError(result) { - if (this.results.indexOf(result) !== -1) { - this.processResultDelta('reset', [result], []); - } - } - /** - * Adds a renderer. - * @param renderer The renderer. - */ - addRenderer(renderer) { - this.renderers.push(renderer); - renderer.render({ - kind: 'validate', - render: this.results.map(result => ({ result, elements: this.elements.get(result) })), - unrender: [] - }); - } - /** - * Removes a renderer. - * @param renderer The renderer. - */ - removeRenderer(renderer) { - this.renderers.splice(this.renderers.indexOf(renderer), 1); - renderer.render({ - kind: 'reset', - render: [], - unrender: this.results.map(result => ({ result, elements: this.elements.get(result) })) - }); - } - /** - * Registers a binding with the controller. - * @param binding The binding instance. - * @param target The DOM element. - * @param rules (optional) rules associated with the binding. Validator implementation specific. - */ - registerBinding(binding, target, rules) { - this.bindings.set(binding, { target, rules, propertyInfo: null }); - } - /** - * Unregisters a binding with the controller. - * @param binding The binding instance. - */ - unregisterBinding(binding) { - this.resetBinding(binding); - this.bindings.delete(binding); - } - /** - * Interprets the instruction and returns a predicate that will identify - * relevant results in the list of rendered validation results. - */ - getInstructionPredicate(instruction) { - if (instruction) { - const { object, propertyName, rules } = instruction; - let predicate; - if (instruction.propertyName) { - predicate = x => x.object === object && x.propertyName === propertyName; - } - else { - predicate = x => x.object === object; - } - if (rules) { - return x => predicate(x) && this.validator.ruleExists(rules, x.rule); - } - return predicate; - } - else { - return () => true; - } - } - /** - * Validates and renders results. - * @param instruction Optional. Instructions on what to validate. If undefined, all - * objects and bindings will be validated. - */ - validate(instruction) { - // Get a function that will process the validation instruction. - let execute; - if (instruction) { - // tslint:disable-next-line:prefer-const - let { object, propertyName, rules } = instruction; - // if rules were not specified, check the object map. - rules = rules || this.objects.get(object); - // property specified? - if (instruction.propertyName === undefined) { - // validate the specified object. - execute = () => this.validator.validateObject(object, rules); - } - else { - // validate the specified property. - execute = () => this.validator.validateProperty(object, propertyName, rules); - } - } - else { - // validate all objects and bindings. - execute = () => { - const promises = []; - for (const [object, rules] of Array.from(this.objects)) { - promises.push(this.validator.validateObject(object, rules)); - } - for (const [binding, { rules }] of Array.from(this.bindings)) { - const propertyInfo = getPropertyInfo(binding.sourceExpression, binding.source); - if (!propertyInfo || this.objects.has(propertyInfo.object)) { - continue; - } - promises.push(this.validator.validateProperty(propertyInfo.object, propertyInfo.propertyName, rules)); - } - return Promise.all(promises).then(resultSets => resultSets.reduce((a, b) => a.concat(b), [])); - }; - } - // Wait for any existing validation to finish, execute the instruction, render the results. - this.validating = true; - const returnPromise = this.finishValidating - .then(execute) - .then((newResults) => { - const predicate = this.getInstructionPredicate(instruction); - const oldResults = this.results.filter(predicate); - this.processResultDelta('validate', oldResults, newResults); - if (returnPromise === this.finishValidating) { - this.validating = false; - } - const result = { - instruction, - valid: newResults.find(x => !x.valid) === undefined, - results: newResults - }; - this.invokeCallbacks(instruction, result); - return result; - }) - .catch(exception => { - // recover, to enable subsequent calls to validate() - this.validating = false; - this.finishValidating = Promise.resolve(); - return Promise.reject(exception); - }); - this.finishValidating = returnPromise; - return returnPromise; - } - /** - * Resets any rendered validation results (unrenders). - * @param instruction Optional. Instructions on what to reset. If unspecified all rendered results - * will be unrendered. - */ - reset(instruction) { - const predicate = this.getInstructionPredicate(instruction); - const oldResults = this.results.filter(predicate); - this.processResultDelta('reset', oldResults, []); - this.invokeCallbacks(instruction, null); - } - /** - * Gets the elements associated with an object and propertyName (if any). - */ - getAssociatedElements({ object, propertyName }) { - const elements = []; - for (const [binding, { target }] of Array.from(this.bindings)) { - const propertyInfo = getPropertyInfo(binding.sourceExpression, binding.source); - if (propertyInfo && propertyInfo.object === object && propertyInfo.propertyName === propertyName) { - elements.push(target); - } - } - return elements; - } - processResultDelta(kind, oldResults, newResults) { - // prepare the instruction. - const instruction = { - kind, - render: [], - unrender: [] - }; - // create a shallow copy of newResults so we can mutate it without causing side-effects. - newResults = newResults.slice(0); - // create unrender instructions from the old results. - for (const oldResult of oldResults) { - // get the elements associated with the old result. - const elements = this.elements.get(oldResult); - // remove the old result from the element map. - this.elements.delete(oldResult); - // create the unrender instruction. - instruction.unrender.push({ result: oldResult, elements }); - // determine if there's a corresponding new result for the old result we are unrendering. - const newResultIndex = newResults.findIndex(x => x.rule === oldResult.rule && x.object === oldResult.object && x.propertyName === oldResult.propertyName); - if (newResultIndex === -1) { - // no corresponding new result... simple remove. - this.results.splice(this.results.indexOf(oldResult), 1); - if (!oldResult.valid) { - this.errors.splice(this.errors.indexOf(oldResult), 1); - } - } - else { - // there is a corresponding new result... - const newResult = newResults.splice(newResultIndex, 1)[0]; - // get the elements that are associated with the new result. - const elements = this.getAssociatedElements(newResult); - this.elements.set(newResult, elements); - // create a render instruction for the new result. - instruction.render.push({ result: newResult, elements }); - // do an in-place replacement of the old result with the new result. - // this ensures any repeats bound to this.results will not thrash. - this.results.splice(this.results.indexOf(oldResult), 1, newResult); - if (!oldResult.valid && newResult.valid) { - this.errors.splice(this.errors.indexOf(oldResult), 1); - } - else if (!oldResult.valid && !newResult.valid) { - this.errors.splice(this.errors.indexOf(oldResult), 1, newResult); - } - else if (!newResult.valid) { - this.errors.push(newResult); - } - } - } - // create render instructions from the remaining new results. - for (const result of newResults) { - const elements = this.getAssociatedElements(result); - instruction.render.push({ result, elements }); - this.elements.set(result, elements); - this.results.push(result); - if (!result.valid) { - this.errors.push(result); - } - } - // render. - for (const renderer of this.renderers) { - renderer.render(instruction); - } - } - /** - * Validates the property associated with a binding. - */ - validateBinding(binding) { - if (!binding.isBound) { - return; - } - const propertyInfo = getPropertyInfo(binding.sourceExpression, binding.source); - let rules; - const registeredBinding = this.bindings.get(binding); - if (registeredBinding) { - rules = registeredBinding.rules; - registeredBinding.propertyInfo = propertyInfo; - } - if (!propertyInfo) { - return; - } - const { object, propertyName } = propertyInfo; - this.validate({ object, propertyName, rules }); - } - /** - * Resets the results for a property associated with a binding. - */ - resetBinding(binding) { - const registeredBinding = this.bindings.get(binding); - let propertyInfo = getPropertyInfo(binding.sourceExpression, binding.source); - if (!propertyInfo && registeredBinding) { - propertyInfo = registeredBinding.propertyInfo; - } - if (registeredBinding) { - registeredBinding.propertyInfo = null; - } - if (!propertyInfo) { - return; - } - const { object, propertyName } = propertyInfo; - this.reset({ object, propertyName }); - } - /** - * Changes the controller's validateTrigger. - * @param newTrigger The new validateTrigger - */ - changeTrigger(newTrigger) { - this.validateTrigger = newTrigger; - const bindings = Array.from(this.bindings.keys()); - for (const binding of bindings) { - const source = binding.source; - binding.unbind(); - binding.bind(source); - } - } - /** - * Revalidates the controller's current set of errors. - */ - revalidateErrors() { - for (const { object, propertyName, rule } of this.errors) { - if (rule.__manuallyAdded__) { - continue; - } - const rules = [[rule]]; - this.validate({ object, propertyName, rules }); - } - } - invokeCallbacks(instruction, result) { - if (this.eventCallbacks.length === 0) { - return; - } - const event = new ValidateEvent(result ? 'validate' : 'reset', this.errors, this.results, instruction || null, result); - for (let i = 0; i < this.eventCallbacks.length; i++) { - this.eventCallbacks[i](event); - } - } -} -ValidationController.inject = [Validator, PropertyAccessorParser]; - -/** - * Binding behavior. Indicates the bound property should be validated. - */ -class ValidateBindingBehaviorBase { - constructor(taskQueue) { - this.taskQueue = taskQueue; - } - bind(binding, source, rulesOrController, rules) { - // identify the target element. - const target = getTargetDOMElement(binding, source); - // locate the controller. - let controller; - if (rulesOrController instanceof ValidationController) { - controller = rulesOrController; - } - else { - controller = source.container.get(Optional.of(ValidationController)); - rules = rulesOrController; - } - if (controller === null) { - throw new Error(`A ValidationController has not been registered.`); - } - controller.registerBinding(binding, target, rules); - binding.validationController = controller; - const trigger = this.getValidateTrigger(controller); - // tslint:disable-next-line:no-bitwise - if (trigger & validateTrigger.change) { - binding.vbbUpdateSource = binding.updateSource; - // tslint:disable-next-line:only-arrow-functions - // tslint:disable-next-line:space-before-function-paren - binding.updateSource = function (value) { - this.vbbUpdateSource(value); - this.validationController.validateBinding(this); - }; - } - // tslint:disable-next-line:no-bitwise - if (trigger & validateTrigger.blur) { - binding.validateBlurHandler = () => { - this.taskQueue.queueMicroTask(() => controller.validateBinding(binding)); - }; - binding.validateTarget = target; - target.addEventListener('blur', binding.validateBlurHandler); - } - if (trigger !== validateTrigger.manual) { - binding.standardUpdateTarget = binding.updateTarget; - // tslint:disable-next-line:only-arrow-functions - // tslint:disable-next-line:space-before-function-paren - binding.updateTarget = function (value) { - this.standardUpdateTarget(value); - this.validationController.resetBinding(this); - }; - } - } - unbind(binding) { - // reset the binding to it's original state. - if (binding.vbbUpdateSource) { - binding.updateSource = binding.vbbUpdateSource; - binding.vbbUpdateSource = null; - } - if (binding.standardUpdateTarget) { - binding.updateTarget = binding.standardUpdateTarget; - binding.standardUpdateTarget = null; - } - if (binding.validateBlurHandler) { - binding.validateTarget.removeEventListener('blur', binding.validateBlurHandler); - binding.validateBlurHandler = null; - binding.validateTarget = null; - } - binding.validationController.unregisterBinding(binding); - binding.validationController = null; - } -} - -/** - * Binding behavior. Indicates the bound property should be validated - * when the validate trigger specified by the associated controller's - * validateTrigger property occurs. - */ -let ValidateBindingBehavior = class ValidateBindingBehavior extends ValidateBindingBehaviorBase { - getValidateTrigger(controller) { - return controller.validateTrigger; - } -}; -ValidateBindingBehavior.inject = [TaskQueue]; -ValidateBindingBehavior = __decorate([ - bindingBehavior('validate') -], ValidateBindingBehavior); -/** - * Binding behavior. Indicates the bound property will be validated - * manually, by calling controller.validate(). No automatic validation - * triggered by data-entry or blur will occur. - */ -let ValidateManuallyBindingBehavior = class ValidateManuallyBindingBehavior extends ValidateBindingBehaviorBase { - getValidateTrigger() { - return validateTrigger.manual; - } -}; -ValidateManuallyBindingBehavior.inject = [TaskQueue]; -ValidateManuallyBindingBehavior = __decorate([ - bindingBehavior('validateManually') -], ValidateManuallyBindingBehavior); -/** - * Binding behavior. Indicates the bound property should be validated - * when the associated element blurs. - */ -let ValidateOnBlurBindingBehavior = class ValidateOnBlurBindingBehavior extends ValidateBindingBehaviorBase { - getValidateTrigger() { - return validateTrigger.blur; - } -}; -ValidateOnBlurBindingBehavior.inject = [TaskQueue]; -ValidateOnBlurBindingBehavior = __decorate([ - bindingBehavior('validateOnBlur') -], ValidateOnBlurBindingBehavior); -/** - * Binding behavior. Indicates the bound property should be validated - * when the associated element is changed by the user, causing a change - * to the model. - */ -let ValidateOnChangeBindingBehavior = class ValidateOnChangeBindingBehavior extends ValidateBindingBehaviorBase { - getValidateTrigger() { - return validateTrigger.change; - } -}; -ValidateOnChangeBindingBehavior.inject = [TaskQueue]; -ValidateOnChangeBindingBehavior = __decorate([ - bindingBehavior('validateOnChange') -], ValidateOnChangeBindingBehavior); -/** - * Binding behavior. Indicates the bound property should be validated - * when the associated element blurs or is changed by the user, causing - * a change to the model. - */ -let ValidateOnChangeOrBlurBindingBehavior = class ValidateOnChangeOrBlurBindingBehavior extends ValidateBindingBehaviorBase { - getValidateTrigger() { - return validateTrigger.changeOrBlur; - } -}; -ValidateOnChangeOrBlurBindingBehavior.inject = [TaskQueue]; -ValidateOnChangeOrBlurBindingBehavior = __decorate([ - bindingBehavior('validateOnChangeOrBlur') -], ValidateOnChangeOrBlurBindingBehavior); - -/** - * Creates ValidationController instances. - */ -class ValidationControllerFactory { - constructor(container) { - this.container = container; - } - static get(container) { - return new ValidationControllerFactory(container); - } - /** - * Creates a new controller instance. - */ - create(validator) { - if (!validator) { - validator = this.container.get(Validator); - } - const propertyParser = this.container.get(PropertyAccessorParser); - return new ValidationController(validator, propertyParser); - } - /** - * Creates a new controller and registers it in the current element's container so that it's - * available to the validate binding behavior and renderers. - */ - createForCurrentScope(validator) { - const controller = this.create(validator); - this.container.registerInstance(ValidationController, controller); - return controller; - } -} -ValidationControllerFactory['protocol:aurelia:resolver'] = true; - -let ValidationErrorsCustomAttribute = class ValidationErrorsCustomAttribute { - constructor(boundaryElement, controllerAccessor) { - this.boundaryElement = boundaryElement; - this.controllerAccessor = controllerAccessor; - this.controller = null; - this.errors = []; - this.errorsInternal = []; - } - static inject() { - return [DOM.Element, Lazy.of(ValidationController)]; - } - sort() { - this.errorsInternal.sort((a, b) => { - if (a.targets[0] === b.targets[0]) { - return 0; - } - // tslint:disable-next-line:no-bitwise - return a.targets[0].compareDocumentPosition(b.targets[0]) & 2 ? 1 : -1; - }); - } - interestingElements(elements) { - return elements.filter(e => this.boundaryElement.contains(e)); - } - render(instruction) { - for (const { result } of instruction.unrender) { - const index = this.errorsInternal.findIndex(x => x.error === result); - if (index !== -1) { - this.errorsInternal.splice(index, 1); - } - } - for (const { result, elements } of instruction.render) { - if (result.valid) { - continue; - } - const targets = this.interestingElements(elements); - if (targets.length) { - this.errorsInternal.push({ error: result, targets }); - } - } - this.sort(); - this.errors = this.errorsInternal; - } - bind() { - if (!this.controller) { - this.controller = this.controllerAccessor(); - } - // this will call render() with the side-effect of updating this.errors - this.controller.addRenderer(this); - } - unbind() { - if (this.controller) { - this.controller.removeRenderer(this); - } - } -}; -__decorate([ - bindable({ defaultBindingMode: bindingMode.oneWay }) -], ValidationErrorsCustomAttribute.prototype, "controller", void 0); -__decorate([ - bindable({ primaryProperty: true, defaultBindingMode: bindingMode.twoWay }) -], ValidationErrorsCustomAttribute.prototype, "errors", void 0); -ValidationErrorsCustomAttribute = __decorate([ - customAttribute('validation-errors') -], ValidationErrorsCustomAttribute); - -let ValidationRendererCustomAttribute = class ValidationRendererCustomAttribute { - created(view) { - this.container = view.container; - } - bind() { - this.controller = this.container.get(ValidationController); - this.renderer = this.container.get(this.value); - this.controller.addRenderer(this.renderer); - } - unbind() { - this.controller.removeRenderer(this.renderer); - this.controller = null; - this.renderer = null; - } -}; -ValidationRendererCustomAttribute = __decorate([ - customAttribute('validation-renderer') -], ValidationRendererCustomAttribute); - -/** - * Sets, unsets and retrieves rules on an object or constructor function. - */ -class Rules { - /** - * Applies the rules to a target. - */ - static set(target, rules) { - if (target instanceof Function) { - target = target.prototype; - } - Object.defineProperty(target, Rules.key, { enumerable: false, configurable: false, writable: true, value: rules }); - } - /** - * Removes rules from a target. - */ - static unset(target) { - if (target instanceof Function) { - target = target.prototype; - } - target[Rules.key] = null; - } - /** - * Retrieves the target's rules. - */ - static get(target) { - return target[Rules.key] || null; - } -} -/** - * The name of the property that stores the rules. - */ -Rules.key = '__rules__'; - -// tslint:disable:no-empty -class ExpressionVisitor { - visitChain(chain) { - this.visitArgs(chain.expressions); - } - visitBindingBehavior(behavior) { - behavior.expression.accept(this); - this.visitArgs(behavior.args); - } - visitValueConverter(converter) { - converter.expression.accept(this); - this.visitArgs(converter.args); - } - visitAssign(assign) { - assign.target.accept(this); - assign.value.accept(this); - } - visitConditional(conditional) { - conditional.condition.accept(this); - conditional.yes.accept(this); - conditional.no.accept(this); - } - visitAccessThis(access) { - access.ancestor = access.ancestor; - } - visitAccessScope(access) { - access.name = access.name; - } - visitAccessMember(access) { - access.object.accept(this); - } - visitAccessKeyed(access) { - access.object.accept(this); - access.key.accept(this); - } - visitCallScope(call) { - this.visitArgs(call.args); - } - visitCallFunction(call) { - call.func.accept(this); - this.visitArgs(call.args); - } - visitCallMember(call) { - call.object.accept(this); - this.visitArgs(call.args); - } - visitPrefix(prefix) { - prefix.expression.accept(this); - } - visitBinary(binary) { - binary.left.accept(this); - binary.right.accept(this); - } - visitLiteralPrimitive(literal) { - literal.value = literal.value; - } - visitLiteralArray(literal) { - this.visitArgs(literal.elements); - } - visitLiteralObject(literal) { - this.visitArgs(literal.values); - } - visitLiteralString(literal) { - literal.value = literal.value; - } - visitArgs(args) { - for (let i = 0; i < args.length; i++) { - args[i].accept(this); - } - } -} - -class ValidationMessageParser { - constructor(bindinqLanguage) { - this.bindinqLanguage = bindinqLanguage; - this.emptyStringExpression = new LiteralString(''); - this.nullExpression = new LiteralPrimitive(null); - this.undefinedExpression = new LiteralPrimitive(undefined); - this.cache = {}; - } - parse(message) { - if (this.cache[message] !== undefined) { - return this.cache[message]; - } - const parts = this.bindinqLanguage.parseInterpolation(null, message); - if (parts === null) { - return new LiteralString(message); - } - let expression = new LiteralString(parts[0]); - for (let i = 1; i < parts.length; i += 2) { - expression = new Binary('+', expression, new Binary('+', this.coalesce(parts[i]), new LiteralString(parts[i + 1]))); - } - MessageExpressionValidator.validate(expression, message); - this.cache[message] = expression; - return expression; - } - coalesce(part) { - // part === null || part === undefined ? '' : part - return new Conditional(new Binary('||', new Binary('===', part, this.nullExpression), new Binary('===', part, this.undefinedExpression)), this.emptyStringExpression, new CallMember(part, 'toString', [])); - } -} -ValidationMessageParser.inject = [BindingLanguage]; -class MessageExpressionValidator extends ExpressionVisitor { - constructor(originalMessage) { - super(); - this.originalMessage = originalMessage; - } - static validate(expression, originalMessage) { - const visitor = new MessageExpressionValidator(originalMessage); - expression.accept(visitor); - } - visitAccessScope(access) { - if (access.ancestor !== 0) { - throw new Error('$parent is not permitted in validation message expressions.'); - } - if (['displayName', 'propertyName', 'value', 'object', 'config', 'getDisplayName'].indexOf(access.name) !== -1) { - getLogger('aurelia-validation') - // tslint:disable-next-line:max-line-length - .warn(`Did you mean to use "$${access.name}" instead of "${access.name}" in this validation message template: "${this.originalMessage}"?`); - } - } -} - -/** - * Dictionary of validation messages. [messageKey]: messageExpression - */ -const validationMessages = { - /** - * The default validation message. Used with rules that have no standard message. - */ - default: `\${$displayName} is invalid.`, - required: `\${$displayName} is required.`, - matches: `\${$displayName} is not correctly formatted.`, - email: `\${$displayName} is not a valid email.`, - minLength: `\${$displayName} must be at least \${$config.length} character\${$config.length === 1 ? '' : 's'}.`, - maxLength: `\${$displayName} cannot be longer than \${$config.length} character\${$config.length === 1 ? '' : 's'}.`, - minItems: `\${$displayName} must contain at least \${$config.count} item\${$config.count === 1 ? '' : 's'}.`, - maxItems: `\${$displayName} cannot contain more than \${$config.count} item\${$config.count === 1 ? '' : 's'}.`, - equals: `\${$displayName} must be \${$config.expectedValue}.`, -}; -/** - * Retrieves validation messages and property display names. - */ -class ValidationMessageProvider { - constructor(parser) { - this.parser = parser; - } - /** - * Returns a message binding expression that corresponds to the key. - * @param key The message key. - */ - getMessage(key) { - let message; - if (key in validationMessages) { - message = validationMessages[key]; - } - else { - message = validationMessages['default']; - } - return this.parser.parse(message); - } - /** - * Formulates a property display name using the property name and the configured - * displayName (if provided). - * Override this with your own custom logic. - * @param propertyName The property name. - */ - getDisplayName(propertyName, displayName) { - if (displayName !== null && displayName !== undefined) { - return (displayName instanceof Function) ? displayName() : displayName; - } - // split on upper-case letters. - const words = propertyName.toString().split(/(?=[A-Z])/).join(' '); - // capitalize first letter. - return words.charAt(0).toUpperCase() + words.slice(1); - } -} -ValidationMessageProvider.inject = [ValidationMessageParser]; - -/** - * Validates. - * Responsible for validating objects and properties. - */ -class StandardValidator extends Validator { - constructor(messageProvider, resources) { - super(); - this.messageProvider = messageProvider; - this.lookupFunctions = resources.lookupFunctions; - this.getDisplayName = messageProvider.getDisplayName.bind(messageProvider); - } - /** - * Validates the specified property. - * @param object The object to validate. - * @param propertyName The name of the property to validate. - * @param rules Optional. If unspecified, the rules will be looked up using the metadata - * for the object created by ValidationRules....on(class/object) - */ - validateProperty(object, propertyName, rules) { - return this.validate(object, propertyName, rules || null); - } - /** - * Validates all rules for specified object and it's properties. - * @param object The object to validate. - * @param rules Optional. If unspecified, the rules will be looked up using the metadata - * for the object created by ValidationRules....on(class/object) - */ - validateObject(object, rules) { - return this.validate(object, null, rules || null); - } - /** - * Determines whether a rule exists in a set of rules. - * @param rules The rules to search. - * @parem rule The rule to find. - */ - ruleExists(rules, rule) { - let i = rules.length; - while (i--) { - if (rules[i].indexOf(rule) !== -1) { - return true; - } - } - return false; - } - getMessage(rule, object, value) { - const expression = rule.message || this.messageProvider.getMessage(rule.messageKey); - // tslint:disable-next-line:prefer-const - let { name: propertyName, displayName } = rule.property; - if (propertyName !== null) { - displayName = this.messageProvider.getDisplayName(propertyName, displayName); - } - const overrideContext = { - $displayName: displayName, - $propertyName: propertyName, - $value: value, - $object: object, - $config: rule.config, - // returns the name of a given property, given just the property name (irrespective of the property's displayName) - // split on capital letters, first letter ensured to be capitalized - $getDisplayName: this.getDisplayName - }; - return expression.evaluate({ bindingContext: object, overrideContext }, this.lookupFunctions); - } - validateRuleSequence(object, propertyName, ruleSequence, sequence, results) { - // are we validating all properties or a single property? - const validateAllProperties = propertyName === null || propertyName === undefined; - const rules = ruleSequence[sequence]; - let allValid = true; - // validate each rule. - const promises = []; - for (let i = 0; i < rules.length; i++) { - const rule = rules[i]; - // is the rule related to the property we're validating. - // tslint:disable-next-line:triple-equals | Use loose equality for property keys - if (!validateAllProperties && rule.property.name != propertyName) { - continue; - } - // is this a conditional rule? is the condition met? - if (rule.when && !rule.when(object)) { - continue; - } - // validate. - const value = rule.property.name === null ? object : object[rule.property.name]; - let promiseOrBoolean = rule.condition(value, object); - if (!(promiseOrBoolean instanceof Promise)) { - promiseOrBoolean = Promise.resolve(promiseOrBoolean); - } - promises.push(promiseOrBoolean.then(valid => { - const message = valid ? null : this.getMessage(rule, object, value); - results.push(new ValidateResult(rule, object, rule.property.name, valid, message)); - allValid = allValid && valid; - return valid; - })); - } - return Promise.all(promises) - .then(() => { - sequence++; - if (allValid && sequence < ruleSequence.length) { - return this.validateRuleSequence(object, propertyName, ruleSequence, sequence, results); - } - return results; - }); - } - validate(object, propertyName, rules) { - // rules specified? - if (!rules) { - // no. attempt to locate the rules. - rules = Rules.get(object); - } - // any rules? - if (!rules || rules.length === 0) { - return Promise.resolve([]); - } - return this.validateRuleSequence(object, propertyName, rules, 0, []); - } -} -StandardValidator.inject = [ValidationMessageProvider, ViewResources]; - -/** - * Part of the fluent rule API. Enables customizing property rules. - */ -class FluentRuleCustomizer { - constructor(property, condition, config = {}, fluentEnsure, fluentRules, parsers) { - this.fluentEnsure = fluentEnsure; - this.fluentRules = fluentRules; - this.parsers = parsers; - this.rule = { - property, - condition, - config, - when: null, - messageKey: 'default', - message: null, - sequence: fluentRules.sequence - }; - this.fluentEnsure._addRule(this.rule); - } - /** - * Validate subsequent rules after previously declared rules have - * been validated successfully. Use to postpone validation of costly - * rules until less expensive rules pass validation. - */ - then() { - this.fluentRules.sequence++; - return this; - } - /** - * Specifies the key to use when looking up the rule's validation message. - */ - withMessageKey(key) { - this.rule.messageKey = key; - this.rule.message = null; - return this; - } - /** - * Specifies rule's validation message. - */ - withMessage(message) { - this.rule.messageKey = 'custom'; - this.rule.message = this.parsers.message.parse(message); - return this; - } - /** - * Specifies a condition that must be met before attempting to validate the rule. - * @param condition A function that accepts the object as a parameter and returns true - * or false whether the rule should be evaluated. - */ - when(condition) { - this.rule.when = condition; - return this; - } - /** - * Tags the rule instance, enabling the rule to be found easily - * using ValidationRules.taggedRules(rules, tag) - */ - tag(tag) { - this.rule.tag = tag; - return this; - } - ///// FluentEnsure APIs ///// - /** - * Target a property with validation rules. - * @param property The property to target. Can be the property name or a property accessor function. - */ - ensure(subject) { - return this.fluentEnsure.ensure(subject); - } - /** - * Targets an object with validation rules. - */ - ensureObject() { - return this.fluentEnsure.ensureObject(); - } - /** - * Rules that have been defined using the fluent API. - */ - get rules() { - return this.fluentEnsure.rules; - } - /** - * Applies the rules to a class or object, making them discoverable by the StandardValidator. - * @param target A class or object. - */ - on(target) { - return this.fluentEnsure.on(target); - } - ///////// FluentRules APIs ///////// - /** - * Applies an ad-hoc rule function to the ensured property or object. - * @param condition The function to validate the rule. - * Will be called with two arguments, the property value and the object. - * Should return a boolean or a Promise that resolves to a boolean. - */ - satisfies(condition, config) { - return this.fluentRules.satisfies(condition, config); - } - /** - * Applies a rule by name. - * @param name The name of the custom or standard rule. - * @param args The rule's arguments. - */ - satisfiesRule(name, ...args) { - return this.fluentRules.satisfiesRule(name, ...args); - } - /** - * Applies the "required" rule to the property. - * The value cannot be null, undefined or whitespace. - */ - required() { - return this.fluentRules.required(); - } - /** - * Applies the "matches" rule to the property. - * Value must match the specified regular expression. - * null, undefined and empty-string values are considered valid. - */ - matches(regex) { - return this.fluentRules.matches(regex); - } - /** - * Applies the "email" rule to the property. - * null, undefined and empty-string values are considered valid. - */ - email() { - return this.fluentRules.email(); - } - /** - * Applies the "minLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - minLength(length) { - return this.fluentRules.minLength(length); - } - /** - * Applies the "maxLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - maxLength(length) { - return this.fluentRules.maxLength(length); - } - /** - * Applies the "minItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - minItems(count) { - return this.fluentRules.minItems(count); - } - /** - * Applies the "maxItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - maxItems(count) { - return this.fluentRules.maxItems(count); - } - /** - * Applies the "equals" validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - equals(expectedValue) { - return this.fluentRules.equals(expectedValue); - } -} -/** - * Part of the fluent rule API. Enables applying rules to properties and objects. - */ -class FluentRules { - constructor(fluentEnsure, parsers, property) { - this.fluentEnsure = fluentEnsure; - this.parsers = parsers; - this.property = property; - /** - * Current rule sequence number. Used to postpone evaluation of rules until rules - * with lower sequence number have successfully validated. The "then" fluent API method - * manages this property, there's usually no need to set it directly. - */ - this.sequence = 0; - } - /** - * Sets the display name of the ensured property. - */ - displayName(name) { - this.property.displayName = name; - return this; - } - /** - * Applies an ad-hoc rule function to the ensured property or object. - * @param condition The function to validate the rule. - * Will be called with two arguments, the property value and the object. - * Should return a boolean or a Promise that resolves to a boolean. - */ - satisfies(condition, config) { - return new FluentRuleCustomizer(this.property, condition, config, this.fluentEnsure, this, this.parsers); - } - /** - * Applies a rule by name. - * @param name The name of the custom or standard rule. - * @param args The rule's arguments. - */ - satisfiesRule(name, ...args) { - let rule = FluentRules.customRules[name]; - if (!rule) { - // standard rule? - rule = this[name]; - if (rule instanceof Function) { - return rule.call(this, ...args); - } - throw new Error(`Rule with name "${name}" does not exist.`); - } - const config = rule.argsToConfig ? rule.argsToConfig(...args) : undefined; - return this.satisfies((value, obj) => rule.condition.call(this, value, obj, ...args), config) - .withMessageKey(name); - } - /** - * Applies the "required" rule to the property. - * The value cannot be null, undefined or whitespace. - */ - required() { - return this.satisfies(value => value !== null - && value !== undefined - && !(isString(value) && !/\S/.test(value))).withMessageKey('required'); - } - /** - * Applies the "matches" rule to the property. - * Value must match the specified regular expression. - * null, undefined and empty-string values are considered valid. - */ - matches(regex) { - return this.satisfies(value => value === null || value === undefined || value.length === 0 || regex.test(value)) - .withMessageKey('matches'); - } - /** - * Applies the "email" rule to the property. - * null, undefined and empty-string values are considered valid. - */ - email() { - // regex from https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address - /* tslint:disable:max-line-length */ - return this.matches(/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/) - /* tslint:enable:max-line-length */ - .withMessageKey('email'); - } - /** - * Applies the "minLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - minLength(length) { - return this.satisfies((value) => value === null || value === undefined || value.length === 0 || value.length >= length, { length }) - .withMessageKey('minLength'); - } - /** - * Applies the "maxLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - maxLength(length) { - return this.satisfies((value) => value === null || value === undefined || value.length === 0 || value.length <= length, { length }) - .withMessageKey('maxLength'); - } - /** - * Applies the "minItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - minItems(count) { - return this.satisfies((value) => value === null || value === undefined || value.length >= count, { count }) - .withMessageKey('minItems'); - } - /** - * Applies the "maxItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - maxItems(count) { - return this.satisfies((value) => value === null || value === undefined || value.length <= count, { count }) - .withMessageKey('maxItems'); - } - /** - * Applies the "equals" validation rule to the property. - * null and undefined values are considered valid. - */ - equals(expectedValue) { - return this.satisfies(value => value === null || value === undefined || value === '' || value === expectedValue, { expectedValue }) - .withMessageKey('equals'); - } -} -FluentRules.customRules = {}; -/** - * Part of the fluent rule API. Enables targeting properties and objects with rules. - */ -class FluentEnsure { - constructor(parsers) { - this.parsers = parsers; - /** - * Rules that have been defined using the fluent API. - */ - this.rules = []; - } - /** - * Target a property with validation rules. - * @param property The property to target. Can be the property name or a property accessor - * function. - */ - ensure(property) { - this.assertInitialized(); - const name = this.parsers.property.parse(property); - const fluentRules = new FluentRules(this, this.parsers, { name, displayName: null }); - return this.mergeRules(fluentRules, name); - } - /** - * Targets an object with validation rules. - */ - ensureObject() { - this.assertInitialized(); - const fluentRules = new FluentRules(this, this.parsers, { name: null, displayName: null }); - return this.mergeRules(fluentRules, null); - } - /** - * Applies the rules to a class or object, making them discoverable by the StandardValidator. - * @param target A class or object. - */ - on(target) { - Rules.set(target, this.rules); - return this; - } - /** - * Adds a rule definition to the sequenced ruleset. - * @internal - */ - _addRule(rule) { - while (this.rules.length < rule.sequence + 1) { - this.rules.push([]); - } - this.rules[rule.sequence].push(rule); - } - assertInitialized() { - if (this.parsers) { - return; - } - throw new Error(`Did you forget to add ".plugin('aurelia-validation')" to your main.js?`); - } - mergeRules(fluentRules, propertyName) { - // tslint:disable-next-line:triple-equals | Use loose equality for property keys - const existingRules = this.rules.find(r => r.length > 0 && r[0].property.name == propertyName); - if (existingRules) { - const rule = existingRules[existingRules.length - 1]; - fluentRules.sequence = rule.sequence; - if (rule.property.displayName !== null) { - fluentRules = fluentRules.displayName(rule.property.displayName); - } - } - return fluentRules; - } -} -/** - * Fluent rule definition API. - */ -class ValidationRules { - static initialize(messageParser, propertyParser) { - this.parsers = { - message: messageParser, - property: propertyParser - }; - } - /** - * Target a property with validation rules. - * @param property The property to target. Can be the property name or a property accessor function. - */ - static ensure(property) { - return new FluentEnsure(ValidationRules.parsers).ensure(property); - } - /** - * Targets an object with validation rules. - */ - static ensureObject() { - return new FluentEnsure(ValidationRules.parsers).ensureObject(); - } - /** - * Defines a custom rule. - * @param name The name of the custom rule. Also serves as the message key. - * @param condition The rule function. - * @param message The message expression - * @param argsToConfig A function that maps the rule's arguments to a "config" - * object that can be used when evaluating the message expression. - */ - static customRule(name, condition, message, argsToConfig) { - validationMessages[name] = message; - FluentRules.customRules[name] = { condition, argsToConfig }; - } - /** - * Returns rules with the matching tag. - * @param rules The rules to search. - * @param tag The tag to search for. - */ - static taggedRules(rules, tag) { - return rules.map(x => x.filter(r => r.tag === tag)); - } - /** - * Returns rules that have no tag. - * @param rules The rules to search. - */ - static untaggedRules(rules) { - return rules.map(x => x.filter(r => r.tag === undefined)); - } - /** - * Removes the rules from a class or object. - * @param target A class or object. - */ - static off(target) { - Rules.unset(target); - } -} - -// Exports -/** - * Aurelia Validation Configuration API - */ -class AureliaValidationConfiguration { - constructor() { - this.validatorType = StandardValidator; - } - /** - * Use a custom Validator implementation. - */ - customValidator(type) { - this.validatorType = type; - } - /** - * Applies the configuration. - */ - apply(container) { - const validator = container.get(this.validatorType); - container.registerInstance(Validator, validator); - } -} -/** - * Configures the plugin. - */ -function configure( -// tslint:disable-next-line:ban-types -frameworkConfig, callback) { - // the fluent rule definition API needs the parser to translate messages - // to interpolation expressions. - const messageParser = frameworkConfig.container.get(ValidationMessageParser); - const propertyParser = frameworkConfig.container.get(PropertyAccessorParser); - ValidationRules.initialize(messageParser, propertyParser); - // configure... - const config = new AureliaValidationConfiguration(); - if (callback instanceof Function) { - callback(config); - } - config.apply(frameworkConfig.container); - // globalize the behaviors. - if (frameworkConfig.globalResources) { - frameworkConfig.globalResources(ValidateBindingBehavior, ValidateManuallyBindingBehavior, ValidateOnBlurBindingBehavior, ValidateOnChangeBindingBehavior, ValidateOnChangeOrBlurBindingBehavior, ValidationErrorsCustomAttribute, ValidationRendererCustomAttribute); - } -} - -export { AureliaValidationConfiguration, configure, getTargetDOMElement, getPropertyInfo, PropertyAccessorParser, getAccessorExpression, ValidateBindingBehavior, ValidateManuallyBindingBehavior, ValidateOnBlurBindingBehavior, ValidateOnChangeBindingBehavior, ValidateOnChangeOrBlurBindingBehavior, ValidateEvent, ValidateResult, validateTrigger, ValidationController, ValidationControllerFactory, ValidationErrorsCustomAttribute, ValidationRendererCustomAttribute, Validator, Rules, StandardValidator, validationMessages, ValidationMessageProvider, ValidationMessageParser, MessageExpressionValidator, FluentRuleCustomizer, FluentRules, FluentEnsure, ValidationRules }; diff --git a/dist/native-modules/aurelia-validation.js b/dist/native-modules/aurelia-validation.js deleted file mode 100644 index 72c02d98..00000000 --- a/dist/native-modules/aurelia-validation.js +++ /dev/null @@ -1,1786 +0,0 @@ -import { DOM } from 'aurelia-pal'; -import { AccessMember, AccessScope, AccessKeyed, BindingBehavior, ValueConverter, getContextFor, Parser, bindingBehavior, bindingMode, LiteralString, Binary, Conditional, LiteralPrimitive, CallMember } from 'aurelia-binding'; -import { Optional, Lazy } from 'aurelia-dependency-injection'; -import { TaskQueue } from 'aurelia-task-queue'; -import { customAttribute, bindable, BindingLanguage, ViewResources } from 'aurelia-templating'; -import { getLogger } from 'aurelia-logging'; - -/** - * Gets the DOM element associated with the data-binding. Most of the time it's - * the binding.target but sometimes binding.target is an aurelia custom element, - * or custom attribute which is a javascript "class" instance, so we need to use - * the controller's container to retrieve the actual DOM element. - */ -function getTargetDOMElement(binding, view) { - var target = binding.target; - // DOM element - if (target instanceof Element) { - return target; - } - // custom element or custom attribute - // tslint:disable-next-line:prefer-const - for (var i = 0, ii = view.controllers.length; i < ii; i++) { - var controller = view.controllers[i]; - if (controller.viewModel === target) { - var element = controller.container.get(DOM.Element); - if (element) { - return element; - } - throw new Error("Unable to locate target element for \"" + binding.sourceExpression + "\"."); - } - } - throw new Error("Unable to locate target element for \"" + binding.sourceExpression + "\"."); -} - -function getObject(expression, objectExpression, source) { - var value = objectExpression.evaluate(source, null); - if (value === null || value === undefined || value instanceof Object) { - return value; - } - // tslint:disable-next-line:max-line-length - throw new Error("The '" + objectExpression + "' part of '" + expression + "' evaluates to " + value + " instead of an object, null or undefined."); -} -/** - * Retrieves the object and property name for the specified expression. - * @param expression The expression - * @param source The scope - */ -function getPropertyInfo(expression, source) { - var originalExpression = expression; - while (expression instanceof BindingBehavior || expression instanceof ValueConverter) { - expression = expression.expression; - } - var object; - var propertyName; - if (expression instanceof AccessScope) { - object = getContextFor(expression.name, source, expression.ancestor); - propertyName = expression.name; - } - else if (expression instanceof AccessMember) { - object = getObject(originalExpression, expression.object, source); - propertyName = expression.name; - } - else if (expression instanceof AccessKeyed) { - object = getObject(originalExpression, expression.object, source); - propertyName = expression.key.evaluate(source); - } - else { - throw new Error("Expression '" + originalExpression + "' is not compatible with the validate binding-behavior."); - } - if (object === null || object === undefined) { - return null; - } - return { object: object, propertyName: propertyName }; -} - -function isString(value) { - return Object.prototype.toString.call(value) === '[object String]'; -} -function isNumber(value) { - return Object.prototype.toString.call(value) === '[object Number]'; -} - -var PropertyAccessorParser = /** @class */ (function () { - function PropertyAccessorParser(parser) { - this.parser = parser; - } - PropertyAccessorParser.prototype.parse = function (property) { - if (isString(property) || isNumber(property)) { - return property; - } - var accessorText = getAccessorExpression(property.toString()); - var accessor = this.parser.parse(accessorText); - if (accessor instanceof AccessScope - || accessor instanceof AccessMember && accessor.object instanceof AccessScope) { - return accessor.name; - } - throw new Error("Invalid property expression: \"" + accessor + "\""); - }; - PropertyAccessorParser.inject = [Parser]; - return PropertyAccessorParser; -}()); -function getAccessorExpression(fn) { - /* tslint:disable:max-line-length */ - var classic = /^function\s*\([$_\w\d]+\)\s*\{(?:\s*"use strict";)?\s*(?:[$_\w\d.['"\]+;]+)?\s*return\s+[$_\w\d]+\.([$_\w\d]+)\s*;?\s*\}$/; - /* tslint:enable:max-line-length */ - var arrow = /^\(?[$_\w\d]+\)?\s*=>\s*[$_\w\d]+\.([$_\w\d]+)$/; - var match = classic.exec(fn) || arrow.exec(fn); - if (match === null) { - throw new Error("Unable to parse accessor function:\n" + fn); - } - return match[1]; -} - -/*! ***************************************************************************** -Copyright (c) Microsoft Corporation. All rights reserved. -Licensed under the Apache License, Version 2.0 (the "License"); you may not use -this file except in compliance with the License. You may obtain a copy of the -License at http://www.apache.org/licenses/LICENSE-2.0 - -THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED -WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, -MERCHANTABLITY OR NON-INFRINGEMENT. - -See the Apache Version 2.0 License for specific language governing permissions -and limitations under the License. -***************************************************************************** */ -/* global Reflect, Promise */ - -var extendStatics = function(d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); -}; - -function __extends(d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); -} - -function __decorate(decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -} - -/** - * Validation triggers. - */ -var validateTrigger; -(function (validateTrigger) { - /** - * Manual validation. Use the controller's `validate()` and `reset()` methods - * to validate all bindings. - */ - validateTrigger[validateTrigger["manual"] = 0] = "manual"; - /** - * Validate the binding when the binding's target element fires a DOM "blur" event. - */ - validateTrigger[validateTrigger["blur"] = 1] = "blur"; - /** - * Validate the binding when it updates the model due to a change in the view. - */ - validateTrigger[validateTrigger["change"] = 2] = "change"; - /** - * Validate the binding when the binding's target element fires a DOM "blur" event and - * when it updates the model due to a change in the view. - */ - validateTrigger[validateTrigger["changeOrBlur"] = 3] = "changeOrBlur"; -})(validateTrigger || (validateTrigger = {})); - -/** - * Validates objects and properties. - */ -var Validator = /** @class */ (function () { - function Validator() { - } - return Validator; -}()); - -/** - * The result of validating an individual validation rule. - */ -var ValidateResult = /** @class */ (function () { - /** - * @param rule The rule associated with the result. Validator implementation specific. - * @param object The object that was validated. - * @param propertyName The name of the property that was validated. - * @param error The error, if the result is a validation error. - */ - function ValidateResult(rule, object, propertyName, valid, message) { - if (message === void 0) { message = null; } - this.rule = rule; - this.object = object; - this.propertyName = propertyName; - this.valid = valid; - this.message = message; - this.id = ValidateResult.nextId++; - } - ValidateResult.prototype.toString = function () { - return this.valid ? 'Valid.' : this.message; - }; - ValidateResult.nextId = 0; - return ValidateResult; -}()); - -var ValidateEvent = /** @class */ (function () { - function ValidateEvent( - /** - * The type of validate event. Either "validate" or "reset". - */ - type, - /** - * The controller's current array of errors. For an array containing both - * failed rules and passed rules, use the "results" property. - */ - errors, - /** - * The controller's current array of validate results. This - * includes both passed rules and failed rules. For an array of only failed rules, - * use the "errors" property. - */ - results, - /** - * The instruction passed to the "validate" or "reset" event. Will be null when - * the controller's validate/reset method was called with no instruction argument. - */ - instruction, - /** - * In events with type === "validate", this property will contain the result - * of validating the instruction (see "instruction" property). Use the controllerValidateResult - * to access the validate results specific to the call to "validate" - * (as opposed to using the "results" and "errors" properties to access the controller's entire - * set of results/errors). - */ - controllerValidateResult) { - this.type = type; - this.errors = errors; - this.results = results; - this.instruction = instruction; - this.controllerValidateResult = controllerValidateResult; - } - return ValidateEvent; -}()); - -/** - * Orchestrates validation. - * Manages a set of bindings, renderers and objects. - * Exposes the current list of validation results for binding purposes. - */ -var ValidationController = /** @class */ (function () { - function ValidationController(validator, propertyParser) { - this.validator = validator; - this.propertyParser = propertyParser; - // Registered bindings (via the validate binding behavior) - this.bindings = new Map(); - // Renderers that have been added to the controller instance. - this.renderers = []; - /** - * Validation results that have been rendered by the controller. - */ - this.results = []; - /** - * Validation errors that have been rendered by the controller. - */ - this.errors = []; - /** - * Whether the controller is currently validating. - */ - this.validating = false; - // Elements related to validation results that have been rendered. - this.elements = new Map(); - // Objects that have been added to the controller instance (entity-style validation). - this.objects = new Map(); - /** - * The trigger that will invoke automatic validation of a property used in a binding. - */ - this.validateTrigger = validateTrigger.blur; - // Promise that resolves when validation has completed. - this.finishValidating = Promise.resolve(); - this.eventCallbacks = []; - } - /** - * Subscribe to controller validate and reset events. These events occur when the - * controller's "validate"" and "reset" methods are called. - * @param callback The callback to be invoked when the controller validates or resets. - */ - ValidationController.prototype.subscribe = function (callback) { - var _this = this; - this.eventCallbacks.push(callback); - return { - dispose: function () { - var index = _this.eventCallbacks.indexOf(callback); - if (index === -1) { - return; - } - _this.eventCallbacks.splice(index, 1); - } - }; - }; - /** - * Adds an object to the set of objects that should be validated when validate is called. - * @param object The object. - * @param rules Optional. The rules. If rules aren't supplied the Validator implementation will lookup the rules. - */ - ValidationController.prototype.addObject = function (object, rules) { - this.objects.set(object, rules); - }; - /** - * Removes an object from the set of objects that should be validated when validate is called. - * @param object The object. - */ - ValidationController.prototype.removeObject = function (object) { - this.objects.delete(object); - this.processResultDelta('reset', this.results.filter(function (result) { return result.object === object; }), []); - }; - /** - * Adds and renders an error. - */ - ValidationController.prototype.addError = function (message, object, propertyName) { - if (propertyName === void 0) { propertyName = null; } - var resolvedPropertyName; - if (propertyName === null) { - resolvedPropertyName = propertyName; - } - else { - resolvedPropertyName = this.propertyParser.parse(propertyName); - } - var result = new ValidateResult({ __manuallyAdded__: true }, object, resolvedPropertyName, false, message); - this.processResultDelta('validate', [], [result]); - return result; - }; - /** - * Removes and unrenders an error. - */ - ValidationController.prototype.removeError = function (result) { - if (this.results.indexOf(result) !== -1) { - this.processResultDelta('reset', [result], []); - } - }; - /** - * Adds a renderer. - * @param renderer The renderer. - */ - ValidationController.prototype.addRenderer = function (renderer) { - var _this = this; - this.renderers.push(renderer); - renderer.render({ - kind: 'validate', - render: this.results.map(function (result) { return ({ result: result, elements: _this.elements.get(result) }); }), - unrender: [] - }); - }; - /** - * Removes a renderer. - * @param renderer The renderer. - */ - ValidationController.prototype.removeRenderer = function (renderer) { - var _this = this; - this.renderers.splice(this.renderers.indexOf(renderer), 1); - renderer.render({ - kind: 'reset', - render: [], - unrender: this.results.map(function (result) { return ({ result: result, elements: _this.elements.get(result) }); }) - }); - }; - /** - * Registers a binding with the controller. - * @param binding The binding instance. - * @param target The DOM element. - * @param rules (optional) rules associated with the binding. Validator implementation specific. - */ - ValidationController.prototype.registerBinding = function (binding, target, rules) { - this.bindings.set(binding, { target: target, rules: rules, propertyInfo: null }); - }; - /** - * Unregisters a binding with the controller. - * @param binding The binding instance. - */ - ValidationController.prototype.unregisterBinding = function (binding) { - this.resetBinding(binding); - this.bindings.delete(binding); - }; - /** - * Interprets the instruction and returns a predicate that will identify - * relevant results in the list of rendered validation results. - */ - ValidationController.prototype.getInstructionPredicate = function (instruction) { - var _this = this; - if (instruction) { - var object_1 = instruction.object, propertyName_1 = instruction.propertyName, rules_1 = instruction.rules; - var predicate_1; - if (instruction.propertyName) { - predicate_1 = function (x) { return x.object === object_1 && x.propertyName === propertyName_1; }; - } - else { - predicate_1 = function (x) { return x.object === object_1; }; - } - if (rules_1) { - return function (x) { return predicate_1(x) && _this.validator.ruleExists(rules_1, x.rule); }; - } - return predicate_1; - } - else { - return function () { return true; }; - } - }; - /** - * Validates and renders results. - * @param instruction Optional. Instructions on what to validate. If undefined, all - * objects and bindings will be validated. - */ - ValidationController.prototype.validate = function (instruction) { - var _this = this; - // Get a function that will process the validation instruction. - var execute; - if (instruction) { - // tslint:disable-next-line:prefer-const - var object_2 = instruction.object, propertyName_2 = instruction.propertyName, rules_2 = instruction.rules; - // if rules were not specified, check the object map. - rules_2 = rules_2 || this.objects.get(object_2); - // property specified? - if (instruction.propertyName === undefined) { - // validate the specified object. - execute = function () { return _this.validator.validateObject(object_2, rules_2); }; - } - else { - // validate the specified property. - execute = function () { return _this.validator.validateProperty(object_2, propertyName_2, rules_2); }; - } - } - else { - // validate all objects and bindings. - execute = function () { - var promises = []; - for (var _i = 0, _a = Array.from(_this.objects); _i < _a.length; _i++) { - var _b = _a[_i], object = _b[0], rules = _b[1]; - promises.push(_this.validator.validateObject(object, rules)); - } - for (var _c = 0, _d = Array.from(_this.bindings); _c < _d.length; _c++) { - var _e = _d[_c], binding = _e[0], rules = _e[1].rules; - var propertyInfo = getPropertyInfo(binding.sourceExpression, binding.source); - if (!propertyInfo || _this.objects.has(propertyInfo.object)) { - continue; - } - promises.push(_this.validator.validateProperty(propertyInfo.object, propertyInfo.propertyName, rules)); - } - return Promise.all(promises).then(function (resultSets) { return resultSets.reduce(function (a, b) { return a.concat(b); }, []); }); - }; - } - // Wait for any existing validation to finish, execute the instruction, render the results. - this.validating = true; - var returnPromise = this.finishValidating - .then(execute) - .then(function (newResults) { - var predicate = _this.getInstructionPredicate(instruction); - var oldResults = _this.results.filter(predicate); - _this.processResultDelta('validate', oldResults, newResults); - if (returnPromise === _this.finishValidating) { - _this.validating = false; - } - var result = { - instruction: instruction, - valid: newResults.find(function (x) { return !x.valid; }) === undefined, - results: newResults - }; - _this.invokeCallbacks(instruction, result); - return result; - }) - .catch(function (exception) { - // recover, to enable subsequent calls to validate() - _this.validating = false; - _this.finishValidating = Promise.resolve(); - return Promise.reject(exception); - }); - this.finishValidating = returnPromise; - return returnPromise; - }; - /** - * Resets any rendered validation results (unrenders). - * @param instruction Optional. Instructions on what to reset. If unspecified all rendered results - * will be unrendered. - */ - ValidationController.prototype.reset = function (instruction) { - var predicate = this.getInstructionPredicate(instruction); - var oldResults = this.results.filter(predicate); - this.processResultDelta('reset', oldResults, []); - this.invokeCallbacks(instruction, null); - }; - /** - * Gets the elements associated with an object and propertyName (if any). - */ - ValidationController.prototype.getAssociatedElements = function (_a) { - var object = _a.object, propertyName = _a.propertyName; - var elements = []; - for (var _i = 0, _b = Array.from(this.bindings); _i < _b.length; _i++) { - var _c = _b[_i], binding = _c[0], target = _c[1].target; - var propertyInfo = getPropertyInfo(binding.sourceExpression, binding.source); - if (propertyInfo && propertyInfo.object === object && propertyInfo.propertyName === propertyName) { - elements.push(target); - } - } - return elements; - }; - ValidationController.prototype.processResultDelta = function (kind, oldResults, newResults) { - // prepare the instruction. - var instruction = { - kind: kind, - render: [], - unrender: [] - }; - // create a shallow copy of newResults so we can mutate it without causing side-effects. - newResults = newResults.slice(0); - var _loop_1 = function (oldResult) { - // get the elements associated with the old result. - var elements = this_1.elements.get(oldResult); - // remove the old result from the element map. - this_1.elements.delete(oldResult); - // create the unrender instruction. - instruction.unrender.push({ result: oldResult, elements: elements }); - // determine if there's a corresponding new result for the old result we are unrendering. - var newResultIndex = newResults.findIndex(function (x) { return x.rule === oldResult.rule && x.object === oldResult.object && x.propertyName === oldResult.propertyName; }); - if (newResultIndex === -1) { - // no corresponding new result... simple remove. - this_1.results.splice(this_1.results.indexOf(oldResult), 1); - if (!oldResult.valid) { - this_1.errors.splice(this_1.errors.indexOf(oldResult), 1); - } - } - else { - // there is a corresponding new result... - var newResult = newResults.splice(newResultIndex, 1)[0]; - // get the elements that are associated with the new result. - var elements_1 = this_1.getAssociatedElements(newResult); - this_1.elements.set(newResult, elements_1); - // create a render instruction for the new result. - instruction.render.push({ result: newResult, elements: elements_1 }); - // do an in-place replacement of the old result with the new result. - // this ensures any repeats bound to this.results will not thrash. - this_1.results.splice(this_1.results.indexOf(oldResult), 1, newResult); - if (!oldResult.valid && newResult.valid) { - this_1.errors.splice(this_1.errors.indexOf(oldResult), 1); - } - else if (!oldResult.valid && !newResult.valid) { - this_1.errors.splice(this_1.errors.indexOf(oldResult), 1, newResult); - } - else if (!newResult.valid) { - this_1.errors.push(newResult); - } - } - }; - var this_1 = this; - // create unrender instructions from the old results. - for (var _i = 0, oldResults_1 = oldResults; _i < oldResults_1.length; _i++) { - var oldResult = oldResults_1[_i]; - _loop_1(oldResult); - } - // create render instructions from the remaining new results. - for (var _a = 0, newResults_1 = newResults; _a < newResults_1.length; _a++) { - var result = newResults_1[_a]; - var elements = this.getAssociatedElements(result); - instruction.render.push({ result: result, elements: elements }); - this.elements.set(result, elements); - this.results.push(result); - if (!result.valid) { - this.errors.push(result); - } - } - // render. - for (var _b = 0, _c = this.renderers; _b < _c.length; _b++) { - var renderer = _c[_b]; - renderer.render(instruction); - } - }; - /** - * Validates the property associated with a binding. - */ - ValidationController.prototype.validateBinding = function (binding) { - if (!binding.isBound) { - return; - } - var propertyInfo = getPropertyInfo(binding.sourceExpression, binding.source); - var rules; - var registeredBinding = this.bindings.get(binding); - if (registeredBinding) { - rules = registeredBinding.rules; - registeredBinding.propertyInfo = propertyInfo; - } - if (!propertyInfo) { - return; - } - var object = propertyInfo.object, propertyName = propertyInfo.propertyName; - this.validate({ object: object, propertyName: propertyName, rules: rules }); - }; - /** - * Resets the results for a property associated with a binding. - */ - ValidationController.prototype.resetBinding = function (binding) { - var registeredBinding = this.bindings.get(binding); - var propertyInfo = getPropertyInfo(binding.sourceExpression, binding.source); - if (!propertyInfo && registeredBinding) { - propertyInfo = registeredBinding.propertyInfo; - } - if (registeredBinding) { - registeredBinding.propertyInfo = null; - } - if (!propertyInfo) { - return; - } - var object = propertyInfo.object, propertyName = propertyInfo.propertyName; - this.reset({ object: object, propertyName: propertyName }); - }; - /** - * Changes the controller's validateTrigger. - * @param newTrigger The new validateTrigger - */ - ValidationController.prototype.changeTrigger = function (newTrigger) { - this.validateTrigger = newTrigger; - var bindings = Array.from(this.bindings.keys()); - for (var _i = 0, bindings_1 = bindings; _i < bindings_1.length; _i++) { - var binding = bindings_1[_i]; - var source = binding.source; - binding.unbind(); - binding.bind(source); - } - }; - /** - * Revalidates the controller's current set of errors. - */ - ValidationController.prototype.revalidateErrors = function () { - for (var _i = 0, _a = this.errors; _i < _a.length; _i++) { - var _b = _a[_i], object = _b.object, propertyName = _b.propertyName, rule = _b.rule; - if (rule.__manuallyAdded__) { - continue; - } - var rules = [[rule]]; - this.validate({ object: object, propertyName: propertyName, rules: rules }); - } - }; - ValidationController.prototype.invokeCallbacks = function (instruction, result) { - if (this.eventCallbacks.length === 0) { - return; - } - var event = new ValidateEvent(result ? 'validate' : 'reset', this.errors, this.results, instruction || null, result); - for (var i = 0; i < this.eventCallbacks.length; i++) { - this.eventCallbacks[i](event); - } - }; - ValidationController.inject = [Validator, PropertyAccessorParser]; - return ValidationController; -}()); - -/** - * Binding behavior. Indicates the bound property should be validated. - */ -var ValidateBindingBehaviorBase = /** @class */ (function () { - function ValidateBindingBehaviorBase(taskQueue) { - this.taskQueue = taskQueue; - } - ValidateBindingBehaviorBase.prototype.bind = function (binding, source, rulesOrController, rules) { - var _this = this; - // identify the target element. - var target = getTargetDOMElement(binding, source); - // locate the controller. - var controller; - if (rulesOrController instanceof ValidationController) { - controller = rulesOrController; - } - else { - controller = source.container.get(Optional.of(ValidationController)); - rules = rulesOrController; - } - if (controller === null) { - throw new Error("A ValidationController has not been registered."); - } - controller.registerBinding(binding, target, rules); - binding.validationController = controller; - var trigger = this.getValidateTrigger(controller); - // tslint:disable-next-line:no-bitwise - if (trigger & validateTrigger.change) { - binding.vbbUpdateSource = binding.updateSource; - // tslint:disable-next-line:only-arrow-functions - // tslint:disable-next-line:space-before-function-paren - binding.updateSource = function (value) { - this.vbbUpdateSource(value); - this.validationController.validateBinding(this); - }; - } - // tslint:disable-next-line:no-bitwise - if (trigger & validateTrigger.blur) { - binding.validateBlurHandler = function () { - _this.taskQueue.queueMicroTask(function () { return controller.validateBinding(binding); }); - }; - binding.validateTarget = target; - target.addEventListener('blur', binding.validateBlurHandler); - } - if (trigger !== validateTrigger.manual) { - binding.standardUpdateTarget = binding.updateTarget; - // tslint:disable-next-line:only-arrow-functions - // tslint:disable-next-line:space-before-function-paren - binding.updateTarget = function (value) { - this.standardUpdateTarget(value); - this.validationController.resetBinding(this); - }; - } - }; - ValidateBindingBehaviorBase.prototype.unbind = function (binding) { - // reset the binding to it's original state. - if (binding.vbbUpdateSource) { - binding.updateSource = binding.vbbUpdateSource; - binding.vbbUpdateSource = null; - } - if (binding.standardUpdateTarget) { - binding.updateTarget = binding.standardUpdateTarget; - binding.standardUpdateTarget = null; - } - if (binding.validateBlurHandler) { - binding.validateTarget.removeEventListener('blur', binding.validateBlurHandler); - binding.validateBlurHandler = null; - binding.validateTarget = null; - } - binding.validationController.unregisterBinding(binding); - binding.validationController = null; - }; - return ValidateBindingBehaviorBase; -}()); - -/** - * Binding behavior. Indicates the bound property should be validated - * when the validate trigger specified by the associated controller's - * validateTrigger property occurs. - */ -var ValidateBindingBehavior = /** @class */ (function (_super) { - __extends(ValidateBindingBehavior, _super); - function ValidateBindingBehavior() { - return _super !== null && _super.apply(this, arguments) || this; - } - ValidateBindingBehavior.prototype.getValidateTrigger = function (controller) { - return controller.validateTrigger; - }; - ValidateBindingBehavior.inject = [TaskQueue]; - ValidateBindingBehavior = __decorate([ - bindingBehavior('validate') - ], ValidateBindingBehavior); - return ValidateBindingBehavior; -}(ValidateBindingBehaviorBase)); -/** - * Binding behavior. Indicates the bound property will be validated - * manually, by calling controller.validate(). No automatic validation - * triggered by data-entry or blur will occur. - */ -var ValidateManuallyBindingBehavior = /** @class */ (function (_super) { - __extends(ValidateManuallyBindingBehavior, _super); - function ValidateManuallyBindingBehavior() { - return _super !== null && _super.apply(this, arguments) || this; - } - ValidateManuallyBindingBehavior.prototype.getValidateTrigger = function () { - return validateTrigger.manual; - }; - ValidateManuallyBindingBehavior.inject = [TaskQueue]; - ValidateManuallyBindingBehavior = __decorate([ - bindingBehavior('validateManually') - ], ValidateManuallyBindingBehavior); - return ValidateManuallyBindingBehavior; -}(ValidateBindingBehaviorBase)); -/** - * Binding behavior. Indicates the bound property should be validated - * when the associated element blurs. - */ -var ValidateOnBlurBindingBehavior = /** @class */ (function (_super) { - __extends(ValidateOnBlurBindingBehavior, _super); - function ValidateOnBlurBindingBehavior() { - return _super !== null && _super.apply(this, arguments) || this; - } - ValidateOnBlurBindingBehavior.prototype.getValidateTrigger = function () { - return validateTrigger.blur; - }; - ValidateOnBlurBindingBehavior.inject = [TaskQueue]; - ValidateOnBlurBindingBehavior = __decorate([ - bindingBehavior('validateOnBlur') - ], ValidateOnBlurBindingBehavior); - return ValidateOnBlurBindingBehavior; -}(ValidateBindingBehaviorBase)); -/** - * Binding behavior. Indicates the bound property should be validated - * when the associated element is changed by the user, causing a change - * to the model. - */ -var ValidateOnChangeBindingBehavior = /** @class */ (function (_super) { - __extends(ValidateOnChangeBindingBehavior, _super); - function ValidateOnChangeBindingBehavior() { - return _super !== null && _super.apply(this, arguments) || this; - } - ValidateOnChangeBindingBehavior.prototype.getValidateTrigger = function () { - return validateTrigger.change; - }; - ValidateOnChangeBindingBehavior.inject = [TaskQueue]; - ValidateOnChangeBindingBehavior = __decorate([ - bindingBehavior('validateOnChange') - ], ValidateOnChangeBindingBehavior); - return ValidateOnChangeBindingBehavior; -}(ValidateBindingBehaviorBase)); -/** - * Binding behavior. Indicates the bound property should be validated - * when the associated element blurs or is changed by the user, causing - * a change to the model. - */ -var ValidateOnChangeOrBlurBindingBehavior = /** @class */ (function (_super) { - __extends(ValidateOnChangeOrBlurBindingBehavior, _super); - function ValidateOnChangeOrBlurBindingBehavior() { - return _super !== null && _super.apply(this, arguments) || this; - } - ValidateOnChangeOrBlurBindingBehavior.prototype.getValidateTrigger = function () { - return validateTrigger.changeOrBlur; - }; - ValidateOnChangeOrBlurBindingBehavior.inject = [TaskQueue]; - ValidateOnChangeOrBlurBindingBehavior = __decorate([ - bindingBehavior('validateOnChangeOrBlur') - ], ValidateOnChangeOrBlurBindingBehavior); - return ValidateOnChangeOrBlurBindingBehavior; -}(ValidateBindingBehaviorBase)); - -/** - * Creates ValidationController instances. - */ -var ValidationControllerFactory = /** @class */ (function () { - function ValidationControllerFactory(container) { - this.container = container; - } - ValidationControllerFactory.get = function (container) { - return new ValidationControllerFactory(container); - }; - /** - * Creates a new controller instance. - */ - ValidationControllerFactory.prototype.create = function (validator) { - if (!validator) { - validator = this.container.get(Validator); - } - var propertyParser = this.container.get(PropertyAccessorParser); - return new ValidationController(validator, propertyParser); - }; - /** - * Creates a new controller and registers it in the current element's container so that it's - * available to the validate binding behavior and renderers. - */ - ValidationControllerFactory.prototype.createForCurrentScope = function (validator) { - var controller = this.create(validator); - this.container.registerInstance(ValidationController, controller); - return controller; - }; - return ValidationControllerFactory; -}()); -ValidationControllerFactory['protocol:aurelia:resolver'] = true; - -var ValidationErrorsCustomAttribute = /** @class */ (function () { - function ValidationErrorsCustomAttribute(boundaryElement, controllerAccessor) { - this.boundaryElement = boundaryElement; - this.controllerAccessor = controllerAccessor; - this.controller = null; - this.errors = []; - this.errorsInternal = []; - } - ValidationErrorsCustomAttribute.inject = function () { - return [DOM.Element, Lazy.of(ValidationController)]; - }; - ValidationErrorsCustomAttribute.prototype.sort = function () { - this.errorsInternal.sort(function (a, b) { - if (a.targets[0] === b.targets[0]) { - return 0; - } - // tslint:disable-next-line:no-bitwise - return a.targets[0].compareDocumentPosition(b.targets[0]) & 2 ? 1 : -1; - }); - }; - ValidationErrorsCustomAttribute.prototype.interestingElements = function (elements) { - var _this = this; - return elements.filter(function (e) { return _this.boundaryElement.contains(e); }); - }; - ValidationErrorsCustomAttribute.prototype.render = function (instruction) { - var _loop_1 = function (result) { - var index = this_1.errorsInternal.findIndex(function (x) { return x.error === result; }); - if (index !== -1) { - this_1.errorsInternal.splice(index, 1); - } - }; - var this_1 = this; - for (var _i = 0, _a = instruction.unrender; _i < _a.length; _i++) { - var result = _a[_i].result; - _loop_1(result); - } - for (var _b = 0, _c = instruction.render; _b < _c.length; _b++) { - var _d = _c[_b], result = _d.result, elements = _d.elements; - if (result.valid) { - continue; - } - var targets = this.interestingElements(elements); - if (targets.length) { - this.errorsInternal.push({ error: result, targets: targets }); - } - } - this.sort(); - this.errors = this.errorsInternal; - }; - ValidationErrorsCustomAttribute.prototype.bind = function () { - if (!this.controller) { - this.controller = this.controllerAccessor(); - } - // this will call render() with the side-effect of updating this.errors - this.controller.addRenderer(this); - }; - ValidationErrorsCustomAttribute.prototype.unbind = function () { - if (this.controller) { - this.controller.removeRenderer(this); - } - }; - __decorate([ - bindable({ defaultBindingMode: bindingMode.oneWay }) - ], ValidationErrorsCustomAttribute.prototype, "controller", void 0); - __decorate([ - bindable({ primaryProperty: true, defaultBindingMode: bindingMode.twoWay }) - ], ValidationErrorsCustomAttribute.prototype, "errors", void 0); - ValidationErrorsCustomAttribute = __decorate([ - customAttribute('validation-errors') - ], ValidationErrorsCustomAttribute); - return ValidationErrorsCustomAttribute; -}()); - -var ValidationRendererCustomAttribute = /** @class */ (function () { - function ValidationRendererCustomAttribute() { - } - ValidationRendererCustomAttribute.prototype.created = function (view) { - this.container = view.container; - }; - ValidationRendererCustomAttribute.prototype.bind = function () { - this.controller = this.container.get(ValidationController); - this.renderer = this.container.get(this.value); - this.controller.addRenderer(this.renderer); - }; - ValidationRendererCustomAttribute.prototype.unbind = function () { - this.controller.removeRenderer(this.renderer); - this.controller = null; - this.renderer = null; - }; - ValidationRendererCustomAttribute = __decorate([ - customAttribute('validation-renderer') - ], ValidationRendererCustomAttribute); - return ValidationRendererCustomAttribute; -}()); - -/** - * Sets, unsets and retrieves rules on an object or constructor function. - */ -var Rules = /** @class */ (function () { - function Rules() { - } - /** - * Applies the rules to a target. - */ - Rules.set = function (target, rules) { - if (target instanceof Function) { - target = target.prototype; - } - Object.defineProperty(target, Rules.key, { enumerable: false, configurable: false, writable: true, value: rules }); - }; - /** - * Removes rules from a target. - */ - Rules.unset = function (target) { - if (target instanceof Function) { - target = target.prototype; - } - target[Rules.key] = null; - }; - /** - * Retrieves the target's rules. - */ - Rules.get = function (target) { - return target[Rules.key] || null; - }; - /** - * The name of the property that stores the rules. - */ - Rules.key = '__rules__'; - return Rules; -}()); - -// tslint:disable:no-empty -var ExpressionVisitor = /** @class */ (function () { - function ExpressionVisitor() { - } - ExpressionVisitor.prototype.visitChain = function (chain) { - this.visitArgs(chain.expressions); - }; - ExpressionVisitor.prototype.visitBindingBehavior = function (behavior) { - behavior.expression.accept(this); - this.visitArgs(behavior.args); - }; - ExpressionVisitor.prototype.visitValueConverter = function (converter) { - converter.expression.accept(this); - this.visitArgs(converter.args); - }; - ExpressionVisitor.prototype.visitAssign = function (assign) { - assign.target.accept(this); - assign.value.accept(this); - }; - ExpressionVisitor.prototype.visitConditional = function (conditional) { - conditional.condition.accept(this); - conditional.yes.accept(this); - conditional.no.accept(this); - }; - ExpressionVisitor.prototype.visitAccessThis = function (access) { - access.ancestor = access.ancestor; - }; - ExpressionVisitor.prototype.visitAccessScope = function (access) { - access.name = access.name; - }; - ExpressionVisitor.prototype.visitAccessMember = function (access) { - access.object.accept(this); - }; - ExpressionVisitor.prototype.visitAccessKeyed = function (access) { - access.object.accept(this); - access.key.accept(this); - }; - ExpressionVisitor.prototype.visitCallScope = function (call) { - this.visitArgs(call.args); - }; - ExpressionVisitor.prototype.visitCallFunction = function (call) { - call.func.accept(this); - this.visitArgs(call.args); - }; - ExpressionVisitor.prototype.visitCallMember = function (call) { - call.object.accept(this); - this.visitArgs(call.args); - }; - ExpressionVisitor.prototype.visitPrefix = function (prefix) { - prefix.expression.accept(this); - }; - ExpressionVisitor.prototype.visitBinary = function (binary) { - binary.left.accept(this); - binary.right.accept(this); - }; - ExpressionVisitor.prototype.visitLiteralPrimitive = function (literal) { - literal.value = literal.value; - }; - ExpressionVisitor.prototype.visitLiteralArray = function (literal) { - this.visitArgs(literal.elements); - }; - ExpressionVisitor.prototype.visitLiteralObject = function (literal) { - this.visitArgs(literal.values); - }; - ExpressionVisitor.prototype.visitLiteralString = function (literal) { - literal.value = literal.value; - }; - ExpressionVisitor.prototype.visitArgs = function (args) { - for (var i = 0; i < args.length; i++) { - args[i].accept(this); - } - }; - return ExpressionVisitor; -}()); - -var ValidationMessageParser = /** @class */ (function () { - function ValidationMessageParser(bindinqLanguage) { - this.bindinqLanguage = bindinqLanguage; - this.emptyStringExpression = new LiteralString(''); - this.nullExpression = new LiteralPrimitive(null); - this.undefinedExpression = new LiteralPrimitive(undefined); - this.cache = {}; - } - ValidationMessageParser.prototype.parse = function (message) { - if (this.cache[message] !== undefined) { - return this.cache[message]; - } - var parts = this.bindinqLanguage.parseInterpolation(null, message); - if (parts === null) { - return new LiteralString(message); - } - var expression = new LiteralString(parts[0]); - for (var i = 1; i < parts.length; i += 2) { - expression = new Binary('+', expression, new Binary('+', this.coalesce(parts[i]), new LiteralString(parts[i + 1]))); - } - MessageExpressionValidator.validate(expression, message); - this.cache[message] = expression; - return expression; - }; - ValidationMessageParser.prototype.coalesce = function (part) { - // part === null || part === undefined ? '' : part - return new Conditional(new Binary('||', new Binary('===', part, this.nullExpression), new Binary('===', part, this.undefinedExpression)), this.emptyStringExpression, new CallMember(part, 'toString', [])); - }; - ValidationMessageParser.inject = [BindingLanguage]; - return ValidationMessageParser; -}()); -var MessageExpressionValidator = /** @class */ (function (_super) { - __extends(MessageExpressionValidator, _super); - function MessageExpressionValidator(originalMessage) { - var _this = _super.call(this) || this; - _this.originalMessage = originalMessage; - return _this; - } - MessageExpressionValidator.validate = function (expression, originalMessage) { - var visitor = new MessageExpressionValidator(originalMessage); - expression.accept(visitor); - }; - MessageExpressionValidator.prototype.visitAccessScope = function (access) { - if (access.ancestor !== 0) { - throw new Error('$parent is not permitted in validation message expressions.'); - } - if (['displayName', 'propertyName', 'value', 'object', 'config', 'getDisplayName'].indexOf(access.name) !== -1) { - getLogger('aurelia-validation') - // tslint:disable-next-line:max-line-length - .warn("Did you mean to use \"$" + access.name + "\" instead of \"" + access.name + "\" in this validation message template: \"" + this.originalMessage + "\"?"); - } - }; - return MessageExpressionValidator; -}(ExpressionVisitor)); - -/** - * Dictionary of validation messages. [messageKey]: messageExpression - */ -var validationMessages = { - /** - * The default validation message. Used with rules that have no standard message. - */ - default: "${$displayName} is invalid.", - required: "${$displayName} is required.", - matches: "${$displayName} is not correctly formatted.", - email: "${$displayName} is not a valid email.", - minLength: "${$displayName} must be at least ${$config.length} character${$config.length === 1 ? '' : 's'}.", - maxLength: "${$displayName} cannot be longer than ${$config.length} character${$config.length === 1 ? '' : 's'}.", - minItems: "${$displayName} must contain at least ${$config.count} item${$config.count === 1 ? '' : 's'}.", - maxItems: "${$displayName} cannot contain more than ${$config.count} item${$config.count === 1 ? '' : 's'}.", - equals: "${$displayName} must be ${$config.expectedValue}.", -}; -/** - * Retrieves validation messages and property display names. - */ -var ValidationMessageProvider = /** @class */ (function () { - function ValidationMessageProvider(parser) { - this.parser = parser; - } - /** - * Returns a message binding expression that corresponds to the key. - * @param key The message key. - */ - ValidationMessageProvider.prototype.getMessage = function (key) { - var message; - if (key in validationMessages) { - message = validationMessages[key]; - } - else { - message = validationMessages['default']; - } - return this.parser.parse(message); - }; - /** - * Formulates a property display name using the property name and the configured - * displayName (if provided). - * Override this with your own custom logic. - * @param propertyName The property name. - */ - ValidationMessageProvider.prototype.getDisplayName = function (propertyName, displayName) { - if (displayName !== null && displayName !== undefined) { - return (displayName instanceof Function) ? displayName() : displayName; - } - // split on upper-case letters. - var words = propertyName.toString().split(/(?=[A-Z])/).join(' '); - // capitalize first letter. - return words.charAt(0).toUpperCase() + words.slice(1); - }; - ValidationMessageProvider.inject = [ValidationMessageParser]; - return ValidationMessageProvider; -}()); - -/** - * Validates. - * Responsible for validating objects and properties. - */ -var StandardValidator = /** @class */ (function (_super) { - __extends(StandardValidator, _super); - function StandardValidator(messageProvider, resources) { - var _this = _super.call(this) || this; - _this.messageProvider = messageProvider; - _this.lookupFunctions = resources.lookupFunctions; - _this.getDisplayName = messageProvider.getDisplayName.bind(messageProvider); - return _this; - } - /** - * Validates the specified property. - * @param object The object to validate. - * @param propertyName The name of the property to validate. - * @param rules Optional. If unspecified, the rules will be looked up using the metadata - * for the object created by ValidationRules....on(class/object) - */ - StandardValidator.prototype.validateProperty = function (object, propertyName, rules) { - return this.validate(object, propertyName, rules || null); - }; - /** - * Validates all rules for specified object and it's properties. - * @param object The object to validate. - * @param rules Optional. If unspecified, the rules will be looked up using the metadata - * for the object created by ValidationRules....on(class/object) - */ - StandardValidator.prototype.validateObject = function (object, rules) { - return this.validate(object, null, rules || null); - }; - /** - * Determines whether a rule exists in a set of rules. - * @param rules The rules to search. - * @parem rule The rule to find. - */ - StandardValidator.prototype.ruleExists = function (rules, rule) { - var i = rules.length; - while (i--) { - if (rules[i].indexOf(rule) !== -1) { - return true; - } - } - return false; - }; - StandardValidator.prototype.getMessage = function (rule, object, value) { - var expression = rule.message || this.messageProvider.getMessage(rule.messageKey); - // tslint:disable-next-line:prefer-const - var _a = rule.property, propertyName = _a.name, displayName = _a.displayName; - if (propertyName !== null) { - displayName = this.messageProvider.getDisplayName(propertyName, displayName); - } - var overrideContext = { - $displayName: displayName, - $propertyName: propertyName, - $value: value, - $object: object, - $config: rule.config, - // returns the name of a given property, given just the property name (irrespective of the property's displayName) - // split on capital letters, first letter ensured to be capitalized - $getDisplayName: this.getDisplayName - }; - return expression.evaluate({ bindingContext: object, overrideContext: overrideContext }, this.lookupFunctions); - }; - StandardValidator.prototype.validateRuleSequence = function (object, propertyName, ruleSequence, sequence, results) { - var _this = this; - // are we validating all properties or a single property? - var validateAllProperties = propertyName === null || propertyName === undefined; - var rules = ruleSequence[sequence]; - var allValid = true; - // validate each rule. - var promises = []; - var _loop_1 = function (i) { - var rule = rules[i]; - // is the rule related to the property we're validating. - // tslint:disable-next-line:triple-equals | Use loose equality for property keys - if (!validateAllProperties && rule.property.name != propertyName) { - return "continue"; - } - // is this a conditional rule? is the condition met? - if (rule.when && !rule.when(object)) { - return "continue"; - } - // validate. - var value = rule.property.name === null ? object : object[rule.property.name]; - var promiseOrBoolean = rule.condition(value, object); - if (!(promiseOrBoolean instanceof Promise)) { - promiseOrBoolean = Promise.resolve(promiseOrBoolean); - } - promises.push(promiseOrBoolean.then(function (valid) { - var message = valid ? null : _this.getMessage(rule, object, value); - results.push(new ValidateResult(rule, object, rule.property.name, valid, message)); - allValid = allValid && valid; - return valid; - })); - }; - for (var i = 0; i < rules.length; i++) { - _loop_1(i); - } - return Promise.all(promises) - .then(function () { - sequence++; - if (allValid && sequence < ruleSequence.length) { - return _this.validateRuleSequence(object, propertyName, ruleSequence, sequence, results); - } - return results; - }); - }; - StandardValidator.prototype.validate = function (object, propertyName, rules) { - // rules specified? - if (!rules) { - // no. attempt to locate the rules. - rules = Rules.get(object); - } - // any rules? - if (!rules || rules.length === 0) { - return Promise.resolve([]); - } - return this.validateRuleSequence(object, propertyName, rules, 0, []); - }; - StandardValidator.inject = [ValidationMessageProvider, ViewResources]; - return StandardValidator; -}(Validator)); - -/** - * Part of the fluent rule API. Enables customizing property rules. - */ -var FluentRuleCustomizer = /** @class */ (function () { - function FluentRuleCustomizer(property, condition, config, fluentEnsure, fluentRules, parsers) { - if (config === void 0) { config = {}; } - this.fluentEnsure = fluentEnsure; - this.fluentRules = fluentRules; - this.parsers = parsers; - this.rule = { - property: property, - condition: condition, - config: config, - when: null, - messageKey: 'default', - message: null, - sequence: fluentRules.sequence - }; - this.fluentEnsure._addRule(this.rule); - } - /** - * Validate subsequent rules after previously declared rules have - * been validated successfully. Use to postpone validation of costly - * rules until less expensive rules pass validation. - */ - FluentRuleCustomizer.prototype.then = function () { - this.fluentRules.sequence++; - return this; - }; - /** - * Specifies the key to use when looking up the rule's validation message. - */ - FluentRuleCustomizer.prototype.withMessageKey = function (key) { - this.rule.messageKey = key; - this.rule.message = null; - return this; - }; - /** - * Specifies rule's validation message. - */ - FluentRuleCustomizer.prototype.withMessage = function (message) { - this.rule.messageKey = 'custom'; - this.rule.message = this.parsers.message.parse(message); - return this; - }; - /** - * Specifies a condition that must be met before attempting to validate the rule. - * @param condition A function that accepts the object as a parameter and returns true - * or false whether the rule should be evaluated. - */ - FluentRuleCustomizer.prototype.when = function (condition) { - this.rule.when = condition; - return this; - }; - /** - * Tags the rule instance, enabling the rule to be found easily - * using ValidationRules.taggedRules(rules, tag) - */ - FluentRuleCustomizer.prototype.tag = function (tag) { - this.rule.tag = tag; - return this; - }; - ///// FluentEnsure APIs ///// - /** - * Target a property with validation rules. - * @param property The property to target. Can be the property name or a property accessor function. - */ - FluentRuleCustomizer.prototype.ensure = function (subject) { - return this.fluentEnsure.ensure(subject); - }; - /** - * Targets an object with validation rules. - */ - FluentRuleCustomizer.prototype.ensureObject = function () { - return this.fluentEnsure.ensureObject(); - }; - Object.defineProperty(FluentRuleCustomizer.prototype, "rules", { - /** - * Rules that have been defined using the fluent API. - */ - get: function () { - return this.fluentEnsure.rules; - }, - enumerable: true, - configurable: true - }); - /** - * Applies the rules to a class or object, making them discoverable by the StandardValidator. - * @param target A class or object. - */ - FluentRuleCustomizer.prototype.on = function (target) { - return this.fluentEnsure.on(target); - }; - ///////// FluentRules APIs ///////// - /** - * Applies an ad-hoc rule function to the ensured property or object. - * @param condition The function to validate the rule. - * Will be called with two arguments, the property value and the object. - * Should return a boolean or a Promise that resolves to a boolean. - */ - FluentRuleCustomizer.prototype.satisfies = function (condition, config) { - return this.fluentRules.satisfies(condition, config); - }; - /** - * Applies a rule by name. - * @param name The name of the custom or standard rule. - * @param args The rule's arguments. - */ - FluentRuleCustomizer.prototype.satisfiesRule = function (name) { - var args = []; - for (var _i = 1; _i < arguments.length; _i++) { - args[_i - 1] = arguments[_i]; - } - var _a; - return (_a = this.fluentRules).satisfiesRule.apply(_a, [name].concat(args)); - }; - /** - * Applies the "required" rule to the property. - * The value cannot be null, undefined or whitespace. - */ - FluentRuleCustomizer.prototype.required = function () { - return this.fluentRules.required(); - }; - /** - * Applies the "matches" rule to the property. - * Value must match the specified regular expression. - * null, undefined and empty-string values are considered valid. - */ - FluentRuleCustomizer.prototype.matches = function (regex) { - return this.fluentRules.matches(regex); - }; - /** - * Applies the "email" rule to the property. - * null, undefined and empty-string values are considered valid. - */ - FluentRuleCustomizer.prototype.email = function () { - return this.fluentRules.email(); - }; - /** - * Applies the "minLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - FluentRuleCustomizer.prototype.minLength = function (length) { - return this.fluentRules.minLength(length); - }; - /** - * Applies the "maxLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - FluentRuleCustomizer.prototype.maxLength = function (length) { - return this.fluentRules.maxLength(length); - }; - /** - * Applies the "minItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - FluentRuleCustomizer.prototype.minItems = function (count) { - return this.fluentRules.minItems(count); - }; - /** - * Applies the "maxItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - FluentRuleCustomizer.prototype.maxItems = function (count) { - return this.fluentRules.maxItems(count); - }; - /** - * Applies the "equals" validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - FluentRuleCustomizer.prototype.equals = function (expectedValue) { - return this.fluentRules.equals(expectedValue); - }; - return FluentRuleCustomizer; -}()); -/** - * Part of the fluent rule API. Enables applying rules to properties and objects. - */ -var FluentRules = /** @class */ (function () { - function FluentRules(fluentEnsure, parsers, property) { - this.fluentEnsure = fluentEnsure; - this.parsers = parsers; - this.property = property; - /** - * Current rule sequence number. Used to postpone evaluation of rules until rules - * with lower sequence number have successfully validated. The "then" fluent API method - * manages this property, there's usually no need to set it directly. - */ - this.sequence = 0; - } - /** - * Sets the display name of the ensured property. - */ - FluentRules.prototype.displayName = function (name) { - this.property.displayName = name; - return this; - }; - /** - * Applies an ad-hoc rule function to the ensured property or object. - * @param condition The function to validate the rule. - * Will be called with two arguments, the property value and the object. - * Should return a boolean or a Promise that resolves to a boolean. - */ - FluentRules.prototype.satisfies = function (condition, config) { - return new FluentRuleCustomizer(this.property, condition, config, this.fluentEnsure, this, this.parsers); - }; - /** - * Applies a rule by name. - * @param name The name of the custom or standard rule. - * @param args The rule's arguments. - */ - FluentRules.prototype.satisfiesRule = function (name) { - var _this = this; - var args = []; - for (var _i = 1; _i < arguments.length; _i++) { - args[_i - 1] = arguments[_i]; - } - var rule = FluentRules.customRules[name]; - if (!rule) { - // standard rule? - rule = this[name]; - if (rule instanceof Function) { - return rule.call.apply(rule, [this].concat(args)); - } - throw new Error("Rule with name \"" + name + "\" does not exist."); - } - var config = rule.argsToConfig ? rule.argsToConfig.apply(rule, args) : undefined; - return this.satisfies(function (value, obj) { - var _a; - return (_a = rule.condition).call.apply(_a, [_this, value, obj].concat(args)); - }, config) - .withMessageKey(name); - }; - /** - * Applies the "required" rule to the property. - * The value cannot be null, undefined or whitespace. - */ - FluentRules.prototype.required = function () { - return this.satisfies(function (value) { - return value !== null - && value !== undefined - && !(isString(value) && !/\S/.test(value)); - }).withMessageKey('required'); - }; - /** - * Applies the "matches" rule to the property. - * Value must match the specified regular expression. - * null, undefined and empty-string values are considered valid. - */ - FluentRules.prototype.matches = function (regex) { - return this.satisfies(function (value) { return value === null || value === undefined || value.length === 0 || regex.test(value); }) - .withMessageKey('matches'); - }; - /** - * Applies the "email" rule to the property. - * null, undefined and empty-string values are considered valid. - */ - FluentRules.prototype.email = function () { - // regex from https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address - /* tslint:disable:max-line-length */ - return this.matches(/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/) - /* tslint:enable:max-line-length */ - .withMessageKey('email'); - }; - /** - * Applies the "minLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - FluentRules.prototype.minLength = function (length) { - return this.satisfies(function (value) { return value === null || value === undefined || value.length === 0 || value.length >= length; }, { length: length }) - .withMessageKey('minLength'); - }; - /** - * Applies the "maxLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - FluentRules.prototype.maxLength = function (length) { - return this.satisfies(function (value) { return value === null || value === undefined || value.length === 0 || value.length <= length; }, { length: length }) - .withMessageKey('maxLength'); - }; - /** - * Applies the "minItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - FluentRules.prototype.minItems = function (count) { - return this.satisfies(function (value) { return value === null || value === undefined || value.length >= count; }, { count: count }) - .withMessageKey('minItems'); - }; - /** - * Applies the "maxItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - FluentRules.prototype.maxItems = function (count) { - return this.satisfies(function (value) { return value === null || value === undefined || value.length <= count; }, { count: count }) - .withMessageKey('maxItems'); - }; - /** - * Applies the "equals" validation rule to the property. - * null and undefined values are considered valid. - */ - FluentRules.prototype.equals = function (expectedValue) { - return this.satisfies(function (value) { return value === null || value === undefined || value === '' || value === expectedValue; }, { expectedValue: expectedValue }) - .withMessageKey('equals'); - }; - FluentRules.customRules = {}; - return FluentRules; -}()); -/** - * Part of the fluent rule API. Enables targeting properties and objects with rules. - */ -var FluentEnsure = /** @class */ (function () { - function FluentEnsure(parsers) { - this.parsers = parsers; - /** - * Rules that have been defined using the fluent API. - */ - this.rules = []; - } - /** - * Target a property with validation rules. - * @param property The property to target. Can be the property name or a property accessor - * function. - */ - FluentEnsure.prototype.ensure = function (property) { - this.assertInitialized(); - var name = this.parsers.property.parse(property); - var fluentRules = new FluentRules(this, this.parsers, { name: name, displayName: null }); - return this.mergeRules(fluentRules, name); - }; - /** - * Targets an object with validation rules. - */ - FluentEnsure.prototype.ensureObject = function () { - this.assertInitialized(); - var fluentRules = new FluentRules(this, this.parsers, { name: null, displayName: null }); - return this.mergeRules(fluentRules, null); - }; - /** - * Applies the rules to a class or object, making them discoverable by the StandardValidator. - * @param target A class or object. - */ - FluentEnsure.prototype.on = function (target) { - Rules.set(target, this.rules); - return this; - }; - /** - * Adds a rule definition to the sequenced ruleset. - * @internal - */ - FluentEnsure.prototype._addRule = function (rule) { - while (this.rules.length < rule.sequence + 1) { - this.rules.push([]); - } - this.rules[rule.sequence].push(rule); - }; - FluentEnsure.prototype.assertInitialized = function () { - if (this.parsers) { - return; - } - throw new Error("Did you forget to add \".plugin('aurelia-validation')\" to your main.js?"); - }; - FluentEnsure.prototype.mergeRules = function (fluentRules, propertyName) { - // tslint:disable-next-line:triple-equals | Use loose equality for property keys - var existingRules = this.rules.find(function (r) { return r.length > 0 && r[0].property.name == propertyName; }); - if (existingRules) { - var rule = existingRules[existingRules.length - 1]; - fluentRules.sequence = rule.sequence; - if (rule.property.displayName !== null) { - fluentRules = fluentRules.displayName(rule.property.displayName); - } - } - return fluentRules; - }; - return FluentEnsure; -}()); -/** - * Fluent rule definition API. - */ -var ValidationRules = /** @class */ (function () { - function ValidationRules() { - } - ValidationRules.initialize = function (messageParser, propertyParser) { - this.parsers = { - message: messageParser, - property: propertyParser - }; - }; - /** - * Target a property with validation rules. - * @param property The property to target. Can be the property name or a property accessor function. - */ - ValidationRules.ensure = function (property) { - return new FluentEnsure(ValidationRules.parsers).ensure(property); - }; - /** - * Targets an object with validation rules. - */ - ValidationRules.ensureObject = function () { - return new FluentEnsure(ValidationRules.parsers).ensureObject(); - }; - /** - * Defines a custom rule. - * @param name The name of the custom rule. Also serves as the message key. - * @param condition The rule function. - * @param message The message expression - * @param argsToConfig A function that maps the rule's arguments to a "config" - * object that can be used when evaluating the message expression. - */ - ValidationRules.customRule = function (name, condition, message, argsToConfig) { - validationMessages[name] = message; - FluentRules.customRules[name] = { condition: condition, argsToConfig: argsToConfig }; - }; - /** - * Returns rules with the matching tag. - * @param rules The rules to search. - * @param tag The tag to search for. - */ - ValidationRules.taggedRules = function (rules, tag) { - return rules.map(function (x) { return x.filter(function (r) { return r.tag === tag; }); }); - }; - /** - * Returns rules that have no tag. - * @param rules The rules to search. - */ - ValidationRules.untaggedRules = function (rules) { - return rules.map(function (x) { return x.filter(function (r) { return r.tag === undefined; }); }); - }; - /** - * Removes the rules from a class or object. - * @param target A class or object. - */ - ValidationRules.off = function (target) { - Rules.unset(target); - }; - return ValidationRules; -}()); - -// Exports -/** - * Aurelia Validation Configuration API - */ -var AureliaValidationConfiguration = /** @class */ (function () { - function AureliaValidationConfiguration() { - this.validatorType = StandardValidator; - } - /** - * Use a custom Validator implementation. - */ - AureliaValidationConfiguration.prototype.customValidator = function (type) { - this.validatorType = type; - }; - /** - * Applies the configuration. - */ - AureliaValidationConfiguration.prototype.apply = function (container) { - var validator = container.get(this.validatorType); - container.registerInstance(Validator, validator); - }; - return AureliaValidationConfiguration; -}()); -/** - * Configures the plugin. - */ -function configure( -// tslint:disable-next-line:ban-types -frameworkConfig, callback) { - // the fluent rule definition API needs the parser to translate messages - // to interpolation expressions. - var messageParser = frameworkConfig.container.get(ValidationMessageParser); - var propertyParser = frameworkConfig.container.get(PropertyAccessorParser); - ValidationRules.initialize(messageParser, propertyParser); - // configure... - var config = new AureliaValidationConfiguration(); - if (callback instanceof Function) { - callback(config); - } - config.apply(frameworkConfig.container); - // globalize the behaviors. - if (frameworkConfig.globalResources) { - frameworkConfig.globalResources(ValidateBindingBehavior, ValidateManuallyBindingBehavior, ValidateOnBlurBindingBehavior, ValidateOnChangeBindingBehavior, ValidateOnChangeOrBlurBindingBehavior, ValidationErrorsCustomAttribute, ValidationRendererCustomAttribute); - } -} - -export { AureliaValidationConfiguration, configure, getTargetDOMElement, getPropertyInfo, PropertyAccessorParser, getAccessorExpression, ValidateBindingBehavior, ValidateManuallyBindingBehavior, ValidateOnBlurBindingBehavior, ValidateOnChangeBindingBehavior, ValidateOnChangeOrBlurBindingBehavior, ValidateEvent, ValidateResult, validateTrigger, ValidationController, ValidationControllerFactory, ValidationErrorsCustomAttribute, ValidationRendererCustomAttribute, Validator, Rules, StandardValidator, validationMessages, ValidationMessageProvider, ValidationMessageParser, MessageExpressionValidator, FluentRuleCustomizer, FluentRules, FluentEnsure, ValidationRules }; diff --git a/dist/system/aurelia-validation.js b/dist/system/aurelia-validation.js deleted file mode 100644 index d1578309..00000000 --- a/dist/system/aurelia-validation.js +++ /dev/null @@ -1,1825 +0,0 @@ -System.register(['aurelia-pal', 'aurelia-binding', 'aurelia-dependency-injection', 'aurelia-task-queue', 'aurelia-templating', 'aurelia-logging'], function (exports, module) { - 'use strict'; - var DOM, AccessMember, AccessScope, AccessKeyed, BindingBehavior, ValueConverter, getContextFor, Parser, bindingBehavior, bindingMode, LiteralString, Binary, Conditional, LiteralPrimitive, CallMember, Optional, Lazy, TaskQueue, customAttribute, bindable, BindingLanguage, ViewResources, getLogger; - return { - setters: [function (module) { - DOM = module.DOM; - }, function (module) { - AccessMember = module.AccessMember; - AccessScope = module.AccessScope; - AccessKeyed = module.AccessKeyed; - BindingBehavior = module.BindingBehavior; - ValueConverter = module.ValueConverter; - getContextFor = module.getContextFor; - Parser = module.Parser; - bindingBehavior = module.bindingBehavior; - bindingMode = module.bindingMode; - LiteralString = module.LiteralString; - Binary = module.Binary; - Conditional = module.Conditional; - LiteralPrimitive = module.LiteralPrimitive; - CallMember = module.CallMember; - }, function (module) { - Optional = module.Optional; - Lazy = module.Lazy; - }, function (module) { - TaskQueue = module.TaskQueue; - }, function (module) { - customAttribute = module.customAttribute; - bindable = module.bindable; - BindingLanguage = module.BindingLanguage; - ViewResources = module.ViewResources; - }, function (module) { - getLogger = module.getLogger; - }], - execute: function () { - - exports({ - configure: configure, - getTargetDOMElement: getTargetDOMElement, - getPropertyInfo: getPropertyInfo, - getAccessorExpression: getAccessorExpression, - validateTrigger: void 0 - }); - - /** - * Gets the DOM element associated with the data-binding. Most of the time it's - * the binding.target but sometimes binding.target is an aurelia custom element, - * or custom attribute which is a javascript "class" instance, so we need to use - * the controller's container to retrieve the actual DOM element. - */ - function getTargetDOMElement(binding, view) { - var target = binding.target; - // DOM element - if (target instanceof Element) { - return target; - } - // custom element or custom attribute - // tslint:disable-next-line:prefer-const - for (var i = 0, ii = view.controllers.length; i < ii; i++) { - var controller = view.controllers[i]; - if (controller.viewModel === target) { - var element = controller.container.get(DOM.Element); - if (element) { - return element; - } - throw new Error("Unable to locate target element for \"" + binding.sourceExpression + "\"."); - } - } - throw new Error("Unable to locate target element for \"" + binding.sourceExpression + "\"."); - } - - function getObject(expression, objectExpression, source) { - var value = objectExpression.evaluate(source, null); - if (value === null || value === undefined || value instanceof Object) { - return value; - } - // tslint:disable-next-line:max-line-length - throw new Error("The '" + objectExpression + "' part of '" + expression + "' evaluates to " + value + " instead of an object, null or undefined."); - } - /** - * Retrieves the object and property name for the specified expression. - * @param expression The expression - * @param source The scope - */ - function getPropertyInfo(expression, source) { - var originalExpression = expression; - while (expression instanceof BindingBehavior || expression instanceof ValueConverter) { - expression = expression.expression; - } - var object; - var propertyName; - if (expression instanceof AccessScope) { - object = getContextFor(expression.name, source, expression.ancestor); - propertyName = expression.name; - } - else if (expression instanceof AccessMember) { - object = getObject(originalExpression, expression.object, source); - propertyName = expression.name; - } - else if (expression instanceof AccessKeyed) { - object = getObject(originalExpression, expression.object, source); - propertyName = expression.key.evaluate(source); - } - else { - throw new Error("Expression '" + originalExpression + "' is not compatible with the validate binding-behavior."); - } - if (object === null || object === undefined) { - return null; - } - return { object: object, propertyName: propertyName }; - } - - function isString(value) { - return Object.prototype.toString.call(value) === '[object String]'; - } - function isNumber(value) { - return Object.prototype.toString.call(value) === '[object Number]'; - } - - var PropertyAccessorParser = exports('PropertyAccessorParser', /** @class */ (function () { - function PropertyAccessorParser(parser) { - this.parser = parser; - } - PropertyAccessorParser.prototype.parse = function (property) { - if (isString(property) || isNumber(property)) { - return property; - } - var accessorText = getAccessorExpression(property.toString()); - var accessor = this.parser.parse(accessorText); - if (accessor instanceof AccessScope - || accessor instanceof AccessMember && accessor.object instanceof AccessScope) { - return accessor.name; - } - throw new Error("Invalid property expression: \"" + accessor + "\""); - }; - PropertyAccessorParser.inject = [Parser]; - return PropertyAccessorParser; - }())); - function getAccessorExpression(fn) { - /* tslint:disable:max-line-length */ - var classic = /^function\s*\([$_\w\d]+\)\s*\{(?:\s*"use strict";)?\s*(?:[$_\w\d.['"\]+;]+)?\s*return\s+[$_\w\d]+\.([$_\w\d]+)\s*;?\s*\}$/; - /* tslint:enable:max-line-length */ - var arrow = /^\(?[$_\w\d]+\)?\s*=>\s*[$_\w\d]+\.([$_\w\d]+)$/; - var match = classic.exec(fn) || arrow.exec(fn); - if (match === null) { - throw new Error("Unable to parse accessor function:\n" + fn); - } - return match[1]; - } - - /*! ***************************************************************************** - Copyright (c) Microsoft Corporation. All rights reserved. - Licensed under the Apache License, Version 2.0 (the "License"); you may not use - this file except in compliance with the License. You may obtain a copy of the - License at http://www.apache.org/licenses/LICENSE-2.0 - - THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED - WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, - MERCHANTABLITY OR NON-INFRINGEMENT. - - See the Apache Version 2.0 License for specific language governing permissions - and limitations under the License. - ***************************************************************************** */ - /* global Reflect, Promise */ - - var extendStatics = function(d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - - function __extends(d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - } - - function __decorate(decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; - } - - /** - * Validation triggers. - */ - var validateTrigger; - (function (validateTrigger) { - /** - * Manual validation. Use the controller's `validate()` and `reset()` methods - * to validate all bindings. - */ - validateTrigger[validateTrigger["manual"] = 0] = "manual"; - /** - * Validate the binding when the binding's target element fires a DOM "blur" event. - */ - validateTrigger[validateTrigger["blur"] = 1] = "blur"; - /** - * Validate the binding when it updates the model due to a change in the view. - */ - validateTrigger[validateTrigger["change"] = 2] = "change"; - /** - * Validate the binding when the binding's target element fires a DOM "blur" event and - * when it updates the model due to a change in the view. - */ - validateTrigger[validateTrigger["changeOrBlur"] = 3] = "changeOrBlur"; - })(validateTrigger || (validateTrigger = exports('validateTrigger', {}))); - - /** - * Validates objects and properties. - */ - var Validator = exports('Validator', /** @class */ (function () { - function Validator() { - } - return Validator; - }())); - - /** - * The result of validating an individual validation rule. - */ - var ValidateResult = exports('ValidateResult', /** @class */ (function () { - /** - * @param rule The rule associated with the result. Validator implementation specific. - * @param object The object that was validated. - * @param propertyName The name of the property that was validated. - * @param error The error, if the result is a validation error. - */ - function ValidateResult(rule, object, propertyName, valid, message) { - if (message === void 0) { message = null; } - this.rule = rule; - this.object = object; - this.propertyName = propertyName; - this.valid = valid; - this.message = message; - this.id = ValidateResult.nextId++; - } - ValidateResult.prototype.toString = function () { - return this.valid ? 'Valid.' : this.message; - }; - ValidateResult.nextId = 0; - return ValidateResult; - }())); - - var ValidateEvent = exports('ValidateEvent', /** @class */ (function () { - function ValidateEvent( - /** - * The type of validate event. Either "validate" or "reset". - */ - type, - /** - * The controller's current array of errors. For an array containing both - * failed rules and passed rules, use the "results" property. - */ - errors, - /** - * The controller's current array of validate results. This - * includes both passed rules and failed rules. For an array of only failed rules, - * use the "errors" property. - */ - results, - /** - * The instruction passed to the "validate" or "reset" event. Will be null when - * the controller's validate/reset method was called with no instruction argument. - */ - instruction, - /** - * In events with type === "validate", this property will contain the result - * of validating the instruction (see "instruction" property). Use the controllerValidateResult - * to access the validate results specific to the call to "validate" - * (as opposed to using the "results" and "errors" properties to access the controller's entire - * set of results/errors). - */ - controllerValidateResult) { - this.type = type; - this.errors = errors; - this.results = results; - this.instruction = instruction; - this.controllerValidateResult = controllerValidateResult; - } - return ValidateEvent; - }())); - - /** - * Orchestrates validation. - * Manages a set of bindings, renderers and objects. - * Exposes the current list of validation results for binding purposes. - */ - var ValidationController = exports('ValidationController', /** @class */ (function () { - function ValidationController(validator, propertyParser) { - this.validator = validator; - this.propertyParser = propertyParser; - // Registered bindings (via the validate binding behavior) - this.bindings = new Map(); - // Renderers that have been added to the controller instance. - this.renderers = []; - /** - * Validation results that have been rendered by the controller. - */ - this.results = []; - /** - * Validation errors that have been rendered by the controller. - */ - this.errors = []; - /** - * Whether the controller is currently validating. - */ - this.validating = false; - // Elements related to validation results that have been rendered. - this.elements = new Map(); - // Objects that have been added to the controller instance (entity-style validation). - this.objects = new Map(); - /** - * The trigger that will invoke automatic validation of a property used in a binding. - */ - this.validateTrigger = validateTrigger.blur; - // Promise that resolves when validation has completed. - this.finishValidating = Promise.resolve(); - this.eventCallbacks = []; - } - /** - * Subscribe to controller validate and reset events. These events occur when the - * controller's "validate"" and "reset" methods are called. - * @param callback The callback to be invoked when the controller validates or resets. - */ - ValidationController.prototype.subscribe = function (callback) { - var _this = this; - this.eventCallbacks.push(callback); - return { - dispose: function () { - var index = _this.eventCallbacks.indexOf(callback); - if (index === -1) { - return; - } - _this.eventCallbacks.splice(index, 1); - } - }; - }; - /** - * Adds an object to the set of objects that should be validated when validate is called. - * @param object The object. - * @param rules Optional. The rules. If rules aren't supplied the Validator implementation will lookup the rules. - */ - ValidationController.prototype.addObject = function (object, rules) { - this.objects.set(object, rules); - }; - /** - * Removes an object from the set of objects that should be validated when validate is called. - * @param object The object. - */ - ValidationController.prototype.removeObject = function (object) { - this.objects.delete(object); - this.processResultDelta('reset', this.results.filter(function (result) { return result.object === object; }), []); - }; - /** - * Adds and renders an error. - */ - ValidationController.prototype.addError = function (message, object, propertyName) { - if (propertyName === void 0) { propertyName = null; } - var resolvedPropertyName; - if (propertyName === null) { - resolvedPropertyName = propertyName; - } - else { - resolvedPropertyName = this.propertyParser.parse(propertyName); - } - var result = new ValidateResult({ __manuallyAdded__: true }, object, resolvedPropertyName, false, message); - this.processResultDelta('validate', [], [result]); - return result; - }; - /** - * Removes and unrenders an error. - */ - ValidationController.prototype.removeError = function (result) { - if (this.results.indexOf(result) !== -1) { - this.processResultDelta('reset', [result], []); - } - }; - /** - * Adds a renderer. - * @param renderer The renderer. - */ - ValidationController.prototype.addRenderer = function (renderer) { - var _this = this; - this.renderers.push(renderer); - renderer.render({ - kind: 'validate', - render: this.results.map(function (result) { return ({ result: result, elements: _this.elements.get(result) }); }), - unrender: [] - }); - }; - /** - * Removes a renderer. - * @param renderer The renderer. - */ - ValidationController.prototype.removeRenderer = function (renderer) { - var _this = this; - this.renderers.splice(this.renderers.indexOf(renderer), 1); - renderer.render({ - kind: 'reset', - render: [], - unrender: this.results.map(function (result) { return ({ result: result, elements: _this.elements.get(result) }); }) - }); - }; - /** - * Registers a binding with the controller. - * @param binding The binding instance. - * @param target The DOM element. - * @param rules (optional) rules associated with the binding. Validator implementation specific. - */ - ValidationController.prototype.registerBinding = function (binding, target, rules) { - this.bindings.set(binding, { target: target, rules: rules, propertyInfo: null }); - }; - /** - * Unregisters a binding with the controller. - * @param binding The binding instance. - */ - ValidationController.prototype.unregisterBinding = function (binding) { - this.resetBinding(binding); - this.bindings.delete(binding); - }; - /** - * Interprets the instruction and returns a predicate that will identify - * relevant results in the list of rendered validation results. - */ - ValidationController.prototype.getInstructionPredicate = function (instruction) { - var _this = this; - if (instruction) { - var object_1 = instruction.object, propertyName_1 = instruction.propertyName, rules_1 = instruction.rules; - var predicate_1; - if (instruction.propertyName) { - predicate_1 = function (x) { return x.object === object_1 && x.propertyName === propertyName_1; }; - } - else { - predicate_1 = function (x) { return x.object === object_1; }; - } - if (rules_1) { - return function (x) { return predicate_1(x) && _this.validator.ruleExists(rules_1, x.rule); }; - } - return predicate_1; - } - else { - return function () { return true; }; - } - }; - /** - * Validates and renders results. - * @param instruction Optional. Instructions on what to validate. If undefined, all - * objects and bindings will be validated. - */ - ValidationController.prototype.validate = function (instruction) { - var _this = this; - // Get a function that will process the validation instruction. - var execute; - if (instruction) { - // tslint:disable-next-line:prefer-const - var object_2 = instruction.object, propertyName_2 = instruction.propertyName, rules_2 = instruction.rules; - // if rules were not specified, check the object map. - rules_2 = rules_2 || this.objects.get(object_2); - // property specified? - if (instruction.propertyName === undefined) { - // validate the specified object. - execute = function () { return _this.validator.validateObject(object_2, rules_2); }; - } - else { - // validate the specified property. - execute = function () { return _this.validator.validateProperty(object_2, propertyName_2, rules_2); }; - } - } - else { - // validate all objects and bindings. - execute = function () { - var promises = []; - for (var _i = 0, _a = Array.from(_this.objects); _i < _a.length; _i++) { - var _b = _a[_i], object = _b[0], rules = _b[1]; - promises.push(_this.validator.validateObject(object, rules)); - } - for (var _c = 0, _d = Array.from(_this.bindings); _c < _d.length; _c++) { - var _e = _d[_c], binding = _e[0], rules = _e[1].rules; - var propertyInfo = getPropertyInfo(binding.sourceExpression, binding.source); - if (!propertyInfo || _this.objects.has(propertyInfo.object)) { - continue; - } - promises.push(_this.validator.validateProperty(propertyInfo.object, propertyInfo.propertyName, rules)); - } - return Promise.all(promises).then(function (resultSets) { return resultSets.reduce(function (a, b) { return a.concat(b); }, []); }); - }; - } - // Wait for any existing validation to finish, execute the instruction, render the results. - this.validating = true; - var returnPromise = this.finishValidating - .then(execute) - .then(function (newResults) { - var predicate = _this.getInstructionPredicate(instruction); - var oldResults = _this.results.filter(predicate); - _this.processResultDelta('validate', oldResults, newResults); - if (returnPromise === _this.finishValidating) { - _this.validating = false; - } - var result = { - instruction: instruction, - valid: newResults.find(function (x) { return !x.valid; }) === undefined, - results: newResults - }; - _this.invokeCallbacks(instruction, result); - return result; - }) - .catch(function (exception) { - // recover, to enable subsequent calls to validate() - _this.validating = false; - _this.finishValidating = Promise.resolve(); - return Promise.reject(exception); - }); - this.finishValidating = returnPromise; - return returnPromise; - }; - /** - * Resets any rendered validation results (unrenders). - * @param instruction Optional. Instructions on what to reset. If unspecified all rendered results - * will be unrendered. - */ - ValidationController.prototype.reset = function (instruction) { - var predicate = this.getInstructionPredicate(instruction); - var oldResults = this.results.filter(predicate); - this.processResultDelta('reset', oldResults, []); - this.invokeCallbacks(instruction, null); - }; - /** - * Gets the elements associated with an object and propertyName (if any). - */ - ValidationController.prototype.getAssociatedElements = function (_a) { - var object = _a.object, propertyName = _a.propertyName; - var elements = []; - for (var _i = 0, _b = Array.from(this.bindings); _i < _b.length; _i++) { - var _c = _b[_i], binding = _c[0], target = _c[1].target; - var propertyInfo = getPropertyInfo(binding.sourceExpression, binding.source); - if (propertyInfo && propertyInfo.object === object && propertyInfo.propertyName === propertyName) { - elements.push(target); - } - } - return elements; - }; - ValidationController.prototype.processResultDelta = function (kind, oldResults, newResults) { - // prepare the instruction. - var instruction = { - kind: kind, - render: [], - unrender: [] - }; - // create a shallow copy of newResults so we can mutate it without causing side-effects. - newResults = newResults.slice(0); - var _loop_1 = function (oldResult) { - // get the elements associated with the old result. - var elements = this_1.elements.get(oldResult); - // remove the old result from the element map. - this_1.elements.delete(oldResult); - // create the unrender instruction. - instruction.unrender.push({ result: oldResult, elements: elements }); - // determine if there's a corresponding new result for the old result we are unrendering. - var newResultIndex = newResults.findIndex(function (x) { return x.rule === oldResult.rule && x.object === oldResult.object && x.propertyName === oldResult.propertyName; }); - if (newResultIndex === -1) { - // no corresponding new result... simple remove. - this_1.results.splice(this_1.results.indexOf(oldResult), 1); - if (!oldResult.valid) { - this_1.errors.splice(this_1.errors.indexOf(oldResult), 1); - } - } - else { - // there is a corresponding new result... - var newResult = newResults.splice(newResultIndex, 1)[0]; - // get the elements that are associated with the new result. - var elements_1 = this_1.getAssociatedElements(newResult); - this_1.elements.set(newResult, elements_1); - // create a render instruction for the new result. - instruction.render.push({ result: newResult, elements: elements_1 }); - // do an in-place replacement of the old result with the new result. - // this ensures any repeats bound to this.results will not thrash. - this_1.results.splice(this_1.results.indexOf(oldResult), 1, newResult); - if (!oldResult.valid && newResult.valid) { - this_1.errors.splice(this_1.errors.indexOf(oldResult), 1); - } - else if (!oldResult.valid && !newResult.valid) { - this_1.errors.splice(this_1.errors.indexOf(oldResult), 1, newResult); - } - else if (!newResult.valid) { - this_1.errors.push(newResult); - } - } - }; - var this_1 = this; - // create unrender instructions from the old results. - for (var _i = 0, oldResults_1 = oldResults; _i < oldResults_1.length; _i++) { - var oldResult = oldResults_1[_i]; - _loop_1(oldResult); - } - // create render instructions from the remaining new results. - for (var _a = 0, newResults_1 = newResults; _a < newResults_1.length; _a++) { - var result = newResults_1[_a]; - var elements = this.getAssociatedElements(result); - instruction.render.push({ result: result, elements: elements }); - this.elements.set(result, elements); - this.results.push(result); - if (!result.valid) { - this.errors.push(result); - } - } - // render. - for (var _b = 0, _c = this.renderers; _b < _c.length; _b++) { - var renderer = _c[_b]; - renderer.render(instruction); - } - }; - /** - * Validates the property associated with a binding. - */ - ValidationController.prototype.validateBinding = function (binding) { - if (!binding.isBound) { - return; - } - var propertyInfo = getPropertyInfo(binding.sourceExpression, binding.source); - var rules; - var registeredBinding = this.bindings.get(binding); - if (registeredBinding) { - rules = registeredBinding.rules; - registeredBinding.propertyInfo = propertyInfo; - } - if (!propertyInfo) { - return; - } - var object = propertyInfo.object, propertyName = propertyInfo.propertyName; - this.validate({ object: object, propertyName: propertyName, rules: rules }); - }; - /** - * Resets the results for a property associated with a binding. - */ - ValidationController.prototype.resetBinding = function (binding) { - var registeredBinding = this.bindings.get(binding); - var propertyInfo = getPropertyInfo(binding.sourceExpression, binding.source); - if (!propertyInfo && registeredBinding) { - propertyInfo = registeredBinding.propertyInfo; - } - if (registeredBinding) { - registeredBinding.propertyInfo = null; - } - if (!propertyInfo) { - return; - } - var object = propertyInfo.object, propertyName = propertyInfo.propertyName; - this.reset({ object: object, propertyName: propertyName }); - }; - /** - * Changes the controller's validateTrigger. - * @param newTrigger The new validateTrigger - */ - ValidationController.prototype.changeTrigger = function (newTrigger) { - this.validateTrigger = newTrigger; - var bindings = Array.from(this.bindings.keys()); - for (var _i = 0, bindings_1 = bindings; _i < bindings_1.length; _i++) { - var binding = bindings_1[_i]; - var source = binding.source; - binding.unbind(); - binding.bind(source); - } - }; - /** - * Revalidates the controller's current set of errors. - */ - ValidationController.prototype.revalidateErrors = function () { - for (var _i = 0, _a = this.errors; _i < _a.length; _i++) { - var _b = _a[_i], object = _b.object, propertyName = _b.propertyName, rule = _b.rule; - if (rule.__manuallyAdded__) { - continue; - } - var rules = [[rule]]; - this.validate({ object: object, propertyName: propertyName, rules: rules }); - } - }; - ValidationController.prototype.invokeCallbacks = function (instruction, result) { - if (this.eventCallbacks.length === 0) { - return; - } - var event = new ValidateEvent(result ? 'validate' : 'reset', this.errors, this.results, instruction || null, result); - for (var i = 0; i < this.eventCallbacks.length; i++) { - this.eventCallbacks[i](event); - } - }; - ValidationController.inject = [Validator, PropertyAccessorParser]; - return ValidationController; - }())); - - /** - * Binding behavior. Indicates the bound property should be validated. - */ - var ValidateBindingBehaviorBase = /** @class */ (function () { - function ValidateBindingBehaviorBase(taskQueue) { - this.taskQueue = taskQueue; - } - ValidateBindingBehaviorBase.prototype.bind = function (binding, source, rulesOrController, rules) { - var _this = this; - // identify the target element. - var target = getTargetDOMElement(binding, source); - // locate the controller. - var controller; - if (rulesOrController instanceof ValidationController) { - controller = rulesOrController; - } - else { - controller = source.container.get(Optional.of(ValidationController)); - rules = rulesOrController; - } - if (controller === null) { - throw new Error("A ValidationController has not been registered."); - } - controller.registerBinding(binding, target, rules); - binding.validationController = controller; - var trigger = this.getValidateTrigger(controller); - // tslint:disable-next-line:no-bitwise - if (trigger & validateTrigger.change) { - binding.vbbUpdateSource = binding.updateSource; - // tslint:disable-next-line:only-arrow-functions - // tslint:disable-next-line:space-before-function-paren - binding.updateSource = function (value) { - this.vbbUpdateSource(value); - this.validationController.validateBinding(this); - }; - } - // tslint:disable-next-line:no-bitwise - if (trigger & validateTrigger.blur) { - binding.validateBlurHandler = function () { - _this.taskQueue.queueMicroTask(function () { return controller.validateBinding(binding); }); - }; - binding.validateTarget = target; - target.addEventListener('blur', binding.validateBlurHandler); - } - if (trigger !== validateTrigger.manual) { - binding.standardUpdateTarget = binding.updateTarget; - // tslint:disable-next-line:only-arrow-functions - // tslint:disable-next-line:space-before-function-paren - binding.updateTarget = function (value) { - this.standardUpdateTarget(value); - this.validationController.resetBinding(this); - }; - } - }; - ValidateBindingBehaviorBase.prototype.unbind = function (binding) { - // reset the binding to it's original state. - if (binding.vbbUpdateSource) { - binding.updateSource = binding.vbbUpdateSource; - binding.vbbUpdateSource = null; - } - if (binding.standardUpdateTarget) { - binding.updateTarget = binding.standardUpdateTarget; - binding.standardUpdateTarget = null; - } - if (binding.validateBlurHandler) { - binding.validateTarget.removeEventListener('blur', binding.validateBlurHandler); - binding.validateBlurHandler = null; - binding.validateTarget = null; - } - binding.validationController.unregisterBinding(binding); - binding.validationController = null; - }; - return ValidateBindingBehaviorBase; - }()); - - /** - * Binding behavior. Indicates the bound property should be validated - * when the validate trigger specified by the associated controller's - * validateTrigger property occurs. - */ - var ValidateBindingBehavior = exports('ValidateBindingBehavior', /** @class */ (function (_super) { - __extends(ValidateBindingBehavior, _super); - function ValidateBindingBehavior() { - return _super !== null && _super.apply(this, arguments) || this; - } - ValidateBindingBehavior.prototype.getValidateTrigger = function (controller) { - return controller.validateTrigger; - }; - ValidateBindingBehavior.inject = [TaskQueue]; - ValidateBindingBehavior = __decorate([ - bindingBehavior('validate') - ], ValidateBindingBehavior); - return ValidateBindingBehavior; - }(ValidateBindingBehaviorBase))); - /** - * Binding behavior. Indicates the bound property will be validated - * manually, by calling controller.validate(). No automatic validation - * triggered by data-entry or blur will occur. - */ - var ValidateManuallyBindingBehavior = exports('ValidateManuallyBindingBehavior', /** @class */ (function (_super) { - __extends(ValidateManuallyBindingBehavior, _super); - function ValidateManuallyBindingBehavior() { - return _super !== null && _super.apply(this, arguments) || this; - } - ValidateManuallyBindingBehavior.prototype.getValidateTrigger = function () { - return validateTrigger.manual; - }; - ValidateManuallyBindingBehavior.inject = [TaskQueue]; - ValidateManuallyBindingBehavior = __decorate([ - bindingBehavior('validateManually') - ], ValidateManuallyBindingBehavior); - return ValidateManuallyBindingBehavior; - }(ValidateBindingBehaviorBase))); - /** - * Binding behavior. Indicates the bound property should be validated - * when the associated element blurs. - */ - var ValidateOnBlurBindingBehavior = exports('ValidateOnBlurBindingBehavior', /** @class */ (function (_super) { - __extends(ValidateOnBlurBindingBehavior, _super); - function ValidateOnBlurBindingBehavior() { - return _super !== null && _super.apply(this, arguments) || this; - } - ValidateOnBlurBindingBehavior.prototype.getValidateTrigger = function () { - return validateTrigger.blur; - }; - ValidateOnBlurBindingBehavior.inject = [TaskQueue]; - ValidateOnBlurBindingBehavior = __decorate([ - bindingBehavior('validateOnBlur') - ], ValidateOnBlurBindingBehavior); - return ValidateOnBlurBindingBehavior; - }(ValidateBindingBehaviorBase))); - /** - * Binding behavior. Indicates the bound property should be validated - * when the associated element is changed by the user, causing a change - * to the model. - */ - var ValidateOnChangeBindingBehavior = exports('ValidateOnChangeBindingBehavior', /** @class */ (function (_super) { - __extends(ValidateOnChangeBindingBehavior, _super); - function ValidateOnChangeBindingBehavior() { - return _super !== null && _super.apply(this, arguments) || this; - } - ValidateOnChangeBindingBehavior.prototype.getValidateTrigger = function () { - return validateTrigger.change; - }; - ValidateOnChangeBindingBehavior.inject = [TaskQueue]; - ValidateOnChangeBindingBehavior = __decorate([ - bindingBehavior('validateOnChange') - ], ValidateOnChangeBindingBehavior); - return ValidateOnChangeBindingBehavior; - }(ValidateBindingBehaviorBase))); - /** - * Binding behavior. Indicates the bound property should be validated - * when the associated element blurs or is changed by the user, causing - * a change to the model. - */ - var ValidateOnChangeOrBlurBindingBehavior = exports('ValidateOnChangeOrBlurBindingBehavior', /** @class */ (function (_super) { - __extends(ValidateOnChangeOrBlurBindingBehavior, _super); - function ValidateOnChangeOrBlurBindingBehavior() { - return _super !== null && _super.apply(this, arguments) || this; - } - ValidateOnChangeOrBlurBindingBehavior.prototype.getValidateTrigger = function () { - return validateTrigger.changeOrBlur; - }; - ValidateOnChangeOrBlurBindingBehavior.inject = [TaskQueue]; - ValidateOnChangeOrBlurBindingBehavior = __decorate([ - bindingBehavior('validateOnChangeOrBlur') - ], ValidateOnChangeOrBlurBindingBehavior); - return ValidateOnChangeOrBlurBindingBehavior; - }(ValidateBindingBehaviorBase))); - - /** - * Creates ValidationController instances. - */ - var ValidationControllerFactory = exports('ValidationControllerFactory', /** @class */ (function () { - function ValidationControllerFactory(container) { - this.container = container; - } - ValidationControllerFactory.get = function (container) { - return new ValidationControllerFactory(container); - }; - /** - * Creates a new controller instance. - */ - ValidationControllerFactory.prototype.create = function (validator) { - if (!validator) { - validator = this.container.get(Validator); - } - var propertyParser = this.container.get(PropertyAccessorParser); - return new ValidationController(validator, propertyParser); - }; - /** - * Creates a new controller and registers it in the current element's container so that it's - * available to the validate binding behavior and renderers. - */ - ValidationControllerFactory.prototype.createForCurrentScope = function (validator) { - var controller = this.create(validator); - this.container.registerInstance(ValidationController, controller); - return controller; - }; - return ValidationControllerFactory; - }())); - ValidationControllerFactory['protocol:aurelia:resolver'] = true; - - var ValidationErrorsCustomAttribute = exports('ValidationErrorsCustomAttribute', /** @class */ (function () { - function ValidationErrorsCustomAttribute(boundaryElement, controllerAccessor) { - this.boundaryElement = boundaryElement; - this.controllerAccessor = controllerAccessor; - this.controller = null; - this.errors = []; - this.errorsInternal = []; - } - ValidationErrorsCustomAttribute.inject = function () { - return [DOM.Element, Lazy.of(ValidationController)]; - }; - ValidationErrorsCustomAttribute.prototype.sort = function () { - this.errorsInternal.sort(function (a, b) { - if (a.targets[0] === b.targets[0]) { - return 0; - } - // tslint:disable-next-line:no-bitwise - return a.targets[0].compareDocumentPosition(b.targets[0]) & 2 ? 1 : -1; - }); - }; - ValidationErrorsCustomAttribute.prototype.interestingElements = function (elements) { - var _this = this; - return elements.filter(function (e) { return _this.boundaryElement.contains(e); }); - }; - ValidationErrorsCustomAttribute.prototype.render = function (instruction) { - var _loop_1 = function (result) { - var index = this_1.errorsInternal.findIndex(function (x) { return x.error === result; }); - if (index !== -1) { - this_1.errorsInternal.splice(index, 1); - } - }; - var this_1 = this; - for (var _i = 0, _a = instruction.unrender; _i < _a.length; _i++) { - var result = _a[_i].result; - _loop_1(result); - } - for (var _b = 0, _c = instruction.render; _b < _c.length; _b++) { - var _d = _c[_b], result = _d.result, elements = _d.elements; - if (result.valid) { - continue; - } - var targets = this.interestingElements(elements); - if (targets.length) { - this.errorsInternal.push({ error: result, targets: targets }); - } - } - this.sort(); - this.errors = this.errorsInternal; - }; - ValidationErrorsCustomAttribute.prototype.bind = function () { - if (!this.controller) { - this.controller = this.controllerAccessor(); - } - // this will call render() with the side-effect of updating this.errors - this.controller.addRenderer(this); - }; - ValidationErrorsCustomAttribute.prototype.unbind = function () { - if (this.controller) { - this.controller.removeRenderer(this); - } - }; - __decorate([ - bindable({ defaultBindingMode: bindingMode.oneWay }) - ], ValidationErrorsCustomAttribute.prototype, "controller", void 0); - __decorate([ - bindable({ primaryProperty: true, defaultBindingMode: bindingMode.twoWay }) - ], ValidationErrorsCustomAttribute.prototype, "errors", void 0); - ValidationErrorsCustomAttribute = __decorate([ - customAttribute('validation-errors') - ], ValidationErrorsCustomAttribute); - return ValidationErrorsCustomAttribute; - }())); - - var ValidationRendererCustomAttribute = exports('ValidationRendererCustomAttribute', /** @class */ (function () { - function ValidationRendererCustomAttribute() { - } - ValidationRendererCustomAttribute.prototype.created = function (view) { - this.container = view.container; - }; - ValidationRendererCustomAttribute.prototype.bind = function () { - this.controller = this.container.get(ValidationController); - this.renderer = this.container.get(this.value); - this.controller.addRenderer(this.renderer); - }; - ValidationRendererCustomAttribute.prototype.unbind = function () { - this.controller.removeRenderer(this.renderer); - this.controller = null; - this.renderer = null; - }; - ValidationRendererCustomAttribute = __decorate([ - customAttribute('validation-renderer') - ], ValidationRendererCustomAttribute); - return ValidationRendererCustomAttribute; - }())); - - /** - * Sets, unsets and retrieves rules on an object or constructor function. - */ - var Rules = exports('Rules', /** @class */ (function () { - function Rules() { - } - /** - * Applies the rules to a target. - */ - Rules.set = function (target, rules) { - if (target instanceof Function) { - target = target.prototype; - } - Object.defineProperty(target, Rules.key, { enumerable: false, configurable: false, writable: true, value: rules }); - }; - /** - * Removes rules from a target. - */ - Rules.unset = function (target) { - if (target instanceof Function) { - target = target.prototype; - } - target[Rules.key] = null; - }; - /** - * Retrieves the target's rules. - */ - Rules.get = function (target) { - return target[Rules.key] || null; - }; - /** - * The name of the property that stores the rules. - */ - Rules.key = '__rules__'; - return Rules; - }())); - - // tslint:disable:no-empty - var ExpressionVisitor = /** @class */ (function () { - function ExpressionVisitor() { - } - ExpressionVisitor.prototype.visitChain = function (chain) { - this.visitArgs(chain.expressions); - }; - ExpressionVisitor.prototype.visitBindingBehavior = function (behavior) { - behavior.expression.accept(this); - this.visitArgs(behavior.args); - }; - ExpressionVisitor.prototype.visitValueConverter = function (converter) { - converter.expression.accept(this); - this.visitArgs(converter.args); - }; - ExpressionVisitor.prototype.visitAssign = function (assign) { - assign.target.accept(this); - assign.value.accept(this); - }; - ExpressionVisitor.prototype.visitConditional = function (conditional) { - conditional.condition.accept(this); - conditional.yes.accept(this); - conditional.no.accept(this); - }; - ExpressionVisitor.prototype.visitAccessThis = function (access) { - access.ancestor = access.ancestor; - }; - ExpressionVisitor.prototype.visitAccessScope = function (access) { - access.name = access.name; - }; - ExpressionVisitor.prototype.visitAccessMember = function (access) { - access.object.accept(this); - }; - ExpressionVisitor.prototype.visitAccessKeyed = function (access) { - access.object.accept(this); - access.key.accept(this); - }; - ExpressionVisitor.prototype.visitCallScope = function (call) { - this.visitArgs(call.args); - }; - ExpressionVisitor.prototype.visitCallFunction = function (call) { - call.func.accept(this); - this.visitArgs(call.args); - }; - ExpressionVisitor.prototype.visitCallMember = function (call) { - call.object.accept(this); - this.visitArgs(call.args); - }; - ExpressionVisitor.prototype.visitPrefix = function (prefix) { - prefix.expression.accept(this); - }; - ExpressionVisitor.prototype.visitBinary = function (binary) { - binary.left.accept(this); - binary.right.accept(this); - }; - ExpressionVisitor.prototype.visitLiteralPrimitive = function (literal) { - literal.value = literal.value; - }; - ExpressionVisitor.prototype.visitLiteralArray = function (literal) { - this.visitArgs(literal.elements); - }; - ExpressionVisitor.prototype.visitLiteralObject = function (literal) { - this.visitArgs(literal.values); - }; - ExpressionVisitor.prototype.visitLiteralString = function (literal) { - literal.value = literal.value; - }; - ExpressionVisitor.prototype.visitArgs = function (args) { - for (var i = 0; i < args.length; i++) { - args[i].accept(this); - } - }; - return ExpressionVisitor; - }()); - - var ValidationMessageParser = exports('ValidationMessageParser', /** @class */ (function () { - function ValidationMessageParser(bindinqLanguage) { - this.bindinqLanguage = bindinqLanguage; - this.emptyStringExpression = new LiteralString(''); - this.nullExpression = new LiteralPrimitive(null); - this.undefinedExpression = new LiteralPrimitive(undefined); - this.cache = {}; - } - ValidationMessageParser.prototype.parse = function (message) { - if (this.cache[message] !== undefined) { - return this.cache[message]; - } - var parts = this.bindinqLanguage.parseInterpolation(null, message); - if (parts === null) { - return new LiteralString(message); - } - var expression = new LiteralString(parts[0]); - for (var i = 1; i < parts.length; i += 2) { - expression = new Binary('+', expression, new Binary('+', this.coalesce(parts[i]), new LiteralString(parts[i + 1]))); - } - MessageExpressionValidator.validate(expression, message); - this.cache[message] = expression; - return expression; - }; - ValidationMessageParser.prototype.coalesce = function (part) { - // part === null || part === undefined ? '' : part - return new Conditional(new Binary('||', new Binary('===', part, this.nullExpression), new Binary('===', part, this.undefinedExpression)), this.emptyStringExpression, new CallMember(part, 'toString', [])); - }; - ValidationMessageParser.inject = [BindingLanguage]; - return ValidationMessageParser; - }())); - var MessageExpressionValidator = exports('MessageExpressionValidator', /** @class */ (function (_super) { - __extends(MessageExpressionValidator, _super); - function MessageExpressionValidator(originalMessage) { - var _this = _super.call(this) || this; - _this.originalMessage = originalMessage; - return _this; - } - MessageExpressionValidator.validate = function (expression, originalMessage) { - var visitor = new MessageExpressionValidator(originalMessage); - expression.accept(visitor); - }; - MessageExpressionValidator.prototype.visitAccessScope = function (access) { - if (access.ancestor !== 0) { - throw new Error('$parent is not permitted in validation message expressions.'); - } - if (['displayName', 'propertyName', 'value', 'object', 'config', 'getDisplayName'].indexOf(access.name) !== -1) { - getLogger('aurelia-validation') - // tslint:disable-next-line:max-line-length - .warn("Did you mean to use \"$" + access.name + "\" instead of \"" + access.name + "\" in this validation message template: \"" + this.originalMessage + "\"?"); - } - }; - return MessageExpressionValidator; - }(ExpressionVisitor))); - - /** - * Dictionary of validation messages. [messageKey]: messageExpression - */ - var validationMessages = exports('validationMessages', { - /** - * The default validation message. Used with rules that have no standard message. - */ - default: "${$displayName} is invalid.", - required: "${$displayName} is required.", - matches: "${$displayName} is not correctly formatted.", - email: "${$displayName} is not a valid email.", - minLength: "${$displayName} must be at least ${$config.length} character${$config.length === 1 ? '' : 's'}.", - maxLength: "${$displayName} cannot be longer than ${$config.length} character${$config.length === 1 ? '' : 's'}.", - minItems: "${$displayName} must contain at least ${$config.count} item${$config.count === 1 ? '' : 's'}.", - maxItems: "${$displayName} cannot contain more than ${$config.count} item${$config.count === 1 ? '' : 's'}.", - equals: "${$displayName} must be ${$config.expectedValue}.", - }); - /** - * Retrieves validation messages and property display names. - */ - var ValidationMessageProvider = exports('ValidationMessageProvider', /** @class */ (function () { - function ValidationMessageProvider(parser) { - this.parser = parser; - } - /** - * Returns a message binding expression that corresponds to the key. - * @param key The message key. - */ - ValidationMessageProvider.prototype.getMessage = function (key) { - var message; - if (key in validationMessages) { - message = validationMessages[key]; - } - else { - message = validationMessages['default']; - } - return this.parser.parse(message); - }; - /** - * Formulates a property display name using the property name and the configured - * displayName (if provided). - * Override this with your own custom logic. - * @param propertyName The property name. - */ - ValidationMessageProvider.prototype.getDisplayName = function (propertyName, displayName) { - if (displayName !== null && displayName !== undefined) { - return (displayName instanceof Function) ? displayName() : displayName; - } - // split on upper-case letters. - var words = propertyName.toString().split(/(?=[A-Z])/).join(' '); - // capitalize first letter. - return words.charAt(0).toUpperCase() + words.slice(1); - }; - ValidationMessageProvider.inject = [ValidationMessageParser]; - return ValidationMessageProvider; - }())); - - /** - * Validates. - * Responsible for validating objects and properties. - */ - var StandardValidator = exports('StandardValidator', /** @class */ (function (_super) { - __extends(StandardValidator, _super); - function StandardValidator(messageProvider, resources) { - var _this = _super.call(this) || this; - _this.messageProvider = messageProvider; - _this.lookupFunctions = resources.lookupFunctions; - _this.getDisplayName = messageProvider.getDisplayName.bind(messageProvider); - return _this; - } - /** - * Validates the specified property. - * @param object The object to validate. - * @param propertyName The name of the property to validate. - * @param rules Optional. If unspecified, the rules will be looked up using the metadata - * for the object created by ValidationRules....on(class/object) - */ - StandardValidator.prototype.validateProperty = function (object, propertyName, rules) { - return this.validate(object, propertyName, rules || null); - }; - /** - * Validates all rules for specified object and it's properties. - * @param object The object to validate. - * @param rules Optional. If unspecified, the rules will be looked up using the metadata - * for the object created by ValidationRules....on(class/object) - */ - StandardValidator.prototype.validateObject = function (object, rules) { - return this.validate(object, null, rules || null); - }; - /** - * Determines whether a rule exists in a set of rules. - * @param rules The rules to search. - * @parem rule The rule to find. - */ - StandardValidator.prototype.ruleExists = function (rules, rule) { - var i = rules.length; - while (i--) { - if (rules[i].indexOf(rule) !== -1) { - return true; - } - } - return false; - }; - StandardValidator.prototype.getMessage = function (rule, object, value) { - var expression = rule.message || this.messageProvider.getMessage(rule.messageKey); - // tslint:disable-next-line:prefer-const - var _a = rule.property, propertyName = _a.name, displayName = _a.displayName; - if (propertyName !== null) { - displayName = this.messageProvider.getDisplayName(propertyName, displayName); - } - var overrideContext = { - $displayName: displayName, - $propertyName: propertyName, - $value: value, - $object: object, - $config: rule.config, - // returns the name of a given property, given just the property name (irrespective of the property's displayName) - // split on capital letters, first letter ensured to be capitalized - $getDisplayName: this.getDisplayName - }; - return expression.evaluate({ bindingContext: object, overrideContext: overrideContext }, this.lookupFunctions); - }; - StandardValidator.prototype.validateRuleSequence = function (object, propertyName, ruleSequence, sequence, results) { - var _this = this; - // are we validating all properties or a single property? - var validateAllProperties = propertyName === null || propertyName === undefined; - var rules = ruleSequence[sequence]; - var allValid = true; - // validate each rule. - var promises = []; - var _loop_1 = function (i) { - var rule = rules[i]; - // is the rule related to the property we're validating. - // tslint:disable-next-line:triple-equals | Use loose equality for property keys - if (!validateAllProperties && rule.property.name != propertyName) { - return "continue"; - } - // is this a conditional rule? is the condition met? - if (rule.when && !rule.when(object)) { - return "continue"; - } - // validate. - var value = rule.property.name === null ? object : object[rule.property.name]; - var promiseOrBoolean = rule.condition(value, object); - if (!(promiseOrBoolean instanceof Promise)) { - promiseOrBoolean = Promise.resolve(promiseOrBoolean); - } - promises.push(promiseOrBoolean.then(function (valid) { - var message = valid ? null : _this.getMessage(rule, object, value); - results.push(new ValidateResult(rule, object, rule.property.name, valid, message)); - allValid = allValid && valid; - return valid; - })); - }; - for (var i = 0; i < rules.length; i++) { - _loop_1(i); - } - return Promise.all(promises) - .then(function () { - sequence++; - if (allValid && sequence < ruleSequence.length) { - return _this.validateRuleSequence(object, propertyName, ruleSequence, sequence, results); - } - return results; - }); - }; - StandardValidator.prototype.validate = function (object, propertyName, rules) { - // rules specified? - if (!rules) { - // no. attempt to locate the rules. - rules = Rules.get(object); - } - // any rules? - if (!rules || rules.length === 0) { - return Promise.resolve([]); - } - return this.validateRuleSequence(object, propertyName, rules, 0, []); - }; - StandardValidator.inject = [ValidationMessageProvider, ViewResources]; - return StandardValidator; - }(Validator))); - - /** - * Part of the fluent rule API. Enables customizing property rules. - */ - var FluentRuleCustomizer = exports('FluentRuleCustomizer', /** @class */ (function () { - function FluentRuleCustomizer(property, condition, config, fluentEnsure, fluentRules, parsers) { - if (config === void 0) { config = {}; } - this.fluentEnsure = fluentEnsure; - this.fluentRules = fluentRules; - this.parsers = parsers; - this.rule = { - property: property, - condition: condition, - config: config, - when: null, - messageKey: 'default', - message: null, - sequence: fluentRules.sequence - }; - this.fluentEnsure._addRule(this.rule); - } - /** - * Validate subsequent rules after previously declared rules have - * been validated successfully. Use to postpone validation of costly - * rules until less expensive rules pass validation. - */ - FluentRuleCustomizer.prototype.then = function () { - this.fluentRules.sequence++; - return this; - }; - /** - * Specifies the key to use when looking up the rule's validation message. - */ - FluentRuleCustomizer.prototype.withMessageKey = function (key) { - this.rule.messageKey = key; - this.rule.message = null; - return this; - }; - /** - * Specifies rule's validation message. - */ - FluentRuleCustomizer.prototype.withMessage = function (message) { - this.rule.messageKey = 'custom'; - this.rule.message = this.parsers.message.parse(message); - return this; - }; - /** - * Specifies a condition that must be met before attempting to validate the rule. - * @param condition A function that accepts the object as a parameter and returns true - * or false whether the rule should be evaluated. - */ - FluentRuleCustomizer.prototype.when = function (condition) { - this.rule.when = condition; - return this; - }; - /** - * Tags the rule instance, enabling the rule to be found easily - * using ValidationRules.taggedRules(rules, tag) - */ - FluentRuleCustomizer.prototype.tag = function (tag) { - this.rule.tag = tag; - return this; - }; - ///// FluentEnsure APIs ///// - /** - * Target a property with validation rules. - * @param property The property to target. Can be the property name or a property accessor function. - */ - FluentRuleCustomizer.prototype.ensure = function (subject) { - return this.fluentEnsure.ensure(subject); - }; - /** - * Targets an object with validation rules. - */ - FluentRuleCustomizer.prototype.ensureObject = function () { - return this.fluentEnsure.ensureObject(); - }; - Object.defineProperty(FluentRuleCustomizer.prototype, "rules", { - /** - * Rules that have been defined using the fluent API. - */ - get: function () { - return this.fluentEnsure.rules; - }, - enumerable: true, - configurable: true - }); - /** - * Applies the rules to a class or object, making them discoverable by the StandardValidator. - * @param target A class or object. - */ - FluentRuleCustomizer.prototype.on = function (target) { - return this.fluentEnsure.on(target); - }; - ///////// FluentRules APIs ///////// - /** - * Applies an ad-hoc rule function to the ensured property or object. - * @param condition The function to validate the rule. - * Will be called with two arguments, the property value and the object. - * Should return a boolean or a Promise that resolves to a boolean. - */ - FluentRuleCustomizer.prototype.satisfies = function (condition, config) { - return this.fluentRules.satisfies(condition, config); - }; - /** - * Applies a rule by name. - * @param name The name of the custom or standard rule. - * @param args The rule's arguments. - */ - FluentRuleCustomizer.prototype.satisfiesRule = function (name) { - var args = []; - for (var _i = 1; _i < arguments.length; _i++) { - args[_i - 1] = arguments[_i]; - } - var _a; - return (_a = this.fluentRules).satisfiesRule.apply(_a, [name].concat(args)); - }; - /** - * Applies the "required" rule to the property. - * The value cannot be null, undefined or whitespace. - */ - FluentRuleCustomizer.prototype.required = function () { - return this.fluentRules.required(); - }; - /** - * Applies the "matches" rule to the property. - * Value must match the specified regular expression. - * null, undefined and empty-string values are considered valid. - */ - FluentRuleCustomizer.prototype.matches = function (regex) { - return this.fluentRules.matches(regex); - }; - /** - * Applies the "email" rule to the property. - * null, undefined and empty-string values are considered valid. - */ - FluentRuleCustomizer.prototype.email = function () { - return this.fluentRules.email(); - }; - /** - * Applies the "minLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - FluentRuleCustomizer.prototype.minLength = function (length) { - return this.fluentRules.minLength(length); - }; - /** - * Applies the "maxLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - FluentRuleCustomizer.prototype.maxLength = function (length) { - return this.fluentRules.maxLength(length); - }; - /** - * Applies the "minItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - FluentRuleCustomizer.prototype.minItems = function (count) { - return this.fluentRules.minItems(count); - }; - /** - * Applies the "maxItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - FluentRuleCustomizer.prototype.maxItems = function (count) { - return this.fluentRules.maxItems(count); - }; - /** - * Applies the "equals" validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - FluentRuleCustomizer.prototype.equals = function (expectedValue) { - return this.fluentRules.equals(expectedValue); - }; - return FluentRuleCustomizer; - }())); - /** - * Part of the fluent rule API. Enables applying rules to properties and objects. - */ - var FluentRules = exports('FluentRules', /** @class */ (function () { - function FluentRules(fluentEnsure, parsers, property) { - this.fluentEnsure = fluentEnsure; - this.parsers = parsers; - this.property = property; - /** - * Current rule sequence number. Used to postpone evaluation of rules until rules - * with lower sequence number have successfully validated. The "then" fluent API method - * manages this property, there's usually no need to set it directly. - */ - this.sequence = 0; - } - /** - * Sets the display name of the ensured property. - */ - FluentRules.prototype.displayName = function (name) { - this.property.displayName = name; - return this; - }; - /** - * Applies an ad-hoc rule function to the ensured property or object. - * @param condition The function to validate the rule. - * Will be called with two arguments, the property value and the object. - * Should return a boolean or a Promise that resolves to a boolean. - */ - FluentRules.prototype.satisfies = function (condition, config) { - return new FluentRuleCustomizer(this.property, condition, config, this.fluentEnsure, this, this.parsers); - }; - /** - * Applies a rule by name. - * @param name The name of the custom or standard rule. - * @param args The rule's arguments. - */ - FluentRules.prototype.satisfiesRule = function (name) { - var _this = this; - var args = []; - for (var _i = 1; _i < arguments.length; _i++) { - args[_i - 1] = arguments[_i]; - } - var rule = FluentRules.customRules[name]; - if (!rule) { - // standard rule? - rule = this[name]; - if (rule instanceof Function) { - return rule.call.apply(rule, [this].concat(args)); - } - throw new Error("Rule with name \"" + name + "\" does not exist."); - } - var config = rule.argsToConfig ? rule.argsToConfig.apply(rule, args) : undefined; - return this.satisfies(function (value, obj) { - var _a; - return (_a = rule.condition).call.apply(_a, [_this, value, obj].concat(args)); - }, config) - .withMessageKey(name); - }; - /** - * Applies the "required" rule to the property. - * The value cannot be null, undefined or whitespace. - */ - FluentRules.prototype.required = function () { - return this.satisfies(function (value) { - return value !== null - && value !== undefined - && !(isString(value) && !/\S/.test(value)); - }).withMessageKey('required'); - }; - /** - * Applies the "matches" rule to the property. - * Value must match the specified regular expression. - * null, undefined and empty-string values are considered valid. - */ - FluentRules.prototype.matches = function (regex) { - return this.satisfies(function (value) { return value === null || value === undefined || value.length === 0 || regex.test(value); }) - .withMessageKey('matches'); - }; - /** - * Applies the "email" rule to the property. - * null, undefined and empty-string values are considered valid. - */ - FluentRules.prototype.email = function () { - // regex from https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address - /* tslint:disable:max-line-length */ - return this.matches(/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/) - /* tslint:enable:max-line-length */ - .withMessageKey('email'); - }; - /** - * Applies the "minLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - FluentRules.prototype.minLength = function (length) { - return this.satisfies(function (value) { return value === null || value === undefined || value.length === 0 || value.length >= length; }, { length: length }) - .withMessageKey('minLength'); - }; - /** - * Applies the "maxLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - FluentRules.prototype.maxLength = function (length) { - return this.satisfies(function (value) { return value === null || value === undefined || value.length === 0 || value.length <= length; }, { length: length }) - .withMessageKey('maxLength'); - }; - /** - * Applies the "minItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - FluentRules.prototype.minItems = function (count) { - return this.satisfies(function (value) { return value === null || value === undefined || value.length >= count; }, { count: count }) - .withMessageKey('minItems'); - }; - /** - * Applies the "maxItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - FluentRules.prototype.maxItems = function (count) { - return this.satisfies(function (value) { return value === null || value === undefined || value.length <= count; }, { count: count }) - .withMessageKey('maxItems'); - }; - /** - * Applies the "equals" validation rule to the property. - * null and undefined values are considered valid. - */ - FluentRules.prototype.equals = function (expectedValue) { - return this.satisfies(function (value) { return value === null || value === undefined || value === '' || value === expectedValue; }, { expectedValue: expectedValue }) - .withMessageKey('equals'); - }; - FluentRules.customRules = {}; - return FluentRules; - }())); - /** - * Part of the fluent rule API. Enables targeting properties and objects with rules. - */ - var FluentEnsure = exports('FluentEnsure', /** @class */ (function () { - function FluentEnsure(parsers) { - this.parsers = parsers; - /** - * Rules that have been defined using the fluent API. - */ - this.rules = []; - } - /** - * Target a property with validation rules. - * @param property The property to target. Can be the property name or a property accessor - * function. - */ - FluentEnsure.prototype.ensure = function (property) { - this.assertInitialized(); - var name = this.parsers.property.parse(property); - var fluentRules = new FluentRules(this, this.parsers, { name: name, displayName: null }); - return this.mergeRules(fluentRules, name); - }; - /** - * Targets an object with validation rules. - */ - FluentEnsure.prototype.ensureObject = function () { - this.assertInitialized(); - var fluentRules = new FluentRules(this, this.parsers, { name: null, displayName: null }); - return this.mergeRules(fluentRules, null); - }; - /** - * Applies the rules to a class or object, making them discoverable by the StandardValidator. - * @param target A class or object. - */ - FluentEnsure.prototype.on = function (target) { - Rules.set(target, this.rules); - return this; - }; - /** - * Adds a rule definition to the sequenced ruleset. - * @internal - */ - FluentEnsure.prototype._addRule = function (rule) { - while (this.rules.length < rule.sequence + 1) { - this.rules.push([]); - } - this.rules[rule.sequence].push(rule); - }; - FluentEnsure.prototype.assertInitialized = function () { - if (this.parsers) { - return; - } - throw new Error("Did you forget to add \".plugin('aurelia-validation')\" to your main.js?"); - }; - FluentEnsure.prototype.mergeRules = function (fluentRules, propertyName) { - // tslint:disable-next-line:triple-equals | Use loose equality for property keys - var existingRules = this.rules.find(function (r) { return r.length > 0 && r[0].property.name == propertyName; }); - if (existingRules) { - var rule = existingRules[existingRules.length - 1]; - fluentRules.sequence = rule.sequence; - if (rule.property.displayName !== null) { - fluentRules = fluentRules.displayName(rule.property.displayName); - } - } - return fluentRules; - }; - return FluentEnsure; - }())); - /** - * Fluent rule definition API. - */ - var ValidationRules = exports('ValidationRules', /** @class */ (function () { - function ValidationRules() { - } - ValidationRules.initialize = function (messageParser, propertyParser) { - this.parsers = { - message: messageParser, - property: propertyParser - }; - }; - /** - * Target a property with validation rules. - * @param property The property to target. Can be the property name or a property accessor function. - */ - ValidationRules.ensure = function (property) { - return new FluentEnsure(ValidationRules.parsers).ensure(property); - }; - /** - * Targets an object with validation rules. - */ - ValidationRules.ensureObject = function () { - return new FluentEnsure(ValidationRules.parsers).ensureObject(); - }; - /** - * Defines a custom rule. - * @param name The name of the custom rule. Also serves as the message key. - * @param condition The rule function. - * @param message The message expression - * @param argsToConfig A function that maps the rule's arguments to a "config" - * object that can be used when evaluating the message expression. - */ - ValidationRules.customRule = function (name, condition, message, argsToConfig) { - validationMessages[name] = message; - FluentRules.customRules[name] = { condition: condition, argsToConfig: argsToConfig }; - }; - /** - * Returns rules with the matching tag. - * @param rules The rules to search. - * @param tag The tag to search for. - */ - ValidationRules.taggedRules = function (rules, tag) { - return rules.map(function (x) { return x.filter(function (r) { return r.tag === tag; }); }); - }; - /** - * Returns rules that have no tag. - * @param rules The rules to search. - */ - ValidationRules.untaggedRules = function (rules) { - return rules.map(function (x) { return x.filter(function (r) { return r.tag === undefined; }); }); - }; - /** - * Removes the rules from a class or object. - * @param target A class or object. - */ - ValidationRules.off = function (target) { - Rules.unset(target); - }; - return ValidationRules; - }())); - - // Exports - /** - * Aurelia Validation Configuration API - */ - var AureliaValidationConfiguration = exports('AureliaValidationConfiguration', /** @class */ (function () { - function AureliaValidationConfiguration() { - this.validatorType = StandardValidator; - } - /** - * Use a custom Validator implementation. - */ - AureliaValidationConfiguration.prototype.customValidator = function (type) { - this.validatorType = type; - }; - /** - * Applies the configuration. - */ - AureliaValidationConfiguration.prototype.apply = function (container) { - var validator = container.get(this.validatorType); - container.registerInstance(Validator, validator); - }; - return AureliaValidationConfiguration; - }())); - /** - * Configures the plugin. - */ - function configure( - // tslint:disable-next-line:ban-types - frameworkConfig, callback) { - // the fluent rule definition API needs the parser to translate messages - // to interpolation expressions. - var messageParser = frameworkConfig.container.get(ValidationMessageParser); - var propertyParser = frameworkConfig.container.get(PropertyAccessorParser); - ValidationRules.initialize(messageParser, propertyParser); - // configure... - var config = new AureliaValidationConfiguration(); - if (callback instanceof Function) { - callback(config); - } - config.apply(frameworkConfig.container); - // globalize the behaviors. - if (frameworkConfig.globalResources) { - frameworkConfig.globalResources(ValidateBindingBehavior, ValidateManuallyBindingBehavior, ValidateOnBlurBindingBehavior, ValidateOnChangeBindingBehavior, ValidateOnChangeOrBlurBindingBehavior, ValidationErrorsCustomAttribute, ValidationRendererCustomAttribute); - } - } - - } - }; -}); diff --git a/dist/umd/aurelia-validation.js b/dist/umd/aurelia-validation.js deleted file mode 100644 index 19a12650..00000000 --- a/dist/umd/aurelia-validation.js +++ /dev/null @@ -1,1683 +0,0 @@ -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('aurelia-pal'), require('aurelia-binding'), require('aurelia-dependency-injection'), require('aurelia-task-queue'), require('aurelia-templating'), require('aurelia-logging')) : - typeof define === 'function' && define.amd ? define(['exports', 'aurelia-pal', 'aurelia-binding', 'aurelia-dependency-injection', 'aurelia-task-queue', 'aurelia-templating', 'aurelia-logging'], factory) : - (factory((global.au = global.au || {}, global.au.validation = {}),global.au,global.au,global.au,global.au,global.au,global.au.LogManager)); -}(this, (function (exports,aureliaPal,aureliaBinding,aureliaDependencyInjection,aureliaTaskQueue,aureliaTemplating,LogManager) { 'use strict'; - - /** - * Gets the DOM element associated with the data-binding. Most of the time it's - * the binding.target but sometimes binding.target is an aurelia custom element, - * or custom attribute which is a javascript "class" instance, so we need to use - * the controller's container to retrieve the actual DOM element. - */ - function getTargetDOMElement(binding, view) { - const target = binding.target; - // DOM element - if (target instanceof Element) { - return target; - } - // custom element or custom attribute - // tslint:disable-next-line:prefer-const - for (let i = 0, ii = view.controllers.length; i < ii; i++) { - const controller = view.controllers[i]; - if (controller.viewModel === target) { - const element = controller.container.get(aureliaPal.DOM.Element); - if (element) { - return element; - } - throw new Error(`Unable to locate target element for "${binding.sourceExpression}".`); - } - } - throw new Error(`Unable to locate target element for "${binding.sourceExpression}".`); - } - - function getObject(expression, objectExpression, source) { - const value = objectExpression.evaluate(source, null); - if (value === null || value === undefined || value instanceof Object) { - return value; - } - // tslint:disable-next-line:max-line-length - throw new Error(`The '${objectExpression}' part of '${expression}' evaluates to ${value} instead of an object, null or undefined.`); - } - /** - * Retrieves the object and property name for the specified expression. - * @param expression The expression - * @param source The scope - */ - function getPropertyInfo(expression, source) { - const originalExpression = expression; - while (expression instanceof aureliaBinding.BindingBehavior || expression instanceof aureliaBinding.ValueConverter) { - expression = expression.expression; - } - let object; - let propertyName; - if (expression instanceof aureliaBinding.AccessScope) { - object = aureliaBinding.getContextFor(expression.name, source, expression.ancestor); - propertyName = expression.name; - } - else if (expression instanceof aureliaBinding.AccessMember) { - object = getObject(originalExpression, expression.object, source); - propertyName = expression.name; - } - else if (expression instanceof aureliaBinding.AccessKeyed) { - object = getObject(originalExpression, expression.object, source); - propertyName = expression.key.evaluate(source); - } - else { - throw new Error(`Expression '${originalExpression}' is not compatible with the validate binding-behavior.`); - } - if (object === null || object === undefined) { - return null; - } - return { object, propertyName }; - } - - function isString(value) { - return Object.prototype.toString.call(value) === '[object String]'; - } - function isNumber(value) { - return Object.prototype.toString.call(value) === '[object Number]'; - } - - class PropertyAccessorParser { - constructor(parser) { - this.parser = parser; - } - parse(property) { - if (isString(property) || isNumber(property)) { - return property; - } - const accessorText = getAccessorExpression(property.toString()); - const accessor = this.parser.parse(accessorText); - if (accessor instanceof aureliaBinding.AccessScope - || accessor instanceof aureliaBinding.AccessMember && accessor.object instanceof aureliaBinding.AccessScope) { - return accessor.name; - } - throw new Error(`Invalid property expression: "${accessor}"`); - } - } - PropertyAccessorParser.inject = [aureliaBinding.Parser]; - function getAccessorExpression(fn) { - /* tslint:disable:max-line-length */ - const classic = /^function\s*\([$_\w\d]+\)\s*\{(?:\s*"use strict";)?\s*(?:[$_\w\d.['"\]+;]+)?\s*return\s+[$_\w\d]+\.([$_\w\d]+)\s*;?\s*\}$/; - /* tslint:enable:max-line-length */ - const arrow = /^\(?[$_\w\d]+\)?\s*=>\s*[$_\w\d]+\.([$_\w\d]+)$/; - const match = classic.exec(fn) || arrow.exec(fn); - if (match === null) { - throw new Error(`Unable to parse accessor function:\n${fn}`); - } - return match[1]; - } - - /*! ***************************************************************************** - Copyright (c) Microsoft Corporation. All rights reserved. - Licensed under the Apache License, Version 2.0 (the "License"); you may not use - this file except in compliance with the License. You may obtain a copy of the - License at http://www.apache.org/licenses/LICENSE-2.0 - - THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED - WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, - MERCHANTABLITY OR NON-INFRINGEMENT. - - See the Apache Version 2.0 License for specific language governing permissions - and limitations under the License. - ***************************************************************************** */ - - function __decorate(decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; - } - - /** - * Validation triggers. - */ - (function (validateTrigger) { - /** - * Manual validation. Use the controller's `validate()` and `reset()` methods - * to validate all bindings. - */ - validateTrigger[validateTrigger["manual"] = 0] = "manual"; - /** - * Validate the binding when the binding's target element fires a DOM "blur" event. - */ - validateTrigger[validateTrigger["blur"] = 1] = "blur"; - /** - * Validate the binding when it updates the model due to a change in the view. - */ - validateTrigger[validateTrigger["change"] = 2] = "change"; - /** - * Validate the binding when the binding's target element fires a DOM "blur" event and - * when it updates the model due to a change in the view. - */ - validateTrigger[validateTrigger["changeOrBlur"] = 3] = "changeOrBlur"; - })(exports.validateTrigger || (exports.validateTrigger = {})); - - /** - * Validates objects and properties. - */ - class Validator { - } - - /** - * The result of validating an individual validation rule. - */ - class ValidateResult { - /** - * @param rule The rule associated with the result. Validator implementation specific. - * @param object The object that was validated. - * @param propertyName The name of the property that was validated. - * @param error The error, if the result is a validation error. - */ - constructor(rule, object, propertyName, valid, message = null) { - this.rule = rule; - this.object = object; - this.propertyName = propertyName; - this.valid = valid; - this.message = message; - this.id = ValidateResult.nextId++; - } - toString() { - return this.valid ? 'Valid.' : this.message; - } - } - ValidateResult.nextId = 0; - - class ValidateEvent { - constructor( - /** - * The type of validate event. Either "validate" or "reset". - */ - type, - /** - * The controller's current array of errors. For an array containing both - * failed rules and passed rules, use the "results" property. - */ - errors, - /** - * The controller's current array of validate results. This - * includes both passed rules and failed rules. For an array of only failed rules, - * use the "errors" property. - */ - results, - /** - * The instruction passed to the "validate" or "reset" event. Will be null when - * the controller's validate/reset method was called with no instruction argument. - */ - instruction, - /** - * In events with type === "validate", this property will contain the result - * of validating the instruction (see "instruction" property). Use the controllerValidateResult - * to access the validate results specific to the call to "validate" - * (as opposed to using the "results" and "errors" properties to access the controller's entire - * set of results/errors). - */ - controllerValidateResult) { - this.type = type; - this.errors = errors; - this.results = results; - this.instruction = instruction; - this.controllerValidateResult = controllerValidateResult; - } - } - - /** - * Orchestrates validation. - * Manages a set of bindings, renderers and objects. - * Exposes the current list of validation results for binding purposes. - */ - class ValidationController { - constructor(validator, propertyParser) { - this.validator = validator; - this.propertyParser = propertyParser; - // Registered bindings (via the validate binding behavior) - this.bindings = new Map(); - // Renderers that have been added to the controller instance. - this.renderers = []; - /** - * Validation results that have been rendered by the controller. - */ - this.results = []; - /** - * Validation errors that have been rendered by the controller. - */ - this.errors = []; - /** - * Whether the controller is currently validating. - */ - this.validating = false; - // Elements related to validation results that have been rendered. - this.elements = new Map(); - // Objects that have been added to the controller instance (entity-style validation). - this.objects = new Map(); - /** - * The trigger that will invoke automatic validation of a property used in a binding. - */ - this.validateTrigger = exports.validateTrigger.blur; - // Promise that resolves when validation has completed. - this.finishValidating = Promise.resolve(); - this.eventCallbacks = []; - } - /** - * Subscribe to controller validate and reset events. These events occur when the - * controller's "validate"" and "reset" methods are called. - * @param callback The callback to be invoked when the controller validates or resets. - */ - subscribe(callback) { - this.eventCallbacks.push(callback); - return { - dispose: () => { - const index = this.eventCallbacks.indexOf(callback); - if (index === -1) { - return; - } - this.eventCallbacks.splice(index, 1); - } - }; - } - /** - * Adds an object to the set of objects that should be validated when validate is called. - * @param object The object. - * @param rules Optional. The rules. If rules aren't supplied the Validator implementation will lookup the rules. - */ - addObject(object, rules) { - this.objects.set(object, rules); - } - /** - * Removes an object from the set of objects that should be validated when validate is called. - * @param object The object. - */ - removeObject(object) { - this.objects.delete(object); - this.processResultDelta('reset', this.results.filter(result => result.object === object), []); - } - /** - * Adds and renders an error. - */ - addError(message, object, propertyName = null) { - let resolvedPropertyName; - if (propertyName === null) { - resolvedPropertyName = propertyName; - } - else { - resolvedPropertyName = this.propertyParser.parse(propertyName); - } - const result = new ValidateResult({ __manuallyAdded__: true }, object, resolvedPropertyName, false, message); - this.processResultDelta('validate', [], [result]); - return result; - } - /** - * Removes and unrenders an error. - */ - removeError(result) { - if (this.results.indexOf(result) !== -1) { - this.processResultDelta('reset', [result], []); - } - } - /** - * Adds a renderer. - * @param renderer The renderer. - */ - addRenderer(renderer) { - this.renderers.push(renderer); - renderer.render({ - kind: 'validate', - render: this.results.map(result => ({ result, elements: this.elements.get(result) })), - unrender: [] - }); - } - /** - * Removes a renderer. - * @param renderer The renderer. - */ - removeRenderer(renderer) { - this.renderers.splice(this.renderers.indexOf(renderer), 1); - renderer.render({ - kind: 'reset', - render: [], - unrender: this.results.map(result => ({ result, elements: this.elements.get(result) })) - }); - } - /** - * Registers a binding with the controller. - * @param binding The binding instance. - * @param target The DOM element. - * @param rules (optional) rules associated with the binding. Validator implementation specific. - */ - registerBinding(binding, target, rules) { - this.bindings.set(binding, { target, rules, propertyInfo: null }); - } - /** - * Unregisters a binding with the controller. - * @param binding The binding instance. - */ - unregisterBinding(binding) { - this.resetBinding(binding); - this.bindings.delete(binding); - } - /** - * Interprets the instruction and returns a predicate that will identify - * relevant results in the list of rendered validation results. - */ - getInstructionPredicate(instruction) { - if (instruction) { - const { object, propertyName, rules } = instruction; - let predicate; - if (instruction.propertyName) { - predicate = x => x.object === object && x.propertyName === propertyName; - } - else { - predicate = x => x.object === object; - } - if (rules) { - return x => predicate(x) && this.validator.ruleExists(rules, x.rule); - } - return predicate; - } - else { - return () => true; - } - } - /** - * Validates and renders results. - * @param instruction Optional. Instructions on what to validate. If undefined, all - * objects and bindings will be validated. - */ - validate(instruction) { - // Get a function that will process the validation instruction. - let execute; - if (instruction) { - // tslint:disable-next-line:prefer-const - let { object, propertyName, rules } = instruction; - // if rules were not specified, check the object map. - rules = rules || this.objects.get(object); - // property specified? - if (instruction.propertyName === undefined) { - // validate the specified object. - execute = () => this.validator.validateObject(object, rules); - } - else { - // validate the specified property. - execute = () => this.validator.validateProperty(object, propertyName, rules); - } - } - else { - // validate all objects and bindings. - execute = () => { - const promises = []; - for (const [object, rules] of Array.from(this.objects)) { - promises.push(this.validator.validateObject(object, rules)); - } - for (const [binding, { rules }] of Array.from(this.bindings)) { - const propertyInfo = getPropertyInfo(binding.sourceExpression, binding.source); - if (!propertyInfo || this.objects.has(propertyInfo.object)) { - continue; - } - promises.push(this.validator.validateProperty(propertyInfo.object, propertyInfo.propertyName, rules)); - } - return Promise.all(promises).then(resultSets => resultSets.reduce((a, b) => a.concat(b), [])); - }; - } - // Wait for any existing validation to finish, execute the instruction, render the results. - this.validating = true; - const returnPromise = this.finishValidating - .then(execute) - .then((newResults) => { - const predicate = this.getInstructionPredicate(instruction); - const oldResults = this.results.filter(predicate); - this.processResultDelta('validate', oldResults, newResults); - if (returnPromise === this.finishValidating) { - this.validating = false; - } - const result = { - instruction, - valid: newResults.find(x => !x.valid) === undefined, - results: newResults - }; - this.invokeCallbacks(instruction, result); - return result; - }) - .catch(exception => { - // recover, to enable subsequent calls to validate() - this.validating = false; - this.finishValidating = Promise.resolve(); - return Promise.reject(exception); - }); - this.finishValidating = returnPromise; - return returnPromise; - } - /** - * Resets any rendered validation results (unrenders). - * @param instruction Optional. Instructions on what to reset. If unspecified all rendered results - * will be unrendered. - */ - reset(instruction) { - const predicate = this.getInstructionPredicate(instruction); - const oldResults = this.results.filter(predicate); - this.processResultDelta('reset', oldResults, []); - this.invokeCallbacks(instruction, null); - } - /** - * Gets the elements associated with an object and propertyName (if any). - */ - getAssociatedElements({ object, propertyName }) { - const elements = []; - for (const [binding, { target }] of Array.from(this.bindings)) { - const propertyInfo = getPropertyInfo(binding.sourceExpression, binding.source); - if (propertyInfo && propertyInfo.object === object && propertyInfo.propertyName === propertyName) { - elements.push(target); - } - } - return elements; - } - processResultDelta(kind, oldResults, newResults) { - // prepare the instruction. - const instruction = { - kind, - render: [], - unrender: [] - }; - // create a shallow copy of newResults so we can mutate it without causing side-effects. - newResults = newResults.slice(0); - // create unrender instructions from the old results. - for (const oldResult of oldResults) { - // get the elements associated with the old result. - const elements = this.elements.get(oldResult); - // remove the old result from the element map. - this.elements.delete(oldResult); - // create the unrender instruction. - instruction.unrender.push({ result: oldResult, elements }); - // determine if there's a corresponding new result for the old result we are unrendering. - const newResultIndex = newResults.findIndex(x => x.rule === oldResult.rule && x.object === oldResult.object && x.propertyName === oldResult.propertyName); - if (newResultIndex === -1) { - // no corresponding new result... simple remove. - this.results.splice(this.results.indexOf(oldResult), 1); - if (!oldResult.valid) { - this.errors.splice(this.errors.indexOf(oldResult), 1); - } - } - else { - // there is a corresponding new result... - const newResult = newResults.splice(newResultIndex, 1)[0]; - // get the elements that are associated with the new result. - const elements = this.getAssociatedElements(newResult); - this.elements.set(newResult, elements); - // create a render instruction for the new result. - instruction.render.push({ result: newResult, elements }); - // do an in-place replacement of the old result with the new result. - // this ensures any repeats bound to this.results will not thrash. - this.results.splice(this.results.indexOf(oldResult), 1, newResult); - if (!oldResult.valid && newResult.valid) { - this.errors.splice(this.errors.indexOf(oldResult), 1); - } - else if (!oldResult.valid && !newResult.valid) { - this.errors.splice(this.errors.indexOf(oldResult), 1, newResult); - } - else if (!newResult.valid) { - this.errors.push(newResult); - } - } - } - // create render instructions from the remaining new results. - for (const result of newResults) { - const elements = this.getAssociatedElements(result); - instruction.render.push({ result, elements }); - this.elements.set(result, elements); - this.results.push(result); - if (!result.valid) { - this.errors.push(result); - } - } - // render. - for (const renderer of this.renderers) { - renderer.render(instruction); - } - } - /** - * Validates the property associated with a binding. - */ - validateBinding(binding) { - if (!binding.isBound) { - return; - } - const propertyInfo = getPropertyInfo(binding.sourceExpression, binding.source); - let rules; - const registeredBinding = this.bindings.get(binding); - if (registeredBinding) { - rules = registeredBinding.rules; - registeredBinding.propertyInfo = propertyInfo; - } - if (!propertyInfo) { - return; - } - const { object, propertyName } = propertyInfo; - this.validate({ object, propertyName, rules }); - } - /** - * Resets the results for a property associated with a binding. - */ - resetBinding(binding) { - const registeredBinding = this.bindings.get(binding); - let propertyInfo = getPropertyInfo(binding.sourceExpression, binding.source); - if (!propertyInfo && registeredBinding) { - propertyInfo = registeredBinding.propertyInfo; - } - if (registeredBinding) { - registeredBinding.propertyInfo = null; - } - if (!propertyInfo) { - return; - } - const { object, propertyName } = propertyInfo; - this.reset({ object, propertyName }); - } - /** - * Changes the controller's validateTrigger. - * @param newTrigger The new validateTrigger - */ - changeTrigger(newTrigger) { - this.validateTrigger = newTrigger; - const bindings = Array.from(this.bindings.keys()); - for (const binding of bindings) { - const source = binding.source; - binding.unbind(); - binding.bind(source); - } - } - /** - * Revalidates the controller's current set of errors. - */ - revalidateErrors() { - for (const { object, propertyName, rule } of this.errors) { - if (rule.__manuallyAdded__) { - continue; - } - const rules = [[rule]]; - this.validate({ object, propertyName, rules }); - } - } - invokeCallbacks(instruction, result) { - if (this.eventCallbacks.length === 0) { - return; - } - const event = new ValidateEvent(result ? 'validate' : 'reset', this.errors, this.results, instruction || null, result); - for (let i = 0; i < this.eventCallbacks.length; i++) { - this.eventCallbacks[i](event); - } - } - } - ValidationController.inject = [Validator, PropertyAccessorParser]; - - /** - * Binding behavior. Indicates the bound property should be validated. - */ - class ValidateBindingBehaviorBase { - constructor(taskQueue) { - this.taskQueue = taskQueue; - } - bind(binding, source, rulesOrController, rules) { - // identify the target element. - const target = getTargetDOMElement(binding, source); - // locate the controller. - let controller; - if (rulesOrController instanceof ValidationController) { - controller = rulesOrController; - } - else { - controller = source.container.get(aureliaDependencyInjection.Optional.of(ValidationController)); - rules = rulesOrController; - } - if (controller === null) { - throw new Error(`A ValidationController has not been registered.`); - } - controller.registerBinding(binding, target, rules); - binding.validationController = controller; - const trigger = this.getValidateTrigger(controller); - // tslint:disable-next-line:no-bitwise - if (trigger & exports.validateTrigger.change) { - binding.vbbUpdateSource = binding.updateSource; - // tslint:disable-next-line:only-arrow-functions - // tslint:disable-next-line:space-before-function-paren - binding.updateSource = function (value) { - this.vbbUpdateSource(value); - this.validationController.validateBinding(this); - }; - } - // tslint:disable-next-line:no-bitwise - if (trigger & exports.validateTrigger.blur) { - binding.validateBlurHandler = () => { - this.taskQueue.queueMicroTask(() => controller.validateBinding(binding)); - }; - binding.validateTarget = target; - target.addEventListener('blur', binding.validateBlurHandler); - } - if (trigger !== exports.validateTrigger.manual) { - binding.standardUpdateTarget = binding.updateTarget; - // tslint:disable-next-line:only-arrow-functions - // tslint:disable-next-line:space-before-function-paren - binding.updateTarget = function (value) { - this.standardUpdateTarget(value); - this.validationController.resetBinding(this); - }; - } - } - unbind(binding) { - // reset the binding to it's original state. - if (binding.vbbUpdateSource) { - binding.updateSource = binding.vbbUpdateSource; - binding.vbbUpdateSource = null; - } - if (binding.standardUpdateTarget) { - binding.updateTarget = binding.standardUpdateTarget; - binding.standardUpdateTarget = null; - } - if (binding.validateBlurHandler) { - binding.validateTarget.removeEventListener('blur', binding.validateBlurHandler); - binding.validateBlurHandler = null; - binding.validateTarget = null; - } - binding.validationController.unregisterBinding(binding); - binding.validationController = null; - } - } - - /** - * Binding behavior. Indicates the bound property should be validated - * when the validate trigger specified by the associated controller's - * validateTrigger property occurs. - */ - exports.ValidateBindingBehavior = class ValidateBindingBehavior extends ValidateBindingBehaviorBase { - getValidateTrigger(controller) { - return controller.validateTrigger; - } - }; - exports.ValidateBindingBehavior.inject = [aureliaTaskQueue.TaskQueue]; - exports.ValidateBindingBehavior = __decorate([ - aureliaBinding.bindingBehavior('validate') - ], exports.ValidateBindingBehavior); - /** - * Binding behavior. Indicates the bound property will be validated - * manually, by calling controller.validate(). No automatic validation - * triggered by data-entry or blur will occur. - */ - exports.ValidateManuallyBindingBehavior = class ValidateManuallyBindingBehavior extends ValidateBindingBehaviorBase { - getValidateTrigger() { - return exports.validateTrigger.manual; - } - }; - exports.ValidateManuallyBindingBehavior.inject = [aureliaTaskQueue.TaskQueue]; - exports.ValidateManuallyBindingBehavior = __decorate([ - aureliaBinding.bindingBehavior('validateManually') - ], exports.ValidateManuallyBindingBehavior); - /** - * Binding behavior. Indicates the bound property should be validated - * when the associated element blurs. - */ - exports.ValidateOnBlurBindingBehavior = class ValidateOnBlurBindingBehavior extends ValidateBindingBehaviorBase { - getValidateTrigger() { - return exports.validateTrigger.blur; - } - }; - exports.ValidateOnBlurBindingBehavior.inject = [aureliaTaskQueue.TaskQueue]; - exports.ValidateOnBlurBindingBehavior = __decorate([ - aureliaBinding.bindingBehavior('validateOnBlur') - ], exports.ValidateOnBlurBindingBehavior); - /** - * Binding behavior. Indicates the bound property should be validated - * when the associated element is changed by the user, causing a change - * to the model. - */ - exports.ValidateOnChangeBindingBehavior = class ValidateOnChangeBindingBehavior extends ValidateBindingBehaviorBase { - getValidateTrigger() { - return exports.validateTrigger.change; - } - }; - exports.ValidateOnChangeBindingBehavior.inject = [aureliaTaskQueue.TaskQueue]; - exports.ValidateOnChangeBindingBehavior = __decorate([ - aureliaBinding.bindingBehavior('validateOnChange') - ], exports.ValidateOnChangeBindingBehavior); - /** - * Binding behavior. Indicates the bound property should be validated - * when the associated element blurs or is changed by the user, causing - * a change to the model. - */ - exports.ValidateOnChangeOrBlurBindingBehavior = class ValidateOnChangeOrBlurBindingBehavior extends ValidateBindingBehaviorBase { - getValidateTrigger() { - return exports.validateTrigger.changeOrBlur; - } - }; - exports.ValidateOnChangeOrBlurBindingBehavior.inject = [aureliaTaskQueue.TaskQueue]; - exports.ValidateOnChangeOrBlurBindingBehavior = __decorate([ - aureliaBinding.bindingBehavior('validateOnChangeOrBlur') - ], exports.ValidateOnChangeOrBlurBindingBehavior); - - /** - * Creates ValidationController instances. - */ - class ValidationControllerFactory { - constructor(container) { - this.container = container; - } - static get(container) { - return new ValidationControllerFactory(container); - } - /** - * Creates a new controller instance. - */ - create(validator) { - if (!validator) { - validator = this.container.get(Validator); - } - const propertyParser = this.container.get(PropertyAccessorParser); - return new ValidationController(validator, propertyParser); - } - /** - * Creates a new controller and registers it in the current element's container so that it's - * available to the validate binding behavior and renderers. - */ - createForCurrentScope(validator) { - const controller = this.create(validator); - this.container.registerInstance(ValidationController, controller); - return controller; - } - } - ValidationControllerFactory['protocol:aurelia:resolver'] = true; - - exports.ValidationErrorsCustomAttribute = class ValidationErrorsCustomAttribute { - constructor(boundaryElement, controllerAccessor) { - this.boundaryElement = boundaryElement; - this.controllerAccessor = controllerAccessor; - this.controller = null; - this.errors = []; - this.errorsInternal = []; - } - static inject() { - return [aureliaPal.DOM.Element, aureliaDependencyInjection.Lazy.of(ValidationController)]; - } - sort() { - this.errorsInternal.sort((a, b) => { - if (a.targets[0] === b.targets[0]) { - return 0; - } - // tslint:disable-next-line:no-bitwise - return a.targets[0].compareDocumentPosition(b.targets[0]) & 2 ? 1 : -1; - }); - } - interestingElements(elements) { - return elements.filter(e => this.boundaryElement.contains(e)); - } - render(instruction) { - for (const { result } of instruction.unrender) { - const index = this.errorsInternal.findIndex(x => x.error === result); - if (index !== -1) { - this.errorsInternal.splice(index, 1); - } - } - for (const { result, elements } of instruction.render) { - if (result.valid) { - continue; - } - const targets = this.interestingElements(elements); - if (targets.length) { - this.errorsInternal.push({ error: result, targets }); - } - } - this.sort(); - this.errors = this.errorsInternal; - } - bind() { - if (!this.controller) { - this.controller = this.controllerAccessor(); - } - // this will call render() with the side-effect of updating this.errors - this.controller.addRenderer(this); - } - unbind() { - if (this.controller) { - this.controller.removeRenderer(this); - } - } - }; - __decorate([ - aureliaTemplating.bindable({ defaultBindingMode: aureliaBinding.bindingMode.oneWay }) - ], exports.ValidationErrorsCustomAttribute.prototype, "controller", void 0); - __decorate([ - aureliaTemplating.bindable({ primaryProperty: true, defaultBindingMode: aureliaBinding.bindingMode.twoWay }) - ], exports.ValidationErrorsCustomAttribute.prototype, "errors", void 0); - exports.ValidationErrorsCustomAttribute = __decorate([ - aureliaTemplating.customAttribute('validation-errors') - ], exports.ValidationErrorsCustomAttribute); - - exports.ValidationRendererCustomAttribute = class ValidationRendererCustomAttribute { - created(view) { - this.container = view.container; - } - bind() { - this.controller = this.container.get(ValidationController); - this.renderer = this.container.get(this.value); - this.controller.addRenderer(this.renderer); - } - unbind() { - this.controller.removeRenderer(this.renderer); - this.controller = null; - this.renderer = null; - } - }; - exports.ValidationRendererCustomAttribute = __decorate([ - aureliaTemplating.customAttribute('validation-renderer') - ], exports.ValidationRendererCustomAttribute); - - /** - * Sets, unsets and retrieves rules on an object or constructor function. - */ - class Rules { - /** - * Applies the rules to a target. - */ - static set(target, rules) { - if (target instanceof Function) { - target = target.prototype; - } - Object.defineProperty(target, Rules.key, { enumerable: false, configurable: false, writable: true, value: rules }); - } - /** - * Removes rules from a target. - */ - static unset(target) { - if (target instanceof Function) { - target = target.prototype; - } - target[Rules.key] = null; - } - /** - * Retrieves the target's rules. - */ - static get(target) { - return target[Rules.key] || null; - } - } - /** - * The name of the property that stores the rules. - */ - Rules.key = '__rules__'; - - // tslint:disable:no-empty - class ExpressionVisitor { - visitChain(chain) { - this.visitArgs(chain.expressions); - } - visitBindingBehavior(behavior) { - behavior.expression.accept(this); - this.visitArgs(behavior.args); - } - visitValueConverter(converter) { - converter.expression.accept(this); - this.visitArgs(converter.args); - } - visitAssign(assign) { - assign.target.accept(this); - assign.value.accept(this); - } - visitConditional(conditional) { - conditional.condition.accept(this); - conditional.yes.accept(this); - conditional.no.accept(this); - } - visitAccessThis(access) { - access.ancestor = access.ancestor; - } - visitAccessScope(access) { - access.name = access.name; - } - visitAccessMember(access) { - access.object.accept(this); - } - visitAccessKeyed(access) { - access.object.accept(this); - access.key.accept(this); - } - visitCallScope(call) { - this.visitArgs(call.args); - } - visitCallFunction(call) { - call.func.accept(this); - this.visitArgs(call.args); - } - visitCallMember(call) { - call.object.accept(this); - this.visitArgs(call.args); - } - visitPrefix(prefix) { - prefix.expression.accept(this); - } - visitBinary(binary) { - binary.left.accept(this); - binary.right.accept(this); - } - visitLiteralPrimitive(literal) { - literal.value = literal.value; - } - visitLiteralArray(literal) { - this.visitArgs(literal.elements); - } - visitLiteralObject(literal) { - this.visitArgs(literal.values); - } - visitLiteralString(literal) { - literal.value = literal.value; - } - visitArgs(args) { - for (let i = 0; i < args.length; i++) { - args[i].accept(this); - } - } - } - - class ValidationMessageParser { - constructor(bindinqLanguage) { - this.bindinqLanguage = bindinqLanguage; - this.emptyStringExpression = new aureliaBinding.LiteralString(''); - this.nullExpression = new aureliaBinding.LiteralPrimitive(null); - this.undefinedExpression = new aureliaBinding.LiteralPrimitive(undefined); - this.cache = {}; - } - parse(message) { - if (this.cache[message] !== undefined) { - return this.cache[message]; - } - const parts = this.bindinqLanguage.parseInterpolation(null, message); - if (parts === null) { - return new aureliaBinding.LiteralString(message); - } - let expression = new aureliaBinding.LiteralString(parts[0]); - for (let i = 1; i < parts.length; i += 2) { - expression = new aureliaBinding.Binary('+', expression, new aureliaBinding.Binary('+', this.coalesce(parts[i]), new aureliaBinding.LiteralString(parts[i + 1]))); - } - MessageExpressionValidator.validate(expression, message); - this.cache[message] = expression; - return expression; - } - coalesce(part) { - // part === null || part === undefined ? '' : part - return new aureliaBinding.Conditional(new aureliaBinding.Binary('||', new aureliaBinding.Binary('===', part, this.nullExpression), new aureliaBinding.Binary('===', part, this.undefinedExpression)), this.emptyStringExpression, new aureliaBinding.CallMember(part, 'toString', [])); - } - } - ValidationMessageParser.inject = [aureliaTemplating.BindingLanguage]; - class MessageExpressionValidator extends ExpressionVisitor { - constructor(originalMessage) { - super(); - this.originalMessage = originalMessage; - } - static validate(expression, originalMessage) { - const visitor = new MessageExpressionValidator(originalMessage); - expression.accept(visitor); - } - visitAccessScope(access) { - if (access.ancestor !== 0) { - throw new Error('$parent is not permitted in validation message expressions.'); - } - if (['displayName', 'propertyName', 'value', 'object', 'config', 'getDisplayName'].indexOf(access.name) !== -1) { - LogManager.getLogger('aurelia-validation') - // tslint:disable-next-line:max-line-length - .warn(`Did you mean to use "$${access.name}" instead of "${access.name}" in this validation message template: "${this.originalMessage}"?`); - } - } - } - - /** - * Dictionary of validation messages. [messageKey]: messageExpression - */ - const validationMessages = { - /** - * The default validation message. Used with rules that have no standard message. - */ - default: `\${$displayName} is invalid.`, - required: `\${$displayName} is required.`, - matches: `\${$displayName} is not correctly formatted.`, - email: `\${$displayName} is not a valid email.`, - minLength: `\${$displayName} must be at least \${$config.length} character\${$config.length === 1 ? '' : 's'}.`, - maxLength: `\${$displayName} cannot be longer than \${$config.length} character\${$config.length === 1 ? '' : 's'}.`, - minItems: `\${$displayName} must contain at least \${$config.count} item\${$config.count === 1 ? '' : 's'}.`, - maxItems: `\${$displayName} cannot contain more than \${$config.count} item\${$config.count === 1 ? '' : 's'}.`, - equals: `\${$displayName} must be \${$config.expectedValue}.`, - }; - /** - * Retrieves validation messages and property display names. - */ - class ValidationMessageProvider { - constructor(parser) { - this.parser = parser; - } - /** - * Returns a message binding expression that corresponds to the key. - * @param key The message key. - */ - getMessage(key) { - let message; - if (key in validationMessages) { - message = validationMessages[key]; - } - else { - message = validationMessages['default']; - } - return this.parser.parse(message); - } - /** - * Formulates a property display name using the property name and the configured - * displayName (if provided). - * Override this with your own custom logic. - * @param propertyName The property name. - */ - getDisplayName(propertyName, displayName) { - if (displayName !== null && displayName !== undefined) { - return (displayName instanceof Function) ? displayName() : displayName; - } - // split on upper-case letters. - const words = propertyName.toString().split(/(?=[A-Z])/).join(' '); - // capitalize first letter. - return words.charAt(0).toUpperCase() + words.slice(1); - } - } - ValidationMessageProvider.inject = [ValidationMessageParser]; - - /** - * Validates. - * Responsible for validating objects and properties. - */ - class StandardValidator extends Validator { - constructor(messageProvider, resources) { - super(); - this.messageProvider = messageProvider; - this.lookupFunctions = resources.lookupFunctions; - this.getDisplayName = messageProvider.getDisplayName.bind(messageProvider); - } - /** - * Validates the specified property. - * @param object The object to validate. - * @param propertyName The name of the property to validate. - * @param rules Optional. If unspecified, the rules will be looked up using the metadata - * for the object created by ValidationRules....on(class/object) - */ - validateProperty(object, propertyName, rules) { - return this.validate(object, propertyName, rules || null); - } - /** - * Validates all rules for specified object and it's properties. - * @param object The object to validate. - * @param rules Optional. If unspecified, the rules will be looked up using the metadata - * for the object created by ValidationRules....on(class/object) - */ - validateObject(object, rules) { - return this.validate(object, null, rules || null); - } - /** - * Determines whether a rule exists in a set of rules. - * @param rules The rules to search. - * @parem rule The rule to find. - */ - ruleExists(rules, rule) { - let i = rules.length; - while (i--) { - if (rules[i].indexOf(rule) !== -1) { - return true; - } - } - return false; - } - getMessage(rule, object, value) { - const expression = rule.message || this.messageProvider.getMessage(rule.messageKey); - // tslint:disable-next-line:prefer-const - let { name: propertyName, displayName } = rule.property; - if (propertyName !== null) { - displayName = this.messageProvider.getDisplayName(propertyName, displayName); - } - const overrideContext = { - $displayName: displayName, - $propertyName: propertyName, - $value: value, - $object: object, - $config: rule.config, - // returns the name of a given property, given just the property name (irrespective of the property's displayName) - // split on capital letters, first letter ensured to be capitalized - $getDisplayName: this.getDisplayName - }; - return expression.evaluate({ bindingContext: object, overrideContext }, this.lookupFunctions); - } - validateRuleSequence(object, propertyName, ruleSequence, sequence, results) { - // are we validating all properties or a single property? - const validateAllProperties = propertyName === null || propertyName === undefined; - const rules = ruleSequence[sequence]; - let allValid = true; - // validate each rule. - const promises = []; - for (let i = 0; i < rules.length; i++) { - const rule = rules[i]; - // is the rule related to the property we're validating. - // tslint:disable-next-line:triple-equals | Use loose equality for property keys - if (!validateAllProperties && rule.property.name != propertyName) { - continue; - } - // is this a conditional rule? is the condition met? - if (rule.when && !rule.when(object)) { - continue; - } - // validate. - const value = rule.property.name === null ? object : object[rule.property.name]; - let promiseOrBoolean = rule.condition(value, object); - if (!(promiseOrBoolean instanceof Promise)) { - promiseOrBoolean = Promise.resolve(promiseOrBoolean); - } - promises.push(promiseOrBoolean.then(valid => { - const message = valid ? null : this.getMessage(rule, object, value); - results.push(new ValidateResult(rule, object, rule.property.name, valid, message)); - allValid = allValid && valid; - return valid; - })); - } - return Promise.all(promises) - .then(() => { - sequence++; - if (allValid && sequence < ruleSequence.length) { - return this.validateRuleSequence(object, propertyName, ruleSequence, sequence, results); - } - return results; - }); - } - validate(object, propertyName, rules) { - // rules specified? - if (!rules) { - // no. attempt to locate the rules. - rules = Rules.get(object); - } - // any rules? - if (!rules || rules.length === 0) { - return Promise.resolve([]); - } - return this.validateRuleSequence(object, propertyName, rules, 0, []); - } - } - StandardValidator.inject = [ValidationMessageProvider, aureliaTemplating.ViewResources]; - - /** - * Part of the fluent rule API. Enables customizing property rules. - */ - class FluentRuleCustomizer { - constructor(property, condition, config = {}, fluentEnsure, fluentRules, parsers) { - this.fluentEnsure = fluentEnsure; - this.fluentRules = fluentRules; - this.parsers = parsers; - this.rule = { - property, - condition, - config, - when: null, - messageKey: 'default', - message: null, - sequence: fluentRules.sequence - }; - this.fluentEnsure._addRule(this.rule); - } - /** - * Validate subsequent rules after previously declared rules have - * been validated successfully. Use to postpone validation of costly - * rules until less expensive rules pass validation. - */ - then() { - this.fluentRules.sequence++; - return this; - } - /** - * Specifies the key to use when looking up the rule's validation message. - */ - withMessageKey(key) { - this.rule.messageKey = key; - this.rule.message = null; - return this; - } - /** - * Specifies rule's validation message. - */ - withMessage(message) { - this.rule.messageKey = 'custom'; - this.rule.message = this.parsers.message.parse(message); - return this; - } - /** - * Specifies a condition that must be met before attempting to validate the rule. - * @param condition A function that accepts the object as a parameter and returns true - * or false whether the rule should be evaluated. - */ - when(condition) { - this.rule.when = condition; - return this; - } - /** - * Tags the rule instance, enabling the rule to be found easily - * using ValidationRules.taggedRules(rules, tag) - */ - tag(tag) { - this.rule.tag = tag; - return this; - } - ///// FluentEnsure APIs ///// - /** - * Target a property with validation rules. - * @param property The property to target. Can be the property name or a property accessor function. - */ - ensure(subject) { - return this.fluentEnsure.ensure(subject); - } - /** - * Targets an object with validation rules. - */ - ensureObject() { - return this.fluentEnsure.ensureObject(); - } - /** - * Rules that have been defined using the fluent API. - */ - get rules() { - return this.fluentEnsure.rules; - } - /** - * Applies the rules to a class or object, making them discoverable by the StandardValidator. - * @param target A class or object. - */ - on(target) { - return this.fluentEnsure.on(target); - } - ///////// FluentRules APIs ///////// - /** - * Applies an ad-hoc rule function to the ensured property or object. - * @param condition The function to validate the rule. - * Will be called with two arguments, the property value and the object. - * Should return a boolean or a Promise that resolves to a boolean. - */ - satisfies(condition, config) { - return this.fluentRules.satisfies(condition, config); - } - /** - * Applies a rule by name. - * @param name The name of the custom or standard rule. - * @param args The rule's arguments. - */ - satisfiesRule(name, ...args) { - return this.fluentRules.satisfiesRule(name, ...args); - } - /** - * Applies the "required" rule to the property. - * The value cannot be null, undefined or whitespace. - */ - required() { - return this.fluentRules.required(); - } - /** - * Applies the "matches" rule to the property. - * Value must match the specified regular expression. - * null, undefined and empty-string values are considered valid. - */ - matches(regex) { - return this.fluentRules.matches(regex); - } - /** - * Applies the "email" rule to the property. - * null, undefined and empty-string values are considered valid. - */ - email() { - return this.fluentRules.email(); - } - /** - * Applies the "minLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - minLength(length) { - return this.fluentRules.minLength(length); - } - /** - * Applies the "maxLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - maxLength(length) { - return this.fluentRules.maxLength(length); - } - /** - * Applies the "minItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - minItems(count) { - return this.fluentRules.minItems(count); - } - /** - * Applies the "maxItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - maxItems(count) { - return this.fluentRules.maxItems(count); - } - /** - * Applies the "equals" validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - equals(expectedValue) { - return this.fluentRules.equals(expectedValue); - } - } - /** - * Part of the fluent rule API. Enables applying rules to properties and objects. - */ - class FluentRules { - constructor(fluentEnsure, parsers, property) { - this.fluentEnsure = fluentEnsure; - this.parsers = parsers; - this.property = property; - /** - * Current rule sequence number. Used to postpone evaluation of rules until rules - * with lower sequence number have successfully validated. The "then" fluent API method - * manages this property, there's usually no need to set it directly. - */ - this.sequence = 0; - } - /** - * Sets the display name of the ensured property. - */ - displayName(name) { - this.property.displayName = name; - return this; - } - /** - * Applies an ad-hoc rule function to the ensured property or object. - * @param condition The function to validate the rule. - * Will be called with two arguments, the property value and the object. - * Should return a boolean or a Promise that resolves to a boolean. - */ - satisfies(condition, config) { - return new FluentRuleCustomizer(this.property, condition, config, this.fluentEnsure, this, this.parsers); - } - /** - * Applies a rule by name. - * @param name The name of the custom or standard rule. - * @param args The rule's arguments. - */ - satisfiesRule(name, ...args) { - let rule = FluentRules.customRules[name]; - if (!rule) { - // standard rule? - rule = this[name]; - if (rule instanceof Function) { - return rule.call(this, ...args); - } - throw new Error(`Rule with name "${name}" does not exist.`); - } - const config = rule.argsToConfig ? rule.argsToConfig(...args) : undefined; - return this.satisfies((value, obj) => rule.condition.call(this, value, obj, ...args), config) - .withMessageKey(name); - } - /** - * Applies the "required" rule to the property. - * The value cannot be null, undefined or whitespace. - */ - required() { - return this.satisfies(value => value !== null - && value !== undefined - && !(isString(value) && !/\S/.test(value))).withMessageKey('required'); - } - /** - * Applies the "matches" rule to the property. - * Value must match the specified regular expression. - * null, undefined and empty-string values are considered valid. - */ - matches(regex) { - return this.satisfies(value => value === null || value === undefined || value.length === 0 || regex.test(value)) - .withMessageKey('matches'); - } - /** - * Applies the "email" rule to the property. - * null, undefined and empty-string values are considered valid. - */ - email() { - // regex from https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address - /* tslint:disable:max-line-length */ - return this.matches(/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/) - /* tslint:enable:max-line-length */ - .withMessageKey('email'); - } - /** - * Applies the "minLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - minLength(length) { - return this.satisfies((value) => value === null || value === undefined || value.length === 0 || value.length >= length, { length }) - .withMessageKey('minLength'); - } - /** - * Applies the "maxLength" STRING validation rule to the property. - * null, undefined and empty-string values are considered valid. - */ - maxLength(length) { - return this.satisfies((value) => value === null || value === undefined || value.length === 0 || value.length <= length, { length }) - .withMessageKey('maxLength'); - } - /** - * Applies the "minItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - minItems(count) { - return this.satisfies((value) => value === null || value === undefined || value.length >= count, { count }) - .withMessageKey('minItems'); - } - /** - * Applies the "maxItems" ARRAY validation rule to the property. - * null and undefined values are considered valid. - */ - maxItems(count) { - return this.satisfies((value) => value === null || value === undefined || value.length <= count, { count }) - .withMessageKey('maxItems'); - } - /** - * Applies the "equals" validation rule to the property. - * null and undefined values are considered valid. - */ - equals(expectedValue) { - return this.satisfies(value => value === null || value === undefined || value === '' || value === expectedValue, { expectedValue }) - .withMessageKey('equals'); - } - } - FluentRules.customRules = {}; - /** - * Part of the fluent rule API. Enables targeting properties and objects with rules. - */ - class FluentEnsure { - constructor(parsers) { - this.parsers = parsers; - /** - * Rules that have been defined using the fluent API. - */ - this.rules = []; - } - /** - * Target a property with validation rules. - * @param property The property to target. Can be the property name or a property accessor - * function. - */ - ensure(property) { - this.assertInitialized(); - const name = this.parsers.property.parse(property); - const fluentRules = new FluentRules(this, this.parsers, { name, displayName: null }); - return this.mergeRules(fluentRules, name); - } - /** - * Targets an object with validation rules. - */ - ensureObject() { - this.assertInitialized(); - const fluentRules = new FluentRules(this, this.parsers, { name: null, displayName: null }); - return this.mergeRules(fluentRules, null); - } - /** - * Applies the rules to a class or object, making them discoverable by the StandardValidator. - * @param target A class or object. - */ - on(target) { - Rules.set(target, this.rules); - return this; - } - /** - * Adds a rule definition to the sequenced ruleset. - * @internal - */ - _addRule(rule) { - while (this.rules.length < rule.sequence + 1) { - this.rules.push([]); - } - this.rules[rule.sequence].push(rule); - } - assertInitialized() { - if (this.parsers) { - return; - } - throw new Error(`Did you forget to add ".plugin('aurelia-validation')" to your main.js?`); - } - mergeRules(fluentRules, propertyName) { - // tslint:disable-next-line:triple-equals | Use loose equality for property keys - const existingRules = this.rules.find(r => r.length > 0 && r[0].property.name == propertyName); - if (existingRules) { - const rule = existingRules[existingRules.length - 1]; - fluentRules.sequence = rule.sequence; - if (rule.property.displayName !== null) { - fluentRules = fluentRules.displayName(rule.property.displayName); - } - } - return fluentRules; - } - } - /** - * Fluent rule definition API. - */ - class ValidationRules { - static initialize(messageParser, propertyParser) { - this.parsers = { - message: messageParser, - property: propertyParser - }; - } - /** - * Target a property with validation rules. - * @param property The property to target. Can be the property name or a property accessor function. - */ - static ensure(property) { - return new FluentEnsure(ValidationRules.parsers).ensure(property); - } - /** - * Targets an object with validation rules. - */ - static ensureObject() { - return new FluentEnsure(ValidationRules.parsers).ensureObject(); - } - /** - * Defines a custom rule. - * @param name The name of the custom rule. Also serves as the message key. - * @param condition The rule function. - * @param message The message expression - * @param argsToConfig A function that maps the rule's arguments to a "config" - * object that can be used when evaluating the message expression. - */ - static customRule(name, condition, message, argsToConfig) { - validationMessages[name] = message; - FluentRules.customRules[name] = { condition, argsToConfig }; - } - /** - * Returns rules with the matching tag. - * @param rules The rules to search. - * @param tag The tag to search for. - */ - static taggedRules(rules, tag) { - return rules.map(x => x.filter(r => r.tag === tag)); - } - /** - * Returns rules that have no tag. - * @param rules The rules to search. - */ - static untaggedRules(rules) { - return rules.map(x => x.filter(r => r.tag === undefined)); - } - /** - * Removes the rules from a class or object. - * @param target A class or object. - */ - static off(target) { - Rules.unset(target); - } - } - - // Exports - /** - * Aurelia Validation Configuration API - */ - class AureliaValidationConfiguration { - constructor() { - this.validatorType = StandardValidator; - } - /** - * Use a custom Validator implementation. - */ - customValidator(type) { - this.validatorType = type; - } - /** - * Applies the configuration. - */ - apply(container) { - const validator = container.get(this.validatorType); - container.registerInstance(Validator, validator); - } - } - /** - * Configures the plugin. - */ - function configure( - // tslint:disable-next-line:ban-types - frameworkConfig, callback) { - // the fluent rule definition API needs the parser to translate messages - // to interpolation expressions. - const messageParser = frameworkConfig.container.get(ValidationMessageParser); - const propertyParser = frameworkConfig.container.get(PropertyAccessorParser); - ValidationRules.initialize(messageParser, propertyParser); - // configure... - const config = new AureliaValidationConfiguration(); - if (callback instanceof Function) { - callback(config); - } - config.apply(frameworkConfig.container); - // globalize the behaviors. - if (frameworkConfig.globalResources) { - frameworkConfig.globalResources(exports.ValidateBindingBehavior, exports.ValidateManuallyBindingBehavior, exports.ValidateOnBlurBindingBehavior, exports.ValidateOnChangeBindingBehavior, exports.ValidateOnChangeOrBlurBindingBehavior, exports.ValidationErrorsCustomAttribute, exports.ValidationRendererCustomAttribute); - } - } - - exports.AureliaValidationConfiguration = AureliaValidationConfiguration; - exports.configure = configure; - exports.getTargetDOMElement = getTargetDOMElement; - exports.getPropertyInfo = getPropertyInfo; - exports.PropertyAccessorParser = PropertyAccessorParser; - exports.getAccessorExpression = getAccessorExpression; - exports.ValidateEvent = ValidateEvent; - exports.ValidateResult = ValidateResult; - exports.ValidationController = ValidationController; - exports.ValidationControllerFactory = ValidationControllerFactory; - exports.Validator = Validator; - exports.Rules = Rules; - exports.StandardValidator = StandardValidator; - exports.validationMessages = validationMessages; - exports.ValidationMessageProvider = ValidationMessageProvider; - exports.ValidationMessageParser = ValidationMessageParser; - exports.MessageExpressionValidator = MessageExpressionValidator; - exports.FluentRuleCustomizer = FluentRuleCustomizer; - exports.FluentRules = FluentRules; - exports.FluentEnsure = FluentEnsure; - exports.ValidationRules = ValidationRules; - - Object.defineProperty(exports, '__esModule', { value: true }); - -}))); diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index bfcc75b4..b6c162d8 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -1,3 +1,26 @@ +# Change Log + +All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. + +# [1.4.0](https://github.com/aurelia/validation/compare/1.3.3...1.4.0) (2019-03-22) + + +### Bug Fixes + +* **build:** adjust umd build, add umd-es2015 ([895b149](https://github.com/aurelia/validation/commit/895b149)) +* **ci:** remove circle ci config ([5f9cf3c](https://github.com/aurelia/validation/commit/5f9cf3c)) +* **ci:** update ci config ([306b935](https://github.com/aurelia/validation/commit/306b935)) +* **ci:** update circleci ([cc54202](https://github.com/aurelia/validation/commit/cc54202)) +* **ci:** use v2.1 ([71074ef](https://github.com/aurelia/validation/commit/71074ef)) +* **package:** fix unpkg field ([2635ecd](https://github.com/aurelia/validation/commit/2635ecd)) + + +### Features + +* **ValidationRules:** add number validation rules ([f67cf59](https://github.com/aurelia/validation/commit/f67cf59)), closes [#440](https://github.com/aurelia/validation/issues/440) + + + ## [1.3.3](https://github.com/aurelia/validation/compare/1.3.2...1.3.3) (2019-01-19) diff --git a/package-lock.json b/package-lock.json index bb984562..c4ead664 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "aurelia-validation", - "version": "1.3.0", + "version": "1.4.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -187,6 +187,34 @@ "json-schema-traverse": "^0.3.0" } }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "dev": true, + "requires": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true + }, "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", @@ -239,6 +267,12 @@ "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", "dev": true }, + "array-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", + "dev": true + }, "array-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", @@ -675,6 +709,202 @@ } } }, + "babel-core": { + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "babel-generator": { + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", + "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", + "dev": true, + "requires": { + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.7", + "trim-right": "^1.0.1" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "babel-helper-hoist-variables": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", + "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helpers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", + "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-cjs-system-wrapper": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-cjs-system-wrapper/-/babel-plugin-transform-cjs-system-wrapper-0.3.0.tgz", + "integrity": "sha1-9XWfKb7NNW+qt69SyZzejnutCyE=", + "dev": true, + "requires": { + "babel-template": "^6.9.0" + } + }, + "babel-plugin-transform-es2015-modules-systemjs": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", + "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", + "dev": true, + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-global-system-wrapper": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-global-system-wrapper/-/babel-plugin-transform-global-system-wrapper-0.0.1.tgz", + "integrity": "sha1-r7RpzsDgRom5/n6LH9KA/JSm2PI=", + "dev": true, + "requires": { + "babel-template": "^6.9.0" + } + }, + "babel-plugin-transform-system-register": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-system-register/-/babel-plugin-transform-system-register-0.0.1.tgz", + "integrity": "sha1-nf9AOQwnY6xRjwsq18XqT2WlviU=", + "dev": true + }, + "babel-register": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", + "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", + "dev": true, + "requires": { + "babel-core": "^6.26.0", + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "home-or-tmp": "^2.0.0", + "lodash": "^4.17.4", + "mkdirp": "^0.5.1", + "source-map-support": "^0.4.15" + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dev": true, + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" + } + }, + "babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" + } + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, "backo2": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", @@ -784,6 +1014,15 @@ "integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE=", "dev": true }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "dev": true, + "requires": { + "inherits": "~2.0.0" + } + }, "bluebird": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.2.tgz", @@ -880,12 +1119,30 @@ "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", "dev": true }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true + }, "buffer-fill": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", "dev": true }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "buffer-peek-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-peek-stream/-/buffer-peek-stream-1.0.1.tgz", + "integrity": "sha1-U7R1cKE0d4fFutTKLKMCH52LPP0=", + "dev": true + }, "builtin-modules": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", @@ -944,6 +1201,16 @@ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", "dev": true }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "dev": true, + "requires": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + } + }, "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", @@ -1128,6 +1395,18 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, "concurrently": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-4.0.1.tgz", @@ -1423,6 +1702,74 @@ "trim-off-newlines": "^1.0.0" } }, + "conventional-recommended-bump": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/conventional-recommended-bump/-/conventional-recommended-bump-4.0.4.tgz", + "integrity": "sha512-9mY5Yoblq+ZMqJpBzgS+RpSq+SUfP2miOR3H/NR9drGf08WCrY9B6HAGJZEm6+ThsVP917VHAahSOjM6k1vhPg==", + "dev": true, + "requires": { + "concat-stream": "^1.6.0", + "conventional-changelog-preset-loader": "^2.0.2", + "conventional-commits-filter": "^2.0.1", + "conventional-commits-parser": "^3.0.1", + "git-raw-commits": "2.0.0", + "git-semver-tags": "^2.0.2", + "meow": "^4.0.0", + "q": "^1.5.1" + }, + "dependencies": { + "conventional-changelog-preset-loader": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.0.2.tgz", + "integrity": "sha512-pBY+qnUoJPXAXXqVGwQaVmcye05xi6z231QM98wHWamGAmu/ghkBprQAwmF5bdmyobdVxiLhPY3PrCfSeUNzRQ==", + "dev": true + }, + "conventional-commits-filter": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.1.tgz", + "integrity": "sha512-92OU8pz/977udhBjgPEbg3sbYzIxMDFTlQT97w7KdhR9igNqdJvy8smmedAAgn4tPiqseFloKkrVfbXCVd+E7A==", + "dev": true, + "requires": { + "is-subset": "^0.1.1", + "modify-values": "^1.0.0" + } + }, + "conventional-commits-parser": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.0.1.tgz", + "integrity": "sha512-P6U5UOvDeidUJ8ebHVDIoXzI7gMlQ1OF/id6oUvp8cnZvOXMt1n8nYl74Ey9YMn0uVQtxmCtjPQawpsssBWtGg==", + "dev": true, + "requires": { + "JSONStream": "^1.0.4", + "is-text-path": "^1.0.0", + "lodash": "^4.2.1", + "meow": "^4.0.0", + "split2": "^2.0.0", + "through2": "^2.0.0", + "trim-off-newlines": "^1.0.0" + } + }, + "git-semver-tags": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-2.0.2.tgz", + "integrity": "sha512-34lMF7Yo1xEmsK2EkbArdoU79umpvm0MfzaDkSNYSJqtM5QLAVTPWgpiXSVI5o/O9EvZPSrP4Zvnec/CqhSd5w==", + "dev": true, + "requires": { + "meow": "^4.0.0", + "semver": "^5.5.0" + } + } + } + }, + "convert-source-map": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, "cookie": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", @@ -1661,6 +2008,15 @@ "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", "dev": true }, + "d": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", + "dev": true, + "requires": { + "es5-ext": "^0.10.9" + } + }, "dargs": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/dargs/-/dargs-4.1.0.tgz", @@ -1679,6 +2035,12 @@ "assert-plus": "^1.0.0" } }, + "data-uri-to-buffer": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-0.0.4.tgz", + "integrity": "sha1-RuE6udqOMJdFyNAc5UchPr2y/j8=", + "dev": true + }, "date-fns": { "version": "1.29.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.29.0.tgz", @@ -1804,6 +2166,27 @@ "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", "dev": true }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "dev": true + }, + "detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", + "dev": true + }, "di": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", @@ -1846,6 +2229,16 @@ "is-obj": "^1.0.0" } }, + "dotgitignore": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/dotgitignore/-/dotgitignore-2.1.0.tgz", + "integrity": "sha512-sCm11ak2oY6DglEPpCB8TixLjWAxd3kJTs6UIcSasNYxXdFPV+YKlye92c8H4kKFqV5qYMIh7d+cYecEg0dIkA==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "minimatch": "^3.0.4" + } + }, "dts-bundle-generator": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/dts-bundle-generator/-/dts-bundle-generator-1.6.1.tgz", @@ -2099,6 +2492,12 @@ "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", "dev": true }, + "err-code": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz", + "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=", + "dev": true + }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -2108,6 +2507,48 @@ "is-arrayish": "^0.2.1" } }, + "es5-ext": { + "version": "0.10.47", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.47.tgz", + "integrity": "sha512-/1TItLfj+TTfWoeRcDn/0FbGV6SNo4R+On2GGVucPU/j3BWnXE2Co8h8CTo4Tu34gFJtnmwS9xiScKs4EjZhdw==", + "dev": true, + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.1", + "next-tick": "1" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "es6-template-strings": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es6-template-strings/-/es6-template-strings-2.0.1.tgz", + "integrity": "sha1-sWbGpiVi9Hi7d3X2ypYQOlmbSyw=", + "dev": true, + "requires": { + "es5-ext": "^0.10.12", + "esniff": "^1.1" + } + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -2133,6 +2574,16 @@ "source-map": "~0.6.1" } }, + "esniff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-1.1.0.tgz", + "integrity": "sha1-xmhJIp+RRk3t4uDUAgHtar9l8qw=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.12" + } + }, "esprima": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", @@ -2265,6 +2716,15 @@ } } }, + "expand-tilde": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-1.2.2.tgz", + "integrity": "sha1-C4HrqJflo9MdHD0QL48BRB5VlEk=", + "dev": true, + "requires": { + "os-homedir": "^1.0.1" + } + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -2381,6 +2841,24 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dev": true, + "requires": { + "pend": "~1.2.0" + } + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, "filename-regex": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", @@ -2442,6 +2920,59 @@ "locate-path": "^3.0.0" } }, + "findup-sync": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", + "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", + "dev": true, + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^3.1.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "fined": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.1.1.tgz", + "integrity": "sha512-jQp949ZmEbiYHk3gkbdtpJ0G1+kgtLQBNdP5edFP7Fh+WAYceLQz6yO1SBj72Xkg8GVyTB3bBzAYrHJVh5Xd5g==", + "dev": true, + "requires": { + "expand-tilde": "^2.0.2", + "is-plain-object": "^2.0.3", + "object.defaults": "^1.1.0", + "object.pick": "^1.2.0", + "parse-filepath": "^1.0.1" + }, + "dependencies": { + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + } + } + }, + "flagged-respawn": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", + "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", + "dev": true + }, "follow-redirects": { "version": "1.5.9", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.9.tgz", @@ -2549,7 +3080,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -2570,12 +3102,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2590,17 +3124,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -2717,7 +3254,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -2729,6 +3267,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -2743,6 +3282,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -2750,12 +3290,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -2774,6 +3316,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -2854,7 +3397,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -2866,6 +3410,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -2951,7 +3496,8 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -2987,6 +3533,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -3006,6 +3553,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -3049,15 +3597,29 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, + "fstream": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + } + }, "get-caller-file": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", @@ -3313,12 +3875,59 @@ } } }, + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + }, + "dependencies": { + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + } + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true + }, "graceful-fs": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", "dev": true }, + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", + "dev": true + }, "handlebars": { "version": "4.0.12", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.12.tgz", @@ -3431,6 +4040,25 @@ "integrity": "sha512-Sc28JNQNDzaH6PORtRLMvif9RSn1mYuOoX3omVjnb0+HbpPygU2ALBI0R/wsiqCb4/fcp07Gdo8g+fhtFrQl6A==", "dev": true }, + "home-or-tmp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", + "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", + "dev": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.1" + } + }, + "homedir-polyfill": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", + "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", + "dev": true, + "requires": { + "parse-passwd": "^1.0.0" + } + }, "hosted-git-info": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", @@ -3529,12 +4157,31 @@ "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", "dev": true }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + }, "invert-kv": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", "dev": true }, + "is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "dev": true, + "requires": { + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" + } + }, "is-accessor-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", @@ -3728,6 +4375,15 @@ "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", "dev": true }, + "is-relative": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "dev": true, + "requires": { + "is-unc-path": "^1.0.0" + } + }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", @@ -3755,6 +4411,15 @@ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true }, + "is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "dev": true, + "requires": { + "unc-path-regex": "^0.1.2" + } + }, "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", @@ -3868,6 +4533,12 @@ "xml-name-validator": "^2.0.1" } }, + "jsesc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", + "dev": true + }, "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -3892,6 +4563,12 @@ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", "dev": true }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, "jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", @@ -3907,6 +4584,237 @@ "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", "dev": true }, + "jspm": { + "version": "0.16.53", + "resolved": "https://registry.npmjs.org/jspm/-/jspm-0.16.53.tgz", + "integrity": "sha1-VvNR9JWUyJM+XgG2UUWsrr/PtZ4=", + "dev": true, + "requires": { + "chalk": "^1.1.1", + "core-js": "^1.2.6", + "glob": "^6.0.1", + "graceful-fs": "^4.1.2", + "jspm-github": "^0.13.17", + "jspm-npm": "^0.26.12", + "jspm-registry": "^0.4.0", + "liftoff": "^2.2.0", + "minimatch": "^3.0.0", + "mkdirp": "~0.5.1", + "ncp": "^2.0.0", + "proper-lockfile": "^1.1.2", + "request": "^2.67.0", + "rimraf": "^2.4.4", + "rsvp": "^3.1.0", + "semver": "^5.1.0", + "systemjs": "0.19.46", + "systemjs-builder": "0.15.36", + "traceur": "0.0.105", + "uglify-js": "^2.6.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dev": true, + "requires": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=", + "dev": true + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "dev": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "dev": true, + "requires": { + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + } + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "requires": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + } + } + }, + "jspm-github": { + "version": "0.13.20", + "resolved": "https://registry.npmjs.org/jspm-github/-/jspm-github-0.13.20.tgz", + "integrity": "sha512-q+rg4b7Q9HuH4efrpM6QKmCddFYkKg3javGYPK1rcNvxNFAN2PqhNLgc7TpYgR2k8k+iLZvFWeWmC9WxZrB3Dw==", + "dev": true, + "requires": { + "expand-tilde": "^1.2.0", + "graceful-fs": "^4.1.3", + "mkdirp": "^0.5.1", + "netrc": "^0.1.3", + "request": "^2.74.0", + "rimraf": "^2.5.4", + "rsvp": "^3.0.17", + "semver": "^5.0.1", + "tar": "^2.2.1", + "which": "^1.0.9", + "yauzl": "^2.3.1" + } + }, + "jspm-npm": { + "version": "0.26.14", + "resolved": "https://registry.npmjs.org/jspm-npm/-/jspm-npm-0.26.14.tgz", + "integrity": "sha512-2kxX7sa0o4J7sVCMLEw91VTdeMBS68To9Pf0Y0dFoKVks95BXF0anPHbpG7fGAXlkUHLFk7lwMowkXlNavDPKg==", + "dev": true, + "requires": { + "buffer-peek-stream": "^1.0.1", + "glob": "^5.0.10", + "graceful-fs": "^4.1.3", + "mkdirp": "^0.5.1", + "request": "^2.58.0", + "resolve": "^1.1.6", + "rsvp": "^3.0.18", + "semver": "^5.0.1", + "systemjs-builder": "^0.15.0", + "tar": "^1.0.3", + "which": "^1.1.1" + }, + "dependencies": { + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "dev": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "tar": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-1.0.3.tgz", + "integrity": "sha1-FbzaskT6St1E5CRKAXbtuKqaK0Q=", + "dev": true, + "requires": { + "block-stream": "*", + "fstream": "^1.0.2", + "inherits": "2" + } + } + } + }, + "jspm-registry": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/jspm-registry/-/jspm-registry-0.4.4.tgz", + "integrity": "sha1-1TFmA1qHzc5YXWK6o5dWhUaZbXA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.3", + "rimraf": "^2.3.2", + "rsvp": "^3.0.18", + "semver": "^4.3.3" + }, + "dependencies": { + "semver": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", + "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=", + "dev": true + } + } + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -3991,6 +4899,12 @@ "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "dev": true + }, "lcid": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", @@ -4016,6 +4930,22 @@ "type-check": "~0.3.2" } }, + "liftoff": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.5.0.tgz", + "integrity": "sha1-IAkpG7Mc6oYbvxCnwVooyvdcMew=", + "dev": true, + "requires": { + "extend": "^3.0.0", + "findup-sync": "^2.0.0", + "fined": "^1.0.1", + "flagged-respawn": "^1.0.0", + "is-plain-object": "^2.0.4", + "object.map": "^1.0.0", + "rechoir": "^0.6.2", + "resolve": "^1.1.7" + } + }, "load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", @@ -4111,6 +5041,21 @@ } } }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, "loud-rejection": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", @@ -4131,6 +5076,15 @@ "yallist": "^2.1.2" } }, + "make-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", + "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, "map-age-cleaner": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.2.tgz", @@ -4428,16 +5382,40 @@ "to-regex": "^3.0.1" } }, + "ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", + "dev": true + }, "negotiator": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", "dev": true }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "neo-async": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz", + "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==", + "dev": true + }, + "netrc": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/netrc/-/netrc-0.1.4.tgz", + "integrity": "sha1-a+lPysqNd63gqWcNxGCRTJRHJEQ=", + "dev": true + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, "noms": { @@ -4582,6 +5560,56 @@ "isobject": "^3.0.0" } }, + "object.defaults": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", + "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", + "dev": true, + "requires": { + "array-each": "^1.0.1", + "array-slice": "^1.0.0", + "for-own": "^1.0.0", + "isobject": "^3.0.0" + }, + "dependencies": { + "array-slice": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", + "dev": true + }, + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "dev": true, + "requires": { + "for-in": "^1.0.1" + } + } + } + }, + "object.map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", + "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", + "dev": true, + "requires": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + }, + "dependencies": { + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "dev": true, + "requires": { + "for-in": "^1.0.1" + } + } + } + }, "object.omit": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", @@ -4651,6 +5679,12 @@ "wordwrap": "~1.0.0" } }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, "os-locale": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.0.1.tgz", @@ -4710,6 +5744,17 @@ "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", "dev": true }, + "parse-filepath": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", + "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", + "dev": true, + "requires": { + "is-absolute": "^1.0.0", + "map-cache": "^0.2.0", + "path-root": "^0.1.1" + } + }, "parse-github-repo-url": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz", @@ -4755,6 +5800,12 @@ "json-parse-better-errors": "^1.0.1" } }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true + }, "parse5": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz", @@ -4824,6 +5875,21 @@ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, + "path-root": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", + "dev": true, + "requires": { + "path-root-regex": "^0.1.0" + } + }, + "path-root-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", + "dev": true + }, "path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", @@ -4833,6 +5899,12 @@ "pify": "^3.0.0" } }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true + }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -4884,6 +5956,12 @@ "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", "dev": true }, + "private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "dev": true + }, "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", @@ -4896,6 +5974,18 @@ "integrity": "sha512-OE+a6vzqazc+K6LxJrX5UPyKFvGnL5CYmq2jFGNIBWHpc4QyE49/YOumcrpQFJpfejmvRtbJzgO1zPmMCqlbBg==", "dev": true }, + "proper-lockfile": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-1.2.0.tgz", + "integrity": "sha1-zv9d2J0+XxD7deHo52vHWAGlnDQ=", + "dev": true, + "requires": { + "err-code": "^1.0.0", + "extend": "^3.0.0", + "graceful-fs": "^4.1.2", + "retry": "^0.10.0" + } + }, "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", @@ -5132,6 +6222,12 @@ "strip-indent": "^2.0.0" } }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "dev": true + }, "regex-cache": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", @@ -5265,6 +6361,27 @@ "path-parse": "^1.0.5" } }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "dependencies": { + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + } + } + }, "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", @@ -5277,12 +6394,27 @@ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true }, + "retry": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", + "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=", + "dev": true + }, "rfdc": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.2.tgz", "integrity": "sha512-92ktAgvZhBzYTIK0Mja9uen5q5J3NRVMoDkJL2VMwq6SXjVCgqvQeVP2XAaUY6HT+XpQYeLSjb3UoitBryKmdA==", "dev": true }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "dev": true, + "requires": { + "align-text": "^0.1.1" + } + }, "rimraf": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", @@ -5455,6 +6587,12 @@ } } }, + "rsvp": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-3.6.2.tgz", + "integrity": "sha512-OfWGQTb9vnwRjwtA2QwpG2ICclHC3pgXZO5xt8H2EfgDquO0qVdSb5T88L4qJVAEugbS56pAuV4XZM58UX8ulw==", + "dev": true + }, "rxjs": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz", @@ -5564,6 +6702,12 @@ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -5788,6 +6932,23 @@ "urix": "^0.1.0" } }, + "source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "dev": true, + "requires": { + "source-map": "^0.5.6" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, "source-map-url": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", @@ -5882,6 +7043,282 @@ "tweetnacl": "~0.14.0" } }, + "standard-version": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/standard-version/-/standard-version-5.0.2.tgz", + "integrity": "sha512-vvdWZySinwWU9UZhtgYUGGTkYzqrwYMw3c7CFJ17E7vMbAEqVSui/bm+ZcSukAAU2WmphPTWIKFmn8ni+lk4NA==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "conventional-changelog": "^3.0.6", + "conventional-recommended-bump": "^4.0.4", + "detect-indent": "^5.0.0", + "detect-newline": "^2.1.0", + "dotgitignore": "^2.1.0", + "figures": "^2.0.0", + "fs-access": "^1.0.0", + "git-semver-tags": "^2.0.2", + "semver": "^5.2.0", + "stringify-package": "^1.0.0", + "yargs": "^12.0.2" + }, + "dependencies": { + "conventional-changelog": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-3.0.6.tgz", + "integrity": "sha512-1b96x3G67lDKakRvMm+VvYGwgRk+C8aapHKL5iZ/TJzzD/RuyGA2diHNEsR+uPHmQ7/A4Ts7j6N+VNqUoOfksg==", + "dev": true, + "requires": { + "conventional-changelog-angular": "^5.0.3", + "conventional-changelog-atom": "^2.0.1", + "conventional-changelog-codemirror": "^2.0.1", + "conventional-changelog-core": "^3.1.6", + "conventional-changelog-ember": "^2.0.2", + "conventional-changelog-eslint": "^3.0.1", + "conventional-changelog-express": "^2.0.1", + "conventional-changelog-jquery": "^3.0.4", + "conventional-changelog-jshint": "^2.0.1", + "conventional-changelog-preset-loader": "^2.0.2" + } + }, + "conventional-changelog-angular": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.3.tgz", + "integrity": "sha512-YD1xzH7r9yXQte/HF9JBuEDfvjxxwDGGwZU1+ndanbY0oFgA+Po1T9JDSpPLdP0pZT6MhCAsdvFKC4TJ4MTJTA==", + "dev": true, + "requires": { + "compare-func": "^1.3.1", + "q": "^1.5.1" + } + }, + "conventional-changelog-atom": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-atom/-/conventional-changelog-atom-2.0.1.tgz", + "integrity": "sha512-9BniJa4gLwL20Sm7HWSNXd0gd9c5qo49gCi8nylLFpqAHhkFTj7NQfROq3f1VpffRtzfTQp4VKU5nxbe2v+eZQ==", + "dev": true, + "requires": { + "q": "^1.5.1" + } + }, + "conventional-changelog-codemirror": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-codemirror/-/conventional-changelog-codemirror-2.0.1.tgz", + "integrity": "sha512-23kT5IZWa+oNoUaDUzVXMYn60MCdOygTA2I+UjnOMiYVhZgmVwNd6ri/yDlmQGXHqbKhNR5NoXdBzSOSGxsgIQ==", + "dev": true, + "requires": { + "q": "^1.5.1" + } + }, + "conventional-changelog-core": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-3.1.6.tgz", + "integrity": "sha512-5teTAZOtJ4HLR6384h50nPAaKdDr+IaU0rnD2Gg2C3MS7hKsEPH8pZxrDNqam9eOSPQg9tET6uZY79zzgSz+ig==", + "dev": true, + "requires": { + "conventional-changelog-writer": "^4.0.3", + "conventional-commits-parser": "^3.0.1", + "dateformat": "^3.0.0", + "get-pkg-repo": "^1.0.0", + "git-raw-commits": "2.0.0", + "git-remote-origin-url": "^2.0.0", + "git-semver-tags": "^2.0.2", + "lodash": "^4.2.1", + "normalize-package-data": "^2.3.5", + "q": "^1.5.1", + "read-pkg": "^3.0.0", + "read-pkg-up": "^3.0.0", + "through2": "^2.0.0" + } + }, + "conventional-changelog-ember": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-ember/-/conventional-changelog-ember-2.0.2.tgz", + "integrity": "sha512-qtZbA3XefO/n6DDmkYywDYi6wDKNNc98MMl2F9PKSaheJ25Trpi3336W8fDlBhq0X+EJRuseceAdKLEMmuX2tg==", + "dev": true, + "requires": { + "q": "^1.5.1" + } + }, + "conventional-changelog-eslint": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-eslint/-/conventional-changelog-eslint-3.0.1.tgz", + "integrity": "sha512-yH3+bYrtvgKxSFChUBQnKNh9/U9kN2JElYBm253VpYs5wXhPHVc9ENcuVGWijh24nnOkei7wEJmnmUzgZ4ok+A==", + "dev": true, + "requires": { + "q": "^1.5.1" + } + }, + "conventional-changelog-express": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-express/-/conventional-changelog-express-2.0.1.tgz", + "integrity": "sha512-G6uCuCaQhLxdb4eEfAIHpcfcJ2+ao3hJkbLrw/jSK/eROeNfnxCJasaWdDAfFkxsbpzvQT4W01iSynU3OoPLIw==", + "dev": true, + "requires": { + "q": "^1.5.1" + } + }, + "conventional-changelog-jquery": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/conventional-changelog-jquery/-/conventional-changelog-jquery-3.0.4.tgz", + "integrity": "sha512-IVJGI3MseYoY6eybknnTf9WzeQIKZv7aNTm2KQsiFVJH21bfP2q7XVjfoMibdCg95GmgeFlaygMdeoDDa+ZbEQ==", + "dev": true, + "requires": { + "q": "^1.5.1" + } + }, + "conventional-changelog-jshint": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-jshint/-/conventional-changelog-jshint-2.0.1.tgz", + "integrity": "sha512-kRFJsCOZzPFm2tzRHULWP4tauGMvccOlXYf3zGeuSW4U0mZhk5NsjnRZ7xFWrTFPlCLV+PNmHMuXp5atdoZmEg==", + "dev": true, + "requires": { + "compare-func": "^1.3.1", + "q": "^1.5.1" + } + }, + "conventional-changelog-preset-loader": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.0.2.tgz", + "integrity": "sha512-pBY+qnUoJPXAXXqVGwQaVmcye05xi6z231QM98wHWamGAmu/ghkBprQAwmF5bdmyobdVxiLhPY3PrCfSeUNzRQ==", + "dev": true + }, + "conventional-changelog-writer": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.3.tgz", + "integrity": "sha512-bIlpSiQtQZ1+nDVHEEh798Erj2jhN/wEjyw9sfxY9es6h7pREE5BNJjfv0hXGH/FTrAsEpHUq4xzK99eePpwuA==", + "dev": true, + "requires": { + "compare-func": "^1.3.1", + "conventional-commits-filter": "^2.0.1", + "dateformat": "^3.0.0", + "handlebars": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.2.1", + "meow": "^4.0.0", + "semver": "^5.5.0", + "split": "^1.0.0", + "through2": "^2.0.0" + } + }, + "conventional-commits-filter": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.1.tgz", + "integrity": "sha512-92OU8pz/977udhBjgPEbg3sbYzIxMDFTlQT97w7KdhR9igNqdJvy8smmedAAgn4tPiqseFloKkrVfbXCVd+E7A==", + "dev": true, + "requires": { + "is-subset": "^0.1.1", + "modify-values": "^1.0.0" + } + }, + "conventional-commits-parser": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.0.1.tgz", + "integrity": "sha512-P6U5UOvDeidUJ8ebHVDIoXzI7gMlQ1OF/id6oUvp8cnZvOXMt1n8nYl74Ey9YMn0uVQtxmCtjPQawpsssBWtGg==", + "dev": true, + "requires": { + "JSONStream": "^1.0.4", + "is-text-path": "^1.0.0", + "lodash": "^4.2.1", + "meow": "^4.0.0", + "split2": "^2.0.0", + "through2": "^2.0.0", + "trim-off-newlines": "^1.0.0" + } + }, + "detect-indent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz", + "integrity": "sha1-OHHMCmoALow+Wzz38zYmRnXwa50=", + "dev": true + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "git-semver-tags": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-2.0.2.tgz", + "integrity": "sha512-34lMF7Yo1xEmsK2EkbArdoU79umpvm0MfzaDkSNYSJqtM5QLAVTPWgpiXSVI5o/O9EvZPSrP4Zvnec/CqhSd5w==", + "dev": true, + "requires": { + "meow": "^4.0.0", + "semver": "^5.5.0" + } + }, + "handlebars": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.1.tgz", + "integrity": "sha512-3Zhi6C0euYZL5sM0Zcy7lInLXKQ+YLcF/olbN010mzGQ4XVm50JeyBnMqofHh696GrciGruC7kCcApPDJvVgwA==", + "dev": true, + "requires": { + "neo-async": "^2.6.0", + "optimist": "^0.6.1", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + } + } + } + }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -5963,6 +7400,12 @@ "safe-buffer": "~5.1.0" } }, + "stringify-package": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stringify-package/-/stringify-package-1.0.0.tgz", + "integrity": "sha512-JIQqiWmLiEozOC0b0BtxZ/AOUtdUZHCBPgqIZ2kSJJqGwgb9neo44XdTHUC4HZSGqi03hOeB7W/E8rAlKnGe9g==", + "dev": true + }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", @@ -6013,6 +7456,125 @@ "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", "dev": true }, + "systemjs": { + "version": "0.19.46", + "resolved": "https://registry.npmjs.org/systemjs/-/systemjs-0.19.46.tgz", + "integrity": "sha1-wEV0szNfBSoOPHoA7kGIxuTB444=", + "dev": true, + "requires": { + "when": "^3.7.5" + } + }, + "systemjs-builder": { + "version": "0.15.36", + "resolved": "https://registry.npmjs.org/systemjs-builder/-/systemjs-builder-0.15.36.tgz", + "integrity": "sha1-MLAjctQifPN4gPWA/mfLTtt/FCA=", + "dev": true, + "requires": { + "babel-core": "^6.9.0", + "babel-plugin-transform-cjs-system-wrapper": "^0.3.0", + "babel-plugin-transform-es2015-modules-systemjs": "^6.6.5", + "babel-plugin-transform-global-system-wrapper": "0.0.1", + "babel-plugin-transform-system-register": "0.0.1", + "bluebird": "^3.3.4", + "data-uri-to-buffer": "0.0.4", + "es6-template-strings": "^2.0.0", + "glob": "^7.0.3", + "mkdirp": "^0.5.1", + "rollup": "^0.36.3", + "source-map": "^0.5.3", + "systemjs": "^0.19.43", + "traceur": "0.0.105", + "uglify-js": "~2.7.5" + }, + "dependencies": { + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", + "dev": true + }, + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dev": true, + "requires": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "rollup": { + "version": "0.36.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.36.4.tgz", + "integrity": "sha1-oiRJTFOGwdc9OPe7hvafXrARo9I=", + "dev": true, + "requires": { + "source-map-support": "^0.4.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "uglify-js": { + "version": "2.7.5", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.7.5.tgz", + "integrity": "sha1-RhLAx7qu4rp8SH3kkErhIgefLKg=", + "dev": true, + "requires": { + "async": "~0.2.6", + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + } + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "requires": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + } + } + }, + "tar": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "dev": true, + "requires": { + "block-stream": "*", + "fstream": "^1.0.2", + "inherits": "2" + } + }, "tempfile": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-1.1.1.tgz", @@ -6068,6 +7630,12 @@ "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", "dev": true }, + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "dev": true + }, "to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", @@ -6137,6 +7705,67 @@ } } }, + "traceur": { + "version": "0.0.105", + "resolved": "https://registry.npmjs.org/traceur/-/traceur-0.0.105.tgz", + "integrity": "sha1-XPne6D1rd4YcPWxE1ThZrterBHk=", + "dev": true, + "requires": { + "commander": "2.9.x", + "glob": "5.0.x", + "rsvp": "^3.0.13", + "semver": "^4.3.3", + "source-map-support": "~0.2.8" + }, + "dependencies": { + "commander": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", + "dev": true, + "requires": { + "graceful-readlink": ">= 1.0.0" + } + }, + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "dev": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "semver": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", + "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=", + "dev": true + }, + "source-map": { + "version": "0.1.32", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.32.tgz", + "integrity": "sha1-yLbBZ3l7pHQKjqMyUhYv8IWRsmY=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + }, + "source-map-support": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.2.10.tgz", + "integrity": "sha1-6lo5AKHByyUJagrozFwrSxDe09w=", + "dev": true, + "requires": { + "source-map": "0.1.32" + } + } + } + }, "tree-kill": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.0.tgz", @@ -6155,6 +7784,12 @@ "integrity": "sha1-n5up2e+odkw4dpi8v+sshI8RrbM=", "dev": true }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "dev": true + }, "tslib": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", @@ -6224,6 +7859,12 @@ "mime-types": "~2.1.18" } }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, "typedoc": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.12.0.tgz", @@ -6286,12 +7927,24 @@ "source-map": "~0.6.1" } }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "dev": true + }, "ultron": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", "dev": true }, + "unc-path-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", + "dev": true + }, "union-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", @@ -6486,6 +8139,12 @@ "webidl-conversions": "^4.0.2" } }, + "when": { + "version": "3.7.8", + "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz", + "integrity": "sha1-xxMLan6gRpPoQs3J56Hyqjmjn4I=", + "dev": true + }, "which": { "version": "1.2.14", "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", @@ -6501,6 +8160,12 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "dev": true + }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -6636,6 +8301,16 @@ "camelcase": "^4.1.0" } }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dev": true, + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, "yeast": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", diff --git a/package.json b/package.json index f1017fb4..9ad980a5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aurelia-validation", - "version": "1.3.3", + "version": "1.4.0", "description": "Validation for Aurelia applications", "keywords": [ "aurelia", @@ -16,13 +16,21 @@ "main": "dist/commonjs/aurelia-validation.js", "module": "dist/es2015/aurelia-validation.js", "browser": "dist/umd/aurelia-validation.js", - "unpkg": "dist/umd/aurelia-validation.js", + "unpkg": "dist/umd-es2015/aurelia-validation.js", "types": "dist/aurelia-validation.d.ts", "typings": "dist/aurelia-validation.d.ts", "repository": { "type": "git", "url": "https://github.com/aurelia/validation" }, + "files": [ + "dist", + "doc", + "src", + "typings.json", + "README.md", + "LICENSE" + ], "scripts": { "lint": "cross-env tslint --project tsconfig.json", "pretest": "cross-env npm run lint", @@ -35,10 +43,8 @@ "predoc": "cross-env rimraf doc/api.json && rimraf dist/doc-temp && tsc --project tsconfig.build.json --outFile dist/doc-temp/aurelia-validation.js && node doc/shape-defs && copyfiles tsconfig.json dist/doc-temp", "doc": "cross-env typedoc --json doc/api.json --excludeExternals --includeDeclarations --mode modules --target ES6 --name aurelia-validation-docs dist/doc-temp/", "postdoc": "cross-env node doc/shape-doc && rimraf dist/doc-temp", - "changelog": "cross-env conventional-changelog -p angular -i doc/CHANGELOG.md -s", - "bump-version": "npm --no-git-tag-version version", - "preprepare-release": "cross-env npm run test", - "prepare-release": "cross-env npm run changelog && npm run build && npm run doc" + "precut-release": "cross-env npm run test", + "cut-release": "standard-version -t \"\" -i doc/CHANGELOG.md && npm run build && npm run doc" }, "jspm": { "registry": "npm", @@ -88,6 +94,7 @@ "cross-env": "^5.2.0", "dts-bundle-generator": "^1.6.1", "jasmine-core": "^3.2.1", + "jspm": "^0.16.53", "karma": "^3.0.0", "karma-chrome-launcher": "^2.2.0", "karma-ie-launcher": "^1.0.0", @@ -98,6 +105,7 @@ "rimraf": "^2.6.2", "rollup": "^0.66.2", "rollup-plugin-typescript2": "^0.17.0", + "standard-version": "^5.0.2", "tslib": "^1.9.3", "tslint": "^5.11.0", "typedoc": "^0.12.0", diff --git a/rollup.config.js b/rollup.config.js index 16844de2..5aa624d5 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -17,7 +17,7 @@ export default [{ format: 'es' }, { - file: `dist/umd/${entryName}.js`, + file: `dist/umd-es2015/${entryName}.js`, format: 'umd', name: 'au.validation', globals: { @@ -77,6 +77,18 @@ export default [{ { file: `dist/commonjs/${entryName}.js`, format: 'cjs' }, { file: `dist/amd/${entryName}.js`, format: 'amd', amd: { id: entryName } }, { file: `dist/native-modules/${entryName}.js`, format: 'es' }, + { file: `dist/umd/${entryName}.js`, + format: 'umd', + name: 'au.validation', + globals: { + 'aurelia-binding': 'au', + 'aurelia-templating': 'au', + 'aurelia-dependency-injection': "au", + "aurelia-logging": "au.LogManager", + "aurelia-pal": "au", + "aurelia-task-queue": "au", + } + }, { file: `dist/system/${entryName}.js`, format: 'system' } ], plugins: [ @@ -85,7 +97,8 @@ export default [{ // tsconfig: undefined, tsconfigOverride: { compilerOptions: { - module: 'es2015' + module: 'es2015', + target: 'es5' }, include: ['src'], exclude: undefined diff --git a/src/implementation/validation-messages.ts b/src/implementation/validation-messages.ts index 4c8def77..ae763272 100644 --- a/src/implementation/validation-messages.ts +++ b/src/implementation/validation-messages.ts @@ -20,6 +20,10 @@ export const validationMessages: ValidationMessages = { maxLength: `\${$displayName} cannot be longer than \${$config.length} character\${$config.length === 1 ? '' : 's'}.`, minItems: `\${$displayName} must contain at least \${$config.count} item\${$config.count === 1 ? '' : 's'}.`, maxItems: `\${$displayName} cannot contain more than \${$config.count} item\${$config.count === 1 ? '' : 's'}.`, + min: `\${$displayName} must be at least \${$config.constraint}.`, + max: `\${$displayName} must be at most \${$config.constraint}.`, + range: `\${$displayName} must be between or equal to \${$config.min} and \${$config.max}.`, + between: `\${$displayName} must be between but not equal to \${$config.min} and \${$config.max}.`, equals: `\${$displayName} must be \${$config.expectedValue}.`, }; diff --git a/src/implementation/validation-rules.ts b/src/implementation/validation-rules.ts index 5cbfbf90..0221c2c9 100644 --- a/src/implementation/validation-rules.ts +++ b/src/implementation/validation-rules.ts @@ -188,6 +188,42 @@ export class FluentRuleCustomizer { return this.fluentRules.maxItems(count); } + /** + * Applies the "min" NUMBER validation rule to the property. + * Value must be greater than or equal to the specified constraint. + * null and undefined values are considered valid. + */ + public min(value: number) { + return this.fluentRules.min(value); + } + + /** + * Applies the "max" NUMBER validation rule to the property. + * Value must be less than or equal to the specified constraint. + * null and undefined values are considered valid. + */ + public max(value: number) { + return this.fluentRules.max(value); + } + + /** + * Applies the "range" NUMBER validation rule to the property. + * Value must be between or equal to the specified min and max. + * null and undefined values are considered valid. + */ + public range(min: number, max: number) { + return this.fluentRules.range(min, max); + } + + /** + * Applies the "between" NUMBER validation rule to the property. + * Value must be between but not equal to the specified min and max. + * null and undefined values are considered valid. + */ + public between(min: number, max: number) { + return this.fluentRules.between(min, max); + } + /** * Applies the "equals" validation rule to the property. * null, undefined and empty-string values are considered valid. @@ -336,6 +372,48 @@ export class FluentRules { .withMessageKey('maxItems'); } + /** + * Applies the "min" NUMBER validation rule to the property. + * Value must be greater than or equal to the specified constraint. + * null and undefined values are considered valid. + */ + public min(constraint: number) { + return this.satisfies((value: any) => value === null || value === undefined || value >= constraint, { constraint }) + .withMessageKey('min'); + } + + /** + * Applies the "max" NUMBER validation rule to the property. + * Value must be less than or equal to the specified constraint. + * null and undefined values are considered valid. + */ + public max(constraint: number) { + return this.satisfies((value: any) => value === null || value === undefined || value <= constraint, { constraint }) + .withMessageKey('max'); + } + + /** + * Applies the "range" NUMBER validation rule to the property. + * Value must be between or equal to the specified min and max. + * null and undefined values are considered valid. + */ + public range(min: number, max: number) { + return this.satisfies((value: any) => value === null || value === undefined || (value >= min && value <= max), + { min, max }) + .withMessageKey('range'); + } + + /** + * Applies the "between" NUMBER validation rule to the property. + * Value must be between but not equal to the specified min and max. + * null and undefined values are considered valid. + */ + public between(min: number, max: number) { + return this.satisfies((value: any) => value === null || value === undefined || (value > min && value < max), + { min, max }) + .withMessageKey('between'); + } + /** * Applies the "equals" validation rule to the property. * null and undefined values are considered valid. diff --git a/test/validator.ts b/test/validator.ts index b8b8a180..80b32b92 100644 --- a/test/validator.ts +++ b/test/validator.ts @@ -85,6 +85,83 @@ describe('Validator', () => { .then(done); }); + it('validates numeric properties', (done: () => void) => { + const obj = { value: 1 }; + let rules = ValidationRules.ensure('value').min(1).rules; + validator.validateObject(obj, rules) + .then(results => { + const expected = [new ValidateResult(rules[0][0], obj, 'value', true, null)]; + expected[0].id = results[0].id; + expect(results).toEqual(expected); + }) + .then(() => { + rules = ValidationRules.ensure('value').max(1).rules; + return validator.validateObject(obj, rules); + }) + .then(results => { + const expected = [new ValidateResult(rules[0][0], obj, 'value', true, null)]; + expected[0].id = results[0].id; + expect(results).toEqual(expected); + }) + .then(() => { + rules = ValidationRules.ensure('value').range(0, 1).rules; + return validator.validateObject(obj, rules); + }) + .then(results => { + const expected = [new ValidateResult(rules[0][0], obj, 'value', true, null)]; + expected[0].id = results[0].id; + expect(results).toEqual(expected); + }) + .then(() => { + rules = ValidationRules.ensure('value').between(0, 2).rules; + return validator.validateObject(obj, rules); + }) + .then(results => { + const expected = [new ValidateResult(rules[0][0], obj, 'value', true, null)]; + expected[0].id = results[0].id; + expect(results).toEqual(expected); + }) + .then(() => { + rules = ValidationRules.ensure('value').min(2).rules; + return validator.validateObject(obj, rules); + }) + .then(results => { + const expected = [new ValidateResult(rules[0][0], obj, 'value', false, 'Value must be at least 2.')]; + expected[0].id = results[0].id; + expect(results).toEqual(expected); + }) + .then(() => { + rules = ValidationRules.ensure('value').max(0).rules; + return validator.validateObject(obj, rules); + }) + .then(results => { + const expected = [new ValidateResult(rules[0][0], obj, 'value', false, 'Value must be at most 0.')]; + expected[0].id = results[0].id; + expect(results).toEqual(expected); + }) + .then(() => { + rules = ValidationRules.ensure('value').range(2, 3).rules; + return validator.validateObject(obj, rules); + }) + .then(results => { + const expected = [new ValidateResult(rules[0][0], obj, 'value', false, + 'Value must be between or equal to 2 and 3.')]; + expected[0].id = results[0].id; + expect(results).toEqual(expected); + }) + .then(() => { + rules = ValidationRules.ensure('value').between(1, 3).rules; + return validator.validateObject(obj, rules); + }) + .then(results => { + const expected = [new ValidateResult(rules[0][0], obj, 'value', false, + 'Value must be between but not equal to 1 and 3.')]; + expected[0].id = results[0].id; + expect(results).toEqual(expected); + }) + .then(done); + }); + it('handles numeric properties', (done: () => void) => { const objStr = {} as any; objStr['2'] = 'test';