/*============================================================================= Author: Eric M. Barnard - @ericmbarnard License: MIT (http://opensource.org/licenses/mit-license.php) Description: Validation Library for KnockoutJS Version: 2.0.3 ===============================================================================*//*globals require: false, exports: false, define: false, ko: false */ (function (factory) { // Module systems magic dance. if (typeof require === "function" && typeof exports === "object" && typeof module === "object") { // CommonJS or Node: hard-coded dependency on "knockout" factory(require("knockout"), exports); } else if (typeof define === "function" && define["amd"]) { // AMD anonymous module with hard-coded dependency on "knockout" define(["knockout", "exports"], factory); } else { // <script> tag: use the global `ko` object, attaching a `mapping` property factory(ko, ko.validation = {}); }}(function ( ko, exports ) { if (typeof (ko) === 'undefined') { throw new Error('Knockout is required, please ensure it is loaded before loading this validation plug-in'); } // create our namespace object ko.validation = exports; var kv = ko.validation, koUtils = ko.utils, unwrap = koUtils.unwrapObservable, forEach = koUtils.arrayForEach, extend = koUtils.extend;;/*global ko: false*/ var defaults = { registerExtenders: true, messagesOnModified: true, errorsAsTitle: true, // enables/disables showing of errors as title attribute of the target element. errorsAsTitleOnModified: false, // shows the error when hovering the input field (decorateElement must be true) messageTemplate: null, insertMessages: true, // automatically inserts validation messages as <span></span> parseInputAttributes: false, // parses the HTML5 validation attribute from a form element and adds that to the object writeInputAttributes: false, // adds HTML5 input validation attributes to form elements that ko observable's are bound to decorateInputElement: false, // false to keep backward compatibility decorateElementOnModified: true,// true to keep backward compatibility errorClass: null, // single class for error message and element errorElementClass: 'validationElement', // class to decorate error element errorMessageClass: 'validationMessage', // class to decorate error message allowHtmlMessages: false, // allows HTML in validation messages grouping: { deep: false, //by default grouping is shallow observable: true, //and using observables live: false //react to changes to observableArrays if observable === true }, validate: { // throttle: 10 }}; // make a copy so we can use 'reset' latervar configuration = extend({}, defaults); configuration.html5Attributes = ['required', 'pattern', 'min', 'max', 'step'];configuration.html5InputTypes = ['email', 'number', 'date']; configuration.reset = function () { extend(configuration, defaults);}; kv.configuration = configuration;;kv.utils = (function () { var seedId = new Date().getTime(); var domData = {}; //hash of data objects that we reference from dom elements var domDataKey = '__ko_validation__'; return { isArray: function (o) { return o.isArray || Object.prototype.toString.call(o) === '[object Array]'; }, isObject: function (o) { return o !== null && typeof o === 'object'; }, isNumber: function(o) { return !isNaN(o); }, isObservableArray: function(instance) { return !!instance && typeof instance["remove"] === "function" && typeof instance["removeAll"] === "function" && typeof instance["destroy"] === "function" && typeof instance["destroyAll"] === "function" && typeof instance["indexOf"] === "function" && typeof instance["replace"] === "function"; }, values: function (o) { var r = []; for (var i in o) { if (o.hasOwnProperty(i)) { r.push(o[i]); } } return r; }, getValue: function (o) { return (typeof o === 'function' ? o() : o); }, hasAttribute: function (node, attr) { return node.getAttribute(attr) !== null; }, getAttribute: function (element, attr) { return element.getAttribute(attr); }, setAttribute: function (element, attr, value) { return element.setAttribute(attr, value); }, isValidatable: function (o) { return !!(o && o.rules && o.isValid && o.isModified); }, insertAfter: function (node, newNode) { node.parentNode.insertBefore(newNode, node.nextSibling); }, newId: function () { return seedId += 1; }, getConfigOptions: function (element) { var options = kv.utils.contextFor(element); return options || kv.configuration; }, setDomData: function (node, data) { var key = node[domDataKey]; if (!key) { node[domDataKey] = key = kv.utils.newId(); } domData[key] = data; }, getDomData: function (node) { var key = node[domDataKey]; if (!key) { return undefined; } return domData[key]; }, contextFor: function (node) { switch (node.nodeType) { case 1: case 8: var context = kv.utils.getDomData(node); if (context) { return context; } if (node.parentNode) { return kv.utils.contextFor(node.parentNode); } break; } return undefined; }, isEmptyVal: function (val) { if (val === undefined) { return true; } if (val === null) { return true; } if (val === "") { return true; } }, getOriginalElementTitle: function (element) { var savedOriginalTitle = kv.utils.getAttribute(element, 'data-orig-title'), currentTitle = element.title, hasSavedOriginalTitle = kv.utils.hasAttribute(element, 'data-orig-title'); return hasSavedOriginalTitle ? savedOriginalTitle : currentTitle; }, async: function (expr) { if (window.setImmediate) { window.setImmediate(expr); } else { window.setTimeout(expr, 0); } }, forEach: function (object, callback) { if (kv.utils.isArray(object)) { return forEach(object, callback); } for (var prop in object) { if (object.hasOwnProperty(prop)) { callback(object[prop], prop); } } } };}());;var api = (function () { var isInitialized = 0, configuration = kv.configuration, utils = kv.utils; function cleanUpSubscriptions(context) { forEach(context.subscriptions, function (subscription) { subscription.dispose(); }); context.subscriptions = []; } function dispose(context) { if (context.options.deep) { forEach(context.flagged, function (obj) { delete obj.__kv_traversed; }); context.flagged.length = 0; } if (!context.options.live) { cleanUpSubscriptions(context); } } function runTraversal(obj, context) { context.validatables = []; cleanUpSubscriptions(context); traverseGraph(obj, context); dispose(context); } function traverseGraph(obj, context, level) { var objValues = [], val = obj.peek ? obj.peek() : obj; if (obj.__kv_traversed === true) { return; } if (context.options.deep) { obj.__kv_traversed = true; context.flagged.push(obj); } //default level value depends on deep option. level = (level !== undefined ? level : context.options.deep ? 1 : -1); // if object is observable then add it to the list if (ko.isObservable(obj)) { // ensure it's validatable but don't extend validatedObservable because it // would overwrite isValid property. if (!obj.errors && !utils.isValidatable(obj)) { obj.extend({ validatable: true }); } context.validatables.push(obj); if (context.options.live && utils.isObservableArray(obj)) { context.subscriptions.push(obj.subscribe(function () { context.graphMonitor.valueHasMutated(); })); } } //get list of values either from array or object but ignore non-objects // and destroyed objects if (val && !val._destroy) { if (utils.isArray(val)) { objValues = val; } else if (utils.isObject(val)) { objValues = utils.values(val); } } //process recursively if it is deep grouping if (level !== 0) { utils.forEach(objValues, function (observable) { //but not falsy things and not HTML Elements if (observable && !observable.nodeType && (!ko.isComputed(observable) || observable.rules)) { traverseGraph(observable, context, level + 1); } }); } } function collectErrors(array) { var errors = []; forEach(array, function (observable) { // Do not collect validatedObservable errors if (utils.isValidatable(observable) && !observable.isValid()) { // Use peek because we don't want a dependency for 'error' property because it // changes before 'isValid' does. (Issue #99) errors.push(observable.error.peek()); } }); return errors; } return { //Call this on startup //any config can be overridden with the passed in options init: function (options, force) { //done run this multiple times if we don't really want to if (isInitialized > 0 && !force) { return; } //because we will be accessing options properties it has to be an object at least options = options || {}; //if specific error classes are not provided then apply generic errorClass //it has to be done on option so that options.errorClass can override default //errorElementClass and errorMessage class but not those provided in options options.errorElementClass = options.errorElementClass || options.errorClass || configuration.errorElementClass; options.errorMessageClass = options.errorMessageClass || options.errorClass || configuration.errorMessageClass; extend(configuration, options); if (configuration.registerExtenders) { kv.registerExtenders(); } isInitialized = 1; }, // resets the config back to its original state reset: kv.configuration.reset, // recursively walks a viewModel and creates an object that // provides validation information for the entire viewModel // obj -> the viewModel to walk // options -> { // deep: false, // if true, will walk past the first level of viewModel properties // observable: false // if true, returns a computed observable indicating if the viewModel is valid // } group: function group(obj, options) { // array of observables or viewModel options = extend(extend({}, configuration.grouping), options); var context = { options: options, graphMonitor: ko.observable(), flagged: [], subscriptions: [], validatables: [] }; var result = null; //if using observables then traverse structure once and add observables if (options.observable) { result = ko.computed(function () { context.graphMonitor(); //register dependency runTraversal(obj, context); return collectErrors(context.validatables); }); } else { //if not using observables then every call to error() should traverse the structure result = function () { runTraversal(obj, context); return collectErrors(context.validatables); }; } result.showAllMessages = function (show) { // thanks @heliosPortal if (show === undefined) {//default to true show = true; } result.forEach(function (observable) { if (utils.isValidatable(observable)) { observable.isModified(show); } }); }; result.isAnyMessageShown = function () { var invalidAndModifiedPresent; invalidAndModifiedPresent = !!result.find(function (observable) { return utils.isValidatable(observable) && !observable.isValid() && observable.isModified(); }); return invalidAndModifiedPresent; }; result.filter = function(predicate) { predicate = predicate || function () { return true; }; // ensure we have latest changes result(); return koUtils.arrayFilter(context.validatables, predicate); }; result.find = function(predicate) { predicate = predicate || function () { return true; }; // ensure we have latest changes result(); return koUtils.arrayFirst(context.validatables, predicate); }; result.forEach = function(callback) { callback = callback || function () { }; // ensure we have latest changes result(); forEach(context.validatables, callback); }; result.map = function(mapping) { mapping = mapping || function (item) { return item; }; // ensure we have latest changes result(); return koUtils.arrayMap(context.validatables, mapping); }; /** * @private You should not rely on this method being here. * It's a private method and it may change in the future. * * @description Updates the validated object and collects errors from it. */ result._updateState = function(newValue) { if (!utils.isObject(newValue)) { throw new Error('An object is required.'); } obj = newValue; if (options.observable) { context.graphMonitor.valueHasMutated(); } else { runTraversal(newValue, context); return collectErrors(context.validatables); } }; return result; }, formatMessage: function (message, params, observable) { if (utils.isObject(params) && params.typeAttr) { params = params.value; } if (typeof message === 'function') { return message(params, observable); } var replacements = unwrap(params); if (replacements == null) { replacements = []; } if (!utils.isArray(replacements)) { replacements = [replacements]; } return message.replace(/{(\d+)}/gi, function(match, index) { if (typeof replacements[index] !== 'undefined') { return replacements[index]; } return match; }); }, // addRule: // This takes in a ko.observable and a Rule Context - which is just a rule name and params to supply to the validator // ie: kv.addRule(myObservable, { // rule: 'required', // params: true // }); // addRule: function (observable, rule) { observable.extend({ validatable: true }); var hasRule = !!koUtils.arrayFirst(observable.rules(), function(item) { return item.rule && item.rule === rule.rule; }); if (!hasRule) { //push a Rule Context to the observables local array of Rule Contexts observable.rules.push(rule); } return observable; }, // addAnonymousRule: // Anonymous Rules essentially have all the properties of a Rule, but are only specific for a certain property // and developers typically are wanting to add them on the fly or not register a rule with the 'kv.rules' object // // Example: // var test = ko.observable('something').extend{( // validation: { // validator: function(val, someOtherVal){ // return true; // }, // message: "Something must be really wrong!', // params: true // } // )}; addAnonymousRule: function (observable, ruleObj) { if (ruleObj['message'] === undefined) { ruleObj['message'] = 'Error'; } //make sure onlyIf is honouredLines 1–500 of 1511. Load more
/*============================================================================= Author: Eric M. Barnard - @ericmbarnard License: MIT (http://opensource.org/licenses/mit-license.php) Description: Validation Library for KnockoutJS Version: 2.0.3 =============================================================================== */ /*globals require: false, exports: false, define: false, ko: false */ (function (factory) { // Module systems magic dance. if (typeof require === "function" && typeof exports === "object" && typeof module === "object") { // CommonJS or Node: hard-coded dependency on "knockout" factory(require("knockout"), exports); } else if (typeof define === "function" && define["amd"]) { // AMD anonymous module with hard-coded dependency on "knockout" define(["knockout", "exports"], factory); } else { // <script> tag: use the global `ko` object, attaching a `mapping` property factory(ko, ko.validation = {}); } }(function ( ko, exports ) { if (typeof (ko) === 'undefined') { throw new Error('Knockout is required, please ensure it is loaded before loading this validation plug-in'); } // create our namespace object ko.validation = exports; var kv = ko.validation, koUtils = ko.utils, unwrap = koUtils.unwrapObservable, forEach = koUtils.arrayForEach, extend = koUtils.extend; ;/*global ko: false*/ var defaults = { registerExtenders: true, messagesOnModified: true, errorsAsTitle: true, // enables/disables showing of errors as title attribute of the target element. errorsAsTitleOnModified: false, // shows the error when hovering the input field (decorateElement must be true) messageTemplate: null, insertMessages: true, // automatically inserts validation messages as <span></span> parseInputAttributes: false, // parses the HTML5 validation attribute from a form element and adds that to the object writeInputAttributes: false, // adds HTML5 input validation attributes to form elements that ko observable's are bound to decorateInputElement: false, // false to keep backward compatibility decorateElementOnModified: true,// true to keep backward compatibility errorClass: null, // single class for error message and element errorElementClass: 'validationElement', // class to decorate error element errorMessageClass: 'validationMessage', // class to decorate error message allowHtmlMessages: false, // allows HTML in validation messages grouping: { deep: false, //by default grouping is shallow observable: true, //and using observables live: false //react to changes to observableArrays if observable === true }, validate: { // throttle: 10 } }; // make a copy so we can use 'reset' later var configuration = extend({}, defaults); configuration.html5Attributes = ['required', 'pattern', 'min', 'max', 'step']; configuration.html5InputTypes = ['email', 'number', 'date']; configuration.reset = function () { extend(configuration, defaults); }; kv.configuration = configuration; ;kv.utils = (function () { var seedId = new Date().getTime(); var domData = {}; //hash of data objects that we reference from dom elements var domDataKey = '__ko_validation__'; return { isArray: function (o) { return o.isArray || Object.prototype.toString.call(o) === '[object Array]'; }, isObject: function (o) { return o !== null && typeof o === 'object'; }, isNumber: function(o) { return !isNaN(o); }, isObservableArray: function(instance) { return !!instance && typeof instance["remove"] === "function" && typeof instance["removeAll"] === "function" && typeof instance["destroy"] === "function" && typeof instance["destroyAll"] === "function" && typeof instance["indexOf"] === "function" && typeof instance["replace"] === "function"; }, values: function (o) { var r = []; for (var i in o) { if (o.hasOwnProperty(i)) { r.push(o[i]); } } return r; }, getValue: function (o) { return (typeof o === 'function' ? o() : o); }, hasAttribute: function (node, attr) { return node.getAttribute(attr) !== null; }, getAttribute: function (element, attr) { return element.getAttribute(attr); }, setAttribute: function (element, attr, value) { return element.setAttribute(attr, value); }, isValidatable: function (o) { return !!(o && o.rules && o.isValid && o.isModified); }, insertAfter: function (node, newNode) { node.parentNode.insertBefore(newNode, node.nextSibling); }, newId: function () { return seedId += 1; }, getConfigOptions: function (element) { var options = kv.utils.contextFor(element); return options || kv.configuration; }, setDomData: function (node, data) { var key = node[domDataKey]; if (!key) { node[domDataKey] = key = kv.utils.newId(); } domData[key] = data; }, getDomData: function (node) { var key = node[domDataKey]; if (!key) { return undefined; } return domData[key]; }, contextFor: function (node) { switch (node.nodeType) { case 1: case 8: var context = kv.utils.getDomData(node); if (context) { return context; } if (node.parentNode) { return kv.utils.contextFor(node.parentNode); } break; } return undefined; }, isEmptyVal: function (val) { if (val === undefined) { return true; } if (val === null) { return true; } if (val === "") { return true; } }, getOriginalElementTitle: function (element) { var savedOriginalTitle = kv.utils.getAttribute(element, 'data-orig-title'), currentTitle = element.title, hasSavedOriginalTitle = kv.utils.hasAttribute(element, 'data-orig-title'); return hasSavedOriginalTitle ? savedOriginalTitle : currentTitle; }, async: function (expr) { if (window.setImmediate) { window.setImmediate(expr); } else { window.setTimeout(expr, 0); } }, forEach: function (object, callback) { if (kv.utils.isArray(object)) { return forEach(object, callback); } for (var prop in object) { if (object.hasOwnProperty(prop)) { callback(object[prop], prop); } } } }; }());;var api = (function () { var isInitialized = 0, configuration = kv.configuration, utils = kv.utils; function cleanUpSubscriptions(context) { forEach(context.subscriptions, function (subscription) { subscription.dispose(); }); context.subscriptions = []; } function dispose(context) { if (context.options.deep) { forEach(context.flagged, function (obj) { delete obj.__kv_traversed; }); context.flagged.length = 0; } if (!context.options.live) { cleanUpSubscriptions(context); } } function runTraversal(obj, context) { context.validatables = []; cleanUpSubscriptions(context); traverseGraph(obj, context); dispose(context); } function traverseGraph(obj, context, level) { var objValues = [], val = obj.peek ? obj.peek() : obj; if (obj.__kv_traversed === true) { return; } if (context.options.deep) { obj.__kv_traversed = true; context.flagged.push(obj); } //default level value depends on deep option. level = (level !== undefined ? level : context.options.deep ? 1 : -1); // if object is observable then add it to the list if (ko.isObservable(obj)) { // ensure it's validatable but don't extend validatedObservable because it // would overwrite isValid property. if (!obj.errors && !utils.isValidatable(obj)) { obj.extend({ validatable: true }); } context.validatables.push(obj); if (context.options.live && utils.isObservableArray(obj)) { context.subscriptions.push(obj.subscribe(function () { context.graphMonitor.valueHasMutated(); })); } } //get list of values either from array or object but ignore non-objects // and destroyed objects if (val && !val._destroy) { if (utils.isArray(val)) { objValues = val; } else if (utils.isObject(val)) { objValues = utils.values(val); } } //process recursively if it is deep grouping if (level !== 0) { utils.forEach(objValues, function (observable) { //but not falsy things and not HTML Elements if (observable && !observable.nodeType && (!ko.isComputed(observable) || observable.rules)) { traverseGraph(observable, context, level + 1); } }); } } function collectErrors(array) { var errors = []; forEach(array, function (observable) { // Do not collect validatedObservable errors if (utils.isValidatable(observable) && !observable.isValid()) { // Use peek because we don't want a dependency for 'error' property because it // changes before 'isValid' does. (Issue #99) errors.push(observable.error.peek()); } }); return errors; } return { //Call this on startup //any config can be overridden with the passed in options init: function (options, force) { //done run this multiple times if we don't really want to if (isInitialized > 0 && !force) { return; } //because we will be accessing options properties it has to be an object at least options = options || {}; //if specific error classes are not provided then apply generic errorClass //it has to be done on option so that options.errorClass can override default //errorElementClass and errorMessage class but not those provided in options options.errorElementClass = options.errorElementClass || options.errorClass || configuration.errorElementClass; options.errorMessageClass = options.errorMessageClass || options.errorClass || configuration.errorMessageClass; extend(configuration, options); if (configuration.registerExtenders) { kv.registerExtenders(); } isInitialized = 1; }, // resets the config back to its original state reset: kv.configuration.reset, // recursively walks a viewModel and creates an object that // provides validation information for the entire viewModel // obj -> the viewModel to walk // options -> { // deep: false, // if true, will walk past the first level of viewModel properties // observable: false // if true, returns a computed observable indicating if the viewModel is valid // } group: function group(obj, options) { // array of observables or viewModel options = extend(extend({}, configuration.grouping), options); var context = { options: options, graphMonitor: ko.observable(), flagged: [], subscriptions: [], validatables: [] }; var result = null; //if using observables then traverse structure once and add observables if (options.observable) { result = ko.computed(function () { context.graphMonitor(); //register dependency runTraversal(obj, context); return collectErrors(context.validatables); }); } else { //if not using observables then every call to error() should traverse the structure result = function () { runTraversal(obj, context); return collectErrors(context.validatables); }; } result.showAllMessages = function (show) { // thanks @heliosPortal if (show === undefined) {//default to true show = true; } result.forEach(function (observable) { if (utils.isValidatable(observable)) { observable.isModified(show); } }); }; result.isAnyMessageShown = function () { var invalidAndModifiedPresent; invalidAndModifiedPresent = !!result.find(function (observable) { return utils.isValidatable(observable) && !observable.isValid() && observable.isModified(); }); return invalidAndModifiedPresent; }; result.filter = function(predicate) { predicate = predicate || function () { return true; }; // ensure we have latest changes result(); return koUtils.arrayFilter(context.validatables, predicate); }; result.find = function(predicate) { predicate = predicate || function () { return true; }; // ensure we have latest changes result(); return koUtils.arrayFirst(context.validatables, predicate); }; result.forEach = function(callback) { callback = callback || function () { }; // ensure we have latest changes result(); forEach(context.validatables, callback); }; result.map = function(mapping) { mapping = mapping || function (item) { return item; }; // ensure we have latest changes result(); return koUtils.arrayMap(context.validatables, mapping); }; /** * @private You should not rely on this method being here. * It's a private method and it may change in the future. * * @description Updates the validated object and collects errors from it. */ result._updateState = function(newValue) { if (!utils.isObject(newValue)) { throw new Error('An object is required.'); } obj = newValue; if (options.observable) { context.graphMonitor.valueHasMutated(); } else { runTraversal(newValue, context); return collectErrors(context.validatables); } }; return result; }, formatMessage: function (message, params, observable) { if (utils.isObject(params) && params.typeAttr) { params = params.value; } if (typeof message === 'function') { return message(params, observable); } var replacements = unwrap(params); if (replacements == null) { replacements = []; } if (!utils.isArray(replacements)) { replacements = [replacements]; } return message.replace(/{(\d+)}/gi, function(match, index) { if (typeof replacements[index] !== 'undefined') { return replacements[index]; } return match; }); }, // addRule: // This takes in a ko.observable and a Rule Context - which is just a rule name and params to supply to the validator // ie: kv.addRule(myObservable, { // rule: 'required', // params: true // }); // addRule: function (observable, rule) { observable.extend({ validatable: true }); var hasRule = !!koUtils.arrayFirst(observable.rules(), function(item) { return item.rule && item.rule === rule.rule; }); if (!hasRule) { //push a Rule Context to the observables local array of Rule Contexts observable.rules.push(rule); } return observable; }, // addAnonymousRule: // Anonymous Rules essentially have all the properties of a Rule, but are only specific for a certain property // and developers typically are wanting to add them on the fly or not register a rule with the 'kv.rules' object // // Example: // var test = ko.observable('something').extend{( // validation: { // validator: function(val, someOtherVal){ // return true; // }, // message: "Something must be really wrong!', // params: true // } // )}; addAnonymousRule: function (observable, ruleObj) { if (ruleObj['message'] === undefined) { ruleObj['message'] = 'Error'; } //make sure onlyIf is honoured