From aeaf708f1bf80c06f264f8bdafaa4dc63506ca54 Mon Sep 17 00:00:00 2001 From: Steve Greatrex Date: Mon, 4 Nov 2013 17:15:07 +0000 Subject: [PATCH 1/3] Updated to ko 3.0 and updated package.json for npm publishing --- .gitignore | 3 +- Dist/knockout.validation.js | 451 +- Dist/knockout.validation.min.js | 2 +- Lib/knockout-latest.debug.js | 7894 +++++++++++++++++-------------- Localization/ko-KR.js | 78 +- package.json | 3 +- 6 files changed, 4553 insertions(+), 3878 deletions(-) diff --git a/.gitignore b/.gitignore index 21f654f8..d3bcf7c8 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,5 @@ target *.ncrunchsolution node_modules -*.webinfo \ No newline at end of file +*.webinfo +*.log \ No newline at end of file diff --git a/Dist/knockout.validation.js b/Dist/knockout.validation.js index b45fdb8f..93916929 100644 --- a/Dist/knockout.validation.js +++ b/Dist/knockout.validation.js @@ -25,7 +25,14 @@ if (typeof (ko) === undefined) { throw 'Knockout is required, please ensure it is loaded before loading this validation plug-in'; } // create our namespace object - ko.validation = exports;;/*global ko: false*/ + ko.validation = exports; + + var kv = ko.validation; + var koUtils = ko.utils; + var unwrap = koUtils.unwrapObservable; + var forEach = koUtils.arrayForEach; + var extend = koUtils.extend; +;/*global ko: false*/ var defaults = { registerExtenders: true, @@ -44,20 +51,24 @@ var defaults = { grouping: { deep: false, //by default grouping is shallow observable: true //and using observables + }, + validate: { + // throttle: 10 } }; // make a copy so we can use 'reset' later -var configuration = ko.utils.extend({}, defaults); +var configuration = extend({}, defaults); configuration.html5Attributes = ['required', 'pattern', 'min', 'max', 'step']; configuration.html5InputTypes = ['email', 'number', 'date']; configuration.reset = function () { - ko.utils.extend(configuration, defaults); + extend(configuration, defaults); }; -ko.validation.configuration = configuration;;ko.validation.utils = (function () { +kv.configuration = configuration; +;kv.utils = (function () { var seedId = new Date().getTime(); var domData = {}; //hash of data objects that we reference from dom elements @@ -101,15 +112,15 @@ ko.validation.configuration = configuration;;ko.validation.utils = (function () return seedId += 1; }, getConfigOptions: function (element) { - var options = ko.validation.utils.contextFor(element); + var options = kv.utils.contextFor(element); - return options || ko.validation.configuration; + return options || kv.configuration; }, setDomData: function (node, data) { var key = node[domDataKey]; if (!key) { - node[domDataKey] = key = ko.validation.utils.newId(); + node[domDataKey] = key = kv.utils.newId(); } domData[key] = data; @@ -127,9 +138,9 @@ ko.validation.configuration = configuration;;ko.validation.utils = (function () switch (node.nodeType) { case 1: case 8: - var context = ko.validation.utils.getDomData(node); + var context = kv.utils.getDomData(node); if (context) { return context; } - if (node.parentNode) { return ko.validation.utils.contextFor(node.parentNode); } + if (node.parentNode) { return kv.utils.contextFor(node.parentNode); } break; } return undefined; @@ -146,9 +157,9 @@ ko.validation.configuration = configuration;;ko.validation.utils = (function () } }, getOriginalElementTitle: function (element) { - var savedOriginalTitle = ko.validation.utils.getAttribute(element, 'data-orig-title'), + var savedOriginalTitle = kv.utils.getAttribute(element, 'data-orig-title'), currentTitle = element.title, - hasSavedOriginalTitle = ko.validation.utils.hasAttribute(element, 'data-orig-title'); + hasSavedOriginalTitle = kv.utils.hasAttribute(element, 'data-orig-title'); return hasSavedOriginalTitle ? savedOriginalTitle : currentTitle; @@ -161,8 +172,8 @@ ko.validation.configuration = configuration;;ko.validation.utils = (function () }());;var api = (function () { var isInitialized = 0, - configuration = ko.validation.configuration, - utils = ko.validation.utils; + configuration = kv.configuration, + utils = kv.utils; return { //Call this on startup @@ -181,19 +192,19 @@ ko.validation.configuration = configuration;;ko.validation.utils = (function () options.errorElementClass = options.errorElementClass || options.errorClass || configuration.errorElementClass; options.errorMessageClass = options.errorMessageClass || options.errorClass || configuration.errorMessageClass; - ko.utils.extend(configuration, options); + extend(configuration, options); if (configuration.registerExtenders) { - ko.validation.registerExtenders(); + kv.registerExtenders(); } isInitialized = 1; }, // backwards compatability - configure: function (options) { ko.validation.init(options); }, + configure: function (options) { kv.init(options); }, // resets the config back to its original state - reset: ko.validation.configuration.reset, + reset: kv.configuration.reset, // recursivly walks a viewModel and creates an object that // provides validation information for the entire viewModel @@ -203,7 +214,7 @@ ko.validation.configuration = configuration;;ko.validation.utils = (function () // 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 = ko.utils.extend(ko.utils.extend({}, configuration.grouping), options); + options = extend(extend({}, configuration.grouping), options); var validatables = ko.observableArray([]), result = null, @@ -211,7 +222,7 @@ ko.validation.configuration = configuration;;ko.validation.utils = (function () dispose = function () { if (options.deep) { - ko.utils.arrayForEach(flagged, function (obj) { + forEach(flagged, function (obj) { delete obj.__kv_traversed; }); } @@ -221,7 +232,7 @@ ko.validation.configuration = configuration;;ko.validation.utils = (function () //if !options.deep then it will stop on top level traverse = function traverse(obj, level) { var objValues = [], - val = ko.utils.unwrapObservable(obj); + val = unwrap(obj); if (obj.__kv_traversed === true) { return; } @@ -252,7 +263,7 @@ ko.validation.configuration = configuration;;ko.validation.utils = (function () //process recurisvely if it is deep grouping if (level !== 0) { - ko.utils.arrayForEach(objValues, function (observable) { + forEach(objValues, function (observable) { //but not falsy things and not HTML Elements if (observable && !observable.nodeType) { traverse(observable, level + 1); } @@ -268,7 +279,7 @@ ko.validation.configuration = configuration;;ko.validation.utils = (function () result = ko.computed(function () { var errors = []; - ko.utils.arrayForEach(validatables(), function (observable) { + forEach(validatables(), function (observable) { if (!observable.isValid()) { errors.push(observable.error); } @@ -283,7 +294,7 @@ ko.validation.configuration = configuration;;ko.validation.utils = (function () traverse(obj); // and traverse tree again dispose(); - ko.utils.arrayForEach(validatables(), function (observable) { + forEach(validatables(), function (observable) { if (!observable.isValid()) { errors.push(observable.error); } @@ -300,7 +311,7 @@ ko.validation.configuration = configuration;;ko.validation.utils = (function () // ensure we have latest changes result(); - ko.utils.arrayForEach(validatables(), function (observable) { + forEach(validatables(), function (observable) { observable.isModified(show); }); }; @@ -315,7 +326,7 @@ ko.validation.configuration = configuration;;ko.validation.utils = (function () // ensure we have latest changes result(); - ko.utils.arrayForEach(validatables(), function (observable) { + forEach(validatables(), function (observable) { if (!observable.isValid() && observable.isModified()) { invalidAndModifiedPresent = true; } @@ -330,12 +341,12 @@ ko.validation.configuration = configuration;;ko.validation.utils = (function () if (typeof (message) === 'function') { return message(params); } - return message.replace(/\{0\}/gi, ko.utils.unwrapObservable(params)); + return message.replace(/\{0\}/gi, unwrap(params)); }, // 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: ko.validation.addRule(myObservable, { + // ie: kv.addRule(myObservable, { // rule: 'required', // params: true // }); @@ -350,7 +361,7 @@ ko.validation.configuration = configuration;;ko.validation.utils = (function () // 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 'ko.validation.rules' object + // 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{( @@ -370,10 +381,10 @@ ko.validation.configuration = configuration;;ko.validation.utils = (function () } //Create an anonymous rule to reference - ko.validation.rules[ruleName] = ruleObj; + kv.rules[ruleName] = ruleObj; //add the anonymous rule to the observable - ko.validation.addRule(observable, { + kv.addRule(observable, { rule: ruleName, params: ruleObj.params }); @@ -398,14 +409,14 @@ ko.validation.configuration = configuration;;ko.validation.utils = (function () // )}; // if (params.message || params.onlyIf) { //if it has a message or condition object, then its an object literal to use - return ko.validation.addRule(observable, { + return kv.addRule(observable, { rule: ruleName, message: params.message, params: utils.isEmptyVal(params.params) ? true : params.params, condition: params.onlyIf }); } else { - return ko.validation.addRule(observable, { + return kv.addRule(observable, { rule: ruleName, params: params }); @@ -413,14 +424,14 @@ ko.validation.configuration = configuration;;ko.validation.utils = (function () }; }, - // loops through all ko.validation.rules and adds them as extenders to + // loops through all kv.rules and adds them as extenders to // ko.extenders registerExtenders: function () { // root extenders optional, use 'validation' extender if would cause conflicts if (configuration.registerExtenders) { - for (var ruleName in ko.validation.rules) { - if (ko.validation.rules.hasOwnProperty(ruleName)) { + for (var ruleName in kv.rules) { + if (kv.rules.hasOwnProperty(ruleName)) { if (!ko.extenders[ruleName]) { - ko.validation.addExtender(ruleName); + kv.addExtender(ruleName); } } } @@ -438,19 +449,37 @@ ko.validation.configuration = configuration;;ko.validation.utils = (function () // if html-5 validation attributes have been specified, this parses // the attributes on @element parseInputValidationAttributes: function (element, valueAccessor) { - ko.utils.arrayForEach(ko.validation.configuration.html5Attributes, function (attr) { + forEach(kv.configuration.html5Attributes, function (attr) { if (utils.hasAttribute(element, attr)) { - ko.validation.addRule(valueAccessor(), { + + var params = element.getAttribute(attr) || true; + + if (attr === 'min' || attr === 'max') + { + // If we're validating based on the min and max attributes, we'll + // need to know what the 'type' attribute is set to + var typeAttr = element.getAttribute('type'); + if (typeof typeAttr === "undefined" || !typeAttr) + { + // From http://www.w3.org/TR/html-markup/input: + // An input element with no type attribute specified represents the + // same thing as an input element with its type attribute set to "text". + typeAttr = "text"; + } + params = {typeAttr: typeAttr, value: params}; + } + + kv.addRule(valueAccessor(), { rule: attr, - params: element.getAttribute(attr) || true + params: params }); } }); var currentType = element.getAttribute('type'); - ko.utils.arrayForEach(ko.validation.configuration.html5InputTypes, function (type) { + forEach(kv.configuration.html5InputTypes, function (type) { if (type === currentType) { - ko.validation.addRule(valueAccessor(), { + kv.addRule(valueAccessor(), { rule: (type === 'date') ? 'dateISO' : type, params: true }); @@ -469,9 +498,9 @@ ko.validation.configuration = configuration;;ko.validation.utils = (function () var contexts = observable.rules(); // observable array // loop through the attributes and add the information needed - ko.utils.arrayForEach(ko.validation.configuration.html5Attributes, function (attr) { + forEach(kv.configuration.html5Attributes, function (attr) { var params; - var ctx = ko.utils.arrayFirst(contexts, function (ctx) { + var ctx = koUtils.arrayFirst(contexts, function (ctx) { return ctx.rule.toLowerCase() === attr.toLowerCase(); }); @@ -511,12 +540,12 @@ ko.validation.configuration = configuration;;ko.validation.utils = (function () }()); // expose api publicly -ko.utils.extend(ko.validation, api);;//Validation Rules: +extend(ko.validation, api);;//Validation Rules: // You can view and override messages or rules via: -// ko.validation.rules[ruleName] +// kv.rules[ruleName] // // To implement a custom Rule, simply use this template: -// ko.validation.rules[''] = { +// kv.rules[''] = { // validator: function (val, param) { // // return ; @@ -525,15 +554,15 @@ ko.utils.extend(ko.validation, api);;//Validation Rules: // }; // // Example: -// ko.validation.rules['mustEqual'] = { +// kv.rules['mustEqual'] = { // validator: function( val, mustEqualVal ){ // return val === mustEqualVal; // }, // message: 'This field must equal {0}' // }; // -ko.validation.rules = {}; -ko.validation.rules['required'] = { +kv.rules = {}; +kv.rules['required'] = { validator: function (val, required) { var stringTrimRegEx = /^\s+|\s+$/g, testVal; @@ -556,60 +585,150 @@ ko.validation.rules['required'] = { message: 'This field is required.' }; -ko.validation.rules['min'] = { - validator: function (val, min) { - return ko.validation.utils.isEmptyVal(val) || val >= min; - }, +function minMaxValidatorFactory(validatorName) { + var isMaxValidation = validatorName === "max"; + + return function (val, options) { + if (kv.utils.isEmptyVal(val)) { + return true; + } + + var comparisonValue, type; + if (options.typeAttr === undefined) { + // This validator is being called from javascript rather than + // being bound from markup + type = "text"; + comparisonValue = options; + } else { + type = options.typeAttr; + comparisonValue = options.value; + } + + // From http://www.w3.org/TR/2012/WD-html5-20121025/common-input-element-attributes.html#attr-input-min, + // if the value is parseable to a number, then the minimum should be numeric + if (!isNaN(comparisonValue)) { + type = "number"; + } + + var regex, valMatches, comparisonValueMatches; + switch (type.toLowerCase()) { + case "week": + regex = /^(\d{4})-W(\d{2})$/; + valMatches = val.match(regex); + if (valMatches === null) { + throw "Invalid value for " + validatorName + " attribute for week input. Should look like " + + "'2000-W33' http://www.w3.org/TR/html-markup/input.week.html#input.week.attrs.min"; + } + comparisonValueMatches = comparisonValue.match(regex); + // If no regex matches were found, validation fails + if (!comparisonValueMatches) { + return false; + } + + if (isMaxValidation) { + return (valMatches[1] < comparisonValueMatches[1]) || // older year + // same year, older week + ((valMatches[1] === comparisonValueMatches[1]) && (valMatches[2] <= comparisonValueMatches[2])); + } else { + return (valMatches[1] > comparisonValueMatches[1]) || // newer year + // same year, newer week + ((valMatches[1] === comparisonValueMatches[1]) && (valMatches[2] >= comparisonValueMatches[2])); + } + break; + + case "month": + regex = /^(\d{4})-(\d{2})$/; + valMatches = val.match(regex); + if (valMatches === null) { + throw "Invalid value for " + validatorName + " attribute for month input. Should look like " + + "'2000-03' http://www.w3.org/TR/html-markup/input.month.html#input.month.attrs.min"; + } + comparisonValueMatches = comparisonValue.match(regex); + // If no regex matches were found, validation fails + if (!comparisonValueMatches) { + return false; + } + + if (isMaxValidation) { + return ((valMatches[1] < comparisonValueMatches[1]) || // older year + // same year, older month + ((valMatches[1] === comparisonValueMatches[1]) && (valMatches[2] <= comparisonValueMatches[2]))); + } else { + return (valMatches[1] > comparisonValueMatches[1]) || // newer year + // same year, newer month + ((valMatches[1] === comparisonValueMatches[1]) && (valMatches[2] >= comparisonValueMatches[2])); + } + break; + + case "number": + case "range": + if (isMaxValidation) { + return (!isNaN(val) && parseFloat(val) <= parseFloat(comparisonValue)); + } else { + return (!isNaN(val) && parseFloat(val) >= parseFloat(comparisonValue)); + } + break; + + default: + if (isMaxValidation) { + return val <= comparisonValue; + } else { + return val >= comparisonValue; + } + } + }; +} + +kv.rules['min'] = { + validator: minMaxValidatorFactory("min"), message: 'Please enter a value greater than or equal to {0}.' }; -ko.validation.rules['max'] = { - validator: function (val, max) { - return ko.validation.utils.isEmptyVal(val) || val <= max; - }, +kv.rules['max'] = { + validator: minMaxValidatorFactory("max"), message: 'Please enter a value less than or equal to {0}.' }; - -ko.validation.rules['minLength'] = { + +kv.rules['minLength'] = { validator: function (val, minLength) { - return ko.validation.utils.isEmptyVal(val) || val.length >= minLength; + return kv.utils.isEmptyVal(val) || val.length >= minLength; }, message: 'Please enter at least {0} characters.' }; -ko.validation.rules['maxLength'] = { +kv.rules['maxLength'] = { validator: function (val, maxLength) { - return ko.validation.utils.isEmptyVal(val) || val.length <= maxLength; + return kv.utils.isEmptyVal(val) || val.length <= maxLength; }, message: 'Please enter no more than {0} characters.' }; -ko.validation.rules['pattern'] = { +kv.rules['pattern'] = { validator: function (val, regex) { - return ko.validation.utils.isEmptyVal(val) || val.toString().match(regex) !== null; + return kv.utils.isEmptyVal(val) || val.toString().match(regex) !== null; }, message: 'Please check this value.' }; -ko.validation.rules['step'] = { +kv.rules['step'] = { validator: function (val, step) { // in order to handle steps of .1 & .01 etc.. Modulus won't work // if the value is a decimal, so we have to correct for that - if (ko.validation.utils.isEmptyVal(val) || step === 'any') { return true; } + if (kv.utils.isEmptyVal(val) || step === 'any') { return true; } var dif = (val * 100) % (step * 100); return Math.abs(dif) < 0.00001 || Math.abs(1 - dif) < 0.00001; }, message: 'The value must increment by {0}' }; -ko.validation.rules['email'] = { +kv.rules['email'] = { validator: function (val, validate) { if (!validate) { return true; } //I think an empty email address is also a valid entry //if one want's to enforce entry it should be done with 'required: true' - return ko.validation.utils.isEmptyVal(val) || ( + return kv.utils.isEmptyVal(val) || ( // jquery validate regex - thanks Scott Gonzalez validate && /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i.test(val) ); @@ -617,42 +736,42 @@ ko.validation.rules['email'] = { message: 'Please enter a proper email address' }; -ko.validation.rules['date'] = { +kv.rules['date'] = { validator: function (value, validate) { if (!validate) { return true; } - return ko.validation.utils.isEmptyVal(value) || (validate && !/Invalid|NaN/.test(new Date(value))); + return kv.utils.isEmptyVal(value) || (validate && !/Invalid|NaN/.test(new Date(value))); }, message: 'Please enter a proper date' }; -ko.validation.rules['dateISO'] = { +kv.rules['dateISO'] = { validator: function (value, validate) { if (!validate) { return true; } - return ko.validation.utils.isEmptyVal(value) || (validate && /^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}$/.test(value)); + return kv.utils.isEmptyVal(value) || (validate && /^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}$/.test(value)); }, message: 'Please enter a proper date' }; -ko.validation.rules['number'] = { +kv.rules['number'] = { validator: function (value, validate) { if (!validate) { return true; } - return ko.validation.utils.isEmptyVal(value) || (validate && /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value)); + return kv.utils.isEmptyVal(value) || (validate && /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value)); }, message: 'Please enter a number' }; -ko.validation.rules['digit'] = { +kv.rules['digit'] = { validator: function (value, validate) { if (!validate) { return true; } - return ko.validation.utils.isEmptyVal(value) || (validate && /^\d+$/.test(value)); + return kv.utils.isEmptyVal(value) || (validate && /^\d+$/.test(value)); }, message: 'Please enter a digit' }; -ko.validation.rules['phoneUS'] = { +kv.rules['phoneUS'] = { validator: function (phoneNumber, validate) { if (!validate) { return true; } - if (ko.validation.utils.isEmptyVal(phoneNumber)) { return true; } // makes it optional, use 'required' rule if it should be required + if (kv.utils.isEmptyVal(phoneNumber)) { return true; } // makes it optional, use 'required' rule if it should be required if (typeof (phoneNumber) !== 'string') { return false; } phoneNumber = phoneNumber.replace(/\s+/g, ""); return validate && phoneNumber.length > 9 && phoneNumber.match(/^(1-?)?(\([2-9]\d{2}\)|[2-9]\d{2})-?[2-9]\d{2}-?\d{4}$/); @@ -660,18 +779,18 @@ ko.validation.rules['phoneUS'] = { message: 'Please specify a valid phone number' }; -ko.validation.rules['equal'] = { +kv.rules['equal'] = { validator: function (val, params) { var otherValue = params; - return val === ko.validation.utils.getValue(otherValue); + return val === kv.utils.getValue(otherValue); }, message: 'Values must equal' }; -ko.validation.rules['notEqual'] = { +kv.rules['notEqual'] = { validator: function (val, params) { var otherValue = params; - return val !== ko.validation.utils.getValue(otherValue); + return val !== kv.utils.getValue(otherValue); }, message: 'Please choose another value.' }; @@ -683,15 +802,15 @@ ko.validation.rules['notEqual'] = { // valueAccessor: function that returns value from an object stored in collection // if it is null the value is compared directly // external: set to true when object you are validating is automatically updating collection -ko.validation.rules['unique'] = { +kv.rules['unique'] = { validator: function (val, options) { - var c = ko.validation.utils.getValue(options.collection), - external = ko.validation.utils.getValue(options.externalValue), + var c = kv.utils.getValue(options.collection), + external = kv.utils.getValue(options.externalValue), counter = 0; if (!val || !c) { return true; } - ko.utils.arrayFilter(ko.utils.unwrapObservable(c), function (item) { + koUtils.arrayFilter(unwrap(c), function (item) { if (val === (options.valueAccessor ? options.valueAccessor(item) : item)) { counter++; } }); // if value is external even 1 same value in collection means the value is not unique @@ -703,7 +822,7 @@ ko.validation.rules['unique'] = { //now register all of these! (function () { - ko.validation.registerExtenders(); + kv.registerExtenders(); }()); ;// The core binding handler // this allows us to setup any value binding that internally always @@ -712,36 +831,37 @@ ko.bindingHandlers['validationCore'] = (function () { return { init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { - var config = ko.validation.utils.getConfigOptions(element); + var config = kv.utils.getConfigOptions(element); + var observable = valueAccessor(); // parse html5 input validation attributes, optional feature if (config.parseInputAttributes) { - ko.validation.utils.async(function () { ko.validation.parseInputValidationAttributes(element, valueAccessor); }); + kv.utils.async(function () { kv.parseInputValidationAttributes(element, valueAccessor); }); } // if requested insert message element and apply bindings - if (config.insertMessages && ko.validation.utils.isValidatable(valueAccessor())) { + if (config.insertMessages && kv.utils.isValidatable(observable)) { // insert the - var validationMessageElement = ko.validation.insertValidationMessage(element); + var validationMessageElement = kv.insertValidationMessage(element); // if we're told to use a template, make sure that gets rendered if (config.messageTemplate) { - ko.renderTemplate(config.messageTemplate, { field: valueAccessor() }, null, validationMessageElement, 'replaceNode'); + ko.renderTemplate(config.messageTemplate, { field: observable }, null, validationMessageElement, 'replaceNode'); } else { - ko.applyBindingsToNode(validationMessageElement, { validationMessage: valueAccessor() }); + ko.applyBindingsToNode(validationMessageElement, { validationMessage: observable }); } } // write the html5 attributes if indicated by the config - if (config.writeInputAttributes && ko.validation.utils.isValidatable(valueAccessor())) { + if (config.writeInputAttributes && kv.utils.isValidatable(observable)) { - ko.validation.writeInputValidationAttributes(element, valueAccessor); + kv.writeInputValidationAttributes(element, valueAccessor); } // if requested, add binding to decorate element - if (config.decorateElement && ko.validation.utils.isValidatable(valueAccessor())) { - ko.applyBindingsToNode(element, { validationElement: valueAccessor() }); + if (config.decorateElement && kv.utils.isValidatable(observable)) { + ko.applyBindingsToNode(element, { validationElement: observable }); } }, @@ -753,15 +873,15 @@ ko.bindingHandlers['validationCore'] = (function () { }()); // override for KO's default 'value' and 'checked' bindings -ko.validation.makeBindingHandlerValidatable("value"); -ko.validation.makeBindingHandlerValidatable("checked"); +kv.makeBindingHandlerValidatable("value"); +kv.makeBindingHandlerValidatable("checked"); ko.bindingHandlers['validationMessage'] = { // individual error message, if modified or post binding update: function (element, valueAccessor) { var obsv = valueAccessor(), - config = ko.validation.utils.getConfigOptions(element), - val = ko.utils.unwrapObservable(obsv), + config = kv.utils.getConfigOptions(element), + val = unwrap(obsv), msg = null, isModified = false, isValid = false; @@ -793,8 +913,8 @@ ko.bindingHandlers['validationMessage'] = { // individual error message, if modi ko.bindingHandlers['validationElement'] = { update: function (element, valueAccessor) { var obsv = valueAccessor(), - config = ko.validation.utils.getConfigOptions(element), - val = ko.utils.unwrapObservable(obsv), + config = kv.utils.getConfigOptions(element), + val = unwrap(obsv), msg = null, isModified = false, isValid = false; @@ -823,20 +943,17 @@ ko.bindingHandlers['validationElement'] = { ko.bindingHandlers.css.update(element, cssSettingsAccessor); if (!config.errorsAsTitle) { return; } - var origTitle = ko.validation.utils.getAttribute(element, 'data-orig-title'), - elementTitle = element.title, - titleIsErrorMsg = ko.validation.utils.getAttribute(element, 'data-orig-title') === "true"; + ko.bindingHandlers.attr.update(element, function () { + var + hasModification = !config.errorsAsTitleOnModified || isModified, + title = kv.utils.getOriginalElementTitle(element); - var errorMsgTitleAccessor = function () { - if (!config.errorsAsTitleOnModified || isModified) { - if (!isValid) { - return { title: obsv.error, 'data-orig-title': ko.validation.utils.getOriginalElementTitle(element) }; - } else { - return { title: ko.validation.utils.getOriginalElementTitle(element), 'data-orig-title': null }; - } + if (hasModification && !isValid) { + return { title: obsv.error, 'data-orig-title': title }; + } else if (!hasModification || isValid) { + return { title: title, 'data-orig-title': null }; } - }; - ko.bindingHandlers.attr.update(element, errorMsgTitleAccessor); + }); } }; @@ -851,13 +968,13 @@ ko.bindingHandlers['validationElement'] = { ko.bindingHandlers['validationOptions'] = (function () { return { init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { - var options = ko.utils.unwrapObservable(valueAccessor()); + var options = unwrap(valueAccessor()); if (options) { - var newConfig = ko.utils.extend({}, ko.validation.configuration); - ko.utils.extend(newConfig, options); + var newConfig = extend({}, kv.configuration); + extend(newConfig, options); //store the validation options on the node so we can retrieve it later - ko.validation.utils.setDomData(element, newConfig); + kv.utils.setDomData(element, newConfig); } } }; @@ -875,11 +992,11 @@ ko.bindingHandlers['validationOptions'] = (function () { // } // )}; ko.extenders['validation'] = function (observable, rules) { // allow single rule or array - ko.utils.arrayForEach(ko.validation.utils.isArray(rules) ? rules : [rules], function (rule) { + forEach(kv.utils.isArray(rules) ? rules : [rules], function (rule) { // the 'rule' being passed in here has no name to identify a core Rule, // so we add it as an anonymous rule // If the developer is wanting to use a core Rule, but use a different message see the 'addExtender' logic for examples - ko.validation.addAnonymousRule(observable, rule); + kv.addAnonymousRule(observable, rule); }); return observable; }; @@ -891,8 +1008,20 @@ ko.extenders['validation'] = function (observable, rules) { // allow single rule // // 2. test.extend({validatable: false}); // this will remove the validation properties from the Observable object should you need to do that. -ko.extenders['validatable'] = function (observable, enable) { - if (enable && !ko.validation.utils.isValidatable(observable)) { +ko.extenders['validatable'] = function (observable, options) { + if (!kv.utils.isObject(options)) { + options = { enable: options }; + } + + if (!('enable' in options)) { + options.enable = true; + } + + if (options.enable && !kv.utils.isValidatable(observable)) { + var config = kv.configuration.validate || {}; + var validationOptions = { + throttleEvaluation : options.throttle || config.throttle + }; observable.error = ko.observable(null); // holds the error message, we only need one since we stop processing validators when one is invalid @@ -910,21 +1039,8 @@ ko.extenders['validatable'] = function (observable, enable) { observable.isModified = ko.observable(false); - // we use a computed here to ensure that anytime a dependency changes, the - // validation logic evaluates - var h_obsValidationTrigger = ko.computed(function () { - var obs = observable(), - ruleContexts = observable.rules(); - - ko.validation.validateObservable(observable); - - return true; - }); - // a semi-protected observable - observable.isValid = ko.computed(function () { - return observable.__valid__(); - }); + observable.isValid = ko.computed(observable.__valid__); //manually set error state observable.setError = function (error) { @@ -943,6 +1059,21 @@ ko.extenders['validatable'] = function (observable, enable) { observable.isModified(true); }); + // we use a computed here to ensure that anytime a dependency changes, the + // validation logic evaluates + var h_obsValidationTrigger = ko.computed(extend({ + read: function () { + var obs = observable(), + ruleContexts = observable.rules(); + + kv.validateObservable(observable); + + return true; + } + }, validationOptions)); + + extend(h_obsValidationTrigger, validationOptions); + observable._disposeValidation = function () { //first dispose of the subscriptions observable.isValid.dispose(); @@ -960,11 +1091,8 @@ ko.extenders['validatable'] = function (observable, enable) { delete observable['__valid__']; delete observable['isModified']; }; - } else if (enable === false && ko.validation.utils.isValidatable(observable)) { - - if (observable._disposeValidation) { - observable._disposeValidation(); - } + } else if (options.enable === false && observable._disposeValidation) { + observable._disposeValidation(); } return observable; }; @@ -974,8 +1102,7 @@ function validateSync(observable, rule, ctx) { if (!rule.validator(observable(), ctx.params === undefined ? true : ctx.params)) { // default param is true, eg. required = true //not valid, so format the error message and stick it in the 'error' variable - observable.error(ko.validation.formatMessage(ctx.message || rule.message, ctx.params)); - observable.__valid__(false); + observable.setError(kv.formatMessage(ctx.message || rule.message, ctx.params)); return false; } else { return true; @@ -1007,7 +1134,7 @@ function validateAsync(observable, rule, ctx) { if (!isValid) { //not valid, so format the error message and stick it in the 'error' variable - observable.error(ko.validation.formatMessage(msg || ctx.message || rule.message, ctx.params)); + observable.error(kv.formatMessage(msg || ctx.message || rule.message, ctx.params)); observable.__valid__(isValid); } @@ -1019,7 +1146,7 @@ function validateAsync(observable, rule, ctx) { rule.validator(observable(), ctx.params || true, callBack); } -ko.validation.validateObservable = function (observable) { +kv.validateObservable = function (observable) { var i = 0, rule, // the rule validator to execute ctx, // the current Rule Context for the loop @@ -1037,7 +1164,7 @@ ko.validation.validateObservable = function (observable) { } //get the core Rule to use for validation - rule = ko.validation.rules[ctx.rule]; + rule = kv.rules[ctx.rule]; if (rule['async'] || ctx['async']) { //run async validation @@ -1051,19 +1178,19 @@ ko.validation.validateObservable = function (observable) { } } //finally if we got this far, make the observable valid again! - observable.error(null); - observable.__valid__(true); + observable.clearError(); return true; -};; +}; +; //quick function to override rule messages -ko.validation.localize = function (msgTranslations) { +kv.localize = function (msgTranslations) { var msg, rule; //loop the properties in the object and assign the msg to the rule for (rule in msgTranslations) { - if (ko.validation.rules.hasOwnProperty(rule)) { - ko.validation.rules[rule].message = msgTranslations[rule]; + if (kv.rules.hasOwnProperty(rule)) { + kv.rules[rule].message = msgTranslations[rule]; } } };;ko.applyBindingsWithValidation = function (viewModel, rootNode, options) { @@ -1083,9 +1210,9 @@ ko.validation.localize = function (msgTranslations) { } } - ko.validation.init(); + kv.init(); - if (config) { ko.validation.utils.setDomData(node, config); } + if (config) { kv.utils.setDomData(node, config); } ko.applyBindings(viewModel, rootNode); }; @@ -1094,19 +1221,21 @@ ko.validation.localize = function (msgTranslations) { var origApplyBindings = ko.applyBindings; ko.applyBindings = function (viewModel, rootNode) { - ko.validation.init(); + kv.init(); origApplyBindings(viewModel, rootNode); }; ko.validatedObservable = function (initialValue) { - if (!ko.validation.utils.isObject(initialValue)) { return ko.observable(initialValue).extend({ validatable: true }); } + if (!kv.utils.isObject(initialValue)) { return ko.observable(initialValue).extend({ validatable: true }); } var obsv = ko.observable(initialValue); - obsv.errors = ko.validation.group(initialValue); - obsv.isValid = ko.computed(function () { - return obsv.errors().length === 0; + obsv.isValid = ko.observable(); + obsv.errors = kv.group(initialValue); + obsv.errors.subscribe(function (errors) { + obsv.isValid(errors.length === 0); }); return obsv; -};;})); \ No newline at end of file +}; +;})); \ No newline at end of file diff --git a/Dist/knockout.validation.min.js b/Dist/knockout.validation.min.js index 614e3730..5c1fa7d1 100644 --- a/Dist/knockout.validation.min.js +++ b/Dist/knockout.validation.min.js @@ -5,4 +5,4 @@ Description: Validation Library for KnockoutJS =============================================================================== */ -!function(a){"function"==typeof require&&"object"==typeof exports&&"object"==typeof module?a(require("knockout"),exports):"function"==typeof define&&define.amd?define(["knockout","exports"],a):a(ko,ko.validation={})}(function(a,b){function c(b,c,d){return c.validator(b(),void 0===d.params?!0:d.params)?!0:(b.error(a.validation.formatMessage(d.message||c.message,d.params)),b.__valid__(!1),!1)}function d(b,c,d){b.isValidating(!0);var e=function(e){var f=!1,g="";return b.__valid__()?(e.message?(f=e.isValid,g=e.message):f=e,f||(b.error(a.validation.formatMessage(g||d.message||c.message,d.params)),b.__valid__(f)),b.isValidating(!1),void 0):(b.isValidating(!1),void 0)};c.validator(b(),d.params||!0,e)}if(void 0===typeof a)throw"Knockout is required, please ensure it is loaded before loading this validation plug-in";a.validation=b;var e={registerExtenders:!0,messagesOnModified:!0,errorsAsTitle:!0,errorsAsTitleOnModified:!1,messageTemplate:null,insertMessages:!0,parseInputAttributes:!1,writeInputAttributes:!1,decorateElement:!1,decorateElementOnModified:!0,errorClass:null,errorElementClass:"validationElement",errorMessageClass:"validationMessage",grouping:{deep:!1,observable:!0}},f=a.utils.extend({},e);f.html5Attributes=["required","pattern","min","max","step"],f.html5InputTypes=["email","number","date"],f.reset=function(){a.utils.extend(f,e)},a.validation.configuration=f,a.validation.utils=function(){var b=(new Date).getTime(),c={},d="__ko_validation__";return{isArray:function(a){return a.isArray||"[object Array]"===Object.prototype.toString.call(a)},isObject:function(a){return null!==a&&"object"==typeof a},values:function(a){var b=[];for(var c in a)a.hasOwnProperty(c)&&b.push(a[c]);return b},getValue:function(a){return"function"==typeof a?a():a},hasAttribute:function(a,b){return null!==a.getAttribute(b)},getAttribute:function(a,b){return a.getAttribute(b)},setAttribute:function(a,b,c){return a.setAttribute(b,c)},isValidatable:function(a){return a&&a.rules&&a.isValid&&a.isModified},insertAfter:function(a,b){a.parentNode.insertBefore(b,a.nextSibling)},newId:function(){return b+=1},getConfigOptions:function(b){var c=a.validation.utils.contextFor(b);return c||a.validation.configuration},setDomData:function(b,e){var f=b[d];f||(b[d]=f=a.validation.utils.newId()),c[f]=e},getDomData:function(a){var b=a[d];return b?c[b]:void 0},contextFor:function(b){switch(b.nodeType){case 1:case 8:var c=a.validation.utils.getDomData(b);if(c)return c;if(b.parentNode)return a.validation.utils.contextFor(b.parentNode)}return void 0},isEmptyVal:function(a){return void 0===a?!0:null===a?!0:""===a?!0:void 0},getOriginalElementTitle:function(b){var c=a.validation.utils.getAttribute(b,"data-orig-title"),d=b.title,e=a.validation.utils.hasAttribute(b,"data-orig-title");return e?c:d},async:function(a){window.setImmediate?window.setImmediate(a):window.setTimeout(a,0)}}}();var g=function(){var b=0,c=a.validation.configuration,d=a.validation.utils;return{init:function(d,e){b>0&&!e||(d=d||{},d.errorElementClass=d.errorElementClass||d.errorClass||c.errorElementClass,d.errorMessageClass=d.errorMessageClass||d.errorClass||c.errorMessageClass,a.utils.extend(c,d),c.registerExtenders&&a.validation.registerExtenders(),b=1)},configure:function(b){a.validation.init(b)},reset:a.validation.configuration.reset,group:function(b,e){e=a.utils.extend(a.utils.extend({},c.grouping),e);var f=a.observableArray([]),g=null,h=[],i=function(){e.deep&&a.utils.arrayForEach(h,function(a){delete a.__kv_traversed})},j=function k(b,c){var g=[],i=a.utils.unwrapObservable(b);b.__kv_traversed!==!0&&(e.deep&&(b.__kv_traversed=!0,h.push(b)),c=void 0!==c?c:e.deep?1:-1,a.isObservable(b)&&(b.isValid||b.extend({validatable:!0}),f.push(b)),i&&(d.isArray(i)?g=i:d.isObject(i)&&(g=d.values(i))),0!==c&&a.utils.arrayForEach(g,function(a){a&&!a.nodeType&&k(a,c+1)}))};return e.observable?(j(b),i(),g=a.computed(function(){var b=[];return a.utils.arrayForEach(f(),function(a){a.isValid()||b.push(a.error)}),b})):g=function(){var c=[];return f([]),j(b),i(),a.utils.arrayForEach(f(),function(a){a.isValid()||c.push(a.error)}),c},g.showAllMessages=function(b){void 0===b&&(b=!0),g(),a.utils.arrayForEach(f(),function(a){a.isModified(b)})},b.errors=g,b.isValid=function(){return 0===b.errors().length},b.isAnyMessageShown=function(){var b=!1;return g(),a.utils.arrayForEach(f(),function(a){!a.isValid()&&a.isModified()&&(b=!0)}),b},g},formatMessage:function(b,c){return"function"==typeof b?b(c):b.replace(/\{0\}/gi,a.utils.unwrapObservable(c))},addRule:function(a,b){return a.extend({validatable:!0}),a.rules.push(b),a},addAnonymousRule:function(b,c){var e=d.newId();void 0===c.message&&(c.message="Error"),a.validation.rules[e]=c,a.validation.addRule(b,{rule:e,params:c.params})},addExtender:function(b){a.extenders[b]=function(c,e){return e.message||e.onlyIf?a.validation.addRule(c,{rule:b,message:e.message,params:d.isEmptyVal(e.params)?!0:e.params,condition:e.onlyIf}):a.validation.addRule(c,{rule:b,params:e})}},registerExtenders:function(){if(c.registerExtenders)for(var b in a.validation.rules)a.validation.rules.hasOwnProperty(b)&&(a.extenders[b]||a.validation.addExtender(b))},insertValidationMessage:function(a){var b=document.createElement("SPAN");return b.className=d.getConfigOptions(a).errorMessageClass,d.insertAfter(a,b),b},parseInputValidationAttributes:function(b,c){a.utils.arrayForEach(a.validation.configuration.html5Attributes,function(e){d.hasAttribute(b,e)&&a.validation.addRule(c(),{rule:e,params:b.getAttribute(e)||!0})});var e=b.getAttribute("type");a.utils.arrayForEach(a.validation.configuration.html5InputTypes,function(b){b===e&&a.validation.addRule(c(),{rule:"date"===b?"dateISO":b,params:!0})})},writeInputValidationAttributes:function(b,c){var d=c();if(d&&d.rules){var e=d.rules();a.utils.arrayForEach(a.validation.configuration.html5Attributes,function(c){var d,f=a.utils.arrayFirst(e,function(a){return a.rule.toLowerCase()===c.toLowerCase()});f&&(d=f.params,"pattern"===f.rule&&f.params instanceof RegExp&&(d=f.params.source),b.setAttribute(c,d))}),e=null}},makeBindingHandlerValidatable:function(b){var c=a.bindingHandlers[b].init;a.bindingHandlers[b].init=function(b,d,e,f,g){return c(b,d,e,f,g),a.bindingHandlers.validationCore.init(b,d,e,f,g)}}}}();a.utils.extend(a.validation,g),a.validation.rules={},a.validation.rules.required={validator:function(a,b){var c,d=/^\s+|\s+$/g;return void 0===a||null===a?!b:(c=a,"string"==typeof a&&(c=a.replace(d,"")),b?(c+"").length>0:!0)},message:"This field is required."},a.validation.rules.min={validator:function(b,c){return a.validation.utils.isEmptyVal(b)||b>=c},message:"Please enter a value greater than or equal to {0}."},a.validation.rules.max={validator:function(b,c){return a.validation.utils.isEmptyVal(b)||c>=b},message:"Please enter a value less than or equal to {0}."},a.validation.rules.minLength={validator:function(b,c){return a.validation.utils.isEmptyVal(b)||b.length>=c},message:"Please enter at least {0} characters."},a.validation.rules.maxLength={validator:function(b,c){return a.validation.utils.isEmptyVal(b)||b.length<=c},message:"Please enter no more than {0} characters."},a.validation.rules.pattern={validator:function(b,c){return a.validation.utils.isEmptyVal(b)||null!==b.toString().match(c)},message:"Please check this value."},a.validation.rules.step={validator:function(b,c){if(a.validation.utils.isEmptyVal(b)||"any"===c)return!0;var d=100*b%(100*c);return Math.abs(d)<1e-5||Math.abs(1-d)<1e-5},message:"The value must increment by {0}"},a.validation.rules.email={validator:function(b,c){return c?a.validation.utils.isEmptyVal(b)||c&&/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i.test(b):!0},message:"Please enter a proper email address"},a.validation.rules.date={validator:function(b,c){return c?a.validation.utils.isEmptyVal(b)||c&&!/Invalid|NaN/.test(new Date(b)):!0},message:"Please enter a proper date"},a.validation.rules.dateISO={validator:function(b,c){return c?a.validation.utils.isEmptyVal(b)||c&&/^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}$/.test(b):!0},message:"Please enter a proper date"},a.validation.rules.number={validator:function(b,c){return c?a.validation.utils.isEmptyVal(b)||c&&/^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(b):!0},message:"Please enter a number"},a.validation.rules.digit={validator:function(b,c){return c?a.validation.utils.isEmptyVal(b)||c&&/^\d+$/.test(b):!0},message:"Please enter a digit"},a.validation.rules.phoneUS={validator:function(b,c){return c?a.validation.utils.isEmptyVal(b)?!0:"string"!=typeof b?!1:(b=b.replace(/\s+/g,""),c&&b.length>9&&b.match(/^(1-?)?(\([2-9]\d{2}\)|[2-9]\d{2})-?[2-9]\d{2}-?\d{4}$/)):!0},message:"Please specify a valid phone number"},a.validation.rules.equal={validator:function(b,c){var d=c;return b===a.validation.utils.getValue(d)},message:"Values must equal"},a.validation.rules.notEqual={validator:function(b,c){var d=c;return b!==a.validation.utils.getValue(d)},message:"Please choose another value."},a.validation.rules.unique={validator:function(b,c){var d=a.validation.utils.getValue(c.collection),e=a.validation.utils.getValue(c.externalValue),f=0;return b&&d?(a.utils.arrayFilter(a.utils.unwrapObservable(d),function(a){b===(c.valueAccessor?c.valueAccessor(a):a)&&f++}),(void 0!==e&&b!==e?1:2)>f):!0},message:"Please make sure the value is unique."},function(){a.validation.registerExtenders()}(),a.bindingHandlers.validationCore=function(){return{init:function(b,c){var d=a.validation.utils.getConfigOptions(b);if(d.parseInputAttributes&&a.validation.utils.async(function(){a.validation.parseInputValidationAttributes(b,c)}),d.insertMessages&&a.validation.utils.isValidatable(c())){var e=a.validation.insertValidationMessage(b);d.messageTemplate?a.renderTemplate(d.messageTemplate,{field:c()},null,e,"replaceNode"):a.applyBindingsToNode(e,{validationMessage:c()})}d.writeInputAttributes&&a.validation.utils.isValidatable(c())&&a.validation.writeInputValidationAttributes(b,c),d.decorateElement&&a.validation.utils.isValidatable(c())&&a.applyBindingsToNode(b,{validationElement:c()})},update:function(){}}}(),a.validation.makeBindingHandlerValidatable("value"),a.validation.makeBindingHandlerValidatable("checked"),a.bindingHandlers.validationMessage={update:function(b,c){var d=c(),e=a.validation.utils.getConfigOptions(b),f=(a.utils.unwrapObservable(d),!1),g=!1;d.extend({validatable:!0}),f=d.isModified(),g=d.isValid();var h=function(){return!e.messagesOnModified||f?g?null:d.error:null},i=function(){return!e.messagesOnModified||f?!g:!1};a.bindingHandlers.text.update(b,h),a.bindingHandlers.visible.update(b,i)}},a.bindingHandlers.validationElement={update:function(b,c){var d=c(),e=a.validation.utils.getConfigOptions(b),f=(a.utils.unwrapObservable(d),!1),g=!1;d.extend({validatable:!0}),f=d.isModified(),g=d.isValid();var h=function(){var a={},b=!e.decorateElementOnModified||f?!g:!1;return e.decorateElement||(b=!1),a[e.errorElementClass]=b,a};if(a.bindingHandlers.css.update(b,h),e.errorsAsTitle){a.validation.utils.getAttribute(b,"data-orig-title"),b.title,"true"===a.validation.utils.getAttribute(b,"data-orig-title");var i=function(){return!e.errorsAsTitleOnModified||f?g?{title:a.validation.utils.getOriginalElementTitle(b),"data-orig-title":null}:{title:d.error,"data-orig-title":a.validation.utils.getOriginalElementTitle(b)}:void 0};a.bindingHandlers.attr.update(b,i)}}},a.bindingHandlers.validationOptions=function(){return{init:function(b,c){var d=a.utils.unwrapObservable(c());if(d){var e=a.utils.extend({},a.validation.configuration);a.utils.extend(e,d),a.validation.utils.setDomData(b,e)}}}}(),a.extenders.validation=function(b,c){return a.utils.arrayForEach(a.validation.utils.isArray(c)?c:[c],function(c){a.validation.addAnonymousRule(b,c)}),b},a.extenders.validatable=function(b,c){if(c&&!a.validation.utils.isValidatable(b)){b.error=a.observable(null),b.rules=a.observableArray(),b.isValidating=a.observable(!1),b.__valid__=a.observable(!0),b.isModified=a.observable(!1);var d=a.computed(function(){return b(),b.rules(),a.validation.validateObservable(b),!0});b.isValid=a.computed(function(){return b.__valid__()}),b.setError=function(a){b.error(a),b.__valid__(!1)},b.clearError=function(){b.error(null),b.__valid__(!0)};var e=b.subscribe(function(){b.isModified(!0)});b._disposeValidation=function(){b.isValid.dispose(),b.rules.removeAll(),b.isModified._subscriptions.change=[],b.isValidating._subscriptions.change=[],b.__valid__._subscriptions.change=[],e.dispose(),d.dispose(),delete b.rules,delete b.error,delete b.isValid,delete b.isValidating,delete b.__valid__,delete b.isModified}}else c===!1&&a.validation.utils.isValidatable(b)&&b._disposeValidation&&b._disposeValidation();return b},a.validation.validateObservable=function(b){for(var e,f,g=0,h=b.rules(),i=h.length;i>g;g++)if(f=h[g],!f.condition||f.condition())if(e=a.validation.rules[f.rule],e.async||f.async)d(b,e,f);else if(!c(b,e,f))return!1;return b.error(null),b.__valid__(!0),!0},a.validation.localize=function(b){var c;for(c in b)a.validation.rules.hasOwnProperty(c)&&(a.validation.rules[c].message=b[c])},a.applyBindingsWithValidation=function(b,c,d){var e,f,g=arguments.length;g>2?(e=c,f=d):2>g?e=document.body:arguments[1].nodeType?e=c:f=arguments[1],a.validation.init(),f&&a.validation.utils.setDomData(e,f),a.applyBindings(b,c)};var h=a.applyBindings;a.applyBindings=function(b,c){a.validation.init(),h(b,c)},a.validatedObservable=function(b){if(!a.validation.utils.isObject(b))return a.observable(b).extend({validatable:!0});var c=a.observable(b);return c.errors=a.validation.group(b),c.isValid=a.computed(function(){return 0===c.errors().length}),c}}); \ No newline at end of file +!function(a){"function"==typeof require&&"object"==typeof exports&&"object"==typeof module?a(require("knockout"),exports):"function"==typeof define&&define.amd?define(["knockout","exports"],a):a(ko,ko.validation={})}(function(a,b){function c(a){var b="max"===a;return function(c,d){if(f.utils.isEmptyVal(c))return!0;var e,g;void 0===d.typeAttr?(g="text",e=d):(g=d.typeAttr,e=d.value),isNaN(e)||(g="number");var h,i,j;switch(g.toLowerCase()){case"week":if(h=/^(\d{4})-W(\d{2})$/,i=c.match(h),null===i)throw"Invalid value for "+a+" attribute for week input. Should look like "+"'2000-W33' http://www.w3.org/TR/html-markup/input.week.html#input.week.attrs.min";return j=e.match(h),j?b?i[1]j[1]||i[1]===j[1]&&i[2]>=j[2]:!1;case"month":if(h=/^(\d{4})-(\d{2})$/,i=c.match(h),null===i)throw"Invalid value for "+a+" attribute for month input. Should look like "+"'2000-03' http://www.w3.org/TR/html-markup/input.month.html#input.month.attrs.min";return j=e.match(h),j?b?i[1]j[1]||i[1]===j[1]&&i[2]>=j[2]:!1;case"number":case"range":return b?!isNaN(c)&&parseFloat(c)<=parseFloat(e):!isNaN(c)&&parseFloat(c)>=parseFloat(e);default:return b?e>=c:c>=e}}}function d(a,b,c){return b.validator(a(),void 0===c.params?!0:c.params)?!0:(a.setError(f.formatMessage(c.message||b.message,c.params)),!1)}function e(a,b,c){a.isValidating(!0);var d=function(d){var e=!1,g="";return a.__valid__()?(d.message?(e=d.isValid,g=d.message):e=d,e||(a.error(f.formatMessage(g||c.message||b.message,c.params)),a.__valid__(e)),a.isValidating(!1),void 0):(a.isValidating(!1),void 0)};b.validator(a(),c.params||!0,d)}if(void 0===typeof a)throw"Knockout is required, please ensure it is loaded before loading this validation plug-in";a.validation=b;var f=a.validation,g=a.utils,h=g.unwrapObservable,i=g.arrayForEach,j=g.extend,k={registerExtenders:!0,messagesOnModified:!0,errorsAsTitle:!0,errorsAsTitleOnModified:!1,messageTemplate:null,insertMessages:!0,parseInputAttributes:!1,writeInputAttributes:!1,decorateElement:!1,decorateElementOnModified:!0,errorClass:null,errorElementClass:"validationElement",errorMessageClass:"validationMessage",grouping:{deep:!1,observable:!0},validate:{}},l=j({},k);l.html5Attributes=["required","pattern","min","max","step"],l.html5InputTypes=["email","number","date"],l.reset=function(){j(l,k)},f.configuration=l,f.utils=function(){var a=(new Date).getTime(),b={},c="__ko_validation__";return{isArray:function(a){return a.isArray||"[object Array]"===Object.prototype.toString.call(a)},isObject:function(a){return null!==a&&"object"==typeof a},values:function(a){var b=[];for(var c in a)a.hasOwnProperty(c)&&b.push(a[c]);return b},getValue:function(a){return"function"==typeof a?a():a},hasAttribute:function(a,b){return null!==a.getAttribute(b)},getAttribute:function(a,b){return a.getAttribute(b)},setAttribute:function(a,b,c){return a.setAttribute(b,c)},isValidatable:function(a){return a&&a.rules&&a.isValid&&a.isModified},insertAfter:function(a,b){a.parentNode.insertBefore(b,a.nextSibling)},newId:function(){return a+=1},getConfigOptions:function(a){var b=f.utils.contextFor(a);return b||f.configuration},setDomData:function(a,d){var e=a[c];e||(a[c]=e=f.utils.newId()),b[e]=d},getDomData:function(a){var d=a[c];return d?b[d]:void 0},contextFor:function(a){switch(a.nodeType){case 1:case 8:var b=f.utils.getDomData(a);if(b)return b;if(a.parentNode)return f.utils.contextFor(a.parentNode)}return void 0},isEmptyVal:function(a){return void 0===a?!0:null===a?!0:""===a?!0:void 0},getOriginalElementTitle:function(a){var b=f.utils.getAttribute(a,"data-orig-title"),c=a.title,d=f.utils.hasAttribute(a,"data-orig-title");return d?b:c},async:function(a){window.setImmediate?window.setImmediate(a):window.setTimeout(a,0)}}}();var m=function(){var b=0,c=f.configuration,d=f.utils;return{init:function(a,d){b>0&&!d||(a=a||{},a.errorElementClass=a.errorElementClass||a.errorClass||c.errorElementClass,a.errorMessageClass=a.errorMessageClass||a.errorClass||c.errorMessageClass,j(c,a),c.registerExtenders&&f.registerExtenders(),b=1)},configure:function(a){f.init(a)},reset:f.configuration.reset,group:function(b,e){e=j(j({},c.grouping),e);var f=a.observableArray([]),g=null,k=[],l=function(){e.deep&&i(k,function(a){delete a.__kv_traversed})},m=function n(b,c){var g=[],j=h(b);b.__kv_traversed!==!0&&(e.deep&&(b.__kv_traversed=!0,k.push(b)),c=void 0!==c?c:e.deep?1:-1,a.isObservable(b)&&(b.isValid||b.extend({validatable:!0}),f.push(b)),j&&(d.isArray(j)?g=j:d.isObject(j)&&(g=d.values(j))),0!==c&&i(g,function(a){a&&!a.nodeType&&n(a,c+1)}))};return e.observable?(m(b),l(),g=a.computed(function(){var a=[];return i(f(),function(b){b.isValid()||a.push(b.error)}),a})):g=function(){var a=[];return f([]),m(b),l(),i(f(),function(b){b.isValid()||a.push(b.error)}),a},g.showAllMessages=function(a){void 0===a&&(a=!0),g(),i(f(),function(b){b.isModified(a)})},b.errors=g,b.isValid=function(){return 0===b.errors().length},b.isAnyMessageShown=function(){var a=!1;return g(),i(f(),function(b){!b.isValid()&&b.isModified()&&(a=!0)}),a},g},formatMessage:function(a,b){return"function"==typeof a?a(b):a.replace(/\{0\}/gi,h(b))},addRule:function(a,b){return a.extend({validatable:!0}),a.rules.push(b),a},addAnonymousRule:function(a,b){var c=d.newId();void 0===b.message&&(b.message="Error"),f.rules[c]=b,f.addRule(a,{rule:c,params:b.params})},addExtender:function(b){a.extenders[b]=function(a,c){return c.message||c.onlyIf?f.addRule(a,{rule:b,message:c.message,params:d.isEmptyVal(c.params)?!0:c.params,condition:c.onlyIf}):f.addRule(a,{rule:b,params:c})}},registerExtenders:function(){if(c.registerExtenders)for(var b in f.rules)f.rules.hasOwnProperty(b)&&(a.extenders[b]||f.addExtender(b))},insertValidationMessage:function(a){var b=document.createElement("SPAN");return b.className=d.getConfigOptions(a).errorMessageClass,d.insertAfter(a,b),b},parseInputValidationAttributes:function(a,b){i(f.configuration.html5Attributes,function(c){if(d.hasAttribute(a,c)){var e=a.getAttribute(c)||!0;if("min"===c||"max"===c){var g=a.getAttribute("type");"undefined"!=typeof g&&g||(g="text"),e={typeAttr:g,value:e}}f.addRule(b(),{rule:c,params:e})}});var c=a.getAttribute("type");i(f.configuration.html5InputTypes,function(a){a===c&&f.addRule(b(),{rule:"date"===a?"dateISO":a,params:!0})})},writeInputValidationAttributes:function(a,b){var c=b();if(c&&c.rules){var d=c.rules();i(f.configuration.html5Attributes,function(b){var c,e=g.arrayFirst(d,function(a){return a.rule.toLowerCase()===b.toLowerCase()});e&&(c=e.params,"pattern"===e.rule&&e.params instanceof RegExp&&(c=e.params.source),a.setAttribute(b,c))}),d=null}},makeBindingHandlerValidatable:function(b){var c=a.bindingHandlers[b].init;a.bindingHandlers[b].init=function(b,d,e,f,g){return c(b,d,e,f,g),a.bindingHandlers.validationCore.init(b,d,e,f,g)}}}}();j(a.validation,m),f.rules={},f.rules.required={validator:function(a,b){var c,d=/^\s+|\s+$/g;return void 0===a||null===a?!b:(c=a,"string"==typeof a&&(c=a.replace(d,"")),b?(c+"").length>0:!0)},message:"This field is required."},f.rules.min={validator:c("min"),message:"Please enter a value greater than or equal to {0}."},f.rules.max={validator:c("max"),message:"Please enter a value less than or equal to {0}."},f.rules.minLength={validator:function(a,b){return f.utils.isEmptyVal(a)||a.length>=b},message:"Please enter at least {0} characters."},f.rules.maxLength={validator:function(a,b){return f.utils.isEmptyVal(a)||a.length<=b},message:"Please enter no more than {0} characters."},f.rules.pattern={validator:function(a,b){return f.utils.isEmptyVal(a)||null!==a.toString().match(b)},message:"Please check this value."},f.rules.step={validator:function(a,b){if(f.utils.isEmptyVal(a)||"any"===b)return!0;var c=100*a%(100*b);return Math.abs(c)<1e-5||Math.abs(1-c)<1e-5},message:"The value must increment by {0}"},f.rules.email={validator:function(a,b){return b?f.utils.isEmptyVal(a)||b&&/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i.test(a):!0},message:"Please enter a proper email address"},f.rules.date={validator:function(a,b){return b?f.utils.isEmptyVal(a)||b&&!/Invalid|NaN/.test(new Date(a)):!0},message:"Please enter a proper date"},f.rules.dateISO={validator:function(a,b){return b?f.utils.isEmptyVal(a)||b&&/^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}$/.test(a):!0},message:"Please enter a proper date"},f.rules.number={validator:function(a,b){return b?f.utils.isEmptyVal(a)||b&&/^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(a):!0},message:"Please enter a number"},f.rules.digit={validator:function(a,b){return b?f.utils.isEmptyVal(a)||b&&/^\d+$/.test(a):!0},message:"Please enter a digit"},f.rules.phoneUS={validator:function(a,b){return b?f.utils.isEmptyVal(a)?!0:"string"!=typeof a?!1:(a=a.replace(/\s+/g,""),b&&a.length>9&&a.match(/^(1-?)?(\([2-9]\d{2}\)|[2-9]\d{2})-?[2-9]\d{2}-?\d{4}$/)):!0},message:"Please specify a valid phone number"},f.rules.equal={validator:function(a,b){var c=b;return a===f.utils.getValue(c)},message:"Values must equal"},f.rules.notEqual={validator:function(a,b){var c=b;return a!==f.utils.getValue(c)},message:"Please choose another value."},f.rules.unique={validator:function(a,b){var c=f.utils.getValue(b.collection),d=f.utils.getValue(b.externalValue),e=0;return a&&c?(g.arrayFilter(h(c),function(c){a===(b.valueAccessor?b.valueAccessor(c):c)&&e++}),(void 0!==d&&a!==d?1:2)>e):!0},message:"Please make sure the value is unique."},function(){f.registerExtenders()}(),a.bindingHandlers.validationCore=function(){return{init:function(b,c){var d=f.utils.getConfigOptions(b),e=c();if(d.parseInputAttributes&&f.utils.async(function(){f.parseInputValidationAttributes(b,c)}),d.insertMessages&&f.utils.isValidatable(e)){var g=f.insertValidationMessage(b);d.messageTemplate?a.renderTemplate(d.messageTemplate,{field:e},null,g,"replaceNode"):a.applyBindingsToNode(g,{validationMessage:e})}d.writeInputAttributes&&f.utils.isValidatable(e)&&f.writeInputValidationAttributes(b,c),d.decorateElement&&f.utils.isValidatable(e)&&a.applyBindingsToNode(b,{validationElement:e})},update:function(){}}}(),f.makeBindingHandlerValidatable("value"),f.makeBindingHandlerValidatable("checked"),a.bindingHandlers.validationMessage={update:function(b,c){var d=c(),e=f.utils.getConfigOptions(b),g=(h(d),!1),i=!1;d.extend({validatable:!0}),g=d.isModified(),i=d.isValid();var j=function(){return!e.messagesOnModified||g?i?null:d.error:null},k=function(){return!e.messagesOnModified||g?!i:!1};a.bindingHandlers.text.update(b,j),a.bindingHandlers.visible.update(b,k)}},a.bindingHandlers.validationElement={update:function(b,c){var d=c(),e=f.utils.getConfigOptions(b),g=(h(d),!1),i=!1;d.extend({validatable:!0}),g=d.isModified(),i=d.isValid();var j=function(){var a={},b=!e.decorateElementOnModified||g?!i:!1;return e.decorateElement||(b=!1),a[e.errorElementClass]=b,a};a.bindingHandlers.css.update(b,j),e.errorsAsTitle&&a.bindingHandlers.attr.update(b,function(){var a=!e.errorsAsTitleOnModified||g,c=f.utils.getOriginalElementTitle(b);return a&&!i?{title:d.error,"data-orig-title":c}:!a||i?{title:c,"data-orig-title":null}:void 0})}},a.bindingHandlers.validationOptions=function(){return{init:function(a,b){var c=h(b());if(c){var d=j({},f.configuration);j(d,c),f.utils.setDomData(a,d)}}}}(),a.extenders.validation=function(a,b){return i(f.utils.isArray(b)?b:[b],function(b){f.addAnonymousRule(a,b)}),a},a.extenders.validatable=function(b,c){if(f.utils.isObject(c)||(c={enable:c}),"enable"in c||(c.enable=!0),c.enable&&!f.utils.isValidatable(b)){var d=f.configuration.validate||{},e={throttleEvaluation:c.throttle||d.throttle};b.error=a.observable(null),b.rules=a.observableArray(),b.isValidating=a.observable(!1),b.__valid__=a.observable(!0),b.isModified=a.observable(!1),b.isValid=a.computed(b.__valid__),b.setError=function(a){b.error(a),b.__valid__(!1)},b.clearError=function(){b.error(null),b.__valid__(!0)};var g=b.subscribe(function(){b.isModified(!0)}),h=a.computed(j({read:function(){return b(),b.rules(),f.validateObservable(b),!0}},e));j(h,e),b._disposeValidation=function(){b.isValid.dispose(),b.rules.removeAll(),b.isModified._subscriptions.change=[],b.isValidating._subscriptions.change=[],b.__valid__._subscriptions.change=[],g.dispose(),h.dispose(),delete b.rules,delete b.error,delete b.isValid,delete b.isValidating,delete b.__valid__,delete b.isModified}}else c.enable===!1&&b._disposeValidation&&b._disposeValidation();return b},f.validateObservable=function(a){for(var b,c,g=0,h=a.rules(),i=h.length;i>g;g++)if(c=h[g],!c.condition||c.condition())if(b=f.rules[c.rule],b.async||c.async)e(a,b,c);else if(!d(a,b,c))return!1;return a.clearError(),!0},f.localize=function(a){var b;for(b in a)f.rules.hasOwnProperty(b)&&(f.rules[b].message=a[b])},a.applyBindingsWithValidation=function(b,c,d){var e,g,h=arguments.length;h>2?(e=c,g=d):2>h?e=document.body:arguments[1].nodeType?e=c:g=arguments[1],f.init(),g&&f.utils.setDomData(e,g),a.applyBindings(b,c)};var n=a.applyBindings;a.applyBindings=function(a,b){f.init(),n(a,b)},a.validatedObservable=function(b){if(!f.utils.isObject(b))return a.observable(b).extend({validatable:!0});var c=a.observable(b);return c.isValid=a.observable(),c.errors=f.group(b),c.errors.subscribe(function(a){c.isValid(0===a.length)}),c}}); \ No newline at end of file diff --git a/Lib/knockout-latest.debug.js b/Lib/knockout-latest.debug.js index 9fe33c8f..77bb4cff 100644 --- a/Lib/knockout-latest.debug.js +++ b/Lib/knockout-latest.debug.js @@ -1,3679 +1,4225 @@ -// Knockout JavaScript library v2.3.0 +// Knockout JavaScript library v3.0.0 // (c) Steven Sanderson - http://knockoutjs.com/ // License: MIT (http://www.opensource.org/licenses/mit-license.php) +(function(){ +var DEBUG=true; +(function(undefined){ + // (0, eval)('this') is a robust way of getting a reference to the global object + // For details, see http://stackoverflow.com/questions/14119988/return-this-0-evalthis/14120023#14120023 + var window = this || (0, eval)('this'), + document = window['document'], + navigator = window['navigator'], + jQuery = window["jQuery"], + JSON = window["JSON"]; +(function(factory) { + // Support three module loading scenarios + if (typeof require === 'function' && typeof exports === 'object' && typeof module === 'object') { + // [1] CommonJS/Node.js + var target = module['exports'] || exports; // module.exports is for Node.js + factory(target); + } else if (typeof define === 'function' && define['amd']) { + // [2] AMD anonymous module + define(['exports'], factory); + } else { + // [3] No module loader (plain