From c611b3601be8de1330ce671ad80b52bb8582ae37 Mon Sep 17 00:00:00 2001 From: vivane Date: Thu, 28 Jul 2016 10:11:54 -0700 Subject: [PATCH 1/3] Skipped format validation for allowed null values. --- source/validate.js | 9 +++++++++ test/tests/11 - format/01 - register validator.js | 15 +++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/source/validate.js b/source/validate.js index 50b1683..51a33e0 100644 --- a/source/validate.js +++ b/source/validate.js @@ -316,6 +316,15 @@ ValidatorContext.prototype.validateAll = function (data, schema, dataPathParts, return this.handleError(error); }; ValidatorContext.prototype.validateFormat = function (data, schema) { + var allowedTypes = schema.type; + if (!Array.isArray(allowedTypes)) { + allowedTypes = [allowedTypes]; + } + + if (data === null && allowedTypes.indexOf('null') !== -1) { + return null; + } + if (typeof schema.format !== 'string' || !this.formatValidators[schema.format]) { return null; } diff --git a/test/tests/11 - format/01 - register validator.js b/test/tests/11 - format/01 - register validator.js index 8f7b638..7259f00 100644 --- a/test/tests/11 - format/01 - register validator.js +++ b/test/tests/11 - format/01 - register validator.js @@ -58,4 +58,19 @@ describe("Registering custom validator", function () { assert.isFalse(result2.valid); assert.includes(result2.error.message, 'break 2'); }); + + it("Custom validator should be skipped for null values", function () { + tv4.addFormat('null-format', function (data) { + if (data !== "test") { + return "string does not match"; + } + }); + + var schema = {type: ["string", "null"], format: 'null-format'}; + var data1 = "test"; + var data2 = null; + + assert.isTrue(tv4.validate(data1, schema)); + assert.isTrue(tv4.validate(data2, schema)); + }); }); From 08541da23f60b4ffde553ee6c12b19c57cc1b2f3 Mon Sep 17 00:00:00 2001 From: Vlad Ivanenko Date: Fri, 19 Aug 2016 10:13:30 -0700 Subject: [PATCH 2/3] Skipped format validation for allowed undefined properties. Corrected validation of required properties (if they are null or undefined). --- component.json | 1 + source/basic.js | 12 + source/object.js | 2 +- test/all_concat.js | 2636 ----------------- test/all_concat.js.map | 64 - .../11 - format/01 - register validator.js | 24 + test/tests/Misc/enum null failure.js | 11 +- tv4.js | 1677 ----------- tv4.min.js | 1 - 9 files changed, 46 insertions(+), 4382 deletions(-) delete mode 100644 test/all_concat.js delete mode 100644 test/all_concat.js.map delete mode 100644 tv4.js delete mode 100644 tv4.min.js diff --git a/component.json b/component.json index 888ef44..923603f 100644 --- a/component.json +++ b/component.json @@ -16,6 +16,7 @@ "scripts": [ "tv4.js", "lang/de.js", + "lang/es.js", "lang/fr.js", "lang/nb.js", "lang/pl-PL.js", diff --git a/source/basic.js b/source/basic.js index a13b46e..8d07203 100644 --- a/source/basic.js +++ b/source/basic.js @@ -3,6 +3,18 @@ ValidatorContext.prototype.validateBasic = function validateBasic(data, schema, if (error = this.validateType(data, schema, dataPointerPath)) { return error.prefixWith(null, "type"); } + + var dataType = typeof data; + if (data === null) { + dataType = "null"; + } else if (Array.isArray(data)) { + dataType = "array"; + } + + if (dataType === "null" || dataType === "undefined") { + return null; + } + if (error = this.validateEnum(data, schema, dataPointerPath)) { return error.prefixWith(null, "type"); } diff --git a/source/object.js b/source/object.js index ebc74f6..0ca45e7 100644 --- a/source/object.js +++ b/source/object.js @@ -35,7 +35,7 @@ ValidatorContext.prototype.validateObjectRequiredProperties = function validateO if (schema.required !== undefined) { for (var i = 0; i < schema.required.length; i++) { var key = schema.required[i]; - if (data[key] === undefined) { + if (data[key] === undefined || data[key] === null) { var error = this.createError(ErrorCodes.OBJECT_REQUIRED, {key: key}, '', '/required/' + i, null, data, schema); if (this.handleError(error)) { return error; diff --git a/test/all_concat.js b/test/all_concat.js deleted file mode 100644 index d79c5be..0000000 --- a/test/all_concat.js +++ /dev/null @@ -1,2636 +0,0 @@ -"use strict"; - -//need to declare these for node and modern browsers -var tv4; -var assert; - -if (typeof process === 'object' && typeof process.cwd !== 'undefined') { - // NodeJS - tv4 = require('./../').tv4; - assert = require('proclaim'); - require('source-map-support').install(); - - var fs = require('fs'); - var getJSON = function (file) { - var json; - try { - json = JSON.parse(fs.readFileSync(file, 'utf8')); - } - catch (e) { - assert.fail(e, null, file + ': ' + String(e), 'getJSON'); - } - assert.isObject(json, file); - return json; - }; - assert.isFile = function(file, msg) { - if (!fs.existsSync(file)){ - assert.fail(false, true, msg + ': missing file ' + file, 'existsSync'); - } - }; - - describe('Verify package definition files', function (){ - var pkg; - var component; - var bower; - it('pkg', function () { - pkg = getJSON('./package.json'); - - assert.property(pkg, 'main', 'main'); - assert.isFile(pkg.main, 'main'); - }); - it('component', function () { - component = getJSON('./component.json'); - - assert.property(component, 'main', 'main'); - assert.isFile(component.main, 'main'); - - component.scripts.forEach(function(name) { - assert.isFile(name, 'scripts'); - }); - }); - it('bower', function () { - bower = getJSON('./bower.json'); - - assert.property(bower, 'main', 'main'); - assert.isFile(bower.main, 'main'); - - // should verify ignore field - }); - }); -} -else if (typeof window !== 'undefined') { - // import for browser, use from IE7/8 global bypass - assert = window.refs.assert; - tv4 = window.refs.tv4; -} - -//check if we got everything -if (!tv4) { - throw new Error('tv4 not found'); -} -if (!assert) { - throw new Error('proclaim not found'); -} -var helper = {}; -helper.dumpJSON = function (value) { - console.log(JSON.stringify(value, null, 2)); -}; - - -beforeEach(function () { - tv4 = tv4.freshApi(); -}); - - -//duck patch standard assert to chai -//quick-and-dirty wrappers -assert.property = function (object, property, message) { - if (typeof object[property] === 'undefined') { - assert.fail(object, property, message, 'have property'); - } -}; -assert.notProperty = function (object, property, message) { - if (typeof object[property] !== 'undefined') { - assert.fail(object, property, message, 'not have property'); - } -}; - -assert.ownProperty = function (object, property, message) { - if (!object.hasOwnProperty(property)) { - assert.fail(object, property, message, 'have own property'); - } -}; -assert.notOwnProperty = function (object, property, message) { - if (object.hasOwnProperty(property)) { - assert.fail(object, property, message, 'not have own property'); - } -}; - -//not ideal at all -assert.propertyVal = function (object, property, value, message) { - assert.property(object, property, message); - assert.strictEqual(object[property], value, message); -}; -assert.propertyNotVal = function (object, property, value, message) { - assert.property(object, property, message); - assert.notStrictEqual(object[property], value, message); -}; -assert.ownPropertyVal = function (object, property, value, message) { - assert.ownProperty(object, property, message); - assert.strictEqual(object[property], value, message); -}; -assert.notOwnPropertyVal = function (object, property, value, message) { - assert.notOwnProperty(object, property, message); - assert.notStrictEqual(object[property], value, message); -}; -assert.propertyValues = function (object, properties, value, message) { - assert.isObject(object, message); - assert.isObject(properties, message); - //copy properties - var props = {}; - for (var name in properties) { - props[name] = object[name]; - } - assert.deepEqual(props, properties, message); -}; -//import when fix is pushed -assert.notOk = function (value, message) { - if (!!value) { - assert.fail(value, true, message, '!='); - } -}; - -/* jshint -W060 */ - -//end of header.js - -describe("Core 01", function () { - - it("getDocumentUri returns only location part of url", function () { - - assert.strictEqual(tv4.getDocumentUri("http://example.com"), "http://example.com"); - - assert.strictEqual(tv4.getDocumentUri("http://example.com/main"), "http://example.com/main"); - assert.strictEqual(tv4.getDocumentUri("http://example.com/main/"), "http://example.com/main/"); - //assert.strictEqual(tv4.getDocumentUri("http://example.com/main//"), "http://example.com/main/"); - - assert.strictEqual(tv4.getDocumentUri("http://example.com/main/sub"), "http://example.com/main/sub"); - assert.strictEqual(tv4.getDocumentUri("http://example.com/main/sub/"), "http://example.com/main/sub/"); - - assert.strictEqual(tv4.getDocumentUri("http://example.com/main#"), "http://example.com/main"); - assert.strictEqual(tv4.getDocumentUri("http://example.com/main/sub/#"), "http://example.com/main/sub/"); - - assert.strictEqual(tv4.getDocumentUri("http://example.com/main?"), "http://example.com/main?"); - assert.strictEqual(tv4.getDocumentUri("http://example.com/main?q=1"), "http://example.com/main?q=1"); - assert.strictEqual(tv4.getDocumentUri("http://example.com/main?q=1#abc"), "http://example.com/main?q=1"); - - assert.strictEqual(tv4.getDocumentUri("http://example.com/main/#"), "http://example.com/main/"); - assert.strictEqual(tv4.getDocumentUri("http://example.com/main/#?"), "http://example.com/main/"); - assert.strictEqual(tv4.getDocumentUri("http://example.com/main/#?q=a/b/c"), "http://example.com/main/"); - }); -}); - -describe("Core 02", function () { - - it("tv4.freshApi() produces working copy", function () { - var duplicate = tv4.freshApi(); - assert.isObject(duplicate); - // Basic sanity checks - assert.isTrue(duplicate.validate({}, {type: "object"})); - assert.isObject(duplicate.validateMultiple("string", {})); - }); - - it("tv4.freshApi() has separate schema store", function () { - var duplicate = tv4.freshApi(); - - var schemaUrl1 = "http://example.com/schema/schema1"; - var schemaUrl2 = "http://example.com/schema/schema2"; - duplicate.addSchema(schemaUrl1, {}); - tv4.addSchema(schemaUrl2, {}); - - assert.isObject(duplicate.getSchema(schemaUrl1)); - assert.isUndefined(tv4.getSchema(schemaUrl1)); - assert.isUndefined(duplicate.getSchema(schemaUrl2)); - assert.isObject(tv4.getSchema(schemaUrl2)); - }); -}); - -describe("Core 03", function () { - - it("tv4.dropSchemas() drops stored schemas", function () { - var schema = { - "items": {"$ref": "http://example.com/schema/items#"}, - "maxItems": 2 - }; - tv4.addSchema("http://example.com/schema", schema); - assert.strictEqual(tv4.getSchema("http://example.com/schema"), schema, "has schema"); - - tv4.dropSchemas(); - assert.isUndefined(tv4.getSchema("http://example.com/schema"), "doesn't have schema"); - }); - - it("tv4.reset() clears errors, valid and missing", function () { - it("must be string, is integer", function () { - var data = 5; - var schema = {"type": "array", "items" : {"$ref" : "http://example.com"}}; - - assert.notOk(tv4.error, "starts with no error"); - assert.isTrue(tv4.valid, "starts valid"); - assert.length(tv4.missing, 0, "starts with 0 missing"); - - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - assert.ok(tv4.error, "has error"); - assert.isFalse(tv4.valid, "is invalid"); - assert.length(tv4.missing, 1, "missing 1"); - - tv4.reset(); - assert.notOk(tv4.error, "reset to no error"); - assert.isTrue(tv4.valid, "reset to valid"); - assert.length(tv4.missing, 0, "reset to 0 missing"); - }); - }); -}); - -describe("Core 04", function () { - - var schema = { - "type": "string" - }; - - it("ValidationError is Error subtype", function () { - var res = tv4.validateResult(123, schema); - assert.isObject(res); - assert.isObject(res.error); - assert.isInstanceOf(res.error, Error); - assert.isString(res.error.stack); - }); - - it("ValidationError has own stack trace", function () { - function errorA() { - var res = tv4.validateResult(123, schema); - assert.isFalse(res.valid); - assert.isString(res.error.stack); - assert.ok(res.error.stack.indexOf('errorA') > -1, 'has own stack trace A'); - } - - function errorB() { - var res = tv4.validateResult(123, schema); - assert.isFalse(res.valid); - assert.isString(res.error.stack); - assert.ok(res.error.stack.indexOf('errorB') > -1, 'has own stack trace B'); - } - errorA(); - errorB(); - }); -}); - -describe("Any types 01", function () { - - it("no type specified", function () { - var data = {}; - var schema = {}; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("must be object, is object", function () { - var data = {}; - var schema = {"type": "object"}; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("must be object or string, is object", function () { - var data = {}; - var schema = {"type": ["object", "string"]}; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("must be object or string, is array", function () { - var data = []; - var schema = {"type": ["object", "string"]}; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); - - it("must be array, is object", function () { - var data = {}; - var schema = {"type": ["array"]}; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); - - it("must be string, is integer", function () { - var data = 5; - var schema = {"type": ["string"]}; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); - - it("must be object, is null", function () { - var data = null; - var schema = {"type": ["object"]}; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); - - it("must be null, is null", function () { - var data = null; - var schema = {"type": "null"}; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("doesn't crash on invalid type", function () { - var data = null; - var schema = {"type": {"foo": "bar"}}; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); -}); - -describe("Any types 01", function () { - - it("enum [1], was 1", function () { - var data = 1; - var schema = {"enum": [1]}; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("enum [1], was \"1\"", function () { - var data = "1"; - var schema = {"enum": [1]}; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); - - it("enum [{}], was {}", function () { - var data = {}; - var schema = {"enum": [ - {} - ]}; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("enum [{\"key\":\"value\"], was {}", function () { - var data = {}; - var schema = {"enum": [ - {"key": "value"} - ]}; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); - - it("enum [{\"key\":\"value\"], was {\"key\": \"value\"}", function () { - var data = {}; - var schema = {"enum": [ - {"key": "value"} - ]}; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); - - it("Enum with array value - success", function () { - var data = [1, true, 0]; - var schema = {"enum": [ - [1, true, 0], - 5, - {} - ]}; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("Enum with array value - failure 1", function () { - var data = [1, true, 0, 5]; - var schema = {"enum": [ - [1, true, 0], - 5, - {} - ]}; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); - - it("Enum with array value - failure 2", function () { - var data = [1, true, 5]; - var schema = {"enum": [ - [1, true, 0], - 5, - {} - ]}; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); -}); - -describe("Numeric - multipleOf", function () { - - it("pass", function () { - var data = 5; - var schema = {"multipleOf": 2.5}; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("fail", function () { - var data = 5; - var schema = {"multipleOf": 0.75}; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); - - it("floating-point pass 6.6/2.2", function () { - var data = 6.6; - var schema = {"multipleOf": 2.2}; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("floating-point pass 6.6666/2.2222", function () { - var data = 6.6666; - var schema = {"multipleOf": 2.2222}; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); -}); -describe("Numberic 02", function () { - - it("minimum success", function () { - var data = 5; - var schema = {minimum: 2.5}; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("minimum failure", function () { - var data = 5; - var schema = {minimum: 7}; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); - - it("minimum equality success", function () { - var data = 5; - var schema = {minimum: 5}; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("minimum equality failure", function () { - var data = 5; - var schema = {minimum: 5, exclusiveMinimum: true}; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); - - it("maximum success", function () { - var data = 5; - var schema = {maximum: 7}; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("maximum failure", function () { - var data = -5; - var schema = {maximum: -10}; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); - - it("maximum equality success", function () { - var data = 5; - var schema = {maximum: 5}; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("maximum equality failure", function () { - var data = 5; - var schema = {maximum: 5, exclusiveMaximum: true}; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); -}); - -describe("Numeric 03", function () { - - it("NaN failure", function() { - var data = NaN; - var schema = {}; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); - - it("Infinity failure", function() { - var data = Infinity; - var schema = {}; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); - - it("-Infinity failure", function() { - var data = -Infinity; - var schema = {}; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); - - it("string to number failure", function() { - var data = Number('foo'); - var schema = {}; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); - - it("string to number success", function() { - var data = Number('123'); - var schema = {}; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("max value success", function() { - var data = Number.MAX_VALUE; - var schema = {}; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - /* Travis reports: Bad number '1.798e+308' (which is a good thing, as it should be Infinity) - it("big number failure", function() { - var data = 1.798e+308; - var schema = {}; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); - */ -}); - -describe("Strings 01", function () { - - it("no length constraints", function () { - var data = "test"; - var schema = {}; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("minimum length success", function () { - var data = "test"; - var schema = {minLength: 3}; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("minimum length failure", function () { - var data = "test"; - var schema = {minLength: 5}; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); - - it("maximum length success", function () { - var data = "test1234"; - var schema = {maxLength: 10}; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("maximum length failure", function () { - var data = "test1234"; - var schema = {maxLength: 5}; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); - - it("check error message", function () { - var data = "test1234"; - var schema = {maxLength: 5}; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - //return typeof tv4.error.message !== "undefined"; - assert.ok(tv4.error.message); - }); -}); - -describe("Strings 02", function () { - - it("pattern success", function () { - var data = "9test"; - var schema = {"pattern": "^[0-9][a-zA-Z]*$"}; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("pattern failure", function () { - var data = "9test9"; - var schema = {"pattern": "^[0-9][a-zA-Z]*$"}; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); - - it("accepts RegExp object", function () { - var data = "9test"; - var schema = {"pattern": /^[0-9][a-zA-Z]*$/}; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("accepts RegExp literal", function () { - var data = "9TEST"; - var schema = {"pattern": "/^[0-9][a-z]*$/i"}; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); -}); -describe("Arrays 01", function () { - - it("no length constraints", function () { - var data = [1, 2, 3]; - var schema = {}; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("minimum length success", function () { - var data = [1, 2, 3]; - var schema = {minItems: 3}; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("minimum length failure", function () { - var data = [1, 2, 3]; - var schema = {minItems: 5}; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); - - it("maximum length success", function () { - var data = [1, 2, 3, 4, 5]; - var schema = {maxItems: 10}; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("maximum length failure", function () { - var data = [1, 2, 3, 4, 5]; - var schema = {maxItems: 3}; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); -}); - -describe("Arrays 02", function () { - - it("uniqueItems success", function () { - var data = [1, true, "1"]; - var schema = {uniqueItems: true}; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("uniqueItems failure", function () { - var data = [1, true, "1", 1]; - var schema = {uniqueItems: true}; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); -}); - -describe("Arrays 03", function () { - - it("plain items success", function () { - var data = [1, 2, 3, 4]; - var schema = { - "items": { - "type": "integer" - } - }; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("plain items failure", function () { - var data = [1, 2, true, 3]; - var schema = { - "items": { - "type": "integer" - } - }; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); -}); - -describe("Arrays 04", function () { - - it("plain items success", function () { - var data = [1, true, "one"]; - var schema = { - "items": [ - {"type": "integer"}, - {"type": "boolean"} - ] - }; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("plain items failure", function () { - var data = [1, null, "one"]; - var schema = { - "items": [ - {"type": "integer"}, - {"type": "boolean"} - ] - }; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); -}); - -describe("Arrays 05", function () { - - it("additional items schema success", function () { - var data = [1, true, "one", "uno"]; - var schema = { - "items": [ - {"type": "integer"}, - {"type": "boolean"} - ], - "additionalItems": {"type": "string"} - }; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("additional items schema failure", function () { - var data = [1, true, "one", 1]; - var schema = { - "items": [ - {"type": "integer"}, - {"type": "boolean"} - ], - "additionalItems": {"type": "string"} - }; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); - - it("additional items boolean success", function () { - var data = [1, true, "one", "uno"]; - var schema = { - "items": [ - {"type": "integer"}, - {"type": "boolean"} - ], - "additionalItems": true - }; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("additional items boolean failure", function () { - var data = [1, true, "one", "uno"]; - var schema = { - "items": [ - {"type": "integer"}, - {"type": "boolean"} - ], - "additionalItems": false - }; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); -}); - -describe("Objects 01", function () { - - it("minimum length success", function () { - var data = {key1: 1, key2: 2, key3: 3}; - var schema = {minProperties: 3}; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("minimum length failure", function () { - var data = {key1: 1, key2: 2, key3: 3}; - var schema = {minProperties: 5}; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); - - it("maximum length success", function () { - var data = {key1: 1, key2: 2, key3: 3}; - var schema = {maxProperties: 5}; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("maximum length failure", function () { - var data = {key1: 1, key2: 2, key3: 3}; - var schema = {maxProperties: 2}; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); -}); - -describe("Objects 02", function () { - - it("required success", function () { - var data = {key1: 1, key2: 2, key3: 3}; - var schema = {required: ["key1", "key2"]}; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("required failure", function () { - var data = {key1: 1, key2: 2, key3: 3}; - var schema = {required: ["key1", "notDefined"]}; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); -}); - -describe("Objects 03", function () { - - it("properties success", function () { - var data = {intKey: 1, stringKey: "one"}; - var schema = { - properties: { - intKey: {"type": "integer"}, - stringKey: {"type": "string"} - } - }; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("properties failure", function () { - var data = {intKey: 1, stringKey: false}; - var schema = { - properties: { - intKey: {"type": "integer"}, - stringKey: {"type": "string"} - } - }; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); -}); - -describe("Objects 04", function () { - - it("patternProperties success", function () { - var data = {intKey: 1, intKey2: 5}; - var schema = { - properties: { - intKey: {"type": "integer"} - }, - patternProperties: { - "^int": {minimum: 0} - } - }; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("patternProperties failure 1", function () { - var data = {intKey: 1, intKey2: 5}; - var schema = { - properties: { - intKey: {minimum: 5} - }, - patternProperties: { - "^int": {"type": "integer"} - } - }; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); - - it("patternProperties failure 2", function () { - var data = {intKey: 10, intKey2: "string value"}; - var schema = { - properties: { - intKey: {minimum: 5} - }, - patternProperties: { - "^int": {"type": "integer"} - } - }; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); -}); - -describe("Objects 05", function () { - - it("additionalProperties schema success", function () { - var data = {intKey: 1, intKey2: 5, stringKey: "string"}; - var schema = { - properties: { - intKey: {"type": "integer"} - }, - patternProperties: { - "^int": {minimum: 0} - }, - additionalProperties: {"type": "string"} - }; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("patternProperties schema failure", function () { - var data = {intKey: 10, intKey2: 5, stringKey: null}; - var schema = { - properties: { - intKey: {minimum: 5} - }, - patternProperties: { - "^int": {"type": "integer"} - }, - additionalProperties: {"type": "string"} - }; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); - - it("patternProperties boolean success", function () { - var data = {intKey: 10, intKey2: 5, stringKey: null}; - var schema = { - properties: { - intKey: {minimum: 5} - }, - patternProperties: { - "^int": {"type": "integer"} - }, - additionalProperties: true - }; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("patternProperties boolean failure", function () { - var data = {intKey: 10, intKey2: 5, stringKey: null}; - var schema = { - properties: { - intKey: {minimum: 5} - }, - patternProperties: { - "^int": {"type": "integer"} - }, - additionalProperties: false - }; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); -}); -describe("Objects 06", function () { - - it("string dependency success", function () { - var data = {key1: 5, key2: "string"}; - var schema = { - dependencies: { - key1: "key2" - } - }; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("string dependency failure", function () { - var data = {key1: 5}; - var schema = { - dependencies: { - key1: "key2" - } - }; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); - - it("array dependency success", function () { - var data = {key1: 5, key2: "string"}; - var schema = { - dependencies: { - key1: ["key2"] - } - }; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("array dependency failure", function () { - var data = {key1: 5}; - var schema = { - dependencies: { - key1: ["key2"] - } - }; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); - - it("schema dependency success", function () { - var data = {key1: 5, key2: "string"}; - var schema = { - dependencies: { - key1: { - properties: { - key2: {"type": "string"} - } - } - } - }; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("schema dependency failure", function () { - var data = {key1: 5, key2: 5}; - var schema = { - dependencies: { - key1: { - properties: { - key2: {"type": "string"} - } - } - } - }; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); -}); - -describe("Combinators 01", function () { - - it("allOf success", function () { - var data = 10; - var schema = { - "allOf": [ - {"type": "integer"}, - {"minimum": 5} - ] - }; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("allOf failure", function () { - var data = 1; - var schema = { - "allOf": [ - {"type": "integer"}, - {"minimum": 5} - ] - }; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); -}); - -describe("Combinators 02", function () { - - it("anyOf success", function () { - var data = "hello"; - var schema = { - "anyOf": [ - {"type": "integer"}, - {"type": "string"}, - {"minLength": 1} - ] - }; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); -}); - -it("anyOf failure", function () { - var data = true; - var schema = { - "anyOf": [ - {"type": "integer"}, - {"type": "string"} - ] - }; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); -}); -}); - -describe("Combinators 03", function () { - - it("oneOf success", function () { - var data = 5; - var schema = { - "oneOf": [ - {"type": "integer"}, - {"type": "string"}, - {"type": "string", minLength: 1} - ] - }; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("oneOf failure (too many)", function () { - var data = "string"; - var schema = { - "oneOf": [ - {"type": "integer"}, - {"type": "string"}, - {"minLength": 1} - ] - }; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); - - it("oneOf failure (no matches)", function () { - var data = false; - var schema = { - "oneOf": [ - {"type": "integer"}, - {"type": "string"}, - {"type": "string", "minLength": 1} - ] - }; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); -}); - -describe("Combinators 04", function () { - - it("not success", function () { - var data = 5; - var schema = { - "not": {"type": "string"} - }; - var valid = tv4.validate(data, schema); - assert.isTrue(valid); - }); - - it("not failure", function () { - var data = "test"; - var schema = { - "not": {"type": "string"} - }; - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); -}); - -describe("$ref 01", function () { - - it("normalise - untouched immediate $ref", function () { - var schema = { - "items": {"$ref": "#"} - }; - tv4.normSchema(schema); - assert.propertyVal(schema.items, '$ref', "#"); - //return schema.items['$ref'] == "#"; - }); - - it("normalise - id as base", function () { - var schema = { - "id": "baseUrl", - "items": {"$ref": "#"} - }; - tv4.normSchema(schema); - assert.propertyVal(schema.items, '$ref', "baseUrl#"); - //return schema.items['$ref'] == "baseUrl#"; - }); - - it("normalise - id relative to parent", function () { - var schema = { - "id": "http://example.com/schema", - "items": { - "id": "otherSchema", - "items": { - "$ref": "#" - } - } - }; - tv4.normSchema(schema); - assert.strictEqual(schema.items.id, "http://example.com/otherSchema", "schema.items.id"); - assert.strictEqual(schema.items.items['$ref'], "http://example.com/otherSchema#", "$ref"); - //this.assert(schema.items.id == "http://example.com/otherSchema", "schema.items.id"); - //this.assert(schema.items.items['$ref'] == "http://example.com/otherSchema#", "$ref"); - }); - - it("normalise - do not touch contents of \"enum\"", function () { - var schema = { - "id": "http://example.com/schema", - "items": { - "id": "otherSchema", - "enum": [ - { - "$ref": "#" - } - ] - } - }; - tv4.normSchema(schema); - assert.strictEqual(schema.items['enum'][0]['$ref'], "#"); - //this.assert(schema.items['enum'][0]['$ref'] == "#"); - }); - - it("Only normalise id and $ref if they are strings", function () { - var schema = { - "properties": { - "id": {"type": "integer"}, - "$ref": {"type": "integer"} - } - }; - var data = {"id": "test", "$ref": "test"}; - tv4.normSchema(schema); - var valid = tv4.validate(data, schema); - assert.isFalse(valid); - }); -}); - -describe("$ref 02", function () { - - it("skip unneeded", function () { - var schema = { - "items": {"$ref": "http://example.com/schema#"} - }; - tv4.validate([], schema); - assert.notProperty(tv4.missing, "http://example.com/schema"); - assert.length(tv4.missing, 0); - //return !tv4.missing["http://example.com/schema"] - // && tv4.missing.length == 0; - }); - - it("list missing (map)", function () { - var schema = { - "items": {"$ref": "http://example.com/schema#"} - }; - tv4.validate([1, 2, 3], schema); - assert.property(tv4.missing, "http://example.com/schema"); - //return !!tv4.missing["http://example.com/schema"]; - }); - - it("list missing (index)", function () { - var schema = { - "items": {"$ref": "http://example.com/schema#"} - }; - tv4.validate([1, 2, 3], schema); - assert.length(tv4.missing, 1); - assert.strictEqual(tv4.missing[0], "http://example.com/schema"); - //return tv4.missing[0] == "http://example.com/schema"; - }); -}); -describe("$ref 03", function () { - - it("addSchema(), getSchema()", function () { - var url = "http://example.com/schema"; - var schema = { - "test": "value" - }; - tv4.addSchema(url, schema); - var fetched = tv4.getSchema(url); - assert.strictEqual(fetched.test, "value"); - //return fetched.test == "value"; - }); - - it("addSchema(), getSchema() with blank fragment", function () { - var url = "http://example.com/schema"; - var schema = { - "test": "value" - }; - tv4.addSchema(url, schema); - var fetched = tv4.getSchema(url + "#"); - assert.strictEqual(fetched.test, "value"); - //return fetched.test == "value"; - }); - - it("addSchema(), getSchema() with pointer path fragment", function () { - var url = "http://example.com/schema"; - var schema = { - "items": { - "properties": { - "key[]": { - "inner/key~": "value" - } - } - } - }; - tv4.addSchema(url, schema); - var fetched = tv4.getSchema(url + "#/items/properties/key%5B%5D/inner~1key~0"); - assert.strictEqual(fetched, "value"); - //return fetched == "value"; - }); - - it("addSchema(), getSchema() adds referred schemas", function () { - tv4 = tv4.freshApi(); - - var data = [123, true]; - var valid; - var url = "http://example.com/schema"; - var schema = { - "type": "array", - "items": {"$ref": "http://example.com/schema/sub#item"} - }; - tv4.addSchema(url, schema); - - //test missing - valid = tv4.validate(data, schema); - assert.isTrue(valid); - assert.length(tv4.missing, 1); - assert.isUndefined(tv4.getSchema('http://example.com/schema/sub')); - - var item = { - "id": "#item", - "type": "boolean" - }; - var sub = { - "id": "http://example.com/schema/sub", - "type": "object", - "lib": { - "item": item - } - }; - tv4.addSchema(sub); - - //added it? - assert.equal(tv4.getSchema(url), schema); - assert.equal(tv4.getSchema('http://example.com/schema/sub'), sub); - assert.equal(tv4.getSchema('http://example.com/schema/sub#item'), item); - - //now use it - valid = tv4.validate(data, schema); - assert.length(tv4.missing, 0); - assert.isFalse(valid); - - var error = { - code: 0, - message: 'Invalid type: number (expected boolean)', - dataPath: '/0', - schemaPath: '/items/type', - subErrors: null }; - assert.propertyValues(tv4.error, error); - }); -}); -describe("$ref 04", function () { - - it("addSchema(), $ref", function () { - var url = "http://example.com/schema"; - var schema = { - "test": "value" - }; - tv4.addSchema(url, schema); - - var otherSchema = { - "items": {"$ref": url} - }; - var valid = tv4.validate([0,1,2,3], otherSchema); - - assert.isTrue(valid, "should be valid"); - assert.length(tv4.missing, 0, "should have no missing schemas"); - - //this.assert(valid, "should be valid"); - //this.assert(tv4.missing.length == 0, "should have no missing schemas"); - }); - - it("internal $ref", function () { - var schema = { - "type": "array", - "items": {"$ref": "#"} - }; - - assert.isTrue(tv4.validate([[],[[]]], schema), "List of lists should be valid"); - assert.isTrue(!tv4.validate([0,1,2,3], schema), "List of ints should not"); - assert.isTrue(!tv4.validate([[true], []], schema), "List of list with boolean should not"); - - assert.length(tv4.missing, 0, "should have no missing schemas"); - - //this.assert(tv4.validate([[],[[]]], schema), "List of lists should be valid"); - //this.assert(!tv4.validate([0,1,2,3], schema), "List of ints should not"); - //this.assert(!tv4.validate([[true], []], schema), "List of list with boolean should not"); - - //this.assert(tv4.missing.length == 0, "should have no missing schemas"); - }); -}); - -describe("$ref 05", function () { - - it("inline addressing for fragments", function () { - var schema = { - "type": "array", - "items": {"$ref": "#test"}, - "testSchema": { - "id": "#test", - "type": "boolean" - } - }; - var error = { - code: 0, - message: 'Invalid type: number (expected boolean)', - dataPath: '/0', - schemaPath: '/items/type', - subErrors: null - }; - - var data = [0, false]; - var valid = tv4.validate(data, schema); - assert.isFalse(valid, 'inline addressing invalid 0, false'); - assert.propertyValues(tv4.error, error, 'errors equal'); - }); - - it("don't trust non sub-paths", function () { - var examplePathBase = "http://example.com/schema"; - var examplePath = examplePathBase + "/schema"; - var schema = { - "id": examplePath, - "type": "array", - "items": {"$ref": "other-schema"}, - "testSchema": { - "id": "/other-schema", - "type": "boolean" - } - }; - tv4.addSchema(examplePath, schema); - var data = [0, false]; - var valid = tv4.validate(data, examplePath); - - assert.length(tv4.missing, 1, "should have missing schema"); - assert.strictEqual(tv4.missing[0], examplePathBase + "/other-schema", "incorrect schema missing: " + tv4.missing[0]); - assert.isTrue(valid, "should pass, as remote schema not found"); - - //this.assert(tv4.missing.length == 1, "should have missing schema"); - //this.assert(tv4.missing[0] == examplePathBase + "/other-schema", "incorrect schema missing: " + tv4.missing[0]); - //this.assert(valid, "should pass, as remote schema not found"); - }); -}); - -describe("$refs to $refs", function () { - it("addSchema(), $ref", function () { - var schema = { - id: "http://example.com/schema", - some: { - other: {type: "number"} - }, - data: {'$ref': "#/some/other"} - }; - - tv4.addSchema(schema); - assert.isTrue(tv4.validate(42, {"$ref": "http://example.com/schema#/data"}), "42 valid"); - //assert.isFalse(tv4.validate(42, {"$ref": "http://example.com/schema#/data"}), "\"42\" invalid"); - - assert.length(tv4.missing, 0, "should have no missing schemas"); - }); - - it("Don't hang on circle", function () { - var schema = { - id: "http://example.com/schema", - ref1: {"$ref": "#/ref2"}, - ref2: {"$ref": "#/ref1"} - }; - - tv4.addSchema(schema); - var result = tv4.validateResult(42, "http://example.com/schema#/ref1"); - - assert.isFalse(result.valid, "not valid"); - assert.equal(result.error.code, tv4.errorCodes.CIRCULAR_REFERENCE, 'Error code correct'); - }); -}); - -describe("API 01", function () { - - it("validateResult returns object with appropriate properties", function () { - var data = {}; - var schema = {"type": "array"}; - tv4.error = null; - tv4.missing = []; - var result = tv4.validateResult(data, schema); - - assert.isFalse(result.valid, "result.valid === false"); - assert.isTypeOf(result.error, "object", "result.error is object"); - assert.isArray(result.missing, "result.missing is array"); - assert.isFalse(!!tv4.error, "tv4.error == null"); - - //this.assert(result.valid === false, "result.valid === false"); - //this.assert(typeof result.error == "object", "result.error is object"); - //this.assert(Array.isArray(result.missing), "result.missing is array"); - //this.assert(tv4.error == null, "tv4.error == null"); - }); -}); - -describe("API 02", function () { - - it("tv4.errorCodes exists", function () { - assert.isObject(tv4.errorCodes); - //return typeof tv4.errorCodes == "object"; - }); -}); - -describe("API 03", function () { - - it("getSchemaUris() on clean tv4 returns an empty array", function () { - var list = tv4.getSchemaUris(); - assert.isArray(list); - assert.length(list, 0); - }); - - it("getSchemaUris() returns newly added schema urls", function () { - tv4.addSchema("http://example.com/schema", {type: "object"}); - var list = tv4.getSchemaUris(); - assert.isArray(list); - assert.length(list, 1); - assert.strictEqual(list[0], "http://example.com/schema"); - }); - - it("getMissingUris() returns only missing items", function () { - var schema = { - "items": {"$ref": "http://example.com/schema/item#"} - }; - tv4.addSchema("http://example.com/schema/main", schema); - - var item = { - "id": "http://example.com/schema/item", - "type": "boolean" - }; - - var list; - list = tv4.getSchemaUris(); - assert.isArray(list); - assert.length(list, 1); - assert.includes(list, "http://example.com/schema/main", 'map has main uri'); - - list = tv4.getMissingUris(); - assert.isArray(list); - assert.length(list, 1); - assert.includes(list, "http://example.com/schema/item", 'map has item uri'); - - tv4.addSchema(item); - - list = tv4.getMissingUris(); - assert.isArray(list); - assert.length(list, 0); - }); - - it("getSchemaUris() optionally return filtered items", function () { - var schema = { - "items": {"$ref": "http://example.com/schema/item#"} - }; - tv4.addSchema("http://example.com/schema/main", schema); - - var list; - list = tv4.getSchemaUris(/schema\/main/); - assert.isArray(list); - assert.length(list, 1, 'list 1 main'); - assert.includes(list, "http://example.com/schema/main"); - - list = tv4.getMissingUris(/^https?/); - assert.isArray(list); - assert.length(list, 1, 'list 1 item'); - assert.includes(list, "http://example.com/schema/item"); - }); - - it("getSchemaUris() returns unique uris without fragment", function () { - var schema = { - "properties": { - "alpha": { - "$ref": "http://example.com/schema/lib#alpha" - }, - "beta": { - "$ref": "http://example.com/schema/lib#beta" - } - } - }; - tv4.addSchema("http://example.com/schema/main", schema); - var sub = { - "id": "http://example.com/schema/item", - "items": { - "type": "boolean" - } - }; - tv4.addSchema(sub); - - var list; - list = tv4.getSchemaUris(); - assert.isArray(list); - assert.length(list, 2); - assert.includes(list, "http://example.com/schema/main"); - assert.includes(list, "http://example.com/schema/item"); - - list = tv4.getMissingUris(); - assert.isArray(list); - assert.length(list, 1); - assert.includes(list, "http://example.com/schema/lib"); - }); - - - it("getSchemaMap() on clean tv4 returns an empty object", function () { - var map = tv4.getSchemaMap(); - assert.isObject(map); - assert.isNotArray(map); - var list = Object.keys(map); - assert.length(list, 0); - }); - - it("getSchemaMap() returns an object mapping uris to schemas", function () { - var schema = { - "properties": { - "alpha": { - "$ref": "http://example.com/schema/lib#alpha" - }, - "beta": { - "$ref": "http://example.com/schema/lib#beta" - } - } - }; - tv4.addSchema("http://example.com/schema/main", schema); - var sub = { - "id": "http://example.com/schema/item", - "items": { - "type": "boolean" - } - }; - tv4.addSchema(sub); - - var map; - map = tv4.getSchemaMap(); - assert.length(Object.keys(map), 2); - assert.ownPropertyVal(map, "http://example.com/schema/main", schema); - assert.ownPropertyVal(map, "http://example.com/schema/item", sub); - }); -}); - -describe("Multiple errors 01", function () { - - it("validateMultiple returns array of errors", function () { - var data = {}; - var schema = {"type": "array"}; - var result = tv4.validateMultiple(data, schema); - - assert.isFalse(result.valid, "data should not be valid"); - assert.strictEqual(typeof result.errors, "object", "result.errors must be object"); - assert.isNumber(result.errors.length, "result.errors have numberic length"); - - //-> weird: test says be object but it's an array - - //assert.isArray(result.errors, "result.errors must be array-like"); - //assert.isObject(result.errors, "result.errors must be object"); - - //this.assert(result.valid == false, "data should not be valid"); - //this.assert(typeof result.errors == "object" && typeof result.errors.length == "number", "result.errors must be array-like"); - }); - - it("validateMultiple has multiple entries", function () { - var data = {"a": 1, "b": 2}; - var schema = {"additionalProperties": {"type": "string"}}; - var result = tv4.validateMultiple(data, schema); - - assert.length(result.errors, 2, "should return two errors"); - //this.assert(result.errors.length == 2, "should return two errors"); - }); - - it("validateMultiple correctly fails anyOf", function () { - var data = {}; - var schema = { - "anyOf": [ - {"type": "string"}, - {"type": "integer"} - ] - }; - var result = tv4.validateMultiple(data, schema); - - assert.isFalse(result.valid, "should not validate"); - assert.length(result.errors, 1, "should list one error"); - - //this.assert(result.valid == false, "should not validate"); - //this.assert(result.errors.length == 1, "should list one error"); - }); - - it("validateMultiple correctly fails not", function () { - var data = {}; - var schema = { - "not": {"type": "object"} - }; - var result = tv4.validateMultiple(data, schema); - - assert.isFalse(result.valid, "should not validate"); - assert.length(result.errors, 1, "should list one error"); - - //this.assert(result.valid == false, "should not validate"); - //this.assert(result.errors.length == 1, "should list one error"); - }); - - it("validateMultiple correctly passes not", function () { - var data = {}; - var schema = { - "not": {"type": "string"} - }; - var result = tv4.validateMultiple(data, schema); - - assert.isTrue(result.valid, "should validate"); - assert.length(result.errors, 0, "no errors"); - - //this.assert(result.valid == true, "should validate"); - //this.assert(result.errors.length == 0, "no errors"); - }); - - it("validateMultiple correctly fails multiple oneOf", function () { - var data = 5; - var schema = { - "oneOf": [ - {"type": "integer"}, - {"type": "number"} - ] - }; - var result = tv4.validateMultiple(data, schema); - - assert.isFalse(result.valid, "should not validate"); - assert.length(result.errors, 1, "only one error"); - - //this.assert(result.valid == false, "should not validate"); - //this.assert(result.errors.length == 1, "only one error"); - }); - - it("validateMultiple handles multiple missing properties", function () { - var data = {}; - var schema = { - required: ["one", "two"] - }; - var result = tv4.validateMultiple(data, schema); - - assert.isFalse(result.valid, "should not validate"); - assert.length(result.errors, 2, "two errors"); - - //this.assert(result.valid == false, "should not validate"); - //this.assert(result.errors.length == 2, "exactly two errors, not " + result.errors.length); - }); -}); -describe("Multiple errors 02", function () { - - it("validateMultiple returns array of errors", function () { - var data = { - "alternatives": { - "option1": "pattern for option 1" - } - }; - - var schema = { - "type": "object", - "properties": { - "alternatives": { - "type": "object", - "description": "Some options", - "oneOf": [ - { - "properties": { - "option1": { - "type": "string", - "pattern": "^pattern for option 1$" - } - }, - "additionalProperties": false, - "required": [ - "option1" - ] - }, - { - "properties": { - "option2": { - "type": "string", - "pattern": "^pattern for option 2$" - } - }, - "additionalProperties": false, - "required": [ - "option2" - ] - }, - { - "properties": { - "option3": { - "type": "string", - "pattern": "^pattern for option 3$" - } - }, - "additionalProperties": false, - "required": [ - "option3" - ] - } - ] - } - } - }; - var result = tv4.validateMultiple(data, schema); - - assert.isTrue(result.valid, "data should be valid"); - assert.length(result.errors, 0, "should have no errors"); - - //this.assert(result.valid == true, "data should be valid"); - //this.assert(result.errors.length == 0, "should have no errors"); - }); -}); -describe("Recursive objects 01", function () { - it("validate and variants do not choke on recursive objects", function () { - var itemA = {}; - var itemB = { a: itemA }; - itemA.b = itemB; - var aSchema = { properties: { b: { $ref: 'bSchema' }}}; - var bSchema = { properties: { a: { $ref: 'aSchema' }}}; - tv4.addSchema('aSchema', aSchema); - tv4.addSchema('bSchema', bSchema); - tv4.validate(itemA, aSchema, true); - tv4.validate(itemA, aSchema, function () {}, true); - tv4.validateResult(itemA, aSchema, true); - tv4.validateMultiple(itemA, aSchema, true); - }); -}); - -// We don't handle this in general (atm), but some users have had particular problems with things added to the Array prototype -describe("Recursive schemas", function () { - it("due to extra Array.prototype entries", function () { - var testSchema = { - items: [] - }; - Array.prototype._testSchema = testSchema; - - // Failure mode will be a RangeError (stack size limit) - tv4.addSchema('testSchema', testSchema); - - delete Array.prototype._testSchema; - }); -}); - -describe("Registering custom validator", function () { - it("Allows registration of custom validator codes for \"format\" values", function () { - tv4.addFormat('test-format', function () { - return null; - }); - }); - - it("Custom validator is correctly selected", function () { - tv4.addFormat('test-format', function (data) { - if (data !== "test string") { - return "string does not match"; - } - }); - - var schema = {format: 'test-format'}; - var data1 = "test string"; - var data2 = "other string"; - - assert.isTrue(tv4.validate(data1, schema)); - assert.isFalse(tv4.validate(data2, schema)); - assert.includes(tv4.error.message, 'string does not match'); - }); - - it("Custom validator object error format", function () { - tv4.addFormat('test-format', function (data) { - if (data !== "test string") { - return { - dataPath: "", - schemaPath: "/flah", - message: "Error message" - }; - } - }); - - var schema = {format: 'test-format'}; - var data1 = "test string"; - var data2 = "other string"; - - assert.isTrue(tv4.validate(data1, schema)); - assert.isFalse(tv4.validate(data2, schema)); - assert.includes(tv4.error.message, 'Error message'); - assert.equal(tv4.error.schemaPath, '/flah'); - }); - - it("Register multiple using object", function () { - tv4.addFormat({ - 'test1': function () {return 'break 1';}, - 'test2': function () {return 'break 2';} - }); - - var schema1 = {format: 'test1'}; - var result1 = tv4.validateResult("test string", schema1); - assert.isFalse(result1.valid); - assert.includes(result1.error.message, 'break 1'); - - var schema2 = {format: 'test2'}; - var result2 = tv4.validateResult("test string", schema2); - assert.isFalse(result2.valid); - assert.includes(result2.error.message, 'break 2'); - }); -}); - -describe("Ban unknown properties 01", function () { - it("Additional argument to ban additional properties", function () { - var schema = { - properties: { - propA: {}, - propB: {} - } - }; - var data = { - propA: true, - propUnknown: true - }; - var data2 = { - propA: true - }; - - var result = tv4.validateMultiple(data, schema, false, true); - assert.isFalse(result.valid, "Must not be valid"); - - var result2 = tv4.validateMultiple(data2, schema, false, true); - assert.isTrue(result2.valid, "Must still validate"); - }); - - it("Works with validateResult()", function () { - var schema = { - properties: { - propA: {}, - propB: {} - } - }; - var data = { - propA: true, - propUnknown: true - }; - var data2 = { - propA: true - }; - - var result = tv4.validateResult(data, schema, false, true); - assert.isFalse(result.valid, "Must not be valid"); - - var result2 = tv4.validateResult(data2, schema, false, true); - assert.isTrue(result2.valid, "Must be valid"); - }); - - it("Do not complain if additionalArguments is specified", function () { - var schema = { - properties: { - propA: {}, - propB: {} - }, - additionalProperties: true - }; - var data = { - propA: true, - propUnknown: true - }; - var data2 = { - propA: true - }; - - var result = tv4.validateMultiple(data, schema, false, true); - assert.isTrue(result.valid, "Must be valid"); - - var result2 = tv4.validateMultiple(data2, schema, false, true); - assert.isTrue(result2.valid, "Must still validate"); - }); -}); - -describe("Ban unknown properties 02", function () { - it("Do not track property definitions from \"not\"", function () { - var schema = { - "not": { - properties: { - propA: {"type": "string"}, - } - } - }; - var data = { - propA: true, - }; - - var result = tv4.validateMultiple(data, schema, false, true); - assert.isFalse(result.valid, "Must not be valid"); - }); - - it("Do not track property definitions from unselected \"oneOf\"", function () { - var schema = { - "oneOf": [ - { - "type": "object", - "properties": { - "propA": {"type": "string"} - } - }, - { - "type": "object", - "properties": { - "propB": {"type": "boolean"} - } - } - ] - }; - var data = { - propA: true, - propB: true - }; - - var result = tv4.validateMultiple(data, schema, false, true); - assert.isFalse(result.valid, "Must not be valid"); - - var result2 = tv4.validateMultiple(data, schema, false); - assert.isTrue(result2.valid, "Must still be valid without flag"); - }); - - - it("Do not track property definitions from unselected \"anyOf\"", function () { - var schema = { - "anyOf": [ - { - "type": "object", - "properties": { - "propA": {"type": "string"} - } - }, - { - "type": "object", - "properties": { - "propB": {"type": "boolean"} - } - } - ] - }; - var data = { - propA: true, - propB: true - }; - - var result = tv4.validateMultiple(data, schema, false, true); - assert.isFalse(result.valid, "Must not be valid"); - - var result2 = tv4.validateMultiple(data, schema, false); - assert.isTrue(result2.valid, "Must still be valid without flag"); - }); -}); - -describe("Fill dataPath for \"required\" (GitHub Issue #103)", function () { - it("Blank for first-level properties", function () { - var schema = { - required: ['A'] - }; - var data = {}; - - var result = tv4.validateMultiple(data, schema, false, true); - assert.isFalse(result.valid, "Must not be valid"); - assert.deepEqual(result.errors[0].dataPath, ''); - }); - - it("Filled for second-level properties", function () { - var schema = { - properties: { - "foo": { - required: ["bar"] - } - } - }; - var data = {"foo": {}}; - - var result = tv4.validateMultiple(data, schema, false, true); - assert.isFalse(result.valid, "Must not be valid"); - assert.deepEqual(result.errors[0].dataPath, '/foo'); - }); -}); - -describe("Valid schemaPath for \"oneOf\" (GitHub Issue #117)", function () { - it("valid schemaPath in error (simple types)", function () { - var data = {}; - var schema = { - "oneOf": [ - { "type": "string" }, - { "type": "bool" } - ] - }; - - var result = tv4.validateMultiple(data, schema); - var suberr = result.errors[0].subErrors; - assert.equal(suberr[0].schemaPath, '/oneOf/0/type'); - assert.equal(suberr[1].schemaPath, '/oneOf/1/type'); - }); - - it("valid schemaPath in error (required properties)", function () { - /* Test case provided on GitHub Issue #117 */ - var data = {}; - var schema = { - "$schema": "http://json-schema.org/draft-04/schema#", - "oneOf": [ - { - "type": "object", - "properties": { - "data": { - "type": "object" - } - }, - "required": ["data"] - }, - { - "type": "object", - "properties": { - "error": { - "type": "object" - } - }, - "required": ["error"] - } - ] - }; - - var result = tv4.validateMultiple(data, schema); - var suberr = result.errors[0].subErrors; - assert.equal(suberr[0].schemaPath, "/oneOf/0/required/0"); - assert.equal(suberr[1].schemaPath, "/oneOf/1/required/0"); - }); -}); - -describe("Register custom keyword", function () { - it("function called", function () { - var schema = { - customKeyword: "A" - }; - var data = {}; - - tv4.defineKeyword('customKeyword', function () { - return "Custom failure"; - }); - - var result = tv4.validateMultiple(data, schema, false, true); - assert.isFalse(result.valid, "Must not be valid"); - assert.deepEqual(result.errors[0].message, 'Keyword failed: customKeyword (Custom failure)'); - }); - - it("custom error code", function () { - var schema = { - customKeywordFoo: "A" - }; - var data = "test test test"; - - tv4.defineKeyword('customKeywordFoo', function (data, value) { - return { - code: 'CUSTOM_KEYWORD_FOO', - message: {data: data, value: value} - }; - }); - tv4.defineError('CUSTOM_KEYWORD_FOO', 123456789, "{value}: {data}"); - - var result = tv4.validateMultiple(data, schema, false, true); - assert.isFalse(result.valid, "Must not be valid"); - assert.deepEqual(result.errors[0].message, 'A: test test test'); - assert.deepEqual(result.errors[0].code, 123456789); - }); - - it("custom error code (numeric)", function () { - var schema = { - customKeywordBar: "A" - }; - var data = "test test test"; - - tv4.defineKeyword('customKeywordBar', function (data, value) { - return { - code: 1234567890, - message: {data: data, value: value} - }; - }); - tv4.defineError('CUSTOM_KEYWORD_BAR', 1234567890, "{value}: {data}"); - - var result = tv4.validateMultiple(data, schema, false, true); - assert.isFalse(result.valid, "Must not be valid"); - assert.deepEqual(result.errors[0].message, 'A: test test test'); - assert.deepEqual(result.errors[0].code, 1234567890); - }); - - it("restrict custom error codes", function () { - assert.throws(function () { - tv4.defineError('CUSTOM_KEYWORD_BLAH', 9999, "{value}: {data}"); - }); - }); - - it("restrict custom error names", function () { - assert.throws(function () { - tv4.defineError('doesnotmatchpattern', 10002, "{value}: {data}"); - }); - }); - - it("can't defined the same code twice", function () { - assert.throws(function () { - tv4.defineError('CUSTOM_ONE', 10005, "{value}: {data}"); - tv4.defineError('CUSTOM_TWO', 10005, "{value}: {data}"); - }); - }); - - it("function can return existing (non-custom) codes", function () { - var schema = { - "type": "object", - "properties": { - "aStringValue": { - "type": "string", - "my-custom-keyword": "something" - }, - "aBooleanValue": { - "type": "boolean" - } - } - }; - var data = { - "aStringValue": "a string", - "aBooleanValue": true - }; - - tv4.defineKeyword('my-custom-keyword', function () { - return {code: 0, message: "test"}; - }); - - var result = tv4.validateMultiple(data, schema, false, true); - assert.equal(result.errors[0].code, tv4.errorCodes.INVALID_TYPE); - }); - - it("function only called when keyword present", function () { - var schema = { - "type": "object", - "properties": { - "aStringValue": { - "type": "string", - "my-custom-keyword": "something" - }, - "aBooleanValue": { - "type": "boolean" - } - } - }; - var data = { - "aStringValue": "a string", - "aBooleanValue": true - }; - - var callCount = 0; - tv4.defineKeyword('my-custom-keyword', function () { - callCount++; - }); - - tv4.validateMultiple(data, schema, false, true); - assert.deepEqual(callCount, 1, "custom function must be called exactly once"); - }); - - it("function knows dataPointerPath", function () { - var schema = { - "type": "object", - "properties": { - "obj": { - "type": "object", - "properties":{ - "test":{ - "my-custom-keyword": "something", - "type":"string" - } - } - } - } - }; - var data = { "obj":{ "test": "a string"} }; - - var path = null; - tv4.defineKeyword('my-custom-keyword', function (data,value,schema,dataPointerPath) { - path = dataPointerPath; - }); - - tv4.validateMultiple(data, schema, false, true); - assert.strictEqual(path, "/obj/test", "custom function must know its context path"); - }); -}); - -describe("Load language file", function () { - if (typeof process !== 'object' || typeof require !== 'function') { - it.skip("commonjs language", function () { - // dummy - }); - } - else { - it("commonjs language: de", function () { - var tv4 = require('../lang/de'); - - tv4.language('de'); - - var schema = { - properties: { - intKey: {"type": "integer"} - } - }; - var res = tv4.validateResult({intKey: 'bad'}, schema); - assert.isFalse(res.valid); - assert.equal(res.error.message, 'Ungültiger Typ: string (erwartet wurde: integer)'); - }); - } -}); - -describe("Custom error reporting", function () { - it('provides custom message', function () { - var api = tv4.freshApi(); - - api.setErrorReporter(function (error, data, schema) { - assert.deepEqual(data, 5); - assert.deepEqual(schema, {minimum: 10}); - return 'Code: ' + error.code; - }); - - var res = api.validateResult(5, {minimum: 10}); - assert.isFalse(res.valid); - assert.equal(res.error.message, 'Code: 101'); - }); - - it('falls back to default', function () { - var api = tv4.freshApi(); - - api.setErrorReporter(function (error, data, schema) { - assert.deepEqual(data, 5); - assert.deepEqual(schema, {minimum: 10}); - return null; - }); - - var res = api.validateResult(5, {minimum: 10}); - assert.isFalse(res.valid); - assert.isString(res.error.message); - }); -}); - -describe("Load language file", function () { - it("commonjs language: de", function () { - var freshTv4 = tv4.freshApi(); - - freshTv4.addSchema('/polymorphic', { - type: "object", - properties: { - "type": {type: "string"} - }, - required: ["type"], - links: [{ - rel: "describedby", - href: "/schemas/{type}.json" - }] - }); - - var res = freshTv4.validateResult({type: 'monkey'}, "/polymorphic"); - assert.isTrue(res.valid); - assert.includes(res.missing, "/schemas/monkey.json"); - - freshTv4.addSchema('/schemas/tiger.json', { - properties: { - "stripes": {"type": "integer", "minimum": 1} - }, - required: ["stripes"] - }); - - var res2 = freshTv4.validateResult({type: 'tiger', stripes: -1}, "/polymorphic"); - assert.isFalse(res2.valid); - assert.deepEqual(res2.missing.length, 0, "no schemas should be missing"); - - var res3 = freshTv4.validateResult({type: 'tiger', stripes: 50}, "/polymorphic"); - assert.isTrue(res3.valid); - }); -}); - -describe("Issue 108", function () { - - it("Normalise schemas even inside $ref", function () { - - var schema = { - "id": "http://example.com/schema" + Math.random(), - "$ref": "#whatever", - "properties": { - "foo": { - "id": "#test", - "type": "string" - } - } - }; - - tv4.addSchema(schema); - - var result = tv4.validateMultiple("test data", schema.id + '#test'); - assert.isTrue(result.valid, 'validateMultiple() should return valid'); - assert.deepEqual(result.missing.length, 0, 'should have no missing schemas'); - - var result2 = tv4.validateMultiple({"foo":"bar"}, schema.id + '#test'); - assert.isFalse(result2.valid, 'validateMultiple() should return invalid'); - assert.deepEqual(result2.missing.length, 0, 'should have no missing schemas'); - }); -}); -describe("Issue 109", function () { - - it("Don't break on null values with banUnknownProperties", function () { - - var schema = { - "type": "object", - "properties": { - "foo": { - "type": "object", - "additionalProperties": {"type": "string"} - } - } - }; - - var data = {foo: null}; - - var result = tv4.validateMultiple(data, schema, true, true); - - assert.isFalse(result.valid, 'validateMultiple() should return invalid'); - }); -}); -describe("Issue 32", function () { - - it("Example from GitHub issue #32", function () { - var subSchema = { - "title": "SubSchema", - "type": "object", - "properties": { - "attribute": {"type": "string"} - }, - "additionalProperties": false - }; - - var mySchema = { - "title": "My Schema", - "type": "object", - "properties": { - "name": {"type": "string"}, - "subschemas": {"type": "array", "items": {"$ref": "#/definitions/subSchema"}} - }, - "definitions": { - "subSchema": subSchema - }, - "additionalProperties": false - }; - - /* unused variable - var data1 = { - "name": "Joe", - "subschemas": [ - {"attribute": "Hello"} - ] - };*/ - - var addlPropInSubSchema = { - "name": "Joe", - "subschemas": [ - {"attribute": "Hello", "extra": "Not Allowed"} - ] - }; - - // Usage 1 - var expectedUsage1Result = tv4.validate(addlPropInSubSchema, mySchema); - assert.isFalse(expectedUsage1Result, 'plain validate should fail'); - //this.assert(!expectedUsage1Result, 'plain validate should fail'); - - // Usage 2 - var expectedUsage2Result = tv4.validateResult(addlPropInSubSchema, mySchema); - assert.isFalse(expectedUsage2Result.valid, 'validateResult should fail'); - - //-> this has a typo that didn't show because of type conversion! - - //this.assert(!expectedUsage1Result.valud, 'validateResult should fail'); - - // Usage 3 - var expectedMultipleErrorResult = tv4.validateMultiple(addlPropInSubSchema, mySchema); - assert.isFalse(expectedMultipleErrorResult.valid, 'validateMultiple should fail'); - assert.length(expectedMultipleErrorResult.errors, 1, 'validateMultiple should have exactly one error'); - //this.assert(!expectedMultipleErrorResult.valid, 'validateMultiple should fail'); - //this.assert(expectedMultipleErrorResult.errors.length == 1, 'validateMultiple should have exactly one error'); - }); -}); -describe("Issue 67", function () { - - it("Example from GitHub issue #67", function () { - // Make sure null values don't trip up the normalisation - tv4.validate(null, {default: null}); - }); -}); -describe("Issue 86", function () { - - it("Example from GitHub issue #86", function () { - // The "checkRecursive" flag skips some data nodes if it actually needs to check the same data/schema pair twice - - var schema = { - "type": "object", - "properties": { - "shape": { - "oneOf": [ - { "$ref": "#/definitions/squareSchema" }, - { "$ref": "#/definitions/circleSchema" } - ] - } - }, - "definitions": { - "squareSchema": { - "type": "object", - "properties": { - "thetype": { - "type": "string", - "enum": ["square"] - }, - "colour": {}, - "shade": {}, - "boxname": { - "type": "string" - } - }, - "oneOf": [ - { "$ref": "#/definitions/colourSchema" }, - { "$ref": "#/definitions/shadeSchema" } - ], - "required": ["thetype", "boxname"], - "additionalProperties": false - }, - "circleSchema": { - "type": "object", - "properties": { - "thetype": { - "type": "string", - "enum": ["circle"] - }, - "colour": {}, - "shade": {} - }, - "oneOf": [ - { "$ref": "#/definitions/colourSchema" }, - { "$ref": "#/definitions/shadeSchema" } - ], - "additionalProperties": false - }, - "colourSchema": { - "type": "object", - "properties": { - "colour": { - "type": "string" - }, - "shade": { - "type": "null" - } - } - }, - "shadeSchema": { - "type": "object", - "properties": { - "shade": { - "type": "string" - }, - "colour": { - "type": "null" - } - } - } - } - }; - - - var circle = { - "shape": { - "thetype": "circle", - "shade": "red" - } - }; - - var simpleResult = tv4.validate(circle, schema, true); - var multipleResult = tv4.validateMultiple(circle, schema, true); - - assert.isTrue(simpleResult, 'validate() should return valid'); - assert.isTrue(multipleResult.valid, 'validateMultiple() should return valid'); - }); - - it("Second example", function () { - var schema = { - "allOf": [ - { - "oneOf": [ - {"$ref": "#/definitions/option1"}, - {"$ref": "#/definitions/option2"}, - ] - }, - { - "not": {"$ref": "#/definitions/option2"} - } - ], - "definitions": { - "option1": { - "allOf": [{"type": "string"}] - }, - "option2": { - "allOf": [{"type": "number"}] - } - } - }; - - var simpleResult = tv4.validate("test", schema, true); - - assert.isTrue(simpleResult, "validate() should return valid"); - }); -}); -describe("Enum object/null failure", function () { - - it("Doesn't crash", function () { - - var schema = { - "type": "object", - "required": ["value"], - "properties": { - "value": { - "enum": [6, "foo", [], true, {"foo": 12}] - } - } - }; - - var data = {value: null}; // Somehow this is only a problem when a *property* is null, not the root - - var result = tv4.validateMultiple(data, schema); - - assert.isFalse(result.valid, 'validateMultiple() should return invalid'); - }); -}); -//@ sourceMappingURL=all_concat.js.map \ No newline at end of file diff --git a/test/all_concat.js.map b/test/all_concat.js.map deleted file mode 100644 index 3cf1970..0000000 --- a/test/all_concat.js.map +++ /dev/null @@ -1,64 +0,0 @@ -{ - "version": 3, - "sources": [ - "test/_header.js", - "test/tests/00 - Core/01 - utils.js", - "test/tests/00 - Core/02 - duplicateApi.js", - "test/tests/00 - Core/03 - resetAndDrop.js", - "test/tests/00 - Core/04 - error.js", - "test/tests/01 - Any types/01 - type.js", - "test/tests/01 - Any types/02 - enum.js", - "test/tests/02 - Numeric/01 - multipleOf.js", - "test/tests/02 - Numeric/02 - min-max.js", - "test/tests/02 - Numeric/03 - NaN.js", - "test/tests/03 - Strings/01 - min-max length.js", - "test/tests/03 - Strings/02 - pattern.js", - "test/tests/04 - Arrays/01 - min-max length.js", - "test/tests/04 - Arrays/02 - uniqueItems.js", - "test/tests/04 - Arrays/03 - items (plain).js", - "test/tests/04 - Arrays/04 - items (tuple-typing).js", - "test/tests/04 - Arrays/05 - additionalItems.js", - "test/tests/05 - Objects/01 - min-max properties.js", - "test/tests/05 - Objects/02 - required.js", - "test/tests/05 - Objects/03 - properties.js", - "test/tests/05 - Objects/04 - patternProperties.js", - "test/tests/05 - Objects/05 - additionalProperties.js", - "test/tests/05 - Objects/06 - dependencies.js", - "test/tests/06 - Combinations/01 - allOf.js", - "test/tests/06 - Combinations/02- anyOf.js", - "test/tests/06 - Combinations/03 - oneOf.js", - "test/tests/06 - Combinations/04 - not.js", - "test/tests/07 - $ref/01 - normalise.js", - "test/tests/07 - $ref/02 - list missing URLs.js", - "test/tests/07 - $ref/03 - getSchema.js", - "test/tests/07 - $ref/04 - ref.js", - "test/tests/07 - $ref/05 - inline addressing.js", - "test/tests/07 - $ref/06 - multiple refs.js", - "test/tests/08 - API/01 - validateResult.js", - "test/tests/08 - API/02 - errorCodes existence.js", - "test/tests/08 - API/03 - get schema URIs.js", - "test/tests/09 - Multiple errors/01 - validateMultiple.js", - "test/tests/09 - Multiple errors/02 - validateMultiple 2.js", - "test/tests/10 - Recursive objects/01 - validate.js", - "test/tests/10 - Recursive objects/02 - scan.js", - "test/tests/11 - format/01 - register validator.js", - "test/tests/12 - banUnknownProperties/01 - simple case.js", - "test/tests/12 - banUnknownProperties/02 - composite behaviour.js", - "test/tests/13 - error reporting/01 - required dataPath.js", - "test/tests/13 - error reporting/02 - oneOf schemaPath.js", - "test/tests/14 - custom validation/01 - custom keywords.js", - "test/tests/15 - language/01 - language file.js", - "test/tests/15 - language/02 - custom reporter.js", - "test/tests/16 - hypermedia/02 - describedby link.js", - "test/tests/Misc/Issue 108.js", - "test/tests/Misc/Issue 109.js", - "test/tests/Misc/Issue 32.js", - "test/tests/Misc/Issue 67.js", - "test/tests/Misc/Issue 86.js", - "test/tests/Misc/enum null failure.js" - ], - "names": [], - "mappings": "AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AChJA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACxBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACvBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACnCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC/BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AChEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC3EA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,G;AC7BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACzDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACpDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC7CA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,G;AC7BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACpCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACfA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACvBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACzBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACrDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC7BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACfA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACzBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC3CA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,G;AC7DA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC3EA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACzBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC1BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACxCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACnBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACnEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,G;AC/BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,G;AC1FA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACvCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACjDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC9BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACnBA;AACA;AACA;AACA;AACA;AACA;AACA;;ACNA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACnIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,G;ACxGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,G;AChEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACdA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACbA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC5DA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACnEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC3EA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC1BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AChDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACzJA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACtBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC5BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AClCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,G;ACzBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,G;ACpBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,G;AC5DA;AACA;AACA;AACA;AACA;AACA;AACA,G;ACNA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,G;ACvHA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,G", - "file": "all_concat.js", - "sourceRoot": "" -} \ No newline at end of file diff --git a/test/tests/11 - format/01 - register validator.js b/test/tests/11 - format/01 - register validator.js index 7259f00..d8ab5c7 100644 --- a/test/tests/11 - format/01 - register validator.js +++ b/test/tests/11 - format/01 - register validator.js @@ -73,4 +73,28 @@ describe("Registering custom validator", function () { assert.isTrue(tv4.validate(data1, schema)); assert.isTrue(tv4.validate(data2, schema)); }); + + it("Validator should be skipped for null (undefined) enum values", function () { + var schema = { + "type": "object", + "properties": { + "a": { + type: "string" + }, + "b": { + "type": ["string", "null"], + "enum": ['one', 'two', 'three'] + } + } + }; + var data1 = {a: "something", b: "test"}; + var data2 = {a: "something", b: "one"}; + var data3 = {a: "something", b: null}; + var data4 = {a: "something"}; + + assert.isFalse(tv4.validate(data1, schema)); + assert.isTrue(tv4.validate(data2, schema)); + assert.isTrue(tv4.validate(data3, schema)); + assert.isTrue(tv4.validate(data4, schema)); + }); }); diff --git a/test/tests/Misc/enum null failure.js b/test/tests/Misc/enum null failure.js index cfe51a4..eeae1a2 100644 --- a/test/tests/Misc/enum null failure.js +++ b/test/tests/Misc/enum null failure.js @@ -6,16 +6,21 @@ describe("Enum object/null failure", function () { "type": "object", "required": ["value"], "properties": { + "key": { + "type": "string" + }, "value": { "enum": [6, "foo", [], true, {"foo": 12}] } } }; - var data = {value: null}; // Somehow this is only a problem when a *property* is null, not the root - + var data = {key: "test", value: null}; // Somehow this is only a problem when a *property* is null, not the root var result = tv4.validateMultiple(data, schema); - + assert.isFalse(result.valid, 'validateMultiple() should return invalid'); + + data = {key: "test"}; // Undefined required property. + result = tv4.validateMultiple(data, schema); assert.isFalse(result.valid, 'validateMultiple() should return invalid'); }); }); \ No newline at end of file diff --git a/tv4.js b/tv4.js deleted file mode 100644 index 614f443..0000000 --- a/tv4.js +++ /dev/null @@ -1,1677 +0,0 @@ -/* -Author: Geraint Luff and others -Year: 2013 - -This code is released into the "public domain" by its author(s). Anybody may use, alter and distribute the code without restriction. The author makes no guarantees, and takes no liability of any kind for use of this code. - -If you find a bug or make an improvement, it would be courteous to let the author know, but it is not compulsory. -*/ -(function (global, factory) { - if (typeof define === 'function' && define.amd) { - // AMD. Register as an anonymous module. - define([], factory); - } else if (typeof module !== 'undefined' && module.exports){ - // CommonJS. Define export. - module.exports = factory(); - } else { - // Browser globals - global.tv4 = factory(); - } -}(this, function () { - -// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FObject%2Fkeys -if (!Object.keys) { - Object.keys = (function () { - var hasOwnProperty = Object.prototype.hasOwnProperty, - hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'), - dontEnums = [ - 'toString', - 'toLocaleString', - 'valueOf', - 'hasOwnProperty', - 'isPrototypeOf', - 'propertyIsEnumerable', - 'constructor' - ], - dontEnumsLength = dontEnums.length; - - return function (obj) { - if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) { - throw new TypeError('Object.keys called on non-object'); - } - - var result = []; - - for (var prop in obj) { - if (hasOwnProperty.call(obj, prop)) { - result.push(prop); - } - } - - if (hasDontEnumBug) { - for (var i=0; i < dontEnumsLength; i++) { - if (hasOwnProperty.call(obj, dontEnums[i])) { - result.push(dontEnums[i]); - } - } - } - return result; - }; - })(); -} -// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create -if (!Object.create) { - Object.create = (function(){ - function F(){} - - return function(o){ - if (arguments.length !== 1) { - throw new Error('Object.create implementation only accepts one parameter.'); - } - F.prototype = o; - return new F(); - }; - })(); -} -// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FArray%2FisArray -if(!Array.isArray) { - Array.isArray = function (vArg) { - return Object.prototype.toString.call(vArg) === "[object Array]"; - }; -} -// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FArray%2FindexOf -if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) { - if (this === null) { - throw new TypeError(); - } - var t = Object(this); - var len = t.length >>> 0; - - if (len === 0) { - return -1; - } - var n = 0; - if (arguments.length > 1) { - n = Number(arguments[1]); - if (n !== n) { // shortcut for verifying if it's NaN - n = 0; - } else if (n !== 0 && n !== Infinity && n !== -Infinity) { - n = (n > 0 || -1) * Math.floor(Math.abs(n)); - } - } - if (n >= len) { - return -1; - } - var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); - for (; k < len; k++) { - if (k in t && t[k] === searchElement) { - return k; - } - } - return -1; - }; -} - -// Grungey Object.isFrozen hack -if (!Object.isFrozen) { - Object.isFrozen = function (obj) { - var key = "tv4_test_frozen_key"; - while (obj.hasOwnProperty(key)) { - key += Math.random(); - } - try { - obj[key] = true; - delete obj[key]; - return false; - } catch (e) { - return true; - } - }; -} -// Based on: https://github.com/geraintluff/uri-templates, but with all the de-substitution stuff removed - -var uriTemplateGlobalModifiers = { - "+": true, - "#": true, - ".": true, - "/": true, - ";": true, - "?": true, - "&": true -}; -var uriTemplateSuffices = { - "*": true -}; - -function notReallyPercentEncode(string) { - return encodeURI(string).replace(/%25[0-9][0-9]/g, function (doubleEncoded) { - return "%" + doubleEncoded.substring(3); - }); -} - -function uriTemplateSubstitution(spec) { - var modifier = ""; - if (uriTemplateGlobalModifiers[spec.charAt(0)]) { - modifier = spec.charAt(0); - spec = spec.substring(1); - } - var separator = ""; - var prefix = ""; - var shouldEscape = true; - var showVariables = false; - var trimEmptyString = false; - if (modifier === '+') { - shouldEscape = false; - } else if (modifier === ".") { - prefix = "."; - separator = "."; - } else if (modifier === "/") { - prefix = "/"; - separator = "/"; - } else if (modifier === '#') { - prefix = "#"; - shouldEscape = false; - } else if (modifier === ';') { - prefix = ";"; - separator = ";"; - showVariables = true; - trimEmptyString = true; - } else if (modifier === '?') { - prefix = "?"; - separator = "&"; - showVariables = true; - } else if (modifier === '&') { - prefix = "&"; - separator = "&"; - showVariables = true; - } - - var varNames = []; - var varList = spec.split(","); - var varSpecs = []; - var varSpecMap = {}; - for (var i = 0; i < varList.length; i++) { - var varName = varList[i]; - var truncate = null; - if (varName.indexOf(":") !== -1) { - var parts = varName.split(":"); - varName = parts[0]; - truncate = parseInt(parts[1], 10); - } - var suffices = {}; - while (uriTemplateSuffices[varName.charAt(varName.length - 1)]) { - suffices[varName.charAt(varName.length - 1)] = true; - varName = varName.substring(0, varName.length - 1); - } - var varSpec = { - truncate: truncate, - name: varName, - suffices: suffices - }; - varSpecs.push(varSpec); - varSpecMap[varName] = varSpec; - varNames.push(varName); - } - var subFunction = function (valueFunction) { - var result = ""; - var startIndex = 0; - for (var i = 0; i < varSpecs.length; i++) { - var varSpec = varSpecs[i]; - var value = valueFunction(varSpec.name); - if (value === null || value === undefined || (Array.isArray(value) && value.length === 0) || (typeof value === 'object' && Object.keys(value).length === 0)) { - startIndex++; - continue; - } - if (i === startIndex) { - result += prefix; - } else { - result += (separator || ","); - } - if (Array.isArray(value)) { - if (showVariables) { - result += varSpec.name + "="; - } - for (var j = 0; j < value.length; j++) { - if (j > 0) { - result += varSpec.suffices['*'] ? (separator || ",") : ","; - if (varSpec.suffices['*'] && showVariables) { - result += varSpec.name + "="; - } - } - result += shouldEscape ? encodeURIComponent(value[j]).replace(/!/g, "%21") : notReallyPercentEncode(value[j]); - } - } else if (typeof value === "object") { - if (showVariables && !varSpec.suffices['*']) { - result += varSpec.name + "="; - } - var first = true; - for (var key in value) { - if (!first) { - result += varSpec.suffices['*'] ? (separator || ",") : ","; - } - first = false; - result += shouldEscape ? encodeURIComponent(key).replace(/!/g, "%21") : notReallyPercentEncode(key); - result += varSpec.suffices['*'] ? '=' : ","; - result += shouldEscape ? encodeURIComponent(value[key]).replace(/!/g, "%21") : notReallyPercentEncode(value[key]); - } - } else { - if (showVariables) { - result += varSpec.name; - if (!trimEmptyString || value !== "") { - result += "="; - } - } - if (varSpec.truncate != null) { - value = value.substring(0, varSpec.truncate); - } - result += shouldEscape ? encodeURIComponent(value).replace(/!/g, "%21"): notReallyPercentEncode(value); - } - } - return result; - }; - subFunction.varNames = varNames; - return { - prefix: prefix, - substitution: subFunction - }; -} - -function UriTemplate(template) { - if (!(this instanceof UriTemplate)) { - return new UriTemplate(template); - } - var parts = template.split("{"); - var textParts = [parts.shift()]; - var prefixes = []; - var substitutions = []; - var varNames = []; - while (parts.length > 0) { - var part = parts.shift(); - var spec = part.split("}")[0]; - var remainder = part.substring(spec.length + 1); - var funcs = uriTemplateSubstitution(spec); - substitutions.push(funcs.substitution); - prefixes.push(funcs.prefix); - textParts.push(remainder); - varNames = varNames.concat(funcs.substitution.varNames); - } - this.fill = function (valueFunction) { - var result = textParts[0]; - for (var i = 0; i < substitutions.length; i++) { - var substitution = substitutions[i]; - result += substitution(valueFunction); - result += textParts[i + 1]; - } - return result; - }; - this.varNames = varNames; - this.template = template; -} -UriTemplate.prototype = { - toString: function () { - return this.template; - }, - fillFromObject: function (obj) { - return this.fill(function (varName) { - return obj[varName]; - }); - } -}; -var ValidatorContext = function ValidatorContext(parent, collectMultiple, errorReporter, checkRecursive, trackUnknownProperties) { - this.missing = []; - this.missingMap = {}; - this.formatValidators = parent ? Object.create(parent.formatValidators) : {}; - this.schemas = parent ? Object.create(parent.schemas) : {}; - this.collectMultiple = collectMultiple; - this.errors = []; - this.handleError = collectMultiple ? this.collectError : this.returnError; - if (checkRecursive) { - this.checkRecursive = true; - this.scanned = []; - this.scannedFrozen = []; - this.scannedFrozenSchemas = []; - this.scannedFrozenValidationErrors = []; - this.validatedSchemasKey = 'tv4_validation_id'; - this.validationErrorsKey = 'tv4_validation_errors_id'; - } - if (trackUnknownProperties) { - this.trackUnknownProperties = true; - this.knownPropertyPaths = {}; - this.unknownPropertyPaths = {}; - } - this.errorReporter = errorReporter || defaultErrorReporter('en'); - if (typeof this.errorReporter === 'string') { - throw new Error('debug'); - } - this.definedKeywords = {}; - if (parent) { - for (var key in parent.definedKeywords) { - this.definedKeywords[key] = parent.definedKeywords[key].slice(0); - } - } -}; -ValidatorContext.prototype.defineKeyword = function (keyword, keywordFunction) { - this.definedKeywords[keyword] = this.definedKeywords[keyword] || []; - this.definedKeywords[keyword].push(keywordFunction); -}; -ValidatorContext.prototype.createError = function (code, messageParams, dataPath, schemaPath, subErrors, data, schema) { - var error = new ValidationError(code, messageParams, dataPath, schemaPath, subErrors); - error.message = this.errorReporter(error, data, schema); - return error; -}; -ValidatorContext.prototype.returnError = function (error) { - return error; -}; -ValidatorContext.prototype.collectError = function (error) { - if (error) { - this.errors.push(error); - } - return null; -}; -ValidatorContext.prototype.prefixErrors = function (startIndex, dataPath, schemaPath) { - for (var i = startIndex; i < this.errors.length; i++) { - this.errors[i] = this.errors[i].prefixWith(dataPath, schemaPath); - } - return this; -}; -ValidatorContext.prototype.banUnknownProperties = function (data, schema) { - for (var unknownPath in this.unknownPropertyPaths) { - var error = this.createError(ErrorCodes.UNKNOWN_PROPERTY, {path: unknownPath}, unknownPath, "", null, data, schema); - var result = this.handleError(error); - if (result) { - return result; - } - } - return null; -}; - -ValidatorContext.prototype.addFormat = function (format, validator) { - if (typeof format === 'object') { - for (var key in format) { - this.addFormat(key, format[key]); - } - return this; - } - this.formatValidators[format] = validator; -}; -ValidatorContext.prototype.resolveRefs = function (schema, urlHistory) { - if (schema['$ref'] !== undefined) { - urlHistory = urlHistory || {}; - if (urlHistory[schema['$ref']]) { - return this.createError(ErrorCodes.CIRCULAR_REFERENCE, {urls: Object.keys(urlHistory).join(', ')}, '', '', null, undefined, schema); - } - urlHistory[schema['$ref']] = true; - schema = this.getSchema(schema['$ref'], urlHistory); - } - return schema; -}; -ValidatorContext.prototype.getSchema = function (url, urlHistory) { - var schema; - if (this.schemas[url] !== undefined) { - schema = this.schemas[url]; - return this.resolveRefs(schema, urlHistory); - } - var baseUrl = url; - var fragment = ""; - if (url.indexOf('#') !== -1) { - fragment = url.substring(url.indexOf("#") + 1); - baseUrl = url.substring(0, url.indexOf("#")); - } - if (typeof this.schemas[baseUrl] === 'object') { - schema = this.schemas[baseUrl]; - var pointerPath = decodeURIComponent(fragment); - if (pointerPath === "") { - return this.resolveRefs(schema, urlHistory); - } else if (pointerPath.charAt(0) !== "/") { - return undefined; - } - var parts = pointerPath.split("/").slice(1); - for (var i = 0; i < parts.length; i++) { - var component = parts[i].replace(/~1/g, "/").replace(/~0/g, "~"); - if (schema[component] === undefined) { - schema = undefined; - break; - } - schema = schema[component]; - } - if (schema !== undefined) { - return this.resolveRefs(schema, urlHistory); - } - } - if (this.missing[baseUrl] === undefined) { - this.missing.push(baseUrl); - this.missing[baseUrl] = baseUrl; - this.missingMap[baseUrl] = baseUrl; - } -}; -ValidatorContext.prototype.searchSchemas = function (schema, url) { - if (Array.isArray(schema)) { - for (var i = 0; i < schema.length; i++) { - this.searchSchemas(schema[i], url); - } - } else if (schema && typeof schema === "object") { - if (typeof schema.id === "string") { - if (isTrustedUrl(url, schema.id)) { - if (this.schemas[schema.id] === undefined) { - this.schemas[schema.id] = schema; - } - } - } - for (var key in schema) { - if (key !== "enum") { - if (typeof schema[key] === "object") { - this.searchSchemas(schema[key], url); - } else if (key === "$ref") { - var uri = getDocumentUri(schema[key]); - if (uri && this.schemas[uri] === undefined && this.missingMap[uri] === undefined) { - this.missingMap[uri] = uri; - } - } - } - } - } -}; -ValidatorContext.prototype.addSchema = function (url, schema) { - //overload - if (typeof url !== 'string' || typeof schema === 'undefined') { - if (typeof url === 'object' && typeof url.id === 'string') { - schema = url; - url = schema.id; - } - else { - return; - } - } - if (url === getDocumentUri(url) + "#") { - // Remove empty fragment - url = getDocumentUri(url); - } - this.schemas[url] = schema; - delete this.missingMap[url]; - normSchema(schema, url); - this.searchSchemas(schema, url); -}; - -ValidatorContext.prototype.getSchemaMap = function () { - var map = {}; - for (var key in this.schemas) { - map[key] = this.schemas[key]; - } - return map; -}; - -ValidatorContext.prototype.getSchemaUris = function (filterRegExp) { - var list = []; - for (var key in this.schemas) { - if (!filterRegExp || filterRegExp.test(key)) { - list.push(key); - } - } - return list; -}; - -ValidatorContext.prototype.getMissingUris = function (filterRegExp) { - var list = []; - for (var key in this.missingMap) { - if (!filterRegExp || filterRegExp.test(key)) { - list.push(key); - } - } - return list; -}; - -ValidatorContext.prototype.dropSchemas = function () { - this.schemas = {}; - this.reset(); -}; -ValidatorContext.prototype.reset = function () { - this.missing = []; - this.missingMap = {}; - this.errors = []; -}; - -ValidatorContext.prototype.validateAll = function (data, schema, dataPathParts, schemaPathParts, dataPointerPath) { - var topLevel; - schema = this.resolveRefs(schema); - if (!schema) { - return null; - } else if (schema instanceof ValidationError) { - this.errors.push(schema); - return schema; - } - - var startErrorCount = this.errors.length; - var frozenIndex, scannedFrozenSchemaIndex = null, scannedSchemasIndex = null; - if (this.checkRecursive && data && typeof data === 'object') { - topLevel = !this.scanned.length; - if (data[this.validatedSchemasKey]) { - var schemaIndex = data[this.validatedSchemasKey].indexOf(schema); - if (schemaIndex !== -1) { - this.errors = this.errors.concat(data[this.validationErrorsKey][schemaIndex]); - return null; - } - } - if (Object.isFrozen(data)) { - frozenIndex = this.scannedFrozen.indexOf(data); - if (frozenIndex !== -1) { - var frozenSchemaIndex = this.scannedFrozenSchemas[frozenIndex].indexOf(schema); - if (frozenSchemaIndex !== -1) { - this.errors = this.errors.concat(this.scannedFrozenValidationErrors[frozenIndex][frozenSchemaIndex]); - return null; - } - } - } - this.scanned.push(data); - if (Object.isFrozen(data)) { - if (frozenIndex === -1) { - frozenIndex = this.scannedFrozen.length; - this.scannedFrozen.push(data); - this.scannedFrozenSchemas.push([]); - } - scannedFrozenSchemaIndex = this.scannedFrozenSchemas[frozenIndex].length; - this.scannedFrozenSchemas[frozenIndex][scannedFrozenSchemaIndex] = schema; - this.scannedFrozenValidationErrors[frozenIndex][scannedFrozenSchemaIndex] = []; - } else { - if (!data[this.validatedSchemasKey]) { - try { - Object.defineProperty(data, this.validatedSchemasKey, { - value: [], - configurable: true - }); - Object.defineProperty(data, this.validationErrorsKey, { - value: [], - configurable: true - }); - } catch (e) { - //IE 7/8 workaround - data[this.validatedSchemasKey] = []; - data[this.validationErrorsKey] = []; - } - } - scannedSchemasIndex = data[this.validatedSchemasKey].length; - data[this.validatedSchemasKey][scannedSchemasIndex] = schema; - data[this.validationErrorsKey][scannedSchemasIndex] = []; - } - } - - var errorCount = this.errors.length; - var error = this.validateBasic(data, schema, dataPointerPath) - || this.validateNumeric(data, schema, dataPointerPath) - || this.validateString(data, schema, dataPointerPath) - || this.validateArray(data, schema, dataPointerPath) - || this.validateObject(data, schema, dataPointerPath) - || this.validateCombinations(data, schema, dataPointerPath) - || this.validateHypermedia(data, schema, dataPointerPath) - || this.validateFormat(data, schema, dataPointerPath) - || this.validateDefinedKeywords(data, schema, dataPointerPath) - || null; - - if (topLevel) { - while (this.scanned.length) { - var item = this.scanned.pop(); - delete item[this.validatedSchemasKey]; - } - this.scannedFrozen = []; - this.scannedFrozenSchemas = []; - } - - if (error || errorCount !== this.errors.length) { - while ((dataPathParts && dataPathParts.length) || (schemaPathParts && schemaPathParts.length)) { - var dataPart = (dataPathParts && dataPathParts.length) ? "" + dataPathParts.pop() : null; - var schemaPart = (schemaPathParts && schemaPathParts.length) ? "" + schemaPathParts.pop() : null; - if (error) { - error = error.prefixWith(dataPart, schemaPart); - } - this.prefixErrors(errorCount, dataPart, schemaPart); - } - } - - if (scannedFrozenSchemaIndex !== null) { - this.scannedFrozenValidationErrors[frozenIndex][scannedFrozenSchemaIndex] = this.errors.slice(startErrorCount); - } else if (scannedSchemasIndex !== null) { - data[this.validationErrorsKey][scannedSchemasIndex] = this.errors.slice(startErrorCount); - } - - return this.handleError(error); -}; -ValidatorContext.prototype.validateFormat = function (data, schema) { - if (typeof schema.format !== 'string' || !this.formatValidators[schema.format]) { - return null; - } - var errorMessage = this.formatValidators[schema.format].call(null, data, schema); - if (typeof errorMessage === 'string' || typeof errorMessage === 'number') { - return this.createError(ErrorCodes.FORMAT_CUSTOM, {message: errorMessage}, '', '/format', null, data, schema); - } else if (errorMessage && typeof errorMessage === 'object') { - return this.createError(ErrorCodes.FORMAT_CUSTOM, {message: errorMessage.message || "?"}, errorMessage.dataPath || '', errorMessage.schemaPath || "/format", null, data, schema); - } - return null; -}; -ValidatorContext.prototype.validateDefinedKeywords = function (data, schema, dataPointerPath) { - for (var key in this.definedKeywords) { - if (typeof schema[key] === 'undefined') { - continue; - } - var validationFunctions = this.definedKeywords[key]; - for (var i = 0; i < validationFunctions.length; i++) { - var func = validationFunctions[i]; - var result = func(data, schema[key], schema, dataPointerPath); - if (typeof result === 'string' || typeof result === 'number') { - return this.createError(ErrorCodes.KEYWORD_CUSTOM, {key: key, message: result}, '', '', null, data, schema).prefixWith(null, key); - } else if (result && typeof result === 'object') { - var code = result.code; - if (typeof code === 'string') { - if (!ErrorCodes[code]) { - throw new Error('Undefined error code (use defineError): ' + code); - } - code = ErrorCodes[code]; - } else if (typeof code !== 'number') { - code = ErrorCodes.KEYWORD_CUSTOM; - } - var messageParams = (typeof result.message === 'object') ? result.message : {key: key, message: result.message || "?"}; - var schemaPath = result.schemaPath || ("/" + key.replace(/~/g, '~0').replace(/\//g, '~1')); - return this.createError(code, messageParams, result.dataPath || null, schemaPath, null, data, schema); - } - } - } - return null; -}; - -function recursiveCompare(A, B) { - if (A === B) { - return true; - } - if (A && B && typeof A === "object" && typeof B === "object") { - if (Array.isArray(A) !== Array.isArray(B)) { - return false; - } else if (Array.isArray(A)) { - if (A.length !== B.length) { - return false; - } - for (var i = 0; i < A.length; i++) { - if (!recursiveCompare(A[i], B[i])) { - return false; - } - } - } else { - var key; - for (key in A) { - if (B[key] === undefined && A[key] !== undefined) { - return false; - } - } - for (key in B) { - if (A[key] === undefined && B[key] !== undefined) { - return false; - } - } - for (key in A) { - if (!recursiveCompare(A[key], B[key])) { - return false; - } - } - } - return true; - } - return false; -} - -ValidatorContext.prototype.validateBasic = function validateBasic(data, schema, dataPointerPath) { - var error; - if (error = this.validateType(data, schema, dataPointerPath)) { - return error.prefixWith(null, "type"); - } - if (error = this.validateEnum(data, schema, dataPointerPath)) { - return error.prefixWith(null, "type"); - } - return null; -}; - -ValidatorContext.prototype.validateType = function validateType(data, schema) { - if (schema.type === undefined) { - return null; - } - var dataType = typeof data; - if (data === null) { - dataType = "null"; - } else if (Array.isArray(data)) { - dataType = "array"; - } - var allowedTypes = schema.type; - if (!Array.isArray(allowedTypes)) { - allowedTypes = [allowedTypes]; - } - - for (var i = 0; i < allowedTypes.length; i++) { - var type = allowedTypes[i]; - if (type === dataType || (type === "integer" && dataType === "number" && (data % 1 === 0))) { - return null; - } - } - return this.createError(ErrorCodes.INVALID_TYPE, {type: dataType, expected: allowedTypes.join("/")}, '', '', null, data, schema); -}; - -ValidatorContext.prototype.validateEnum = function validateEnum(data, schema) { - if (schema["enum"] === undefined) { - return null; - } - for (var i = 0; i < schema["enum"].length; i++) { - var enumVal = schema["enum"][i]; - if (recursiveCompare(data, enumVal)) { - return null; - } - } - return this.createError(ErrorCodes.ENUM_MISMATCH, {value: (typeof JSON !== 'undefined') ? JSON.stringify(data) : data}, '', '', null, data, schema); -}; - -ValidatorContext.prototype.validateNumeric = function validateNumeric(data, schema, dataPointerPath) { - return this.validateMultipleOf(data, schema, dataPointerPath) - || this.validateMinMax(data, schema, dataPointerPath) - || this.validateNaN(data, schema, dataPointerPath) - || null; -}; - -var CLOSE_ENOUGH_LOW = Math.pow(2, -51); -var CLOSE_ENOUGH_HIGH = 1 - CLOSE_ENOUGH_LOW; -ValidatorContext.prototype.validateMultipleOf = function validateMultipleOf(data, schema) { - var multipleOf = schema.multipleOf || schema.divisibleBy; - if (multipleOf === undefined) { - return null; - } - if (typeof data === "number") { - var remainder = (data/multipleOf)%1; - if (remainder >= CLOSE_ENOUGH_LOW && remainder < CLOSE_ENOUGH_HIGH) { - return this.createError(ErrorCodes.NUMBER_MULTIPLE_OF, {value: data, multipleOf: multipleOf}, '', '', null, data, schema); - } - } - return null; -}; - -ValidatorContext.prototype.validateMinMax = function validateMinMax(data, schema) { - if (typeof data !== "number") { - return null; - } - if (schema.minimum !== undefined) { - if (data < schema.minimum) { - return this.createError(ErrorCodes.NUMBER_MINIMUM, {value: data, minimum: schema.minimum}, '', '/minimum', null, data, schema); - } - if (schema.exclusiveMinimum && data === schema.minimum) { - return this.createError(ErrorCodes.NUMBER_MINIMUM_EXCLUSIVE, {value: data, minimum: schema.minimum}, '', '/exclusiveMinimum', null, data, schema); - } - } - if (schema.maximum !== undefined) { - if (data > schema.maximum) { - return this.createError(ErrorCodes.NUMBER_MAXIMUM, {value: data, maximum: schema.maximum}, '', '/maximum', null, data, schema); - } - if (schema.exclusiveMaximum && data === schema.maximum) { - return this.createError(ErrorCodes.NUMBER_MAXIMUM_EXCLUSIVE, {value: data, maximum: schema.maximum}, '', '/exclusiveMaximum', null, data, schema); - } - } - return null; -}; - -ValidatorContext.prototype.validateNaN = function validateNaN(data, schema) { - if (typeof data !== "number") { - return null; - } - if (isNaN(data) === true || data === Infinity || data === -Infinity) { - return this.createError(ErrorCodes.NUMBER_NOT_A_NUMBER, {value: data}, '', '/type', null, data, schema); - } - return null; -}; - -ValidatorContext.prototype.validateString = function validateString(data, schema, dataPointerPath) { - return this.validateStringLength(data, schema, dataPointerPath) - || this.validateStringPattern(data, schema, dataPointerPath) - || null; -}; - -ValidatorContext.prototype.validateStringLength = function validateStringLength(data, schema) { - if (typeof data !== "string") { - return null; - } - if (schema.minLength !== undefined) { - if (data.length < schema.minLength) { - return this.createError(ErrorCodes.STRING_LENGTH_SHORT, {length: data.length, minimum: schema.minLength}, '', '/minLength', null, data, schema); - } - } - if (schema.maxLength !== undefined) { - if (data.length > schema.maxLength) { - return this.createError(ErrorCodes.STRING_LENGTH_LONG, {length: data.length, maximum: schema.maxLength}, '', '/maxLength', null, data, schema); - } - } - return null; -}; - -ValidatorContext.prototype.validateStringPattern = function validateStringPattern(data, schema) { - if (typeof data !== "string" || (typeof schema.pattern !== "string" && !(schema.pattern instanceof RegExp))) { - return null; - } - var regexp; - if (schema.pattern instanceof RegExp) { - regexp = schema.pattern; - } - else { - var body, flags = ''; - // Check for regular expression literals - // @see http://www.ecma-international.org/ecma-262/5.1/#sec-7.8.5 - var literal = schema.pattern.match(/^\/(.+)\/([img]*)$/); - if (literal) { - body = literal[1]; - flags = literal[2]; - } - else { - body = schema.pattern; - } - regexp = new RegExp(body, flags); - } - if (!regexp.test(data)) { - return this.createError(ErrorCodes.STRING_PATTERN, {pattern: schema.pattern}, '', '/pattern', null, data, schema); - } - return null; -}; - -ValidatorContext.prototype.validateArray = function validateArray(data, schema, dataPointerPath) { - if (!Array.isArray(data)) { - return null; - } - return this.validateArrayLength(data, schema, dataPointerPath) - || this.validateArrayUniqueItems(data, schema, dataPointerPath) - || this.validateArrayItems(data, schema, dataPointerPath) - || null; -}; - -ValidatorContext.prototype.validateArrayLength = function validateArrayLength(data, schema) { - var error; - if (schema.minItems !== undefined) { - if (data.length < schema.minItems) { - error = this.createError(ErrorCodes.ARRAY_LENGTH_SHORT, {length: data.length, minimum: schema.minItems}, '', '/minItems', null, data, schema); - if (this.handleError(error)) { - return error; - } - } - } - if (schema.maxItems !== undefined) { - if (data.length > schema.maxItems) { - error = this.createError(ErrorCodes.ARRAY_LENGTH_LONG, {length: data.length, maximum: schema.maxItems}, '', '/maxItems', null, data, schema); - if (this.handleError(error)) { - return error; - } - } - } - return null; -}; - -ValidatorContext.prototype.validateArrayUniqueItems = function validateArrayUniqueItems(data, schema) { - if (schema.uniqueItems) { - for (var i = 0; i < data.length; i++) { - for (var j = i + 1; j < data.length; j++) { - if (recursiveCompare(data[i], data[j])) { - var error = this.createError(ErrorCodes.ARRAY_UNIQUE, {match1: i, match2: j}, '', '/uniqueItems', null, data, schema); - if (this.handleError(error)) { - return error; - } - } - } - } - } - return null; -}; - -ValidatorContext.prototype.validateArrayItems = function validateArrayItems(data, schema, dataPointerPath) { - if (schema.items === undefined) { - return null; - } - var error, i; - if (Array.isArray(schema.items)) { - for (i = 0; i < data.length; i++) { - if (i < schema.items.length) { - if (error = this.validateAll(data[i], schema.items[i], [i], ["items", i], dataPointerPath + "/" + i)) { - return error; - } - } else if (schema.additionalItems !== undefined) { - if (typeof schema.additionalItems === "boolean") { - if (!schema.additionalItems) { - error = (this.createError(ErrorCodes.ARRAY_ADDITIONAL_ITEMS, {}, '/' + i, '/additionalItems', null, data, schema)); - if (this.handleError(error)) { - return error; - } - } - } else if (error = this.validateAll(data[i], schema.additionalItems, [i], ["additionalItems"], dataPointerPath + "/" + i)) { - return error; - } - } - } - } else { - for (i = 0; i < data.length; i++) { - if (error = this.validateAll(data[i], schema.items, [i], ["items"], dataPointerPath + "/" + i)) { - return error; - } - } - } - return null; -}; - -ValidatorContext.prototype.validateObject = function validateObject(data, schema, dataPointerPath) { - if (typeof data !== "object" || data === null || Array.isArray(data)) { - return null; - } - return this.validateObjectMinMaxProperties(data, schema, dataPointerPath) - || this.validateObjectRequiredProperties(data, schema, dataPointerPath) - || this.validateObjectProperties(data, schema, dataPointerPath) - || this.validateObjectDependencies(data, schema, dataPointerPath) - || null; -}; - -ValidatorContext.prototype.validateObjectMinMaxProperties = function validateObjectMinMaxProperties(data, schema) { - var keys = Object.keys(data); - var error; - if (schema.minProperties !== undefined) { - if (keys.length < schema.minProperties) { - error = this.createError(ErrorCodes.OBJECT_PROPERTIES_MINIMUM, {propertyCount: keys.length, minimum: schema.minProperties}, '', '/minProperties', null, data, schema); - if (this.handleError(error)) { - return error; - } - } - } - if (schema.maxProperties !== undefined) { - if (keys.length > schema.maxProperties) { - error = this.createError(ErrorCodes.OBJECT_PROPERTIES_MAXIMUM, {propertyCount: keys.length, maximum: schema.maxProperties}, '', '/maxProperties', null, data, schema); - if (this.handleError(error)) { - return error; - } - } - } - return null; -}; - -ValidatorContext.prototype.validateObjectRequiredProperties = function validateObjectRequiredProperties(data, schema) { - if (schema.required !== undefined) { - for (var i = 0; i < schema.required.length; i++) { - var key = schema.required[i]; - if (data[key] === undefined) { - var error = this.createError(ErrorCodes.OBJECT_REQUIRED, {key: key}, '', '/required/' + i, null, data, schema); - if (this.handleError(error)) { - return error; - } - } - } - } - return null; -}; - -ValidatorContext.prototype.validateObjectProperties = function validateObjectProperties(data, schema, dataPointerPath) { - var error; - for (var key in data) { - var keyPointerPath = dataPointerPath + "/" + key.replace(/~/g, '~0').replace(/\//g, '~1'); - var foundMatch = false; - if (schema.properties !== undefined && schema.properties[key] !== undefined) { - foundMatch = true; - if (error = this.validateAll(data[key], schema.properties[key], [key], ["properties", key], keyPointerPath)) { - return error; - } - } - if (schema.patternProperties !== undefined) { - for (var patternKey in schema.patternProperties) { - var regexp = new RegExp(patternKey); - if (regexp.test(key)) { - foundMatch = true; - if (error = this.validateAll(data[key], schema.patternProperties[patternKey], [key], ["patternProperties", patternKey], keyPointerPath)) { - return error; - } - } - } - } - if (!foundMatch) { - if (schema.additionalProperties !== undefined) { - if (this.trackUnknownProperties) { - this.knownPropertyPaths[keyPointerPath] = true; - delete this.unknownPropertyPaths[keyPointerPath]; - } - if (typeof schema.additionalProperties === "boolean") { - if (!schema.additionalProperties) { - error = this.createError(ErrorCodes.OBJECT_ADDITIONAL_PROPERTIES, {key: key}, '', '/additionalProperties', null, data, schema).prefixWith(key, null); - if (this.handleError(error)) { - return error; - } - } - } else { - if (error = this.validateAll(data[key], schema.additionalProperties, [key], ["additionalProperties"], keyPointerPath)) { - return error; - } - } - } else if (this.trackUnknownProperties && !this.knownPropertyPaths[keyPointerPath]) { - this.unknownPropertyPaths[keyPointerPath] = true; - } - } else if (this.trackUnknownProperties) { - this.knownPropertyPaths[keyPointerPath] = true; - delete this.unknownPropertyPaths[keyPointerPath]; - } - } - return null; -}; - -ValidatorContext.prototype.validateObjectDependencies = function validateObjectDependencies(data, schema, dataPointerPath) { - var error; - if (schema.dependencies !== undefined) { - for (var depKey in schema.dependencies) { - if (data[depKey] !== undefined) { - var dep = schema.dependencies[depKey]; - if (typeof dep === "string") { - if (data[dep] === undefined) { - error = this.createError(ErrorCodes.OBJECT_DEPENDENCY_KEY, {key: depKey, missing: dep}, '', '', null, data, schema).prefixWith(null, depKey).prefixWith(null, "dependencies"); - if (this.handleError(error)) { - return error; - } - } - } else if (Array.isArray(dep)) { - for (var i = 0; i < dep.length; i++) { - var requiredKey = dep[i]; - if (data[requiredKey] === undefined) { - error = this.createError(ErrorCodes.OBJECT_DEPENDENCY_KEY, {key: depKey, missing: requiredKey}, '', '/' + i, null, data, schema).prefixWith(null, depKey).prefixWith(null, "dependencies"); - if (this.handleError(error)) { - return error; - } - } - } - } else { - if (error = this.validateAll(data, dep, [], ["dependencies", depKey], dataPointerPath)) { - return error; - } - } - } - } - } - return null; -}; - -ValidatorContext.prototype.validateCombinations = function validateCombinations(data, schema, dataPointerPath) { - return this.validateAllOf(data, schema, dataPointerPath) - || this.validateAnyOf(data, schema, dataPointerPath) - || this.validateOneOf(data, schema, dataPointerPath) - || this.validateNot(data, schema, dataPointerPath) - || null; -}; - -ValidatorContext.prototype.validateAllOf = function validateAllOf(data, schema, dataPointerPath) { - if (schema.allOf === undefined) { - return null; - } - var error; - for (var i = 0; i < schema.allOf.length; i++) { - var subSchema = schema.allOf[i]; - if (error = this.validateAll(data, subSchema, [], ["allOf", i], dataPointerPath)) { - return error; - } - } - return null; -}; - -ValidatorContext.prototype.validateAnyOf = function validateAnyOf(data, schema, dataPointerPath) { - if (schema.anyOf === undefined) { - return null; - } - var errors = []; - var startErrorCount = this.errors.length; - var oldUnknownPropertyPaths, oldKnownPropertyPaths; - if (this.trackUnknownProperties) { - oldUnknownPropertyPaths = this.unknownPropertyPaths; - oldKnownPropertyPaths = this.knownPropertyPaths; - } - var errorAtEnd = true; - for (var i = 0; i < schema.anyOf.length; i++) { - if (this.trackUnknownProperties) { - this.unknownPropertyPaths = {}; - this.knownPropertyPaths = {}; - } - var subSchema = schema.anyOf[i]; - - var errorCount = this.errors.length; - var error = this.validateAll(data, subSchema, [], ["anyOf", i], dataPointerPath); - - if (error === null && errorCount === this.errors.length) { - this.errors = this.errors.slice(0, startErrorCount); - - if (this.trackUnknownProperties) { - for (var knownKey in this.knownPropertyPaths) { - oldKnownPropertyPaths[knownKey] = true; - delete oldUnknownPropertyPaths[knownKey]; - } - for (var unknownKey in this.unknownPropertyPaths) { - if (!oldKnownPropertyPaths[unknownKey]) { - oldUnknownPropertyPaths[unknownKey] = true; - } - } - // We need to continue looping so we catch all the property definitions, but we don't want to return an error - errorAtEnd = false; - continue; - } - - return null; - } - if (error) { - errors.push(error.prefixWith(null, "" + i).prefixWith(null, "anyOf")); - } - } - if (this.trackUnknownProperties) { - this.unknownPropertyPaths = oldUnknownPropertyPaths; - this.knownPropertyPaths = oldKnownPropertyPaths; - } - if (errorAtEnd) { - errors = errors.concat(this.errors.slice(startErrorCount)); - this.errors = this.errors.slice(0, startErrorCount); - return this.createError(ErrorCodes.ANY_OF_MISSING, {}, "", "/anyOf", errors, data, schema); - } -}; - -ValidatorContext.prototype.validateOneOf = function validateOneOf(data, schema, dataPointerPath) { - if (schema.oneOf === undefined) { - return null; - } - var validIndex = null; - var errors = []; - var startErrorCount = this.errors.length; - var oldUnknownPropertyPaths, oldKnownPropertyPaths; - if (this.trackUnknownProperties) { - oldUnknownPropertyPaths = this.unknownPropertyPaths; - oldKnownPropertyPaths = this.knownPropertyPaths; - } - for (var i = 0; i < schema.oneOf.length; i++) { - if (this.trackUnknownProperties) { - this.unknownPropertyPaths = {}; - this.knownPropertyPaths = {}; - } - var subSchema = schema.oneOf[i]; - - var errorCount = this.errors.length; - var error = this.validateAll(data, subSchema, [], ["oneOf", i], dataPointerPath); - - if (error === null && errorCount === this.errors.length) { - if (validIndex === null) { - validIndex = i; - } else { - this.errors = this.errors.slice(0, startErrorCount); - return this.createError(ErrorCodes.ONE_OF_MULTIPLE, {index1: validIndex, index2: i}, "", "/oneOf", null, data, schema); - } - if (this.trackUnknownProperties) { - for (var knownKey in this.knownPropertyPaths) { - oldKnownPropertyPaths[knownKey] = true; - delete oldUnknownPropertyPaths[knownKey]; - } - for (var unknownKey in this.unknownPropertyPaths) { - if (!oldKnownPropertyPaths[unknownKey]) { - oldUnknownPropertyPaths[unknownKey] = true; - } - } - } - } else if (error) { - errors.push(error); - } - } - if (this.trackUnknownProperties) { - this.unknownPropertyPaths = oldUnknownPropertyPaths; - this.knownPropertyPaths = oldKnownPropertyPaths; - } - if (validIndex === null) { - errors = errors.concat(this.errors.slice(startErrorCount)); - this.errors = this.errors.slice(0, startErrorCount); - return this.createError(ErrorCodes.ONE_OF_MISSING, {}, "", "/oneOf", errors, data, schema); - } else { - this.errors = this.errors.slice(0, startErrorCount); - } - return null; -}; - -ValidatorContext.prototype.validateNot = function validateNot(data, schema, dataPointerPath) { - if (schema.not === undefined) { - return null; - } - var oldErrorCount = this.errors.length; - var oldUnknownPropertyPaths, oldKnownPropertyPaths; - if (this.trackUnknownProperties) { - oldUnknownPropertyPaths = this.unknownPropertyPaths; - oldKnownPropertyPaths = this.knownPropertyPaths; - this.unknownPropertyPaths = {}; - this.knownPropertyPaths = {}; - } - var error = this.validateAll(data, schema.not, null, null, dataPointerPath); - var notErrors = this.errors.slice(oldErrorCount); - this.errors = this.errors.slice(0, oldErrorCount); - if (this.trackUnknownProperties) { - this.unknownPropertyPaths = oldUnknownPropertyPaths; - this.knownPropertyPaths = oldKnownPropertyPaths; - } - if (error === null && notErrors.length === 0) { - return this.createError(ErrorCodes.NOT_PASSED, {}, "", "/not", null, data, schema); - } - return null; -}; - -ValidatorContext.prototype.validateHypermedia = function validateCombinations(data, schema, dataPointerPath) { - if (!schema.links) { - return null; - } - var error; - for (var i = 0; i < schema.links.length; i++) { - var ldo = schema.links[i]; - if (ldo.rel === "describedby") { - var template = new UriTemplate(ldo.href); - var allPresent = true; - for (var j = 0; j < template.varNames.length; j++) { - if (!(template.varNames[j] in data)) { - allPresent = false; - break; - } - } - if (allPresent) { - var schemaUrl = template.fillFromObject(data); - var subSchema = {"$ref": schemaUrl}; - if (error = this.validateAll(data, subSchema, [], ["links", i], dataPointerPath)) { - return error; - } - } - } - } -}; - -// parseURI() and resolveUrl() are from https://gist.github.com/1088850 -// - released as public domain by author ("Yaffle") - see comments on gist - -function parseURI(url) { - var m = String(url).replace(/^\s+|\s+$/g, '').match(/^([^:\/?#]+:)?(\/\/(?:[^:@]*(?::[^:@]*)?@)?(([^:\/?#]*)(?::(\d*))?))?([^?#]*)(\?[^#]*)?(#[\s\S]*)?/); - // authority = '//' + user + ':' + pass '@' + hostname + ':' port - return (m ? { - href : m[0] || '', - protocol : m[1] || '', - authority: m[2] || '', - host : m[3] || '', - hostname : m[4] || '', - port : m[5] || '', - pathname : m[6] || '', - search : m[7] || '', - hash : m[8] || '' - } : null); -} - -function resolveUrl(base, href) {// RFC 3986 - - function removeDotSegments(input) { - var output = []; - input.replace(/^(\.\.?(\/|$))+/, '') - .replace(/\/(\.(\/|$))+/g, '/') - .replace(/\/\.\.$/, '/../') - .replace(/\/?[^\/]*/g, function (p) { - if (p === '/..') { - output.pop(); - } else { - output.push(p); - } - }); - return output.join('').replace(/^\//, input.charAt(0) === '/' ? '/' : ''); - } - - href = parseURI(href || ''); - base = parseURI(base || ''); - - return !href || !base ? null : (href.protocol || base.protocol) + - (href.protocol || href.authority ? href.authority : base.authority) + - removeDotSegments(href.protocol || href.authority || href.pathname.charAt(0) === '/' ? href.pathname : (href.pathname ? ((base.authority && !base.pathname ? '/' : '') + base.pathname.slice(0, base.pathname.lastIndexOf('/') + 1) + href.pathname) : base.pathname)) + - (href.protocol || href.authority || href.pathname ? href.search : (href.search || base.search)) + - href.hash; -} - -function getDocumentUri(uri) { - return uri.split('#')[0]; -} -function normSchema(schema, baseUri) { - if (schema && typeof schema === "object") { - if (baseUri === undefined) { - baseUri = schema.id; - } else if (typeof schema.id === "string") { - baseUri = resolveUrl(baseUri, schema.id); - schema.id = baseUri; - } - if (Array.isArray(schema)) { - for (var i = 0; i < schema.length; i++) { - normSchema(schema[i], baseUri); - } - } else { - if (typeof schema['$ref'] === "string") { - schema['$ref'] = resolveUrl(baseUri, schema['$ref']); - } - for (var key in schema) { - if (key !== "enum") { - normSchema(schema[key], baseUri); - } - } - } - } -} - -function defaultErrorReporter(language) { - language = language || 'en'; - - var errorMessages = languages[language]; - - return function (error) { - var messageTemplate = errorMessages[error.code] || ErrorMessagesDefault[error.code]; - if (typeof messageTemplate !== 'string') { - return "Unknown error code " + error.code + ": " + JSON.stringify(error.messageParams); - } - var messageParams = error.params; - // Adapted from Crockford's supplant() - return messageTemplate.replace(/\{([^{}]*)\}/g, function (whole, varName) { - var subValue = messageParams[varName]; - return typeof subValue === 'string' || typeof subValue === 'number' ? subValue : whole; - }); - }; -} - -var ErrorCodes = { - INVALID_TYPE: 0, - ENUM_MISMATCH: 1, - ANY_OF_MISSING: 10, - ONE_OF_MISSING: 11, - ONE_OF_MULTIPLE: 12, - NOT_PASSED: 13, - // Numeric errors - NUMBER_MULTIPLE_OF: 100, - NUMBER_MINIMUM: 101, - NUMBER_MINIMUM_EXCLUSIVE: 102, - NUMBER_MAXIMUM: 103, - NUMBER_MAXIMUM_EXCLUSIVE: 104, - NUMBER_NOT_A_NUMBER: 105, - // String errors - STRING_LENGTH_SHORT: 200, - STRING_LENGTH_LONG: 201, - STRING_PATTERN: 202, - // Object errors - OBJECT_PROPERTIES_MINIMUM: 300, - OBJECT_PROPERTIES_MAXIMUM: 301, - OBJECT_REQUIRED: 302, - OBJECT_ADDITIONAL_PROPERTIES: 303, - OBJECT_DEPENDENCY_KEY: 304, - // Array errors - ARRAY_LENGTH_SHORT: 400, - ARRAY_LENGTH_LONG: 401, - ARRAY_UNIQUE: 402, - ARRAY_ADDITIONAL_ITEMS: 403, - // Custom/user-defined errors - FORMAT_CUSTOM: 500, - KEYWORD_CUSTOM: 501, - // Schema structure - CIRCULAR_REFERENCE: 600, - // Non-standard validation options - UNKNOWN_PROPERTY: 1000 -}; -var ErrorCodeLookup = {}; -for (var key in ErrorCodes) { - ErrorCodeLookup[ErrorCodes[key]] = key; -} -var ErrorMessagesDefault = { - INVALID_TYPE: "Invalid type: {type} (expected {expected})", - ENUM_MISMATCH: "No enum match for: {value}", - ANY_OF_MISSING: "Data does not match any schemas from \"anyOf\"", - ONE_OF_MISSING: "Data does not match any schemas from \"oneOf\"", - ONE_OF_MULTIPLE: "Data is valid against more than one schema from \"oneOf\": indices {index1} and {index2}", - NOT_PASSED: "Data matches schema from \"not\"", - // Numeric errors - NUMBER_MULTIPLE_OF: "Value {value} is not a multiple of {multipleOf}", - NUMBER_MINIMUM: "Value {value} is less than minimum {minimum}", - NUMBER_MINIMUM_EXCLUSIVE: "Value {value} is equal to exclusive minimum {minimum}", - NUMBER_MAXIMUM: "Value {value} is greater than maximum {maximum}", - NUMBER_MAXIMUM_EXCLUSIVE: "Value {value} is equal to exclusive maximum {maximum}", - NUMBER_NOT_A_NUMBER: "Value {value} is not a valid number", - // String errors - STRING_LENGTH_SHORT: "String is too short ({length} chars), minimum {minimum}", - STRING_LENGTH_LONG: "String is too long ({length} chars), maximum {maximum}", - STRING_PATTERN: "String does not match pattern: {pattern}", - // Object errors - OBJECT_PROPERTIES_MINIMUM: "Too few properties defined ({propertyCount}), minimum {minimum}", - OBJECT_PROPERTIES_MAXIMUM: "Too many properties defined ({propertyCount}), maximum {maximum}", - OBJECT_REQUIRED: "Missing required property: {key}", - OBJECT_ADDITIONAL_PROPERTIES: "Additional properties not allowed", - OBJECT_DEPENDENCY_KEY: "Dependency failed - key must exist: {missing} (due to key: {key})", - // Array errors - ARRAY_LENGTH_SHORT: "Array is too short ({length}), minimum {minimum}", - ARRAY_LENGTH_LONG: "Array is too long ({length}), maximum {maximum}", - ARRAY_UNIQUE: "Array items are not unique (indices {match1} and {match2})", - ARRAY_ADDITIONAL_ITEMS: "Additional items not allowed", - // Format errors - FORMAT_CUSTOM: "Format validation failed ({message})", - KEYWORD_CUSTOM: "Keyword failed: {key} ({message})", - // Schema structure - CIRCULAR_REFERENCE: "Circular $refs: {urls}", - // Non-standard validation options - UNKNOWN_PROPERTY: "Unknown property (not in schema)" -}; - -function ValidationError(code, params, dataPath, schemaPath, subErrors) { - Error.call(this); - if (code === undefined) { - throw new Error ("No error code supplied: " + schemaPath); - } - this.message = ''; - this.params = params; - this.code = code; - this.dataPath = dataPath || ""; - this.schemaPath = schemaPath || ""; - this.subErrors = subErrors || null; - - var err = new Error(this.message); - this.stack = err.stack || err.stacktrace; - if (!this.stack) { - try { - throw err; - } - catch(err) { - this.stack = err.stack || err.stacktrace; - } - } -} -ValidationError.prototype = Object.create(Error.prototype); -ValidationError.prototype.constructor = ValidationError; -ValidationError.prototype.name = 'ValidationError'; - -ValidationError.prototype.prefixWith = function (dataPrefix, schemaPrefix) { - if (dataPrefix !== null) { - dataPrefix = dataPrefix.replace(/~/g, "~0").replace(/\//g, "~1"); - this.dataPath = "/" + dataPrefix + this.dataPath; - } - if (schemaPrefix !== null) { - schemaPrefix = schemaPrefix.replace(/~/g, "~0").replace(/\//g, "~1"); - this.schemaPath = "/" + schemaPrefix + this.schemaPath; - } - if (this.subErrors !== null) { - for (var i = 0; i < this.subErrors.length; i++) { - this.subErrors[i].prefixWith(dataPrefix, schemaPrefix); - } - } - return this; -}; - -function isTrustedUrl(baseUrl, testUrl) { - if(testUrl.substring(0, baseUrl.length) === baseUrl){ - var remainder = testUrl.substring(baseUrl.length); - if ((testUrl.length > 0 && testUrl.charAt(baseUrl.length - 1) === "/") - || remainder.charAt(0) === "#" - || remainder.charAt(0) === "?") { - return true; - } - } - return false; -} - -var languages = {}; -function createApi(language) { - var globalContext = new ValidatorContext(); - var currentLanguage; - var customErrorReporter; - var api = { - setErrorReporter: function (reporter) { - if (typeof reporter === 'string') { - return this.language(reporter); - } - customErrorReporter = reporter; - return true; - }, - addFormat: function () { - globalContext.addFormat.apply(globalContext, arguments); - }, - language: function (code) { - if (!code) { - return currentLanguage; - } - if (!languages[code]) { - code = code.split('-')[0]; // fall back to base language - } - if (languages[code]) { - currentLanguage = code; - return code; // so you can tell if fall-back has happened - } - return false; - }, - addLanguage: function (code, messageMap) { - var key; - for (key in ErrorCodes) { - if (messageMap[key] && !messageMap[ErrorCodes[key]]) { - messageMap[ErrorCodes[key]] = messageMap[key]; - } - } - var rootCode = code.split('-')[0]; - if (!languages[rootCode]) { // use for base language if not yet defined - languages[code] = messageMap; - languages[rootCode] = messageMap; - } else { - languages[code] = Object.create(languages[rootCode]); - for (key in messageMap) { - if (typeof languages[rootCode][key] === 'undefined') { - languages[rootCode][key] = messageMap[key]; - } - languages[code][key] = messageMap[key]; - } - } - return this; - }, - freshApi: function (language) { - var result = createApi(); - if (language) { - result.language(language); - } - return result; - }, - validate: function (data, schema, checkRecursive, banUnknownProperties) { - var def = defaultErrorReporter(currentLanguage); - var errorReporter = customErrorReporter ? function (error, data, schema) { - return customErrorReporter(error, data, schema) || def(error, data, schema); - } : def; - var context = new ValidatorContext(globalContext, false, errorReporter, checkRecursive, banUnknownProperties); - if (typeof schema === "string") { - schema = {"$ref": schema}; - } - context.addSchema("", schema); - var error = context.validateAll(data, schema, null, null, ""); - if (!error && banUnknownProperties) { - error = context.banUnknownProperties(data, schema); - } - this.error = error; - this.missing = context.missing; - this.valid = (error === null); - return this.valid; - }, - validateResult: function () { - var result = {}; - this.validate.apply(result, arguments); - return result; - }, - validateMultiple: function (data, schema, checkRecursive, banUnknownProperties) { - var def = defaultErrorReporter(currentLanguage); - var errorReporter = customErrorReporter ? function (error, data, schema) { - return customErrorReporter(error, data, schema) || def(error, data, schema); - } : def; - var context = new ValidatorContext(globalContext, true, errorReporter, checkRecursive, banUnknownProperties); - if (typeof schema === "string") { - schema = {"$ref": schema}; - } - context.addSchema("", schema); - context.validateAll(data, schema, null, null, ""); - if (banUnknownProperties) { - context.banUnknownProperties(data, schema); - } - var result = {}; - result.errors = context.errors; - result.missing = context.missing; - result.valid = (result.errors.length === 0); - return result; - }, - addSchema: function () { - return globalContext.addSchema.apply(globalContext, arguments); - }, - getSchema: function () { - return globalContext.getSchema.apply(globalContext, arguments); - }, - getSchemaMap: function () { - return globalContext.getSchemaMap.apply(globalContext, arguments); - }, - getSchemaUris: function () { - return globalContext.getSchemaUris.apply(globalContext, arguments); - }, - getMissingUris: function () { - return globalContext.getMissingUris.apply(globalContext, arguments); - }, - dropSchemas: function () { - globalContext.dropSchemas.apply(globalContext, arguments); - }, - defineKeyword: function () { - globalContext.defineKeyword.apply(globalContext, arguments); - }, - defineError: function (codeName, codeNumber, defaultMessage) { - if (typeof codeName !== 'string' || !/^[A-Z]+(_[A-Z]+)*$/.test(codeName)) { - throw new Error('Code name must be a string in UPPER_CASE_WITH_UNDERSCORES'); - } - if (typeof codeNumber !== 'number' || codeNumber%1 !== 0 || codeNumber < 10000) { - throw new Error('Code number must be an integer > 10000'); - } - if (typeof ErrorCodes[codeName] !== 'undefined') { - throw new Error('Error already defined: ' + codeName + ' as ' + ErrorCodes[codeName]); - } - if (typeof ErrorCodeLookup[codeNumber] !== 'undefined') { - throw new Error('Error code already used: ' + ErrorCodeLookup[codeNumber] + ' as ' + codeNumber); - } - ErrorCodes[codeName] = codeNumber; - ErrorCodeLookup[codeNumber] = codeName; - ErrorMessagesDefault[codeName] = ErrorMessagesDefault[codeNumber] = defaultMessage; - for (var langCode in languages) { - var language = languages[langCode]; - if (language[codeName]) { - language[codeNumber] = language[codeNumber] || language[codeName]; - } - } - }, - reset: function () { - globalContext.reset(); - this.error = null; - this.missing = []; - this.valid = true; - }, - missing: [], - error: null, - valid: true, - normSchema: normSchema, - resolveUrl: resolveUrl, - getDocumentUri: getDocumentUri, - errorCodes: ErrorCodes - }; - api.language(language || 'en'); - return api; -} - -var tv4 = createApi(); -tv4.addLanguage('en-gb', ErrorMessagesDefault); - -//legacy property -tv4.tv4 = tv4; - -return tv4; // used by _header.js to globalise. - -})); \ No newline at end of file diff --git a/tv4.min.js b/tv4.min.js deleted file mode 100644 index addcc9c..0000000 --- a/tv4.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(a,b){"function"==typeof define&&define.amd?define([],b):"undefined"!=typeof module&&module.exports?module.exports=b():a.tv4=b()}(this,function(){function a(a){return encodeURI(a).replace(/%25[0-9][0-9]/g,function(a){return"%"+a.substring(3)})}function b(b){var c="";m[b.charAt(0)]&&(c=b.charAt(0),b=b.substring(1));var d="",e="",f=!0,g=!1,h=!1;"+"===c?f=!1:"."===c?(e=".",d="."):"/"===c?(e="/",d="/"):"#"===c?(e="#",f=!1):";"===c?(e=";",d=";",g=!0,h=!0):"?"===c?(e="?",d="&",g=!0):"&"===c&&(e="&",d="&",g=!0);for(var i=[],j=b.split(","),k=[],l={},o=0;o0&&(c+=l.suffices["*"]?d||",":",",l.suffices["*"]&&g&&(c+=l.name+"=")),c+=f?encodeURIComponent(m[n]).replace(/!/g,"%21"):a(m[n])}else if("object"==typeof m){g&&!l.suffices["*"]&&(c+=l.name+"=");var o=!0;for(var p in m)o||(c+=l.suffices["*"]?d||",":","),o=!1,c+=f?encodeURIComponent(p).replace(/!/g,"%21"):a(p),c+=l.suffices["*"]?"=":",",c+=f?encodeURIComponent(m[p]).replace(/!/g,"%21"):a(m[p])}else g&&(c+=l.name,h&&""===m||(c+="=")),null!=l.truncate&&(m=m.substring(0,l.truncate)),c+=f?encodeURIComponent(m).replace(/!/g,"%21"):a(m)}return c};return u.varNames=i,{prefix:e,substitution:u}}function c(a){if(!(this instanceof c))return new c(a);for(var d=a.split("{"),e=[d.shift()],f=[],g=[],h=[];d.length>0;){var i=d.shift(),j=i.split("}")[0],k=i.substring(j.length+1),l=b(j);g.push(l.substitution),f.push(l.prefix),e.push(k),h=h.concat(l.substitution.varNames)}this.fill=function(a){for(var b=e[0],c=0;c0&&"/"===b.charAt(a.length-1)||"#"===c.charAt(0)||"?"===c.charAt(0))return!0}return!1}function l(a){var b,c,d=new o,e={setErrorReporter:function(a){return"string"==typeof a?this.language(a):(c=a,!0)},addFormat:function(){d.addFormat.apply(d,arguments)},language:function(a){return a?(v[a]||(a=a.split("-")[0]),v[a]?(b=a,a):!1):b},addLanguage:function(a,b){var c;for(c in r)b[c]&&!b[r[c]]&&(b[r[c]]=b[c]);var d=a.split("-")[0];if(v[d]){v[a]=Object.create(v[d]);for(c in b)"undefined"==typeof v[d][c]&&(v[d][c]=b[c]),v[a][c]=b[c]}else v[a]=b,v[d]=b;return this},freshApi:function(a){var b=l();return a&&b.language(a),b},validate:function(a,e,f,g){var h=i(b),j=c?function(a,b,d){return c(a,b,d)||h(a,b,d)}:h,k=new o(d,!1,j,f,g);"string"==typeof e&&(e={$ref:e}),k.addSchema("",e);var l=k.validateAll(a,e,null,null,"");return!l&&g&&(l=k.banUnknownProperties(a,e)),this.error=l,this.missing=k.missing,this.valid=null===l,this.valid},validateResult:function(){var a={};return this.validate.apply(a,arguments),a},validateMultiple:function(a,e,f,g){var h=i(b),j=c?function(a,b,d){return c(a,b,d)||h(a,b,d)}:h,k=new o(d,!0,j,f,g);"string"==typeof e&&(e={$ref:e}),k.addSchema("",e),k.validateAll(a,e,null,null,""),g&&k.banUnknownProperties(a,e);var l={};return l.errors=k.errors,l.missing=k.missing,l.valid=0===l.errors.length,l},addSchema:function(){return d.addSchema.apply(d,arguments)},getSchema:function(){return d.getSchema.apply(d,arguments)},getSchemaMap:function(){return d.getSchemaMap.apply(d,arguments)},getSchemaUris:function(){return d.getSchemaUris.apply(d,arguments)},getMissingUris:function(){return d.getMissingUris.apply(d,arguments)},dropSchemas:function(){d.dropSchemas.apply(d,arguments)},defineKeyword:function(){d.defineKeyword.apply(d,arguments)},defineError:function(a,b,c){if("string"!=typeof a||!/^[A-Z]+(_[A-Z]+)*$/.test(a))throw new Error("Code name must be a string in UPPER_CASE_WITH_UNDERSCORES");if("number"!=typeof b||b%1!==0||1e4>b)throw new Error("Code number must be an integer > 10000");if("undefined"!=typeof r[a])throw new Error("Error already defined: "+a+" as "+r[a]);if("undefined"!=typeof s[b])throw new Error("Error code already used: "+s[b]+" as "+b);r[a]=b,s[b]=a,u[a]=u[b]=c;for(var d in v){var e=v[d];e[a]&&(e[b]=e[b]||e[a])}},reset:function(){d.reset(),this.error=null,this.missing=[],this.valid=!0},missing:[],error:null,valid:!0,normSchema:h,resolveUrl:f,getDocumentUri:g,errorCodes:r};return e.language(a||"en"),e}Object.keys||(Object.keys=function(){var a=Object.prototype.hasOwnProperty,b=!{toString:null}.propertyIsEnumerable("toString"),c=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],d=c.length;return function(e){if("object"!=typeof e&&"function"!=typeof e||null===e)throw new TypeError("Object.keys called on non-object");var f=[];for(var g in e)a.call(e,g)&&f.push(g);if(b)for(var h=0;d>h;h++)a.call(e,c[h])&&f.push(c[h]);return f}}()),Object.create||(Object.create=function(){function a(){}return function(b){if(1!==arguments.length)throw new Error("Object.create implementation only accepts one parameter.");return a.prototype=b,new a}}()),Array.isArray||(Array.isArray=function(a){return"[object Array]"===Object.prototype.toString.call(a)}),Array.prototype.indexOf||(Array.prototype.indexOf=function(a){if(null===this)throw new TypeError;var b=Object(this),c=b.length>>>0;if(0===c)return-1;var d=0;if(arguments.length>1&&(d=Number(arguments[1]),d!==d?d=0:0!==d&&d!==1/0&&d!==-(1/0)&&(d=(d>0||-1)*Math.floor(Math.abs(d)))),d>=c)return-1;for(var e=d>=0?d:Math.max(c-Math.abs(d),0);c>e;e++)if(e in b&&b[e]===a)return e;return-1}),Object.isFrozen||(Object.isFrozen=function(a){for(var b="tv4_test_frozen_key";a.hasOwnProperty(b);)b+=Math.random();try{return a[b]=!0,delete a[b],!1}catch(c){return!0}});var m={"+":!0,"#":!0,".":!0,"/":!0,";":!0,"?":!0,"&":!0},n={"*":!0};c.prototype={toString:function(){return this.template},fillFromObject:function(a){return this.fill(function(b){return a[b]})}};var o=function(a,b,c,d,e){if(this.missing=[],this.missingMap={},this.formatValidators=a?Object.create(a.formatValidators):{},this.schemas=a?Object.create(a.schemas):{},this.collectMultiple=b,this.errors=[],this.handleError=b?this.collectError:this.returnError,d&&(this.checkRecursive=!0,this.scanned=[],this.scannedFrozen=[],this.scannedFrozenSchemas=[],this.scannedFrozenValidationErrors=[],this.validatedSchemasKey="tv4_validation_id",this.validationErrorsKey="tv4_validation_errors_id"),e&&(this.trackUnknownProperties=!0,this.knownPropertyPaths={},this.unknownPropertyPaths={}),this.errorReporter=c||i("en"),"string"==typeof this.errorReporter)throw new Error("debug");if(this.definedKeywords={},a)for(var f in a.definedKeywords)this.definedKeywords[f]=a.definedKeywords[f].slice(0)};o.prototype.defineKeyword=function(a,b){this.definedKeywords[a]=this.definedKeywords[a]||[],this.definedKeywords[a].push(b)},o.prototype.createError=function(a,b,c,d,e,f,g){var h=new j(a,b,c,d,e);return h.message=this.errorReporter(h,f,g),h},o.prototype.returnError=function(a){return a},o.prototype.collectError=function(a){return a&&this.errors.push(a),null},o.prototype.prefixErrors=function(a,b,c){for(var d=a;d=p&&q>d)return this.createError(r.NUMBER_MULTIPLE_OF,{value:a,multipleOf:c},"","",null,a,b)}return null},o.prototype.validateMinMax=function(a,b){if("number"!=typeof a)return null;if(void 0!==b.minimum){if(ab.maximum)return this.createError(r.NUMBER_MAXIMUM,{value:a,maximum:b.maximum},"","/maximum",null,a,b);if(b.exclusiveMaximum&&a===b.maximum)return this.createError(r.NUMBER_MAXIMUM_EXCLUSIVE,{value:a,maximum:b.maximum},"","/exclusiveMaximum",null,a,b)}return null},o.prototype.validateNaN=function(a,b){return"number"!=typeof a?null:isNaN(a)===!0||a===1/0||a===-(1/0)?this.createError(r.NUMBER_NOT_A_NUMBER,{value:a},"","/type",null,a,b):null},o.prototype.validateString=function(a,b,c){return this.validateStringLength(a,b,c)||this.validateStringPattern(a,b,c)||null},o.prototype.validateStringLength=function(a,b){return"string"!=typeof a?null:void 0!==b.minLength&&a.lengthb.maxLength?this.createError(r.STRING_LENGTH_LONG,{length:a.length,maximum:b.maxLength},"","/maxLength",null,a,b):null},o.prototype.validateStringPattern=function(a,b){if("string"!=typeof a||"string"!=typeof b.pattern&&!(b.pattern instanceof RegExp))return null;var c;if(b.pattern instanceof RegExp)c=b.pattern;else{var d,e="",f=b.pattern.match(/^\/(.+)\/([img]*)$/);f?(d=f[1],e=f[2]):d=b.pattern,c=new RegExp(d,e)}return c.test(a)?null:this.createError(r.STRING_PATTERN,{pattern:b.pattern},"","/pattern",null,a,b)},o.prototype.validateArray=function(a,b,c){return Array.isArray(a)?this.validateArrayLength(a,b,c)||this.validateArrayUniqueItems(a,b,c)||this.validateArrayItems(a,b,c)||null:null},o.prototype.validateArrayLength=function(a,b){var c;return void 0!==b.minItems&&a.lengthb.maxItems&&(c=this.createError(r.ARRAY_LENGTH_LONG,{length:a.length,maximum:b.maxItems},"","/maxItems",null,a,b),this.handleError(c))?c:null},o.prototype.validateArrayUniqueItems=function(a,b){if(b.uniqueItems)for(var c=0;cb.maxProperties&&(c=this.createError(r.OBJECT_PROPERTIES_MAXIMUM,{propertyCount:d.length,maximum:b.maxProperties},"","/maxProperties",null,a,b),this.handleError(c))?c:null},o.prototype.validateObjectRequiredProperties=function(a,b){if(void 0!==b.required)for(var c=0;c Date: Fri, 19 Aug 2016 10:26:24 -0700 Subject: [PATCH 3/3] Added missing generated files --- test/all_concat.js | 2680 ++++++++++++++++++++++++++++++++++++++++ test/all_concat.js.map | 64 + tv4.js | 1699 +++++++++++++++++++++++++ tv4.min.js | 2 + 4 files changed, 4445 insertions(+) create mode 100644 test/all_concat.js create mode 100644 test/all_concat.js.map create mode 100644 tv4.js create mode 100644 tv4.min.js diff --git a/test/all_concat.js b/test/all_concat.js new file mode 100644 index 0000000..3a6727c --- /dev/null +++ b/test/all_concat.js @@ -0,0 +1,2680 @@ +"use strict"; + +//need to declare these for node and modern browsers +var tv4; +var assert; + +if (typeof process === 'object' && typeof process.cwd !== 'undefined') { + // NodeJS + tv4 = require('./../').tv4; + assert = require('proclaim'); + require('source-map-support').install(); + + var fs = require('fs'); + var getJSON = function (file) { + var json; + try { + json = JSON.parse(fs.readFileSync(file, 'utf8')); + } + catch (e) { + assert.fail(e, null, file + ': ' + String(e), 'getJSON'); + } + assert.isObject(json, file); + return json; + }; + assert.isFile = function(file, msg) { + if (!fs.existsSync(file)){ + assert.fail(false, true, msg + ': missing file ' + file, 'existsSync'); + } + }; + + describe('Verify package definition files', function (){ + var pkg; + var component; + var bower; + it('pkg', function () { + pkg = getJSON('./package.json'); + + assert.property(pkg, 'main', 'main'); + assert.isFile(pkg.main, 'main'); + }); + it('component', function () { + component = getJSON('./component.json'); + + assert.property(component, 'main', 'main'); + assert.isFile(component.main, 'main'); + + component.scripts.forEach(function(name) { + assert.isFile(name, 'scripts'); + }); + }); + it('bower', function () { + bower = getJSON('./bower.json'); + + assert.property(bower, 'main', 'main'); + assert.isFile(bower.main, 'main'); + + // should verify ignore field + }); + }); +} +else if (typeof window !== 'undefined') { + // import for browser, use from IE7/8 global bypass + assert = window.refs.assert; + tv4 = window.refs.tv4; +} + +//check if we got everything +if (!tv4) { + throw new Error('tv4 not found'); +} +if (!assert) { + throw new Error('proclaim not found'); +} +var helper = {}; +helper.dumpJSON = function (value) { + console.log(JSON.stringify(value, null, 2)); +}; + + +beforeEach(function () { + tv4 = tv4.freshApi(); +}); + + +//duck patch standard assert to chai +//quick-and-dirty wrappers +assert.property = function (object, property, message) { + if (typeof object[property] === 'undefined') { + assert.fail(object, property, message, 'have property'); + } +}; +assert.notProperty = function (object, property, message) { + if (typeof object[property] !== 'undefined') { + assert.fail(object, property, message, 'not have property'); + } +}; + +assert.ownProperty = function (object, property, message) { + if (!object.hasOwnProperty(property)) { + assert.fail(object, property, message, 'have own property'); + } +}; +assert.notOwnProperty = function (object, property, message) { + if (object.hasOwnProperty(property)) { + assert.fail(object, property, message, 'not have own property'); + } +}; + +//not ideal at all +assert.propertyVal = function (object, property, value, message) { + assert.property(object, property, message); + assert.strictEqual(object[property], value, message); +}; +assert.propertyNotVal = function (object, property, value, message) { + assert.property(object, property, message); + assert.notStrictEqual(object[property], value, message); +}; +assert.ownPropertyVal = function (object, property, value, message) { + assert.ownProperty(object, property, message); + assert.strictEqual(object[property], value, message); +}; +assert.notOwnPropertyVal = function (object, property, value, message) { + assert.notOwnProperty(object, property, message); + assert.notStrictEqual(object[property], value, message); +}; +assert.propertyValues = function (object, properties, value, message) { + assert.isObject(object, message); + assert.isObject(properties, message); + //copy properties + var props = {}; + for (var name in properties) { + props[name] = object[name]; + } + assert.deepEqual(props, properties, message); +}; +//import when fix is pushed +assert.notOk = function (value, message) { + if (!!value) { + assert.fail(value, true, message, '!='); + } +}; + +/* jshint -W060 */ + +//end of header.js + +describe("Core 01", function () { + + it("getDocumentUri returns only location part of url", function () { + + assert.strictEqual(tv4.getDocumentUri("http://example.com"), "http://example.com"); + + assert.strictEqual(tv4.getDocumentUri("http://example.com/main"), "http://example.com/main"); + assert.strictEqual(tv4.getDocumentUri("http://example.com/main/"), "http://example.com/main/"); + //assert.strictEqual(tv4.getDocumentUri("http://example.com/main//"), "http://example.com/main/"); + + assert.strictEqual(tv4.getDocumentUri("http://example.com/main/sub"), "http://example.com/main/sub"); + assert.strictEqual(tv4.getDocumentUri("http://example.com/main/sub/"), "http://example.com/main/sub/"); + + assert.strictEqual(tv4.getDocumentUri("http://example.com/main#"), "http://example.com/main"); + assert.strictEqual(tv4.getDocumentUri("http://example.com/main/sub/#"), "http://example.com/main/sub/"); + + assert.strictEqual(tv4.getDocumentUri("http://example.com/main?"), "http://example.com/main?"); + assert.strictEqual(tv4.getDocumentUri("http://example.com/main?q=1"), "http://example.com/main?q=1"); + assert.strictEqual(tv4.getDocumentUri("http://example.com/main?q=1#abc"), "http://example.com/main?q=1"); + + assert.strictEqual(tv4.getDocumentUri("http://example.com/main/#"), "http://example.com/main/"); + assert.strictEqual(tv4.getDocumentUri("http://example.com/main/#?"), "http://example.com/main/"); + assert.strictEqual(tv4.getDocumentUri("http://example.com/main/#?q=a/b/c"), "http://example.com/main/"); + }); +}); + +describe("Core 02", function () { + + it("tv4.freshApi() produces working copy", function () { + var duplicate = tv4.freshApi(); + assert.isObject(duplicate); + // Basic sanity checks + assert.isTrue(duplicate.validate({}, {type: "object"})); + assert.isObject(duplicate.validateMultiple("string", {})); + }); + + it("tv4.freshApi() has separate schema store", function () { + var duplicate = tv4.freshApi(); + + var schemaUrl1 = "http://example.com/schema/schema1"; + var schemaUrl2 = "http://example.com/schema/schema2"; + duplicate.addSchema(schemaUrl1, {}); + tv4.addSchema(schemaUrl2, {}); + + assert.isObject(duplicate.getSchema(schemaUrl1)); + assert.isUndefined(tv4.getSchema(schemaUrl1)); + assert.isUndefined(duplicate.getSchema(schemaUrl2)); + assert.isObject(tv4.getSchema(schemaUrl2)); + }); +}); + +describe("Core 03", function () { + + it("tv4.dropSchemas() drops stored schemas", function () { + var schema = { + "items": {"$ref": "http://example.com/schema/items#"}, + "maxItems": 2 + }; + tv4.addSchema("http://example.com/schema", schema); + assert.strictEqual(tv4.getSchema("http://example.com/schema"), schema, "has schema"); + + tv4.dropSchemas(); + assert.isUndefined(tv4.getSchema("http://example.com/schema"), "doesn't have schema"); + }); + + it("tv4.reset() clears errors, valid and missing", function () { + it("must be string, is integer", function () { + var data = 5; + var schema = {"type": "array", "items" : {"$ref" : "http://example.com"}}; + + assert.notOk(tv4.error, "starts with no error"); + assert.isTrue(tv4.valid, "starts valid"); + assert.length(tv4.missing, 0, "starts with 0 missing"); + + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + assert.ok(tv4.error, "has error"); + assert.isFalse(tv4.valid, "is invalid"); + assert.length(tv4.missing, 1, "missing 1"); + + tv4.reset(); + assert.notOk(tv4.error, "reset to no error"); + assert.isTrue(tv4.valid, "reset to valid"); + assert.length(tv4.missing, 0, "reset to 0 missing"); + }); + }); +}); + +describe("Core 04", function () { + + var schema = { + "type": "string" + }; + + it("ValidationError is Error subtype", function () { + var res = tv4.validateResult(123, schema); + assert.isObject(res); + assert.isObject(res.error); + assert.isInstanceOf(res.error, Error); + assert.isString(res.error.stack); + }); + + it("ValidationError has own stack trace", function () { + function errorA() { + var res = tv4.validateResult(123, schema); + assert.isFalse(res.valid); + assert.isString(res.error.stack); + assert.ok(res.error.stack.indexOf('errorA') > -1, 'has own stack trace A'); + } + + function errorB() { + var res = tv4.validateResult(123, schema); + assert.isFalse(res.valid); + assert.isString(res.error.stack); + assert.ok(res.error.stack.indexOf('errorB') > -1, 'has own stack trace B'); + } + errorA(); + errorB(); + }); +}); + +describe("Any types 01", function () { + + it("no type specified", function () { + var data = {}; + var schema = {}; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("must be object, is object", function () { + var data = {}; + var schema = {"type": "object"}; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("must be object or string, is object", function () { + var data = {}; + var schema = {"type": ["object", "string"]}; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("must be object or string, is array", function () { + var data = []; + var schema = {"type": ["object", "string"]}; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); + + it("must be array, is object", function () { + var data = {}; + var schema = {"type": ["array"]}; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); + + it("must be string, is integer", function () { + var data = 5; + var schema = {"type": ["string"]}; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); + + it("must be object, is null", function () { + var data = null; + var schema = {"type": ["object"]}; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); + + it("must be null, is null", function () { + var data = null; + var schema = {"type": "null"}; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("doesn't crash on invalid type", function () { + var data = null; + var schema = {"type": {"foo": "bar"}}; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); +}); + +describe("Any types 01", function () { + + it("enum [1], was 1", function () { + var data = 1; + var schema = {"enum": [1]}; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("enum [1], was \"1\"", function () { + var data = "1"; + var schema = {"enum": [1]}; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); + + it("enum [{}], was {}", function () { + var data = {}; + var schema = {"enum": [ + {} + ]}; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("enum [{\"key\":\"value\"], was {}", function () { + var data = {}; + var schema = {"enum": [ + {"key": "value"} + ]}; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); + + it("enum [{\"key\":\"value\"], was {\"key\": \"value\"}", function () { + var data = {}; + var schema = {"enum": [ + {"key": "value"} + ]}; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); + + it("Enum with array value - success", function () { + var data = [1, true, 0]; + var schema = {"enum": [ + [1, true, 0], + 5, + {} + ]}; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("Enum with array value - failure 1", function () { + var data = [1, true, 0, 5]; + var schema = {"enum": [ + [1, true, 0], + 5, + {} + ]}; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); + + it("Enum with array value - failure 2", function () { + var data = [1, true, 5]; + var schema = {"enum": [ + [1, true, 0], + 5, + {} + ]}; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); +}); + +describe("Numeric - multipleOf", function () { + + it("pass", function () { + var data = 5; + var schema = {"multipleOf": 2.5}; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("fail", function () { + var data = 5; + var schema = {"multipleOf": 0.75}; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); + + it("floating-point pass 6.6/2.2", function () { + var data = 6.6; + var schema = {"multipleOf": 2.2}; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("floating-point pass 6.6666/2.2222", function () { + var data = 6.6666; + var schema = {"multipleOf": 2.2222}; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); +}); +describe("Numberic 02", function () { + + it("minimum success", function () { + var data = 5; + var schema = {minimum: 2.5}; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("minimum failure", function () { + var data = 5; + var schema = {minimum: 7}; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); + + it("minimum equality success", function () { + var data = 5; + var schema = {minimum: 5}; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("minimum equality failure", function () { + var data = 5; + var schema = {minimum: 5, exclusiveMinimum: true}; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); + + it("maximum success", function () { + var data = 5; + var schema = {maximum: 7}; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("maximum failure", function () { + var data = -5; + var schema = {maximum: -10}; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); + + it("maximum equality success", function () { + var data = 5; + var schema = {maximum: 5}; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("maximum equality failure", function () { + var data = 5; + var schema = {maximum: 5, exclusiveMaximum: true}; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); +}); + +describe("Numeric 03", function () { + + it("NaN failure", function() { + var data = NaN; + var schema = {}; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); + + it("Infinity failure", function() { + var data = Infinity; + var schema = {}; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); + + it("-Infinity failure", function() { + var data = -Infinity; + var schema = {}; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); + + it("string to number failure", function() { + var data = Number('foo'); + var schema = {}; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); + + it("string to number success", function() { + var data = Number('123'); + var schema = {}; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("max value success", function() { + var data = Number.MAX_VALUE; + var schema = {}; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + /* Travis reports: Bad number '1.798e+308' (which is a good thing, as it should be Infinity) + it("big number failure", function() { + var data = 1.798e+308; + var schema = {}; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); + */ +}); + +describe("Strings 01", function () { + + it("no length constraints", function () { + var data = "test"; + var schema = {}; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("minimum length success", function () { + var data = "test"; + var schema = {minLength: 3}; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("minimum length failure", function () { + var data = "test"; + var schema = {minLength: 5}; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); + + it("maximum length success", function () { + var data = "test1234"; + var schema = {maxLength: 10}; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("maximum length failure", function () { + var data = "test1234"; + var schema = {maxLength: 5}; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); + + it("check error message", function () { + var data = "test1234"; + var schema = {maxLength: 5}; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + //return typeof tv4.error.message !== "undefined"; + assert.ok(tv4.error.message); + }); +}); + +describe("Strings 02", function () { + + it("pattern success", function () { + var data = "9test"; + var schema = {"pattern": "^[0-9][a-zA-Z]*$"}; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("pattern failure", function () { + var data = "9test9"; + var schema = {"pattern": "^[0-9][a-zA-Z]*$"}; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); + + it("accepts RegExp object", function () { + var data = "9test"; + var schema = {"pattern": /^[0-9][a-zA-Z]*$/}; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("accepts RegExp literal", function () { + var data = "9TEST"; + var schema = {"pattern": "/^[0-9][a-z]*$/i"}; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); +}); +describe("Arrays 01", function () { + + it("no length constraints", function () { + var data = [1, 2, 3]; + var schema = {}; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("minimum length success", function () { + var data = [1, 2, 3]; + var schema = {minItems: 3}; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("minimum length failure", function () { + var data = [1, 2, 3]; + var schema = {minItems: 5}; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); + + it("maximum length success", function () { + var data = [1, 2, 3, 4, 5]; + var schema = {maxItems: 10}; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("maximum length failure", function () { + var data = [1, 2, 3, 4, 5]; + var schema = {maxItems: 3}; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); +}); + +describe("Arrays 02", function () { + + it("uniqueItems success", function () { + var data = [1, true, "1"]; + var schema = {uniqueItems: true}; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("uniqueItems failure", function () { + var data = [1, true, "1", 1]; + var schema = {uniqueItems: true}; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); +}); + +describe("Arrays 03", function () { + + it("plain items success", function () { + var data = [1, 2, 3, 4]; + var schema = { + "items": { + "type": "integer" + } + }; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("plain items failure", function () { + var data = [1, 2, true, 3]; + var schema = { + "items": { + "type": "integer" + } + }; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); +}); + +describe("Arrays 04", function () { + + it("plain items success", function () { + var data = [1, true, "one"]; + var schema = { + "items": [ + {"type": "integer"}, + {"type": "boolean"} + ] + }; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("plain items failure", function () { + var data = [1, null, "one"]; + var schema = { + "items": [ + {"type": "integer"}, + {"type": "boolean"} + ] + }; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); +}); + +describe("Arrays 05", function () { + + it("additional items schema success", function () { + var data = [1, true, "one", "uno"]; + var schema = { + "items": [ + {"type": "integer"}, + {"type": "boolean"} + ], + "additionalItems": {"type": "string"} + }; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("additional items schema failure", function () { + var data = [1, true, "one", 1]; + var schema = { + "items": [ + {"type": "integer"}, + {"type": "boolean"} + ], + "additionalItems": {"type": "string"} + }; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); + + it("additional items boolean success", function () { + var data = [1, true, "one", "uno"]; + var schema = { + "items": [ + {"type": "integer"}, + {"type": "boolean"} + ], + "additionalItems": true + }; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("additional items boolean failure", function () { + var data = [1, true, "one", "uno"]; + var schema = { + "items": [ + {"type": "integer"}, + {"type": "boolean"} + ], + "additionalItems": false + }; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); +}); + +describe("Objects 01", function () { + + it("minimum length success", function () { + var data = {key1: 1, key2: 2, key3: 3}; + var schema = {minProperties: 3}; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("minimum length failure", function () { + var data = {key1: 1, key2: 2, key3: 3}; + var schema = {minProperties: 5}; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); + + it("maximum length success", function () { + var data = {key1: 1, key2: 2, key3: 3}; + var schema = {maxProperties: 5}; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("maximum length failure", function () { + var data = {key1: 1, key2: 2, key3: 3}; + var schema = {maxProperties: 2}; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); +}); + +describe("Objects 02", function () { + + it("required success", function () { + var data = {key1: 1, key2: 2, key3: 3}; + var schema = {required: ["key1", "key2"]}; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("required failure", function () { + var data = {key1: 1, key2: 2, key3: 3}; + var schema = {required: ["key1", "notDefined"]}; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); +}); + +describe("Objects 03", function () { + + it("properties success", function () { + var data = {intKey: 1, stringKey: "one"}; + var schema = { + properties: { + intKey: {"type": "integer"}, + stringKey: {"type": "string"} + } + }; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("properties failure", function () { + var data = {intKey: 1, stringKey: false}; + var schema = { + properties: { + intKey: {"type": "integer"}, + stringKey: {"type": "string"} + } + }; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); +}); + +describe("Objects 04", function () { + + it("patternProperties success", function () { + var data = {intKey: 1, intKey2: 5}; + var schema = { + properties: { + intKey: {"type": "integer"} + }, + patternProperties: { + "^int": {minimum: 0} + } + }; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("patternProperties failure 1", function () { + var data = {intKey: 1, intKey2: 5}; + var schema = { + properties: { + intKey: {minimum: 5} + }, + patternProperties: { + "^int": {"type": "integer"} + } + }; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); + + it("patternProperties failure 2", function () { + var data = {intKey: 10, intKey2: "string value"}; + var schema = { + properties: { + intKey: {minimum: 5} + }, + patternProperties: { + "^int": {"type": "integer"} + } + }; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); +}); + +describe("Objects 05", function () { + + it("additionalProperties schema success", function () { + var data = {intKey: 1, intKey2: 5, stringKey: "string"}; + var schema = { + properties: { + intKey: {"type": "integer"} + }, + patternProperties: { + "^int": {minimum: 0} + }, + additionalProperties: {"type": "string"} + }; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("patternProperties schema failure", function () { + var data = {intKey: 10, intKey2: 5, stringKey: null}; + var schema = { + properties: { + intKey: {minimum: 5} + }, + patternProperties: { + "^int": {"type": "integer"} + }, + additionalProperties: {"type": "string"} + }; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); + + it("patternProperties boolean success", function () { + var data = {intKey: 10, intKey2: 5, stringKey: null}; + var schema = { + properties: { + intKey: {minimum: 5} + }, + patternProperties: { + "^int": {"type": "integer"} + }, + additionalProperties: true + }; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("patternProperties boolean failure", function () { + var data = {intKey: 10, intKey2: 5, stringKey: null}; + var schema = { + properties: { + intKey: {minimum: 5} + }, + patternProperties: { + "^int": {"type": "integer"} + }, + additionalProperties: false + }; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); +}); +describe("Objects 06", function () { + + it("string dependency success", function () { + var data = {key1: 5, key2: "string"}; + var schema = { + dependencies: { + key1: "key2" + } + }; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("string dependency failure", function () { + var data = {key1: 5}; + var schema = { + dependencies: { + key1: "key2" + } + }; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); + + it("array dependency success", function () { + var data = {key1: 5, key2: "string"}; + var schema = { + dependencies: { + key1: ["key2"] + } + }; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("array dependency failure", function () { + var data = {key1: 5}; + var schema = { + dependencies: { + key1: ["key2"] + } + }; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); + + it("schema dependency success", function () { + var data = {key1: 5, key2: "string"}; + var schema = { + dependencies: { + key1: { + properties: { + key2: {"type": "string"} + } + } + } + }; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("schema dependency failure", function () { + var data = {key1: 5, key2: 5}; + var schema = { + dependencies: { + key1: { + properties: { + key2: {"type": "string"} + } + } + } + }; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); +}); + +describe("Combinators 01", function () { + + it("allOf success", function () { + var data = 10; + var schema = { + "allOf": [ + {"type": "integer"}, + {"minimum": 5} + ] + }; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("allOf failure", function () { + var data = 1; + var schema = { + "allOf": [ + {"type": "integer"}, + {"minimum": 5} + ] + }; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); +}); + +describe("Combinators 02", function () { + + it("anyOf success", function () { + var data = "hello"; + var schema = { + "anyOf": [ + {"type": "integer"}, + {"type": "string"}, + {"minLength": 1} + ] + }; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); +}); + +it("anyOf failure", function () { + var data = true; + var schema = { + "anyOf": [ + {"type": "integer"}, + {"type": "string"} + ] + }; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); +}); +}); + +describe("Combinators 03", function () { + + it("oneOf success", function () { + var data = 5; + var schema = { + "oneOf": [ + {"type": "integer"}, + {"type": "string"}, + {"type": "string", minLength: 1} + ] + }; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("oneOf failure (too many)", function () { + var data = "string"; + var schema = { + "oneOf": [ + {"type": "integer"}, + {"type": "string"}, + {"minLength": 1} + ] + }; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); + + it("oneOf failure (no matches)", function () { + var data = false; + var schema = { + "oneOf": [ + {"type": "integer"}, + {"type": "string"}, + {"type": "string", "minLength": 1} + ] + }; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); +}); + +describe("Combinators 04", function () { + + it("not success", function () { + var data = 5; + var schema = { + "not": {"type": "string"} + }; + var valid = tv4.validate(data, schema); + assert.isTrue(valid); + }); + + it("not failure", function () { + var data = "test"; + var schema = { + "not": {"type": "string"} + }; + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); +}); + +describe("$ref 01", function () { + + it("normalise - untouched immediate $ref", function () { + var schema = { + "items": {"$ref": "#"} + }; + tv4.normSchema(schema); + assert.propertyVal(schema.items, '$ref', "#"); + //return schema.items['$ref'] == "#"; + }); + + it("normalise - id as base", function () { + var schema = { + "id": "baseUrl", + "items": {"$ref": "#"} + }; + tv4.normSchema(schema); + assert.propertyVal(schema.items, '$ref', "baseUrl#"); + //return schema.items['$ref'] == "baseUrl#"; + }); + + it("normalise - id relative to parent", function () { + var schema = { + "id": "http://example.com/schema", + "items": { + "id": "otherSchema", + "items": { + "$ref": "#" + } + } + }; + tv4.normSchema(schema); + assert.strictEqual(schema.items.id, "http://example.com/otherSchema", "schema.items.id"); + assert.strictEqual(schema.items.items['$ref'], "http://example.com/otherSchema#", "$ref"); + //this.assert(schema.items.id == "http://example.com/otherSchema", "schema.items.id"); + //this.assert(schema.items.items['$ref'] == "http://example.com/otherSchema#", "$ref"); + }); + + it("normalise - do not touch contents of \"enum\"", function () { + var schema = { + "id": "http://example.com/schema", + "items": { + "id": "otherSchema", + "enum": [ + { + "$ref": "#" + } + ] + } + }; + tv4.normSchema(schema); + assert.strictEqual(schema.items['enum'][0]['$ref'], "#"); + //this.assert(schema.items['enum'][0]['$ref'] == "#"); + }); + + it("Only normalise id and $ref if they are strings", function () { + var schema = { + "properties": { + "id": {"type": "integer"}, + "$ref": {"type": "integer"} + } + }; + var data = {"id": "test", "$ref": "test"}; + tv4.normSchema(schema); + var valid = tv4.validate(data, schema); + assert.isFalse(valid); + }); +}); + +describe("$ref 02", function () { + + it("skip unneeded", function () { + var schema = { + "items": {"$ref": "http://example.com/schema#"} + }; + tv4.validate([], schema); + assert.notProperty(tv4.missing, "http://example.com/schema"); + assert.length(tv4.missing, 0); + //return !tv4.missing["http://example.com/schema"] + // && tv4.missing.length == 0; + }); + + it("list missing (map)", function () { + var schema = { + "items": {"$ref": "http://example.com/schema#"} + }; + tv4.validate([1, 2, 3], schema); + assert.property(tv4.missing, "http://example.com/schema"); + //return !!tv4.missing["http://example.com/schema"]; + }); + + it("list missing (index)", function () { + var schema = { + "items": {"$ref": "http://example.com/schema#"} + }; + tv4.validate([1, 2, 3], schema); + assert.length(tv4.missing, 1); + assert.strictEqual(tv4.missing[0], "http://example.com/schema"); + //return tv4.missing[0] == "http://example.com/schema"; + }); +}); +describe("$ref 03", function () { + + it("addSchema(), getSchema()", function () { + var url = "http://example.com/schema"; + var schema = { + "test": "value" + }; + tv4.addSchema(url, schema); + var fetched = tv4.getSchema(url); + assert.strictEqual(fetched.test, "value"); + //return fetched.test == "value"; + }); + + it("addSchema(), getSchema() with blank fragment", function () { + var url = "http://example.com/schema"; + var schema = { + "test": "value" + }; + tv4.addSchema(url, schema); + var fetched = tv4.getSchema(url + "#"); + assert.strictEqual(fetched.test, "value"); + //return fetched.test == "value"; + }); + + it("addSchema(), getSchema() with pointer path fragment", function () { + var url = "http://example.com/schema"; + var schema = { + "items": { + "properties": { + "key[]": { + "inner/key~": "value" + } + } + } + }; + tv4.addSchema(url, schema); + var fetched = tv4.getSchema(url + "#/items/properties/key%5B%5D/inner~1key~0"); + assert.strictEqual(fetched, "value"); + //return fetched == "value"; + }); + + it("addSchema(), getSchema() adds referred schemas", function () { + tv4 = tv4.freshApi(); + + var data = [123, true]; + var valid; + var url = "http://example.com/schema"; + var schema = { + "type": "array", + "items": {"$ref": "http://example.com/schema/sub#item"} + }; + tv4.addSchema(url, schema); + + //test missing + valid = tv4.validate(data, schema); + assert.isTrue(valid); + assert.length(tv4.missing, 1); + assert.isUndefined(tv4.getSchema('http://example.com/schema/sub')); + + var item = { + "id": "#item", + "type": "boolean" + }; + var sub = { + "id": "http://example.com/schema/sub", + "type": "object", + "lib": { + "item": item + } + }; + tv4.addSchema(sub); + + //added it? + assert.equal(tv4.getSchema(url), schema); + assert.equal(tv4.getSchema('http://example.com/schema/sub'), sub); + assert.equal(tv4.getSchema('http://example.com/schema/sub#item'), item); + + //now use it + valid = tv4.validate(data, schema); + assert.length(tv4.missing, 0); + assert.isFalse(valid); + + var error = { + code: 0, + message: 'Invalid type: number (expected boolean)', + dataPath: '/0', + schemaPath: '/items/type', + subErrors: null }; + assert.propertyValues(tv4.error, error); + }); +}); +describe("$ref 04", function () { + + it("addSchema(), $ref", function () { + var url = "http://example.com/schema"; + var schema = { + "test": "value" + }; + tv4.addSchema(url, schema); + + var otherSchema = { + "items": {"$ref": url} + }; + var valid = tv4.validate([0,1,2,3], otherSchema); + + assert.isTrue(valid, "should be valid"); + assert.length(tv4.missing, 0, "should have no missing schemas"); + + //this.assert(valid, "should be valid"); + //this.assert(tv4.missing.length == 0, "should have no missing schemas"); + }); + + it("internal $ref", function () { + var schema = { + "type": "array", + "items": {"$ref": "#"} + }; + + assert.isTrue(tv4.validate([[],[[]]], schema), "List of lists should be valid"); + assert.isTrue(!tv4.validate([0,1,2,3], schema), "List of ints should not"); + assert.isTrue(!tv4.validate([[true], []], schema), "List of list with boolean should not"); + + assert.length(tv4.missing, 0, "should have no missing schemas"); + + //this.assert(tv4.validate([[],[[]]], schema), "List of lists should be valid"); + //this.assert(!tv4.validate([0,1,2,3], schema), "List of ints should not"); + //this.assert(!tv4.validate([[true], []], schema), "List of list with boolean should not"); + + //this.assert(tv4.missing.length == 0, "should have no missing schemas"); + }); +}); + +describe("$ref 05", function () { + + it("inline addressing for fragments", function () { + var schema = { + "type": "array", + "items": {"$ref": "#test"}, + "testSchema": { + "id": "#test", + "type": "boolean" + } + }; + var error = { + code: 0, + message: 'Invalid type: number (expected boolean)', + dataPath: '/0', + schemaPath: '/items/type', + subErrors: null + }; + + var data = [0, false]; + var valid = tv4.validate(data, schema); + assert.isFalse(valid, 'inline addressing invalid 0, false'); + assert.propertyValues(tv4.error, error, 'errors equal'); + }); + + it("don't trust non sub-paths", function () { + var examplePathBase = "http://example.com/schema"; + var examplePath = examplePathBase + "/schema"; + var schema = { + "id": examplePath, + "type": "array", + "items": {"$ref": "other-schema"}, + "testSchema": { + "id": "/other-schema", + "type": "boolean" + } + }; + tv4.addSchema(examplePath, schema); + var data = [0, false]; + var valid = tv4.validate(data, examplePath); + + assert.length(tv4.missing, 1, "should have missing schema"); + assert.strictEqual(tv4.missing[0], examplePathBase + "/other-schema", "incorrect schema missing: " + tv4.missing[0]); + assert.isTrue(valid, "should pass, as remote schema not found"); + + //this.assert(tv4.missing.length == 1, "should have missing schema"); + //this.assert(tv4.missing[0] == examplePathBase + "/other-schema", "incorrect schema missing: " + tv4.missing[0]); + //this.assert(valid, "should pass, as remote schema not found"); + }); +}); + +describe("$refs to $refs", function () { + it("addSchema(), $ref", function () { + var schema = { + id: "http://example.com/schema", + some: { + other: {type: "number"} + }, + data: {'$ref': "#/some/other"} + }; + + tv4.addSchema(schema); + assert.isTrue(tv4.validate(42, {"$ref": "http://example.com/schema#/data"}), "42 valid"); + //assert.isFalse(tv4.validate(42, {"$ref": "http://example.com/schema#/data"}), "\"42\" invalid"); + + assert.length(tv4.missing, 0, "should have no missing schemas"); + }); + + it("Don't hang on circle", function () { + var schema = { + id: "http://example.com/schema", + ref1: {"$ref": "#/ref2"}, + ref2: {"$ref": "#/ref1"} + }; + + tv4.addSchema(schema); + var result = tv4.validateResult(42, "http://example.com/schema#/ref1"); + + assert.isFalse(result.valid, "not valid"); + assert.equal(result.error.code, tv4.errorCodes.CIRCULAR_REFERENCE, 'Error code correct'); + }); +}); + +describe("API 01", function () { + + it("validateResult returns object with appropriate properties", function () { + var data = {}; + var schema = {"type": "array"}; + tv4.error = null; + tv4.missing = []; + var result = tv4.validateResult(data, schema); + + assert.isFalse(result.valid, "result.valid === false"); + assert.isTypeOf(result.error, "object", "result.error is object"); + assert.isArray(result.missing, "result.missing is array"); + assert.isFalse(!!tv4.error, "tv4.error == null"); + + //this.assert(result.valid === false, "result.valid === false"); + //this.assert(typeof result.error == "object", "result.error is object"); + //this.assert(Array.isArray(result.missing), "result.missing is array"); + //this.assert(tv4.error == null, "tv4.error == null"); + }); +}); + +describe("API 02", function () { + + it("tv4.errorCodes exists", function () { + assert.isObject(tv4.errorCodes); + //return typeof tv4.errorCodes == "object"; + }); +}); + +describe("API 03", function () { + + it("getSchemaUris() on clean tv4 returns an empty array", function () { + var list = tv4.getSchemaUris(); + assert.isArray(list); + assert.length(list, 0); + }); + + it("getSchemaUris() returns newly added schema urls", function () { + tv4.addSchema("http://example.com/schema", {type: "object"}); + var list = tv4.getSchemaUris(); + assert.isArray(list); + assert.length(list, 1); + assert.strictEqual(list[0], "http://example.com/schema"); + }); + + it("getMissingUris() returns only missing items", function () { + var schema = { + "items": {"$ref": "http://example.com/schema/item#"} + }; + tv4.addSchema("http://example.com/schema/main", schema); + + var item = { + "id": "http://example.com/schema/item", + "type": "boolean" + }; + + var list; + list = tv4.getSchemaUris(); + assert.isArray(list); + assert.length(list, 1); + assert.includes(list, "http://example.com/schema/main", 'map has main uri'); + + list = tv4.getMissingUris(); + assert.isArray(list); + assert.length(list, 1); + assert.includes(list, "http://example.com/schema/item", 'map has item uri'); + + tv4.addSchema(item); + + list = tv4.getMissingUris(); + assert.isArray(list); + assert.length(list, 0); + }); + + it("getSchemaUris() optionally return filtered items", function () { + var schema = { + "items": {"$ref": "http://example.com/schema/item#"} + }; + tv4.addSchema("http://example.com/schema/main", schema); + + var list; + list = tv4.getSchemaUris(/schema\/main/); + assert.isArray(list); + assert.length(list, 1, 'list 1 main'); + assert.includes(list, "http://example.com/schema/main"); + + list = tv4.getMissingUris(/^https?/); + assert.isArray(list); + assert.length(list, 1, 'list 1 item'); + assert.includes(list, "http://example.com/schema/item"); + }); + + it("getSchemaUris() returns unique uris without fragment", function () { + var schema = { + "properties": { + "alpha": { + "$ref": "http://example.com/schema/lib#alpha" + }, + "beta": { + "$ref": "http://example.com/schema/lib#beta" + } + } + }; + tv4.addSchema("http://example.com/schema/main", schema); + var sub = { + "id": "http://example.com/schema/item", + "items": { + "type": "boolean" + } + }; + tv4.addSchema(sub); + + var list; + list = tv4.getSchemaUris(); + assert.isArray(list); + assert.length(list, 2); + assert.includes(list, "http://example.com/schema/main"); + assert.includes(list, "http://example.com/schema/item"); + + list = tv4.getMissingUris(); + assert.isArray(list); + assert.length(list, 1); + assert.includes(list, "http://example.com/schema/lib"); + }); + + + it("getSchemaMap() on clean tv4 returns an empty object", function () { + var map = tv4.getSchemaMap(); + assert.isObject(map); + assert.isNotArray(map); + var list = Object.keys(map); + assert.length(list, 0); + }); + + it("getSchemaMap() returns an object mapping uris to schemas", function () { + var schema = { + "properties": { + "alpha": { + "$ref": "http://example.com/schema/lib#alpha" + }, + "beta": { + "$ref": "http://example.com/schema/lib#beta" + } + } + }; + tv4.addSchema("http://example.com/schema/main", schema); + var sub = { + "id": "http://example.com/schema/item", + "items": { + "type": "boolean" + } + }; + tv4.addSchema(sub); + + var map; + map = tv4.getSchemaMap(); + assert.length(Object.keys(map), 2); + assert.ownPropertyVal(map, "http://example.com/schema/main", schema); + assert.ownPropertyVal(map, "http://example.com/schema/item", sub); + }); +}); + +describe("Multiple errors 01", function () { + + it("validateMultiple returns array of errors", function () { + var data = {}; + var schema = {"type": "array"}; + var result = tv4.validateMultiple(data, schema); + + assert.isFalse(result.valid, "data should not be valid"); + assert.strictEqual(typeof result.errors, "object", "result.errors must be object"); + assert.isNumber(result.errors.length, "result.errors have numberic length"); + + //-> weird: test says be object but it's an array + + //assert.isArray(result.errors, "result.errors must be array-like"); + //assert.isObject(result.errors, "result.errors must be object"); + + //this.assert(result.valid == false, "data should not be valid"); + //this.assert(typeof result.errors == "object" && typeof result.errors.length == "number", "result.errors must be array-like"); + }); + + it("validateMultiple has multiple entries", function () { + var data = {"a": 1, "b": 2}; + var schema = {"additionalProperties": {"type": "string"}}; + var result = tv4.validateMultiple(data, schema); + + assert.length(result.errors, 2, "should return two errors"); + //this.assert(result.errors.length == 2, "should return two errors"); + }); + + it("validateMultiple correctly fails anyOf", function () { + var data = {}; + var schema = { + "anyOf": [ + {"type": "string"}, + {"type": "integer"} + ] + }; + var result = tv4.validateMultiple(data, schema); + + assert.isFalse(result.valid, "should not validate"); + assert.length(result.errors, 1, "should list one error"); + + //this.assert(result.valid == false, "should not validate"); + //this.assert(result.errors.length == 1, "should list one error"); + }); + + it("validateMultiple correctly fails not", function () { + var data = {}; + var schema = { + "not": {"type": "object"} + }; + var result = tv4.validateMultiple(data, schema); + + assert.isFalse(result.valid, "should not validate"); + assert.length(result.errors, 1, "should list one error"); + + //this.assert(result.valid == false, "should not validate"); + //this.assert(result.errors.length == 1, "should list one error"); + }); + + it("validateMultiple correctly passes not", function () { + var data = {}; + var schema = { + "not": {"type": "string"} + }; + var result = tv4.validateMultiple(data, schema); + + assert.isTrue(result.valid, "should validate"); + assert.length(result.errors, 0, "no errors"); + + //this.assert(result.valid == true, "should validate"); + //this.assert(result.errors.length == 0, "no errors"); + }); + + it("validateMultiple correctly fails multiple oneOf", function () { + var data = 5; + var schema = { + "oneOf": [ + {"type": "integer"}, + {"type": "number"} + ] + }; + var result = tv4.validateMultiple(data, schema); + + assert.isFalse(result.valid, "should not validate"); + assert.length(result.errors, 1, "only one error"); + + //this.assert(result.valid == false, "should not validate"); + //this.assert(result.errors.length == 1, "only one error"); + }); + + it("validateMultiple handles multiple missing properties", function () { + var data = {}; + var schema = { + required: ["one", "two"] + }; + var result = tv4.validateMultiple(data, schema); + + assert.isFalse(result.valid, "should not validate"); + assert.length(result.errors, 2, "two errors"); + + //this.assert(result.valid == false, "should not validate"); + //this.assert(result.errors.length == 2, "exactly two errors, not " + result.errors.length); + }); +}); +describe("Multiple errors 02", function () { + + it("validateMultiple returns array of errors", function () { + var data = { + "alternatives": { + "option1": "pattern for option 1" + } + }; + + var schema = { + "type": "object", + "properties": { + "alternatives": { + "type": "object", + "description": "Some options", + "oneOf": [ + { + "properties": { + "option1": { + "type": "string", + "pattern": "^pattern for option 1$" + } + }, + "additionalProperties": false, + "required": [ + "option1" + ] + }, + { + "properties": { + "option2": { + "type": "string", + "pattern": "^pattern for option 2$" + } + }, + "additionalProperties": false, + "required": [ + "option2" + ] + }, + { + "properties": { + "option3": { + "type": "string", + "pattern": "^pattern for option 3$" + } + }, + "additionalProperties": false, + "required": [ + "option3" + ] + } + ] + } + } + }; + var result = tv4.validateMultiple(data, schema); + + assert.isTrue(result.valid, "data should be valid"); + assert.length(result.errors, 0, "should have no errors"); + + //this.assert(result.valid == true, "data should be valid"); + //this.assert(result.errors.length == 0, "should have no errors"); + }); +}); +describe("Recursive objects 01", function () { + it("validate and variants do not choke on recursive objects", function () { + var itemA = {}; + var itemB = { a: itemA }; + itemA.b = itemB; + var aSchema = { properties: { b: { $ref: 'bSchema' }}}; + var bSchema = { properties: { a: { $ref: 'aSchema' }}}; + tv4.addSchema('aSchema', aSchema); + tv4.addSchema('bSchema', bSchema); + tv4.validate(itemA, aSchema, true); + tv4.validate(itemA, aSchema, function () {}, true); + tv4.validateResult(itemA, aSchema, true); + tv4.validateMultiple(itemA, aSchema, true); + }); +}); + +// We don't handle this in general (atm), but some users have had particular problems with things added to the Array prototype +describe("Recursive schemas", function () { + it("due to extra Array.prototype entries", function () { + var testSchema = { + items: [] + }; + Array.prototype._testSchema = testSchema; + + // Failure mode will be a RangeError (stack size limit) + tv4.addSchema('testSchema', testSchema); + + delete Array.prototype._testSchema; + }); +}); + +describe("Registering custom validator", function () { + it("Allows registration of custom validator codes for \"format\" values", function () { + tv4.addFormat('test-format', function () { + return null; + }); + }); + + it("Custom validator is correctly selected", function () { + tv4.addFormat('test-format', function (data) { + if (data !== "test string") { + return "string does not match"; + } + }); + + var schema = {format: 'test-format'}; + var data1 = "test string"; + var data2 = "other string"; + + assert.isTrue(tv4.validate(data1, schema)); + assert.isFalse(tv4.validate(data2, schema)); + assert.includes(tv4.error.message, 'string does not match'); + }); + + it("Custom validator object error format", function () { + tv4.addFormat('test-format', function (data) { + if (data !== "test string") { + return { + dataPath: "", + schemaPath: "/flah", + message: "Error message" + }; + } + }); + + var schema = {format: 'test-format'}; + var data1 = "test string"; + var data2 = "other string"; + + assert.isTrue(tv4.validate(data1, schema)); + assert.isFalse(tv4.validate(data2, schema)); + assert.includes(tv4.error.message, 'Error message'); + assert.equal(tv4.error.schemaPath, '/flah'); + }); + + it("Register multiple using object", function () { + tv4.addFormat({ + 'test1': function () {return 'break 1';}, + 'test2': function () {return 'break 2';} + }); + + var schema1 = {format: 'test1'}; + var result1 = tv4.validateResult("test string", schema1); + assert.isFalse(result1.valid); + assert.includes(result1.error.message, 'break 1'); + + var schema2 = {format: 'test2'}; + var result2 = tv4.validateResult("test string", schema2); + assert.isFalse(result2.valid); + assert.includes(result2.error.message, 'break 2'); + }); + + it("Custom validator should be skipped for null values", function () { + tv4.addFormat('null-format', function (data) { + if (data !== "test") { + return "string does not match"; + } + }); + + var schema = {type: ["string", "null"], format: 'null-format'}; + var data1 = "test"; + var data2 = null; + + assert.isTrue(tv4.validate(data1, schema)); + assert.isTrue(tv4.validate(data2, schema)); + }); + + it("Validator should be skipped for null (undefined) enum values", function () { + var schema = { + "type": "object", + "properties": { + "a": { + type: "string" + }, + "b": { + "type": ["string", "null"], + "enum": ['one', 'two', 'three'] + } + } + }; + var data1 = {a: "something", b: "test"}; + var data2 = {a: "something", b: "one"}; + var data3 = {a: "something", b: null}; + var data4 = {a: "something"}; + + assert.isFalse(tv4.validate(data1, schema)); + assert.isTrue(tv4.validate(data2, schema)); + assert.isTrue(tv4.validate(data3, schema)); + assert.isTrue(tv4.validate(data4, schema)); + }); +}); + +describe("Ban unknown properties 01", function () { + it("Additional argument to ban additional properties", function () { + var schema = { + properties: { + propA: {}, + propB: {} + } + }; + var data = { + propA: true, + propUnknown: true + }; + var data2 = { + propA: true + }; + + var result = tv4.validateMultiple(data, schema, false, true); + assert.isFalse(result.valid, "Must not be valid"); + + var result2 = tv4.validateMultiple(data2, schema, false, true); + assert.isTrue(result2.valid, "Must still validate"); + }); + + it("Works with validateResult()", function () { + var schema = { + properties: { + propA: {}, + propB: {} + } + }; + var data = { + propA: true, + propUnknown: true + }; + var data2 = { + propA: true + }; + + var result = tv4.validateResult(data, schema, false, true); + assert.isFalse(result.valid, "Must not be valid"); + + var result2 = tv4.validateResult(data2, schema, false, true); + assert.isTrue(result2.valid, "Must be valid"); + }); + + it("Do not complain if additionalArguments is specified", function () { + var schema = { + properties: { + propA: {}, + propB: {} + }, + additionalProperties: true + }; + var data = { + propA: true, + propUnknown: true + }; + var data2 = { + propA: true + }; + + var result = tv4.validateMultiple(data, schema, false, true); + assert.isTrue(result.valid, "Must be valid"); + + var result2 = tv4.validateMultiple(data2, schema, false, true); + assert.isTrue(result2.valid, "Must still validate"); + }); +}); + +describe("Ban unknown properties 02", function () { + it("Do not track property definitions from \"not\"", function () { + var schema = { + "not": { + properties: { + propA: {"type": "string"}, + } + } + }; + var data = { + propA: true, + }; + + var result = tv4.validateMultiple(data, schema, false, true); + assert.isFalse(result.valid, "Must not be valid"); + }); + + it("Do not track property definitions from unselected \"oneOf\"", function () { + var schema = { + "oneOf": [ + { + "type": "object", + "properties": { + "propA": {"type": "string"} + } + }, + { + "type": "object", + "properties": { + "propB": {"type": "boolean"} + } + } + ] + }; + var data = { + propA: true, + propB: true + }; + + var result = tv4.validateMultiple(data, schema, false, true); + assert.isFalse(result.valid, "Must not be valid"); + + var result2 = tv4.validateMultiple(data, schema, false); + assert.isTrue(result2.valid, "Must still be valid without flag"); + }); + + + it("Do not track property definitions from unselected \"anyOf\"", function () { + var schema = { + "anyOf": [ + { + "type": "object", + "properties": { + "propA": {"type": "string"} + } + }, + { + "type": "object", + "properties": { + "propB": {"type": "boolean"} + } + } + ] + }; + var data = { + propA: true, + propB: true + }; + + var result = tv4.validateMultiple(data, schema, false, true); + assert.isFalse(result.valid, "Must not be valid"); + + var result2 = tv4.validateMultiple(data, schema, false); + assert.isTrue(result2.valid, "Must still be valid without flag"); + }); +}); + +describe("Fill dataPath for \"required\" (GitHub Issue #103)", function () { + it("Blank for first-level properties", function () { + var schema = { + required: ['A'] + }; + var data = {}; + + var result = tv4.validateMultiple(data, schema, false, true); + assert.isFalse(result.valid, "Must not be valid"); + assert.deepEqual(result.errors[0].dataPath, ''); + }); + + it("Filled for second-level properties", function () { + var schema = { + properties: { + "foo": { + required: ["bar"] + } + } + }; + var data = {"foo": {}}; + + var result = tv4.validateMultiple(data, schema, false, true); + assert.isFalse(result.valid, "Must not be valid"); + assert.deepEqual(result.errors[0].dataPath, '/foo'); + }); +}); + +describe("Valid schemaPath for \"oneOf\" (GitHub Issue #117)", function () { + it("valid schemaPath in error (simple types)", function () { + var data = {}; + var schema = { + "oneOf": [ + { "type": "string" }, + { "type": "bool" } + ] + }; + + var result = tv4.validateMultiple(data, schema); + var suberr = result.errors[0].subErrors; + assert.equal(suberr[0].schemaPath, '/oneOf/0/type'); + assert.equal(suberr[1].schemaPath, '/oneOf/1/type'); + }); + + it("valid schemaPath in error (required properties)", function () { + /* Test case provided on GitHub Issue #117 */ + var data = {}; + var schema = { + "$schema": "http://json-schema.org/draft-04/schema#", + "oneOf": [ + { + "type": "object", + "properties": { + "data": { + "type": "object" + } + }, + "required": ["data"] + }, + { + "type": "object", + "properties": { + "error": { + "type": "object" + } + }, + "required": ["error"] + } + ] + }; + + var result = tv4.validateMultiple(data, schema); + var suberr = result.errors[0].subErrors; + assert.equal(suberr[0].schemaPath, "/oneOf/0/required/0"); + assert.equal(suberr[1].schemaPath, "/oneOf/1/required/0"); + }); +}); + +describe("Register custom keyword", function () { + it("function called", function () { + var schema = { + customKeyword: "A" + }; + var data = {}; + + tv4.defineKeyword('customKeyword', function () { + return "Custom failure"; + }); + + var result = tv4.validateMultiple(data, schema, false, true); + assert.isFalse(result.valid, "Must not be valid"); + assert.deepEqual(result.errors[0].message, 'Keyword failed: customKeyword (Custom failure)'); + }); + + it("custom error code", function () { + var schema = { + customKeywordFoo: "A" + }; + var data = "test test test"; + + tv4.defineKeyword('customKeywordFoo', function (data, value) { + return { + code: 'CUSTOM_KEYWORD_FOO', + message: {data: data, value: value} + }; + }); + tv4.defineError('CUSTOM_KEYWORD_FOO', 123456789, "{value}: {data}"); + + var result = tv4.validateMultiple(data, schema, false, true); + assert.isFalse(result.valid, "Must not be valid"); + assert.deepEqual(result.errors[0].message, 'A: test test test'); + assert.deepEqual(result.errors[0].code, 123456789); + }); + + it("custom error code (numeric)", function () { + var schema = { + customKeywordBar: "A" + }; + var data = "test test test"; + + tv4.defineKeyword('customKeywordBar', function (data, value) { + return { + code: 1234567890, + message: {data: data, value: value} + }; + }); + tv4.defineError('CUSTOM_KEYWORD_BAR', 1234567890, "{value}: {data}"); + + var result = tv4.validateMultiple(data, schema, false, true); + assert.isFalse(result.valid, "Must not be valid"); + assert.deepEqual(result.errors[0].message, 'A: test test test'); + assert.deepEqual(result.errors[0].code, 1234567890); + }); + + it("restrict custom error codes", function () { + assert.throws(function () { + tv4.defineError('CUSTOM_KEYWORD_BLAH', 9999, "{value}: {data}"); + }); + }); + + it("restrict custom error names", function () { + assert.throws(function () { + tv4.defineError('doesnotmatchpattern', 10002, "{value}: {data}"); + }); + }); + + it("can't defined the same code twice", function () { + assert.throws(function () { + tv4.defineError('CUSTOM_ONE', 10005, "{value}: {data}"); + tv4.defineError('CUSTOM_TWO', 10005, "{value}: {data}"); + }); + }); + + it("function can return existing (non-custom) codes", function () { + var schema = { + "type": "object", + "properties": { + "aStringValue": { + "type": "string", + "my-custom-keyword": "something" + }, + "aBooleanValue": { + "type": "boolean" + } + } + }; + var data = { + "aStringValue": "a string", + "aBooleanValue": true + }; + + tv4.defineKeyword('my-custom-keyword', function () { + return {code: 0, message: "test"}; + }); + + var result = tv4.validateMultiple(data, schema, false, true); + assert.equal(result.errors[0].code, tv4.errorCodes.INVALID_TYPE); + }); + + it("function only called when keyword present", function () { + var schema = { + "type": "object", + "properties": { + "aStringValue": { + "type": "string", + "my-custom-keyword": "something" + }, + "aBooleanValue": { + "type": "boolean" + } + } + }; + var data = { + "aStringValue": "a string", + "aBooleanValue": true + }; + + var callCount = 0; + tv4.defineKeyword('my-custom-keyword', function () { + callCount++; + }); + + tv4.validateMultiple(data, schema, false, true); + assert.deepEqual(callCount, 1, "custom function must be called exactly once"); + }); + + it("function knows dataPointerPath", function () { + var schema = { + "type": "object", + "properties": { + "obj": { + "type": "object", + "properties":{ + "test":{ + "my-custom-keyword": "something", + "type":"string" + } + } + } + } + }; + var data = { "obj":{ "test": "a string"} }; + + var path = null; + tv4.defineKeyword('my-custom-keyword', function (data,value,schema,dataPointerPath) { + path = dataPointerPath; + }); + + tv4.validateMultiple(data, schema, false, true); + assert.strictEqual(path, "/obj/test", "custom function must know its context path"); + }); +}); + +describe("Load language file", function () { + if (typeof process !== 'object' || typeof require !== 'function') { + it.skip("commonjs language", function () { + // dummy + }); + } + else { + it("commonjs language: de", function () { + var tv4 = require('../lang/de'); + + tv4.language('de'); + + var schema = { + properties: { + intKey: {"type": "integer"} + } + }; + var res = tv4.validateResult({intKey: 'bad'}, schema); + assert.isFalse(res.valid); + assert.equal(res.error.message, 'Ungültiger Typ: string (erwartet wurde: integer)'); + }); + } +}); + +describe("Custom error reporting", function () { + it('provides custom message', function () { + var api = tv4.freshApi(); + + api.setErrorReporter(function (error, data, schema) { + assert.deepEqual(data, 5); + assert.deepEqual(schema, {minimum: 10}); + return 'Code: ' + error.code; + }); + + var res = api.validateResult(5, {minimum: 10}); + assert.isFalse(res.valid); + assert.equal(res.error.message, 'Code: 101'); + }); + + it('falls back to default', function () { + var api = tv4.freshApi(); + + api.setErrorReporter(function (error, data, schema) { + assert.deepEqual(data, 5); + assert.deepEqual(schema, {minimum: 10}); + return null; + }); + + var res = api.validateResult(5, {minimum: 10}); + assert.isFalse(res.valid); + assert.isString(res.error.message); + }); +}); + +describe("Load language file", function () { + it("commonjs language: de", function () { + var freshTv4 = tv4.freshApi(); + + freshTv4.addSchema('/polymorphic', { + type: "object", + properties: { + "type": {type: "string"} + }, + required: ["type"], + links: [{ + rel: "describedby", + href: "/schemas/{type}.json" + }] + }); + + var res = freshTv4.validateResult({type: 'monkey'}, "/polymorphic"); + assert.isTrue(res.valid); + assert.includes(res.missing, "/schemas/monkey.json"); + + freshTv4.addSchema('/schemas/tiger.json', { + properties: { + "stripes": {"type": "integer", "minimum": 1} + }, + required: ["stripes"] + }); + + var res2 = freshTv4.validateResult({type: 'tiger', stripes: -1}, "/polymorphic"); + assert.isFalse(res2.valid); + assert.deepEqual(res2.missing.length, 0, "no schemas should be missing"); + + var res3 = freshTv4.validateResult({type: 'tiger', stripes: 50}, "/polymorphic"); + assert.isTrue(res3.valid); + }); +}); + +describe("Issue 108", function () { + + it("Normalise schemas even inside $ref", function () { + + var schema = { + "id": "http://example.com/schema" + Math.random(), + "$ref": "#whatever", + "properties": { + "foo": { + "id": "#test", + "type": "string" + } + } + }; + + tv4.addSchema(schema); + + var result = tv4.validateMultiple("test data", schema.id + '#test'); + assert.isTrue(result.valid, 'validateMultiple() should return valid'); + assert.deepEqual(result.missing.length, 0, 'should have no missing schemas'); + + var result2 = tv4.validateMultiple({"foo":"bar"}, schema.id + '#test'); + assert.isFalse(result2.valid, 'validateMultiple() should return invalid'); + assert.deepEqual(result2.missing.length, 0, 'should have no missing schemas'); + }); +}); +describe("Issue 109", function () { + + it("Don't break on null values with banUnknownProperties", function () { + + var schema = { + "type": "object", + "properties": { + "foo": { + "type": "object", + "additionalProperties": {"type": "string"} + } + } + }; + + var data = {foo: null}; + + var result = tv4.validateMultiple(data, schema, true, true); + + assert.isFalse(result.valid, 'validateMultiple() should return invalid'); + }); +}); +describe("Issue 32", function () { + + it("Example from GitHub issue #32", function () { + var subSchema = { + "title": "SubSchema", + "type": "object", + "properties": { + "attribute": {"type": "string"} + }, + "additionalProperties": false + }; + + var mySchema = { + "title": "My Schema", + "type": "object", + "properties": { + "name": {"type": "string"}, + "subschemas": {"type": "array", "items": {"$ref": "#/definitions/subSchema"}} + }, + "definitions": { + "subSchema": subSchema + }, + "additionalProperties": false + }; + + /* unused variable + var data1 = { + "name": "Joe", + "subschemas": [ + {"attribute": "Hello"} + ] + };*/ + + var addlPropInSubSchema = { + "name": "Joe", + "subschemas": [ + {"attribute": "Hello", "extra": "Not Allowed"} + ] + }; + + // Usage 1 + var expectedUsage1Result = tv4.validate(addlPropInSubSchema, mySchema); + assert.isFalse(expectedUsage1Result, 'plain validate should fail'); + //this.assert(!expectedUsage1Result, 'plain validate should fail'); + + // Usage 2 + var expectedUsage2Result = tv4.validateResult(addlPropInSubSchema, mySchema); + assert.isFalse(expectedUsage2Result.valid, 'validateResult should fail'); + + //-> this has a typo that didn't show because of type conversion! + + //this.assert(!expectedUsage1Result.valud, 'validateResult should fail'); + + // Usage 3 + var expectedMultipleErrorResult = tv4.validateMultiple(addlPropInSubSchema, mySchema); + assert.isFalse(expectedMultipleErrorResult.valid, 'validateMultiple should fail'); + assert.length(expectedMultipleErrorResult.errors, 1, 'validateMultiple should have exactly one error'); + //this.assert(!expectedMultipleErrorResult.valid, 'validateMultiple should fail'); + //this.assert(expectedMultipleErrorResult.errors.length == 1, 'validateMultiple should have exactly one error'); + }); +}); +describe("Issue 67", function () { + + it("Example from GitHub issue #67", function () { + // Make sure null values don't trip up the normalisation + tv4.validate(null, {default: null}); + }); +}); +describe("Issue 86", function () { + + it("Example from GitHub issue #86", function () { + // The "checkRecursive" flag skips some data nodes if it actually needs to check the same data/schema pair twice + + var schema = { + "type": "object", + "properties": { + "shape": { + "oneOf": [ + { "$ref": "#/definitions/squareSchema" }, + { "$ref": "#/definitions/circleSchema" } + ] + } + }, + "definitions": { + "squareSchema": { + "type": "object", + "properties": { + "thetype": { + "type": "string", + "enum": ["square"] + }, + "colour": {}, + "shade": {}, + "boxname": { + "type": "string" + } + }, + "oneOf": [ + { "$ref": "#/definitions/colourSchema" }, + { "$ref": "#/definitions/shadeSchema" } + ], + "required": ["thetype", "boxname"], + "additionalProperties": false + }, + "circleSchema": { + "type": "object", + "properties": { + "thetype": { + "type": "string", + "enum": ["circle"] + }, + "colour": {}, + "shade": {} + }, + "oneOf": [ + { "$ref": "#/definitions/colourSchema" }, + { "$ref": "#/definitions/shadeSchema" } + ], + "additionalProperties": false + }, + "colourSchema": { + "type": "object", + "properties": { + "colour": { + "type": "string" + }, + "shade": { + "type": "null" + } + } + }, + "shadeSchema": { + "type": "object", + "properties": { + "shade": { + "type": "string" + }, + "colour": { + "type": "null" + } + } + } + } + }; + + + var circle = { + "shape": { + "thetype": "circle", + "shade": "red" + } + }; + + var simpleResult = tv4.validate(circle, schema, true); + var multipleResult = tv4.validateMultiple(circle, schema, true); + + assert.isTrue(simpleResult, 'validate() should return valid'); + assert.isTrue(multipleResult.valid, 'validateMultiple() should return valid'); + }); + + it("Second example", function () { + var schema = { + "allOf": [ + { + "oneOf": [ + {"$ref": "#/definitions/option1"}, + {"$ref": "#/definitions/option2"}, + ] + }, + { + "not": {"$ref": "#/definitions/option2"} + } + ], + "definitions": { + "option1": { + "allOf": [{"type": "string"}] + }, + "option2": { + "allOf": [{"type": "number"}] + } + } + }; + + var simpleResult = tv4.validate("test", schema, true); + + assert.isTrue(simpleResult, "validate() should return valid"); + }); +}); +describe("Enum object/null failure", function () { + + it("Doesn't crash", function () { + + var schema = { + "type": "object", + "required": ["value"], + "properties": { + "key": { + "type": "string" + }, + "value": { + "enum": [6, "foo", [], true, {"foo": 12}] + } + } + }; + + var data = {key: "test", value: null}; // Somehow this is only a problem when a *property* is null, not the root + var result = tv4.validateMultiple(data, schema); + assert.isFalse(result.valid, 'validateMultiple() should return invalid'); + + data = {key: "test"}; // Undefined required property. + result = tv4.validateMultiple(data, schema); + assert.isFalse(result.valid, 'validateMultiple() should return invalid'); + }); +}); +//@ sourceMappingURL=all_concat.js.map \ No newline at end of file diff --git a/test/all_concat.js.map b/test/all_concat.js.map new file mode 100644 index 0000000..98500dd --- /dev/null +++ b/test/all_concat.js.map @@ -0,0 +1,64 @@ +{ + "version": 3, + "sources": [ + "test/_header.js", + "test/tests/00 - Core/01 - utils.js", + "test/tests/00 - Core/02 - duplicateApi.js", + "test/tests/00 - Core/03 - resetAndDrop.js", + "test/tests/00 - Core/04 - error.js", + "test/tests/01 - Any types/01 - type.js", + "test/tests/01 - Any types/02 - enum.js", + "test/tests/02 - Numeric/01 - multipleOf.js", + "test/tests/02 - Numeric/02 - min-max.js", + "test/tests/02 - Numeric/03 - NaN.js", + "test/tests/03 - Strings/01 - min-max length.js", + "test/tests/03 - Strings/02 - pattern.js", + "test/tests/04 - Arrays/01 - min-max length.js", + "test/tests/04 - Arrays/02 - uniqueItems.js", + "test/tests/04 - Arrays/03 - items (plain).js", + "test/tests/04 - Arrays/04 - items (tuple-typing).js", + "test/tests/04 - Arrays/05 - additionalItems.js", + "test/tests/05 - Objects/01 - min-max properties.js", + "test/tests/05 - Objects/02 - required.js", + "test/tests/05 - Objects/03 - properties.js", + "test/tests/05 - Objects/04 - patternProperties.js", + "test/tests/05 - Objects/05 - additionalProperties.js", + "test/tests/05 - Objects/06 - dependencies.js", + "test/tests/06 - Combinations/01 - allOf.js", + "test/tests/06 - Combinations/02- anyOf.js", + "test/tests/06 - Combinations/03 - oneOf.js", + "test/tests/06 - Combinations/04 - not.js", + "test/tests/07 - $ref/01 - normalise.js", + "test/tests/07 - $ref/02 - list missing URLs.js", + "test/tests/07 - $ref/03 - getSchema.js", + "test/tests/07 - $ref/04 - ref.js", + "test/tests/07 - $ref/05 - inline addressing.js", + "test/tests/07 - $ref/06 - multiple refs.js", + "test/tests/08 - API/01 - validateResult.js", + "test/tests/08 - API/02 - errorCodes existence.js", + "test/tests/08 - API/03 - get schema URIs.js", + "test/tests/09 - Multiple errors/01 - validateMultiple.js", + "test/tests/09 - Multiple errors/02 - validateMultiple 2.js", + "test/tests/10 - Recursive objects/01 - validate.js", + "test/tests/10 - Recursive objects/02 - scan.js", + "test/tests/11 - format/01 - register validator.js", + "test/tests/12 - banUnknownProperties/01 - simple case.js", + "test/tests/12 - banUnknownProperties/02 - composite behaviour.js", + "test/tests/13 - error reporting/01 - required dataPath.js", + "test/tests/13 - error reporting/02 - oneOf schemaPath.js", + "test/tests/14 - custom validation/01 - custom keywords.js", + "test/tests/15 - language/01 - language file.js", + "test/tests/15 - language/02 - custom reporter.js", + "test/tests/16 - hypermedia/02 - describedby link.js", + "test/tests/Misc/Issue 108.js", + "test/tests/Misc/Issue 109.js", + "test/tests/Misc/Issue 32.js", + "test/tests/Misc/Issue 67.js", + "test/tests/Misc/Issue 86.js", + "test/tests/Misc/enum null failure.js" + ], + "names": [], + "mappings": "AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AChJA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACxBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACvBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACnCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC/BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AChEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC3EA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,G;AC7BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACzDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACpDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC7CA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,G;AC7BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACpCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACfA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACvBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACzBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACrDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC7BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACfA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACzBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC3CA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,G;AC7DA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC3EA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACzBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC1BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACxCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACnBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACnEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,G;AC/BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,G;AC1FA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACvCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACjDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC9BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACnBA;AACA;AACA;AACA;AACA;AACA;AACA;;ACNA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACnIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,G;ACxGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,G;AChEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACdA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACbA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACnGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACnEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC3EA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC1BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AChDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACzJA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACtBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC5BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AClCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,G;ACzBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,G;ACpBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,G;AC5DA;AACA;AACA;AACA;AACA;AACA;AACA,G;ACNA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,G;ACvHA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,G", + "file": "all_concat.js", + "sourceRoot": "" +} \ No newline at end of file diff --git a/tv4.js b/tv4.js new file mode 100644 index 0000000..ca98601 --- /dev/null +++ b/tv4.js @@ -0,0 +1,1699 @@ +/* +Author: Geraint Luff and others +Year: 2013 + +This code is released into the "public domain" by its author(s). Anybody may use, alter and distribute the code without restriction. The author makes no guarantees, and takes no liability of any kind for use of this code. + +If you find a bug or make an improvement, it would be courteous to let the author know, but it is not compulsory. +*/ +(function (global, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define([], factory); + } else if (typeof module !== 'undefined' && module.exports){ + // CommonJS. Define export. + module.exports = factory(); + } else { + // Browser globals + global.tv4 = factory(); + } +}(this, function () { + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FObject%2Fkeys +if (!Object.keys) { + Object.keys = (function () { + var hasOwnProperty = Object.prototype.hasOwnProperty, + hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'), + dontEnums = [ + 'toString', + 'toLocaleString', + 'valueOf', + 'hasOwnProperty', + 'isPrototypeOf', + 'propertyIsEnumerable', + 'constructor' + ], + dontEnumsLength = dontEnums.length; + + return function (obj) { + if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) { + throw new TypeError('Object.keys called on non-object'); + } + + var result = []; + + for (var prop in obj) { + if (hasOwnProperty.call(obj, prop)) { + result.push(prop); + } + } + + if (hasDontEnumBug) { + for (var i=0; i < dontEnumsLength; i++) { + if (hasOwnProperty.call(obj, dontEnums[i])) { + result.push(dontEnums[i]); + } + } + } + return result; + }; + })(); +} +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create +if (!Object.create) { + Object.create = (function(){ + function F(){} + + return function(o){ + if (arguments.length !== 1) { + throw new Error('Object.create implementation only accepts one parameter.'); + } + F.prototype = o; + return new F(); + }; + })(); +} +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FArray%2FisArray +if(!Array.isArray) { + Array.isArray = function (vArg) { + return Object.prototype.toString.call(vArg) === "[object Array]"; + }; +} +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FArray%2FindexOf +if (!Array.prototype.indexOf) { + Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) { + if (this === null) { + throw new TypeError(); + } + var t = Object(this); + var len = t.length >>> 0; + + if (len === 0) { + return -1; + } + var n = 0; + if (arguments.length > 1) { + n = Number(arguments[1]); + if (n !== n) { // shortcut for verifying if it's NaN + n = 0; + } else if (n !== 0 && n !== Infinity && n !== -Infinity) { + n = (n > 0 || -1) * Math.floor(Math.abs(n)); + } + } + if (n >= len) { + return -1; + } + var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); + for (; k < len; k++) { + if (k in t && t[k] === searchElement) { + return k; + } + } + return -1; + }; +} + +// Grungey Object.isFrozen hack +if (!Object.isFrozen) { + Object.isFrozen = function (obj) { + var key = "tv4_test_frozen_key"; + while (obj.hasOwnProperty(key)) { + key += Math.random(); + } + try { + obj[key] = true; + delete obj[key]; + return false; + } catch (e) { + return true; + } + }; +} +// Based on: https://github.com/geraintluff/uri-templates, but with all the de-substitution stuff removed + +var uriTemplateGlobalModifiers = { + "+": true, + "#": true, + ".": true, + "/": true, + ";": true, + "?": true, + "&": true +}; +var uriTemplateSuffices = { + "*": true +}; + +function notReallyPercentEncode(string) { + return encodeURI(string).replace(/%25[0-9][0-9]/g, function (doubleEncoded) { + return "%" + doubleEncoded.substring(3); + }); +} + +function uriTemplateSubstitution(spec) { + var modifier = ""; + if (uriTemplateGlobalModifiers[spec.charAt(0)]) { + modifier = spec.charAt(0); + spec = spec.substring(1); + } + var separator = ""; + var prefix = ""; + var shouldEscape = true; + var showVariables = false; + var trimEmptyString = false; + if (modifier === '+') { + shouldEscape = false; + } else if (modifier === ".") { + prefix = "."; + separator = "."; + } else if (modifier === "/") { + prefix = "/"; + separator = "/"; + } else if (modifier === '#') { + prefix = "#"; + shouldEscape = false; + } else if (modifier === ';') { + prefix = ";"; + separator = ";"; + showVariables = true; + trimEmptyString = true; + } else if (modifier === '?') { + prefix = "?"; + separator = "&"; + showVariables = true; + } else if (modifier === '&') { + prefix = "&"; + separator = "&"; + showVariables = true; + } + + var varNames = []; + var varList = spec.split(","); + var varSpecs = []; + var varSpecMap = {}; + for (var i = 0; i < varList.length; i++) { + var varName = varList[i]; + var truncate = null; + if (varName.indexOf(":") !== -1) { + var parts = varName.split(":"); + varName = parts[0]; + truncate = parseInt(parts[1], 10); + } + var suffices = {}; + while (uriTemplateSuffices[varName.charAt(varName.length - 1)]) { + suffices[varName.charAt(varName.length - 1)] = true; + varName = varName.substring(0, varName.length - 1); + } + var varSpec = { + truncate: truncate, + name: varName, + suffices: suffices + }; + varSpecs.push(varSpec); + varSpecMap[varName] = varSpec; + varNames.push(varName); + } + var subFunction = function (valueFunction) { + var result = ""; + var startIndex = 0; + for (var i = 0; i < varSpecs.length; i++) { + var varSpec = varSpecs[i]; + var value = valueFunction(varSpec.name); + if (value === null || value === undefined || (Array.isArray(value) && value.length === 0) || (typeof value === 'object' && Object.keys(value).length === 0)) { + startIndex++; + continue; + } + if (i === startIndex) { + result += prefix; + } else { + result += (separator || ","); + } + if (Array.isArray(value)) { + if (showVariables) { + result += varSpec.name + "="; + } + for (var j = 0; j < value.length; j++) { + if (j > 0) { + result += varSpec.suffices['*'] ? (separator || ",") : ","; + if (varSpec.suffices['*'] && showVariables) { + result += varSpec.name + "="; + } + } + result += shouldEscape ? encodeURIComponent(value[j]).replace(/!/g, "%21") : notReallyPercentEncode(value[j]); + } + } else if (typeof value === "object") { + if (showVariables && !varSpec.suffices['*']) { + result += varSpec.name + "="; + } + var first = true; + for (var key in value) { + if (!first) { + result += varSpec.suffices['*'] ? (separator || ",") : ","; + } + first = false; + result += shouldEscape ? encodeURIComponent(key).replace(/!/g, "%21") : notReallyPercentEncode(key); + result += varSpec.suffices['*'] ? '=' : ","; + result += shouldEscape ? encodeURIComponent(value[key]).replace(/!/g, "%21") : notReallyPercentEncode(value[key]); + } + } else { + if (showVariables) { + result += varSpec.name; + if (!trimEmptyString || value !== "") { + result += "="; + } + } + if (varSpec.truncate != null) { + value = value.substring(0, varSpec.truncate); + } + result += shouldEscape ? encodeURIComponent(value).replace(/!/g, "%21"): notReallyPercentEncode(value); + } + } + return result; + }; + subFunction.varNames = varNames; + return { + prefix: prefix, + substitution: subFunction + }; +} + +function UriTemplate(template) { + if (!(this instanceof UriTemplate)) { + return new UriTemplate(template); + } + var parts = template.split("{"); + var textParts = [parts.shift()]; + var prefixes = []; + var substitutions = []; + var varNames = []; + while (parts.length > 0) { + var part = parts.shift(); + var spec = part.split("}")[0]; + var remainder = part.substring(spec.length + 1); + var funcs = uriTemplateSubstitution(spec); + substitutions.push(funcs.substitution); + prefixes.push(funcs.prefix); + textParts.push(remainder); + varNames = varNames.concat(funcs.substitution.varNames); + } + this.fill = function (valueFunction) { + var result = textParts[0]; + for (var i = 0; i < substitutions.length; i++) { + var substitution = substitutions[i]; + result += substitution(valueFunction); + result += textParts[i + 1]; + } + return result; + }; + this.varNames = varNames; + this.template = template; +} +UriTemplate.prototype = { + toString: function () { + return this.template; + }, + fillFromObject: function (obj) { + return this.fill(function (varName) { + return obj[varName]; + }); + } +}; +var ValidatorContext = function ValidatorContext(parent, collectMultiple, errorReporter, checkRecursive, trackUnknownProperties) { + this.missing = []; + this.missingMap = {}; + this.formatValidators = parent ? Object.create(parent.formatValidators) : {}; + this.schemas = parent ? Object.create(parent.schemas) : {}; + this.collectMultiple = collectMultiple; + this.errors = []; + this.handleError = collectMultiple ? this.collectError : this.returnError; + if (checkRecursive) { + this.checkRecursive = true; + this.scanned = []; + this.scannedFrozen = []; + this.scannedFrozenSchemas = []; + this.scannedFrozenValidationErrors = []; + this.validatedSchemasKey = 'tv4_validation_id'; + this.validationErrorsKey = 'tv4_validation_errors_id'; + } + if (trackUnknownProperties) { + this.trackUnknownProperties = true; + this.knownPropertyPaths = {}; + this.unknownPropertyPaths = {}; + } + this.errorReporter = errorReporter || defaultErrorReporter('en'); + if (typeof this.errorReporter === 'string') { + throw new Error('debug'); + } + this.definedKeywords = {}; + if (parent) { + for (var key in parent.definedKeywords) { + this.definedKeywords[key] = parent.definedKeywords[key].slice(0); + } + } +}; +ValidatorContext.prototype.defineKeyword = function (keyword, keywordFunction) { + this.definedKeywords[keyword] = this.definedKeywords[keyword] || []; + this.definedKeywords[keyword].push(keywordFunction); +}; +ValidatorContext.prototype.createError = function (code, messageParams, dataPath, schemaPath, subErrors, data, schema) { + var error = new ValidationError(code, messageParams, dataPath, schemaPath, subErrors); + error.message = this.errorReporter(error, data, schema); + return error; +}; +ValidatorContext.prototype.returnError = function (error) { + return error; +}; +ValidatorContext.prototype.collectError = function (error) { + if (error) { + this.errors.push(error); + } + return null; +}; +ValidatorContext.prototype.prefixErrors = function (startIndex, dataPath, schemaPath) { + for (var i = startIndex; i < this.errors.length; i++) { + this.errors[i] = this.errors[i].prefixWith(dataPath, schemaPath); + } + return this; +}; +ValidatorContext.prototype.banUnknownProperties = function (data, schema) { + for (var unknownPath in this.unknownPropertyPaths) { + var error = this.createError(ErrorCodes.UNKNOWN_PROPERTY, {path: unknownPath}, unknownPath, "", null, data, schema); + var result = this.handleError(error); + if (result) { + return result; + } + } + return null; +}; + +ValidatorContext.prototype.addFormat = function (format, validator) { + if (typeof format === 'object') { + for (var key in format) { + this.addFormat(key, format[key]); + } + return this; + } + this.formatValidators[format] = validator; +}; +ValidatorContext.prototype.resolveRefs = function (schema, urlHistory) { + if (schema['$ref'] !== undefined) { + urlHistory = urlHistory || {}; + if (urlHistory[schema['$ref']]) { + return this.createError(ErrorCodes.CIRCULAR_REFERENCE, {urls: Object.keys(urlHistory).join(', ')}, '', '', null, undefined, schema); + } + urlHistory[schema['$ref']] = true; + schema = this.getSchema(schema['$ref'], urlHistory); + } + return schema; +}; +ValidatorContext.prototype.getSchema = function (url, urlHistory) { + var schema; + if (this.schemas[url] !== undefined) { + schema = this.schemas[url]; + return this.resolveRefs(schema, urlHistory); + } + var baseUrl = url; + var fragment = ""; + if (url.indexOf('#') !== -1) { + fragment = url.substring(url.indexOf("#") + 1); + baseUrl = url.substring(0, url.indexOf("#")); + } + if (typeof this.schemas[baseUrl] === 'object') { + schema = this.schemas[baseUrl]; + var pointerPath = decodeURIComponent(fragment); + if (pointerPath === "") { + return this.resolveRefs(schema, urlHistory); + } else if (pointerPath.charAt(0) !== "/") { + return undefined; + } + var parts = pointerPath.split("/").slice(1); + for (var i = 0; i < parts.length; i++) { + var component = parts[i].replace(/~1/g, "/").replace(/~0/g, "~"); + if (schema[component] === undefined) { + schema = undefined; + break; + } + schema = schema[component]; + } + if (schema !== undefined) { + return this.resolveRefs(schema, urlHistory); + } + } + if (this.missing[baseUrl] === undefined) { + this.missing.push(baseUrl); + this.missing[baseUrl] = baseUrl; + this.missingMap[baseUrl] = baseUrl; + } +}; +ValidatorContext.prototype.searchSchemas = function (schema, url) { + if (Array.isArray(schema)) { + for (var i = 0; i < schema.length; i++) { + this.searchSchemas(schema[i], url); + } + } else if (schema && typeof schema === "object") { + if (typeof schema.id === "string") { + if (isTrustedUrl(url, schema.id)) { + if (this.schemas[schema.id] === undefined) { + this.schemas[schema.id] = schema; + } + } + } + for (var key in schema) { + if (key !== "enum") { + if (typeof schema[key] === "object") { + this.searchSchemas(schema[key], url); + } else if (key === "$ref") { + var uri = getDocumentUri(schema[key]); + if (uri && this.schemas[uri] === undefined && this.missingMap[uri] === undefined) { + this.missingMap[uri] = uri; + } + } + } + } + } +}; +ValidatorContext.prototype.addSchema = function (url, schema) { + //overload + if (typeof url !== 'string' || typeof schema === 'undefined') { + if (typeof url === 'object' && typeof url.id === 'string') { + schema = url; + url = schema.id; + } + else { + return; + } + } + if (url === getDocumentUri(url) + "#") { + // Remove empty fragment + url = getDocumentUri(url); + } + this.schemas[url] = schema; + delete this.missingMap[url]; + normSchema(schema, url); + this.searchSchemas(schema, url); +}; + +ValidatorContext.prototype.getSchemaMap = function () { + var map = {}; + for (var key in this.schemas) { + map[key] = this.schemas[key]; + } + return map; +}; + +ValidatorContext.prototype.getSchemaUris = function (filterRegExp) { + var list = []; + for (var key in this.schemas) { + if (!filterRegExp || filterRegExp.test(key)) { + list.push(key); + } + } + return list; +}; + +ValidatorContext.prototype.getMissingUris = function (filterRegExp) { + var list = []; + for (var key in this.missingMap) { + if (!filterRegExp || filterRegExp.test(key)) { + list.push(key); + } + } + return list; +}; + +ValidatorContext.prototype.dropSchemas = function () { + this.schemas = {}; + this.reset(); +}; +ValidatorContext.prototype.reset = function () { + this.missing = []; + this.missingMap = {}; + this.errors = []; +}; + +ValidatorContext.prototype.validateAll = function (data, schema, dataPathParts, schemaPathParts, dataPointerPath) { + var topLevel; + schema = this.resolveRefs(schema); + if (!schema) { + return null; + } else if (schema instanceof ValidationError) { + this.errors.push(schema); + return schema; + } + + var startErrorCount = this.errors.length; + var frozenIndex, scannedFrozenSchemaIndex = null, scannedSchemasIndex = null; + if (this.checkRecursive && data && typeof data === 'object') { + topLevel = !this.scanned.length; + if (data[this.validatedSchemasKey]) { + var schemaIndex = data[this.validatedSchemasKey].indexOf(schema); + if (schemaIndex !== -1) { + this.errors = this.errors.concat(data[this.validationErrorsKey][schemaIndex]); + return null; + } + } + if (Object.isFrozen(data)) { + frozenIndex = this.scannedFrozen.indexOf(data); + if (frozenIndex !== -1) { + var frozenSchemaIndex = this.scannedFrozenSchemas[frozenIndex].indexOf(schema); + if (frozenSchemaIndex !== -1) { + this.errors = this.errors.concat(this.scannedFrozenValidationErrors[frozenIndex][frozenSchemaIndex]); + return null; + } + } + } + this.scanned.push(data); + if (Object.isFrozen(data)) { + if (frozenIndex === -1) { + frozenIndex = this.scannedFrozen.length; + this.scannedFrozen.push(data); + this.scannedFrozenSchemas.push([]); + } + scannedFrozenSchemaIndex = this.scannedFrozenSchemas[frozenIndex].length; + this.scannedFrozenSchemas[frozenIndex][scannedFrozenSchemaIndex] = schema; + this.scannedFrozenValidationErrors[frozenIndex][scannedFrozenSchemaIndex] = []; + } else { + if (!data[this.validatedSchemasKey]) { + try { + Object.defineProperty(data, this.validatedSchemasKey, { + value: [], + configurable: true + }); + Object.defineProperty(data, this.validationErrorsKey, { + value: [], + configurable: true + }); + } catch (e) { + //IE 7/8 workaround + data[this.validatedSchemasKey] = []; + data[this.validationErrorsKey] = []; + } + } + scannedSchemasIndex = data[this.validatedSchemasKey].length; + data[this.validatedSchemasKey][scannedSchemasIndex] = schema; + data[this.validationErrorsKey][scannedSchemasIndex] = []; + } + } + + var errorCount = this.errors.length; + var error = this.validateBasic(data, schema, dataPointerPath) + || this.validateNumeric(data, schema, dataPointerPath) + || this.validateString(data, schema, dataPointerPath) + || this.validateArray(data, schema, dataPointerPath) + || this.validateObject(data, schema, dataPointerPath) + || this.validateCombinations(data, schema, dataPointerPath) + || this.validateHypermedia(data, schema, dataPointerPath) + || this.validateFormat(data, schema, dataPointerPath) + || this.validateDefinedKeywords(data, schema, dataPointerPath) + || null; + + if (topLevel) { + while (this.scanned.length) { + var item = this.scanned.pop(); + delete item[this.validatedSchemasKey]; + } + this.scannedFrozen = []; + this.scannedFrozenSchemas = []; + } + + if (error || errorCount !== this.errors.length) { + while ((dataPathParts && dataPathParts.length) || (schemaPathParts && schemaPathParts.length)) { + var dataPart = (dataPathParts && dataPathParts.length) ? "" + dataPathParts.pop() : null; + var schemaPart = (schemaPathParts && schemaPathParts.length) ? "" + schemaPathParts.pop() : null; + if (error) { + error = error.prefixWith(dataPart, schemaPart); + } + this.prefixErrors(errorCount, dataPart, schemaPart); + } + } + + if (scannedFrozenSchemaIndex !== null) { + this.scannedFrozenValidationErrors[frozenIndex][scannedFrozenSchemaIndex] = this.errors.slice(startErrorCount); + } else if (scannedSchemasIndex !== null) { + data[this.validationErrorsKey][scannedSchemasIndex] = this.errors.slice(startErrorCount); + } + + return this.handleError(error); +}; +ValidatorContext.prototype.validateFormat = function (data, schema) { + var allowedTypes = schema.type; + if (!Array.isArray(allowedTypes)) { + allowedTypes = [allowedTypes]; + } + + if (data === null && allowedTypes.indexOf('null') !== -1) { + return null; + } + + if (typeof schema.format !== 'string' || !this.formatValidators[schema.format]) { + return null; + } + var errorMessage = this.formatValidators[schema.format].call(null, data, schema); + if (typeof errorMessage === 'string' || typeof errorMessage === 'number') { + return this.createError(ErrorCodes.FORMAT_CUSTOM, {message: errorMessage}, '', '/format', null, data, schema); + } else if (errorMessage && typeof errorMessage === 'object') { + return this.createError(ErrorCodes.FORMAT_CUSTOM, {message: errorMessage.message || "?"}, errorMessage.dataPath || '', errorMessage.schemaPath || "/format", null, data, schema); + } + return null; +}; +ValidatorContext.prototype.validateDefinedKeywords = function (data, schema, dataPointerPath) { + for (var key in this.definedKeywords) { + if (typeof schema[key] === 'undefined') { + continue; + } + var validationFunctions = this.definedKeywords[key]; + for (var i = 0; i < validationFunctions.length; i++) { + var func = validationFunctions[i]; + var result = func(data, schema[key], schema, dataPointerPath); + if (typeof result === 'string' || typeof result === 'number') { + return this.createError(ErrorCodes.KEYWORD_CUSTOM, {key: key, message: result}, '', '', null, data, schema).prefixWith(null, key); + } else if (result && typeof result === 'object') { + var code = result.code; + if (typeof code === 'string') { + if (!ErrorCodes[code]) { + throw new Error('Undefined error code (use defineError): ' + code); + } + code = ErrorCodes[code]; + } else if (typeof code !== 'number') { + code = ErrorCodes.KEYWORD_CUSTOM; + } + var messageParams = (typeof result.message === 'object') ? result.message : {key: key, message: result.message || "?"}; + var schemaPath = result.schemaPath || ("/" + key.replace(/~/g, '~0').replace(/\//g, '~1')); + return this.createError(code, messageParams, result.dataPath || null, schemaPath, null, data, schema); + } + } + } + return null; +}; + +function recursiveCompare(A, B) { + if (A === B) { + return true; + } + if (A && B && typeof A === "object" && typeof B === "object") { + if (Array.isArray(A) !== Array.isArray(B)) { + return false; + } else if (Array.isArray(A)) { + if (A.length !== B.length) { + return false; + } + for (var i = 0; i < A.length; i++) { + if (!recursiveCompare(A[i], B[i])) { + return false; + } + } + } else { + var key; + for (key in A) { + if (B[key] === undefined && A[key] !== undefined) { + return false; + } + } + for (key in B) { + if (A[key] === undefined && B[key] !== undefined) { + return false; + } + } + for (key in A) { + if (!recursiveCompare(A[key], B[key])) { + return false; + } + } + } + return true; + } + return false; +} + +ValidatorContext.prototype.validateBasic = function validateBasic(data, schema, dataPointerPath) { + var error; + if (error = this.validateType(data, schema, dataPointerPath)) { + return error.prefixWith(null, "type"); + } + + var dataType = typeof data; + if (data === null) { + dataType = "null"; + } else if (Array.isArray(data)) { + dataType = "array"; + } + + if (dataType === "null" || dataType === "undefined") { + return null; + } + + if (error = this.validateEnum(data, schema, dataPointerPath)) { + return error.prefixWith(null, "type"); + } + return null; +}; + +ValidatorContext.prototype.validateType = function validateType(data, schema) { + if (schema.type === undefined) { + return null; + } + var dataType = typeof data; + if (data === null) { + dataType = "null"; + } else if (Array.isArray(data)) { + dataType = "array"; + } + var allowedTypes = schema.type; + if (!Array.isArray(allowedTypes)) { + allowedTypes = [allowedTypes]; + } + + for (var i = 0; i < allowedTypes.length; i++) { + var type = allowedTypes[i]; + if (type === dataType || (type === "integer" && dataType === "number" && (data % 1 === 0))) { + return null; + } + } + return this.createError(ErrorCodes.INVALID_TYPE, {type: dataType, expected: allowedTypes.join("/")}, '', '', null, data, schema); +}; + +ValidatorContext.prototype.validateEnum = function validateEnum(data, schema) { + if (schema["enum"] === undefined) { + return null; + } + for (var i = 0; i < schema["enum"].length; i++) { + var enumVal = schema["enum"][i]; + if (recursiveCompare(data, enumVal)) { + return null; + } + } + return this.createError(ErrorCodes.ENUM_MISMATCH, {value: (typeof JSON !== 'undefined') ? JSON.stringify(data) : data}, '', '', null, data, schema); +}; + +ValidatorContext.prototype.validateNumeric = function validateNumeric(data, schema, dataPointerPath) { + return this.validateMultipleOf(data, schema, dataPointerPath) + || this.validateMinMax(data, schema, dataPointerPath) + || this.validateNaN(data, schema, dataPointerPath) + || null; +}; + +var CLOSE_ENOUGH_LOW = Math.pow(2, -51); +var CLOSE_ENOUGH_HIGH = 1 - CLOSE_ENOUGH_LOW; +ValidatorContext.prototype.validateMultipleOf = function validateMultipleOf(data, schema) { + var multipleOf = schema.multipleOf || schema.divisibleBy; + if (multipleOf === undefined) { + return null; + } + if (typeof data === "number") { + var remainder = (data/multipleOf)%1; + if (remainder >= CLOSE_ENOUGH_LOW && remainder < CLOSE_ENOUGH_HIGH) { + return this.createError(ErrorCodes.NUMBER_MULTIPLE_OF, {value: data, multipleOf: multipleOf}, '', '', null, data, schema); + } + } + return null; +}; + +ValidatorContext.prototype.validateMinMax = function validateMinMax(data, schema) { + if (typeof data !== "number") { + return null; + } + if (schema.minimum !== undefined) { + if (data < schema.minimum) { + return this.createError(ErrorCodes.NUMBER_MINIMUM, {value: data, minimum: schema.minimum}, '', '/minimum', null, data, schema); + } + if (schema.exclusiveMinimum && data === schema.minimum) { + return this.createError(ErrorCodes.NUMBER_MINIMUM_EXCLUSIVE, {value: data, minimum: schema.minimum}, '', '/exclusiveMinimum', null, data, schema); + } + } + if (schema.maximum !== undefined) { + if (data > schema.maximum) { + return this.createError(ErrorCodes.NUMBER_MAXIMUM, {value: data, maximum: schema.maximum}, '', '/maximum', null, data, schema); + } + if (schema.exclusiveMaximum && data === schema.maximum) { + return this.createError(ErrorCodes.NUMBER_MAXIMUM_EXCLUSIVE, {value: data, maximum: schema.maximum}, '', '/exclusiveMaximum', null, data, schema); + } + } + return null; +}; + +ValidatorContext.prototype.validateNaN = function validateNaN(data, schema) { + if (typeof data !== "number") { + return null; + } + if (isNaN(data) === true || data === Infinity || data === -Infinity) { + return this.createError(ErrorCodes.NUMBER_NOT_A_NUMBER, {value: data}, '', '/type', null, data, schema); + } + return null; +}; + +ValidatorContext.prototype.validateString = function validateString(data, schema, dataPointerPath) { + return this.validateStringLength(data, schema, dataPointerPath) + || this.validateStringPattern(data, schema, dataPointerPath) + || null; +}; + +ValidatorContext.prototype.validateStringLength = function validateStringLength(data, schema) { + if (typeof data !== "string") { + return null; + } + if (schema.minLength !== undefined) { + if (data.length < schema.minLength) { + return this.createError(ErrorCodes.STRING_LENGTH_SHORT, {length: data.length, minimum: schema.minLength}, '', '/minLength', null, data, schema); + } + } + if (schema.maxLength !== undefined) { + if (data.length > schema.maxLength) { + return this.createError(ErrorCodes.STRING_LENGTH_LONG, {length: data.length, maximum: schema.maxLength}, '', '/maxLength', null, data, schema); + } + } + return null; +}; + +ValidatorContext.prototype.validateStringPattern = function validateStringPattern(data, schema) { + if (typeof data !== "string" || (typeof schema.pattern !== "string" && !(schema.pattern instanceof RegExp))) { + return null; + } + var regexp; + if (schema.pattern instanceof RegExp) { + regexp = schema.pattern; + } + else { + var body, flags = ''; + // Check for regular expression literals + // @see http://www.ecma-international.org/ecma-262/5.1/#sec-7.8.5 + var literal = schema.pattern.match(/^\/(.+)\/([img]*)$/); + if (literal) { + body = literal[1]; + flags = literal[2]; + } + else { + body = schema.pattern; + } + regexp = new RegExp(body, flags); + } + if (!regexp.test(data)) { + return this.createError(ErrorCodes.STRING_PATTERN, {pattern: schema.pattern}, '', '/pattern', null, data, schema); + } + return null; +}; + +ValidatorContext.prototype.validateArray = function validateArray(data, schema, dataPointerPath) { + if (!Array.isArray(data)) { + return null; + } + return this.validateArrayLength(data, schema, dataPointerPath) + || this.validateArrayUniqueItems(data, schema, dataPointerPath) + || this.validateArrayItems(data, schema, dataPointerPath) + || null; +}; + +ValidatorContext.prototype.validateArrayLength = function validateArrayLength(data, schema) { + var error; + if (schema.minItems !== undefined) { + if (data.length < schema.minItems) { + error = this.createError(ErrorCodes.ARRAY_LENGTH_SHORT, {length: data.length, minimum: schema.minItems}, '', '/minItems', null, data, schema); + if (this.handleError(error)) { + return error; + } + } + } + if (schema.maxItems !== undefined) { + if (data.length > schema.maxItems) { + error = this.createError(ErrorCodes.ARRAY_LENGTH_LONG, {length: data.length, maximum: schema.maxItems}, '', '/maxItems', null, data, schema); + if (this.handleError(error)) { + return error; + } + } + } + return null; +}; + +ValidatorContext.prototype.validateArrayUniqueItems = function validateArrayUniqueItems(data, schema) { + if (schema.uniqueItems) { + for (var i = 0; i < data.length; i++) { + for (var j = i + 1; j < data.length; j++) { + if (recursiveCompare(data[i], data[j])) { + var error = this.createError(ErrorCodes.ARRAY_UNIQUE, {match1: i, match2: j}, '', '/uniqueItems', null, data, schema); + if (this.handleError(error)) { + return error; + } + } + } + } + } + return null; +}; + +ValidatorContext.prototype.validateArrayItems = function validateArrayItems(data, schema, dataPointerPath) { + if (schema.items === undefined) { + return null; + } + var error, i; + if (Array.isArray(schema.items)) { + for (i = 0; i < data.length; i++) { + if (i < schema.items.length) { + if (error = this.validateAll(data[i], schema.items[i], [i], ["items", i], dataPointerPath + "/" + i)) { + return error; + } + } else if (schema.additionalItems !== undefined) { + if (typeof schema.additionalItems === "boolean") { + if (!schema.additionalItems) { + error = (this.createError(ErrorCodes.ARRAY_ADDITIONAL_ITEMS, {}, '/' + i, '/additionalItems', null, data, schema)); + if (this.handleError(error)) { + return error; + } + } + } else if (error = this.validateAll(data[i], schema.additionalItems, [i], ["additionalItems"], dataPointerPath + "/" + i)) { + return error; + } + } + } + } else { + for (i = 0; i < data.length; i++) { + if (error = this.validateAll(data[i], schema.items, [i], ["items"], dataPointerPath + "/" + i)) { + return error; + } + } + } + return null; +}; + +ValidatorContext.prototype.validateObject = function validateObject(data, schema, dataPointerPath) { + if (typeof data !== "object" || data === null || Array.isArray(data)) { + return null; + } + return this.validateObjectMinMaxProperties(data, schema, dataPointerPath) + || this.validateObjectRequiredProperties(data, schema, dataPointerPath) + || this.validateObjectProperties(data, schema, dataPointerPath) + || this.validateObjectDependencies(data, schema, dataPointerPath) + || null; +}; + +ValidatorContext.prototype.validateObjectMinMaxProperties = function validateObjectMinMaxProperties(data, schema) { + var keys = Object.keys(data); + var error; + if (schema.minProperties !== undefined) { + if (keys.length < schema.minProperties) { + error = this.createError(ErrorCodes.OBJECT_PROPERTIES_MINIMUM, {propertyCount: keys.length, minimum: schema.minProperties}, '', '/minProperties', null, data, schema); + if (this.handleError(error)) { + return error; + } + } + } + if (schema.maxProperties !== undefined) { + if (keys.length > schema.maxProperties) { + error = this.createError(ErrorCodes.OBJECT_PROPERTIES_MAXIMUM, {propertyCount: keys.length, maximum: schema.maxProperties}, '', '/maxProperties', null, data, schema); + if (this.handleError(error)) { + return error; + } + } + } + return null; +}; + +ValidatorContext.prototype.validateObjectRequiredProperties = function validateObjectRequiredProperties(data, schema) { + if (schema.required !== undefined) { + for (var i = 0; i < schema.required.length; i++) { + var key = schema.required[i]; + if (data[key] === undefined || data[key] === null) { + var error = this.createError(ErrorCodes.OBJECT_REQUIRED, {key: key}, '', '/required/' + i, null, data, schema); + if (this.handleError(error)) { + return error; + } + } + } + } + return null; +}; + +ValidatorContext.prototype.validateObjectProperties = function validateObjectProperties(data, schema, dataPointerPath) { + var error; + for (var key in data) { + var keyPointerPath = dataPointerPath + "/" + key.replace(/~/g, '~0').replace(/\//g, '~1'); + var foundMatch = false; + if (schema.properties !== undefined && schema.properties[key] !== undefined) { + foundMatch = true; + if (error = this.validateAll(data[key], schema.properties[key], [key], ["properties", key], keyPointerPath)) { + return error; + } + } + if (schema.patternProperties !== undefined) { + for (var patternKey in schema.patternProperties) { + var regexp = new RegExp(patternKey); + if (regexp.test(key)) { + foundMatch = true; + if (error = this.validateAll(data[key], schema.patternProperties[patternKey], [key], ["patternProperties", patternKey], keyPointerPath)) { + return error; + } + } + } + } + if (!foundMatch) { + if (schema.additionalProperties !== undefined) { + if (this.trackUnknownProperties) { + this.knownPropertyPaths[keyPointerPath] = true; + delete this.unknownPropertyPaths[keyPointerPath]; + } + if (typeof schema.additionalProperties === "boolean") { + if (!schema.additionalProperties) { + error = this.createError(ErrorCodes.OBJECT_ADDITIONAL_PROPERTIES, {key: key}, '', '/additionalProperties', null, data, schema).prefixWith(key, null); + if (this.handleError(error)) { + return error; + } + } + } else { + if (error = this.validateAll(data[key], schema.additionalProperties, [key], ["additionalProperties"], keyPointerPath)) { + return error; + } + } + } else if (this.trackUnknownProperties && !this.knownPropertyPaths[keyPointerPath]) { + this.unknownPropertyPaths[keyPointerPath] = true; + } + } else if (this.trackUnknownProperties) { + this.knownPropertyPaths[keyPointerPath] = true; + delete this.unknownPropertyPaths[keyPointerPath]; + } + } + return null; +}; + +ValidatorContext.prototype.validateObjectDependencies = function validateObjectDependencies(data, schema, dataPointerPath) { + var error; + if (schema.dependencies !== undefined) { + for (var depKey in schema.dependencies) { + if (data[depKey] !== undefined) { + var dep = schema.dependencies[depKey]; + if (typeof dep === "string") { + if (data[dep] === undefined) { + error = this.createError(ErrorCodes.OBJECT_DEPENDENCY_KEY, {key: depKey, missing: dep}, '', '', null, data, schema).prefixWith(null, depKey).prefixWith(null, "dependencies"); + if (this.handleError(error)) { + return error; + } + } + } else if (Array.isArray(dep)) { + for (var i = 0; i < dep.length; i++) { + var requiredKey = dep[i]; + if (data[requiredKey] === undefined) { + error = this.createError(ErrorCodes.OBJECT_DEPENDENCY_KEY, {key: depKey, missing: requiredKey}, '', '/' + i, null, data, schema).prefixWith(null, depKey).prefixWith(null, "dependencies"); + if (this.handleError(error)) { + return error; + } + } + } + } else { + if (error = this.validateAll(data, dep, [], ["dependencies", depKey], dataPointerPath)) { + return error; + } + } + } + } + } + return null; +}; + +ValidatorContext.prototype.validateCombinations = function validateCombinations(data, schema, dataPointerPath) { + return this.validateAllOf(data, schema, dataPointerPath) + || this.validateAnyOf(data, schema, dataPointerPath) + || this.validateOneOf(data, schema, dataPointerPath) + || this.validateNot(data, schema, dataPointerPath) + || null; +}; + +ValidatorContext.prototype.validateAllOf = function validateAllOf(data, schema, dataPointerPath) { + if (schema.allOf === undefined) { + return null; + } + var error; + for (var i = 0; i < schema.allOf.length; i++) { + var subSchema = schema.allOf[i]; + if (error = this.validateAll(data, subSchema, [], ["allOf", i], dataPointerPath)) { + return error; + } + } + return null; +}; + +ValidatorContext.prototype.validateAnyOf = function validateAnyOf(data, schema, dataPointerPath) { + if (schema.anyOf === undefined) { + return null; + } + var errors = []; + var startErrorCount = this.errors.length; + var oldUnknownPropertyPaths, oldKnownPropertyPaths; + if (this.trackUnknownProperties) { + oldUnknownPropertyPaths = this.unknownPropertyPaths; + oldKnownPropertyPaths = this.knownPropertyPaths; + } + var errorAtEnd = true; + for (var i = 0; i < schema.anyOf.length; i++) { + if (this.trackUnknownProperties) { + this.unknownPropertyPaths = {}; + this.knownPropertyPaths = {}; + } + var subSchema = schema.anyOf[i]; + + var errorCount = this.errors.length; + var error = this.validateAll(data, subSchema, [], ["anyOf", i], dataPointerPath); + + if (error === null && errorCount === this.errors.length) { + this.errors = this.errors.slice(0, startErrorCount); + + if (this.trackUnknownProperties) { + for (var knownKey in this.knownPropertyPaths) { + oldKnownPropertyPaths[knownKey] = true; + delete oldUnknownPropertyPaths[knownKey]; + } + for (var unknownKey in this.unknownPropertyPaths) { + if (!oldKnownPropertyPaths[unknownKey]) { + oldUnknownPropertyPaths[unknownKey] = true; + } + } + // We need to continue looping so we catch all the property definitions, but we don't want to return an error + errorAtEnd = false; + continue; + } + + return null; + } + if (error) { + errors.push(error.prefixWith(null, "" + i).prefixWith(null, "anyOf")); + } + } + if (this.trackUnknownProperties) { + this.unknownPropertyPaths = oldUnknownPropertyPaths; + this.knownPropertyPaths = oldKnownPropertyPaths; + } + if (errorAtEnd) { + errors = errors.concat(this.errors.slice(startErrorCount)); + this.errors = this.errors.slice(0, startErrorCount); + return this.createError(ErrorCodes.ANY_OF_MISSING, {}, "", "/anyOf", errors, data, schema); + } +}; + +ValidatorContext.prototype.validateOneOf = function validateOneOf(data, schema, dataPointerPath) { + if (schema.oneOf === undefined) { + return null; + } + var validIndex = null; + var errors = []; + var startErrorCount = this.errors.length; + var oldUnknownPropertyPaths, oldKnownPropertyPaths; + if (this.trackUnknownProperties) { + oldUnknownPropertyPaths = this.unknownPropertyPaths; + oldKnownPropertyPaths = this.knownPropertyPaths; + } + for (var i = 0; i < schema.oneOf.length; i++) { + if (this.trackUnknownProperties) { + this.unknownPropertyPaths = {}; + this.knownPropertyPaths = {}; + } + var subSchema = schema.oneOf[i]; + + var errorCount = this.errors.length; + var error = this.validateAll(data, subSchema, [], ["oneOf", i], dataPointerPath); + + if (error === null && errorCount === this.errors.length) { + if (validIndex === null) { + validIndex = i; + } else { + this.errors = this.errors.slice(0, startErrorCount); + return this.createError(ErrorCodes.ONE_OF_MULTIPLE, {index1: validIndex, index2: i}, "", "/oneOf", null, data, schema); + } + if (this.trackUnknownProperties) { + for (var knownKey in this.knownPropertyPaths) { + oldKnownPropertyPaths[knownKey] = true; + delete oldUnknownPropertyPaths[knownKey]; + } + for (var unknownKey in this.unknownPropertyPaths) { + if (!oldKnownPropertyPaths[unknownKey]) { + oldUnknownPropertyPaths[unknownKey] = true; + } + } + } + } else if (error) { + errors.push(error); + } + } + if (this.trackUnknownProperties) { + this.unknownPropertyPaths = oldUnknownPropertyPaths; + this.knownPropertyPaths = oldKnownPropertyPaths; + } + if (validIndex === null) { + errors = errors.concat(this.errors.slice(startErrorCount)); + this.errors = this.errors.slice(0, startErrorCount); + return this.createError(ErrorCodes.ONE_OF_MISSING, {}, "", "/oneOf", errors, data, schema); + } else { + this.errors = this.errors.slice(0, startErrorCount); + } + return null; +}; + +ValidatorContext.prototype.validateNot = function validateNot(data, schema, dataPointerPath) { + if (schema.not === undefined) { + return null; + } + var oldErrorCount = this.errors.length; + var oldUnknownPropertyPaths, oldKnownPropertyPaths; + if (this.trackUnknownProperties) { + oldUnknownPropertyPaths = this.unknownPropertyPaths; + oldKnownPropertyPaths = this.knownPropertyPaths; + this.unknownPropertyPaths = {}; + this.knownPropertyPaths = {}; + } + var error = this.validateAll(data, schema.not, null, null, dataPointerPath); + var notErrors = this.errors.slice(oldErrorCount); + this.errors = this.errors.slice(0, oldErrorCount); + if (this.trackUnknownProperties) { + this.unknownPropertyPaths = oldUnknownPropertyPaths; + this.knownPropertyPaths = oldKnownPropertyPaths; + } + if (error === null && notErrors.length === 0) { + return this.createError(ErrorCodes.NOT_PASSED, {}, "", "/not", null, data, schema); + } + return null; +}; + +ValidatorContext.prototype.validateHypermedia = function validateCombinations(data, schema, dataPointerPath) { + if (!schema.links) { + return null; + } + var error; + for (var i = 0; i < schema.links.length; i++) { + var ldo = schema.links[i]; + if (ldo.rel === "describedby") { + var template = new UriTemplate(ldo.href); + var allPresent = true; + for (var j = 0; j < template.varNames.length; j++) { + if (!(template.varNames[j] in data)) { + allPresent = false; + break; + } + } + if (allPresent) { + var schemaUrl = template.fillFromObject(data); + var subSchema = {"$ref": schemaUrl}; + if (error = this.validateAll(data, subSchema, [], ["links", i], dataPointerPath)) { + return error; + } + } + } + } +}; + +// parseURI() and resolveUrl() are from https://gist.github.com/1088850 +// - released as public domain by author ("Yaffle") - see comments on gist + +function parseURI(url) { + var m = String(url).replace(/^\s+|\s+$/g, '').match(/^([^:\/?#]+:)?(\/\/(?:[^:@]*(?::[^:@]*)?@)?(([^:\/?#]*)(?::(\d*))?))?([^?#]*)(\?[^#]*)?(#[\s\S]*)?/); + // authority = '//' + user + ':' + pass '@' + hostname + ':' port + return (m ? { + href : m[0] || '', + protocol : m[1] || '', + authority: m[2] || '', + host : m[3] || '', + hostname : m[4] || '', + port : m[5] || '', + pathname : m[6] || '', + search : m[7] || '', + hash : m[8] || '' + } : null); +} + +function resolveUrl(base, href) {// RFC 3986 + + function removeDotSegments(input) { + var output = []; + input.replace(/^(\.\.?(\/|$))+/, '') + .replace(/\/(\.(\/|$))+/g, '/') + .replace(/\/\.\.$/, '/../') + .replace(/\/?[^\/]*/g, function (p) { + if (p === '/..') { + output.pop(); + } else { + output.push(p); + } + }); + return output.join('').replace(/^\//, input.charAt(0) === '/' ? '/' : ''); + } + + href = parseURI(href || ''); + base = parseURI(base || ''); + + return !href || !base ? null : (href.protocol || base.protocol) + + (href.protocol || href.authority ? href.authority : base.authority) + + removeDotSegments(href.protocol || href.authority || href.pathname.charAt(0) === '/' ? href.pathname : (href.pathname ? ((base.authority && !base.pathname ? '/' : '') + base.pathname.slice(0, base.pathname.lastIndexOf('/') + 1) + href.pathname) : base.pathname)) + + (href.protocol || href.authority || href.pathname ? href.search : (href.search || base.search)) + + href.hash; +} + +function getDocumentUri(uri) { + return uri.split('#')[0]; +} +function normSchema(schema, baseUri) { + if (schema && typeof schema === "object") { + if (baseUri === undefined) { + baseUri = schema.id; + } else if (typeof schema.id === "string") { + baseUri = resolveUrl(baseUri, schema.id); + schema.id = baseUri; + } + if (Array.isArray(schema)) { + for (var i = 0; i < schema.length; i++) { + normSchema(schema[i], baseUri); + } + } else { + if (typeof schema['$ref'] === "string") { + schema['$ref'] = resolveUrl(baseUri, schema['$ref']); + } + for (var key in schema) { + if (key !== "enum") { + normSchema(schema[key], baseUri); + } + } + } + } +} + +function defaultErrorReporter(language) { + language = language || 'en'; + + var errorMessages = languages[language]; + + return function (error) { + var messageTemplate = errorMessages[error.code] || ErrorMessagesDefault[error.code]; + if (typeof messageTemplate !== 'string') { + return "Unknown error code " + error.code + ": " + JSON.stringify(error.messageParams); + } + var messageParams = error.params; + // Adapted from Crockford's supplant() + return messageTemplate.replace(/\{([^{}]*)\}/g, function (whole, varName) { + var subValue = messageParams[varName]; + return typeof subValue === 'string' || typeof subValue === 'number' ? subValue : whole; + }); + }; +} + +var ErrorCodes = { + INVALID_TYPE: 0, + ENUM_MISMATCH: 1, + ANY_OF_MISSING: 10, + ONE_OF_MISSING: 11, + ONE_OF_MULTIPLE: 12, + NOT_PASSED: 13, + // Numeric errors + NUMBER_MULTIPLE_OF: 100, + NUMBER_MINIMUM: 101, + NUMBER_MINIMUM_EXCLUSIVE: 102, + NUMBER_MAXIMUM: 103, + NUMBER_MAXIMUM_EXCLUSIVE: 104, + NUMBER_NOT_A_NUMBER: 105, + // String errors + STRING_LENGTH_SHORT: 200, + STRING_LENGTH_LONG: 201, + STRING_PATTERN: 202, + // Object errors + OBJECT_PROPERTIES_MINIMUM: 300, + OBJECT_PROPERTIES_MAXIMUM: 301, + OBJECT_REQUIRED: 302, + OBJECT_ADDITIONAL_PROPERTIES: 303, + OBJECT_DEPENDENCY_KEY: 304, + // Array errors + ARRAY_LENGTH_SHORT: 400, + ARRAY_LENGTH_LONG: 401, + ARRAY_UNIQUE: 402, + ARRAY_ADDITIONAL_ITEMS: 403, + // Custom/user-defined errors + FORMAT_CUSTOM: 500, + KEYWORD_CUSTOM: 501, + // Schema structure + CIRCULAR_REFERENCE: 600, + // Non-standard validation options + UNKNOWN_PROPERTY: 1000 +}; +var ErrorCodeLookup = {}; +for (var key in ErrorCodes) { + ErrorCodeLookup[ErrorCodes[key]] = key; +} +var ErrorMessagesDefault = { + INVALID_TYPE: "Invalid type: {type} (expected {expected})", + ENUM_MISMATCH: "No enum match for: {value}", + ANY_OF_MISSING: "Data does not match any schemas from \"anyOf\"", + ONE_OF_MISSING: "Data does not match any schemas from \"oneOf\"", + ONE_OF_MULTIPLE: "Data is valid against more than one schema from \"oneOf\": indices {index1} and {index2}", + NOT_PASSED: "Data matches schema from \"not\"", + // Numeric errors + NUMBER_MULTIPLE_OF: "Value {value} is not a multiple of {multipleOf}", + NUMBER_MINIMUM: "Value {value} is less than minimum {minimum}", + NUMBER_MINIMUM_EXCLUSIVE: "Value {value} is equal to exclusive minimum {minimum}", + NUMBER_MAXIMUM: "Value {value} is greater than maximum {maximum}", + NUMBER_MAXIMUM_EXCLUSIVE: "Value {value} is equal to exclusive maximum {maximum}", + NUMBER_NOT_A_NUMBER: "Value {value} is not a valid number", + // String errors + STRING_LENGTH_SHORT: "String is too short ({length} chars), minimum {minimum}", + STRING_LENGTH_LONG: "String is too long ({length} chars), maximum {maximum}", + STRING_PATTERN: "String does not match pattern: {pattern}", + // Object errors + OBJECT_PROPERTIES_MINIMUM: "Too few properties defined ({propertyCount}), minimum {minimum}", + OBJECT_PROPERTIES_MAXIMUM: "Too many properties defined ({propertyCount}), maximum {maximum}", + OBJECT_REQUIRED: "Missing required property: {key}", + OBJECT_ADDITIONAL_PROPERTIES: "Additional properties not allowed", + OBJECT_DEPENDENCY_KEY: "Dependency failed - key must exist: {missing} (due to key: {key})", + // Array errors + ARRAY_LENGTH_SHORT: "Array is too short ({length}), minimum {minimum}", + ARRAY_LENGTH_LONG: "Array is too long ({length}), maximum {maximum}", + ARRAY_UNIQUE: "Array items are not unique (indices {match1} and {match2})", + ARRAY_ADDITIONAL_ITEMS: "Additional items not allowed", + // Format errors + FORMAT_CUSTOM: "Format validation failed ({message})", + KEYWORD_CUSTOM: "Keyword failed: {key} ({message})", + // Schema structure + CIRCULAR_REFERENCE: "Circular $refs: {urls}", + // Non-standard validation options + UNKNOWN_PROPERTY: "Unknown property (not in schema)" +}; + +function ValidationError(code, params, dataPath, schemaPath, subErrors) { + Error.call(this); + if (code === undefined) { + throw new Error ("No error code supplied: " + schemaPath); + } + this.message = ''; + this.params = params; + this.code = code; + this.dataPath = dataPath || ""; + this.schemaPath = schemaPath || ""; + this.subErrors = subErrors || null; + + var err = new Error(this.message); + this.stack = err.stack || err.stacktrace; + if (!this.stack) { + try { + throw err; + } + catch(err) { + this.stack = err.stack || err.stacktrace; + } + } +} +ValidationError.prototype = Object.create(Error.prototype); +ValidationError.prototype.constructor = ValidationError; +ValidationError.prototype.name = 'ValidationError'; + +ValidationError.prototype.prefixWith = function (dataPrefix, schemaPrefix) { + if (dataPrefix !== null) { + dataPrefix = dataPrefix.replace(/~/g, "~0").replace(/\//g, "~1"); + this.dataPath = "/" + dataPrefix + this.dataPath; + } + if (schemaPrefix !== null) { + schemaPrefix = schemaPrefix.replace(/~/g, "~0").replace(/\//g, "~1"); + this.schemaPath = "/" + schemaPrefix + this.schemaPath; + } + if (this.subErrors !== null) { + for (var i = 0; i < this.subErrors.length; i++) { + this.subErrors[i].prefixWith(dataPrefix, schemaPrefix); + } + } + return this; +}; + +function isTrustedUrl(baseUrl, testUrl) { + if(testUrl.substring(0, baseUrl.length) === baseUrl){ + var remainder = testUrl.substring(baseUrl.length); + if ((testUrl.length > 0 && testUrl.charAt(baseUrl.length - 1) === "/") + || remainder.charAt(0) === "#" + || remainder.charAt(0) === "?") { + return true; + } + } + return false; +} + +var languages = {}; +function createApi(language) { + var globalContext = new ValidatorContext(); + var currentLanguage; + var customErrorReporter; + var api = { + setErrorReporter: function (reporter) { + if (typeof reporter === 'string') { + return this.language(reporter); + } + customErrorReporter = reporter; + return true; + }, + addFormat: function () { + globalContext.addFormat.apply(globalContext, arguments); + }, + language: function (code) { + if (!code) { + return currentLanguage; + } + if (!languages[code]) { + code = code.split('-')[0]; // fall back to base language + } + if (languages[code]) { + currentLanguage = code; + return code; // so you can tell if fall-back has happened + } + return false; + }, + addLanguage: function (code, messageMap) { + var key; + for (key in ErrorCodes) { + if (messageMap[key] && !messageMap[ErrorCodes[key]]) { + messageMap[ErrorCodes[key]] = messageMap[key]; + } + } + var rootCode = code.split('-')[0]; + if (!languages[rootCode]) { // use for base language if not yet defined + languages[code] = messageMap; + languages[rootCode] = messageMap; + } else { + languages[code] = Object.create(languages[rootCode]); + for (key in messageMap) { + if (typeof languages[rootCode][key] === 'undefined') { + languages[rootCode][key] = messageMap[key]; + } + languages[code][key] = messageMap[key]; + } + } + return this; + }, + freshApi: function (language) { + var result = createApi(); + if (language) { + result.language(language); + } + return result; + }, + validate: function (data, schema, checkRecursive, banUnknownProperties) { + var def = defaultErrorReporter(currentLanguage); + var errorReporter = customErrorReporter ? function (error, data, schema) { + return customErrorReporter(error, data, schema) || def(error, data, schema); + } : def; + var context = new ValidatorContext(globalContext, false, errorReporter, checkRecursive, banUnknownProperties); + if (typeof schema === "string") { + schema = {"$ref": schema}; + } + context.addSchema("", schema); + var error = context.validateAll(data, schema, null, null, ""); + if (!error && banUnknownProperties) { + error = context.banUnknownProperties(data, schema); + } + this.error = error; + this.missing = context.missing; + this.valid = (error === null); + return this.valid; + }, + validateResult: function () { + var result = {}; + this.validate.apply(result, arguments); + return result; + }, + validateMultiple: function (data, schema, checkRecursive, banUnknownProperties) { + var def = defaultErrorReporter(currentLanguage); + var errorReporter = customErrorReporter ? function (error, data, schema) { + return customErrorReporter(error, data, schema) || def(error, data, schema); + } : def; + var context = new ValidatorContext(globalContext, true, errorReporter, checkRecursive, banUnknownProperties); + if (typeof schema === "string") { + schema = {"$ref": schema}; + } + context.addSchema("", schema); + context.validateAll(data, schema, null, null, ""); + if (banUnknownProperties) { + context.banUnknownProperties(data, schema); + } + var result = {}; + result.errors = context.errors; + result.missing = context.missing; + result.valid = (result.errors.length === 0); + return result; + }, + addSchema: function () { + return globalContext.addSchema.apply(globalContext, arguments); + }, + getSchema: function () { + return globalContext.getSchema.apply(globalContext, arguments); + }, + getSchemaMap: function () { + return globalContext.getSchemaMap.apply(globalContext, arguments); + }, + getSchemaUris: function () { + return globalContext.getSchemaUris.apply(globalContext, arguments); + }, + getMissingUris: function () { + return globalContext.getMissingUris.apply(globalContext, arguments); + }, + dropSchemas: function () { + globalContext.dropSchemas.apply(globalContext, arguments); + }, + defineKeyword: function () { + globalContext.defineKeyword.apply(globalContext, arguments); + }, + defineError: function (codeName, codeNumber, defaultMessage) { + if (typeof codeName !== 'string' || !/^[A-Z]+(_[A-Z]+)*$/.test(codeName)) { + throw new Error('Code name must be a string in UPPER_CASE_WITH_UNDERSCORES'); + } + if (typeof codeNumber !== 'number' || codeNumber%1 !== 0 || codeNumber < 10000) { + throw new Error('Code number must be an integer > 10000'); + } + if (typeof ErrorCodes[codeName] !== 'undefined') { + throw new Error('Error already defined: ' + codeName + ' as ' + ErrorCodes[codeName]); + } + if (typeof ErrorCodeLookup[codeNumber] !== 'undefined') { + throw new Error('Error code already used: ' + ErrorCodeLookup[codeNumber] + ' as ' + codeNumber); + } + ErrorCodes[codeName] = codeNumber; + ErrorCodeLookup[codeNumber] = codeName; + ErrorMessagesDefault[codeName] = ErrorMessagesDefault[codeNumber] = defaultMessage; + for (var langCode in languages) { + var language = languages[langCode]; + if (language[codeName]) { + language[codeNumber] = language[codeNumber] || language[codeName]; + } + } + }, + reset: function () { + globalContext.reset(); + this.error = null; + this.missing = []; + this.valid = true; + }, + missing: [], + error: null, + valid: true, + normSchema: normSchema, + resolveUrl: resolveUrl, + getDocumentUri: getDocumentUri, + errorCodes: ErrorCodes + }; + api.language(language || 'en'); + return api; +} + +var tv4 = createApi(); +tv4.addLanguage('en-gb', ErrorMessagesDefault); + +//legacy property +tv4.tv4 = tv4; + +return tv4; // used by _header.js to globalise. + +})); +//@ sourceMappingURL=tv4.js.map \ No newline at end of file diff --git a/tv4.min.js b/tv4.min.js new file mode 100644 index 0000000..1d96453 --- /dev/null +++ b/tv4.min.js @@ -0,0 +1,2 @@ +!function(a,b){"function"==typeof define&&define.amd?define([],b):"undefined"!=typeof module&&module.exports?module.exports=b():a.tv4=b()}(this,function(){function a(a){return encodeURI(a).replace(/%25[0-9][0-9]/g,function(a){return"%"+a.substring(3)})}function b(b){var c="";m[b.charAt(0)]&&(c=b.charAt(0),b=b.substring(1));var d="",e="",f=!0,g=!1,h=!1;"+"===c?f=!1:"."===c?(e=".",d="."):"/"===c?(e="/",d="/"):"#"===c?(e="#",f=!1):";"===c?(e=";",d=";",g=!0,h=!0):"?"===c?(e="?",d="&",g=!0):"&"===c&&(e="&",d="&",g=!0);for(var i=[],j=b.split(","),k=[],l={},o=0;o0&&(c+=l.suffices["*"]?d||",":",",l.suffices["*"]&&g&&(c+=l.name+"=")),c+=f?encodeURIComponent(m[n]).replace(/!/g,"%21"):a(m[n])}else if("object"==typeof m){g&&!l.suffices["*"]&&(c+=l.name+"=");var o=!0;for(var p in m)o||(c+=l.suffices["*"]?d||",":","),o=!1,c+=f?encodeURIComponent(p).replace(/!/g,"%21"):a(p),c+=l.suffices["*"]?"=":",",c+=f?encodeURIComponent(m[p]).replace(/!/g,"%21"):a(m[p])}else g&&(c+=l.name,h&&""===m||(c+="=")),null!=l.truncate&&(m=m.substring(0,l.truncate)),c+=f?encodeURIComponent(m).replace(/!/g,"%21"):a(m)}return c};return u.varNames=i,{prefix:e,substitution:u}}function c(a){if(!(this instanceof c))return new c(a);for(var d=a.split("{"),e=[d.shift()],f=[],g=[],h=[];d.length>0;){var i=d.shift(),j=i.split("}")[0],k=i.substring(j.length+1),l=b(j);g.push(l.substitution),f.push(l.prefix),e.push(k),h=h.concat(l.substitution.varNames)}this.fill=function(a){for(var b=e[0],c=0;c0&&"/"===b.charAt(a.length-1)||"#"===c.charAt(0)||"?"===c.charAt(0))return!0}return!1}function l(a){var b,c,d=new o,e={setErrorReporter:function(a){return"string"==typeof a?this.language(a):(c=a,!0)},addFormat:function(){d.addFormat.apply(d,arguments)},language:function(a){return a?(v[a]||(a=a.split("-")[0]),v[a]?(b=a,a):!1):b},addLanguage:function(a,b){var c;for(c in r)b[c]&&!b[r[c]]&&(b[r[c]]=b[c]);var d=a.split("-")[0];if(v[d]){v[a]=Object.create(v[d]);for(c in b)"undefined"==typeof v[d][c]&&(v[d][c]=b[c]),v[a][c]=b[c]}else v[a]=b,v[d]=b;return this},freshApi:function(a){var b=l();return a&&b.language(a),b},validate:function(a,e,f,g){var h=i(b),j=c?function(a,b,d){return c(a,b,d)||h(a,b,d)}:h,k=new o(d,!1,j,f,g);"string"==typeof e&&(e={$ref:e}),k.addSchema("",e);var l=k.validateAll(a,e,null,null,"");return!l&&g&&(l=k.banUnknownProperties(a,e)),this.error=l,this.missing=k.missing,this.valid=null===l,this.valid},validateResult:function(){var a={};return this.validate.apply(a,arguments),a},validateMultiple:function(a,e,f,g){var h=i(b),j=c?function(a,b,d){return c(a,b,d)||h(a,b,d)}:h,k=new o(d,!0,j,f,g);"string"==typeof e&&(e={$ref:e}),k.addSchema("",e),k.validateAll(a,e,null,null,""),g&&k.banUnknownProperties(a,e);var l={};return l.errors=k.errors,l.missing=k.missing,l.valid=0===l.errors.length,l},addSchema:function(){return d.addSchema.apply(d,arguments)},getSchema:function(){return d.getSchema.apply(d,arguments)},getSchemaMap:function(){return d.getSchemaMap.apply(d,arguments)},getSchemaUris:function(){return d.getSchemaUris.apply(d,arguments)},getMissingUris:function(){return d.getMissingUris.apply(d,arguments)},dropSchemas:function(){d.dropSchemas.apply(d,arguments)},defineKeyword:function(){d.defineKeyword.apply(d,arguments)},defineError:function(a,b,c){if("string"!=typeof a||!/^[A-Z]+(_[A-Z]+)*$/.test(a))throw new Error("Code name must be a string in UPPER_CASE_WITH_UNDERSCORES");if("number"!=typeof b||b%1!==0||1e4>b)throw new Error("Code number must be an integer > 10000");if("undefined"!=typeof r[a])throw new Error("Error already defined: "+a+" as "+r[a]);if("undefined"!=typeof s[b])throw new Error("Error code already used: "+s[b]+" as "+b);r[a]=b,s[b]=a,u[a]=u[b]=c;for(var d in v){var e=v[d];e[a]&&(e[b]=e[b]||e[a])}},reset:function(){d.reset(),this.error=null,this.missing=[],this.valid=!0},missing:[],error:null,valid:!0,normSchema:h,resolveUrl:f,getDocumentUri:g,errorCodes:r};return e.language(a||"en"),e}Object.keys||(Object.keys=function(){var a=Object.prototype.hasOwnProperty,b=!{toString:null}.propertyIsEnumerable("toString"),c=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],d=c.length;return function(e){if("object"!=typeof e&&"function"!=typeof e||null===e)throw new TypeError("Object.keys called on non-object");var f=[];for(var g in e)a.call(e,g)&&f.push(g);if(b)for(var h=0;d>h;h++)a.call(e,c[h])&&f.push(c[h]);return f}}()),Object.create||(Object.create=function(){function a(){}return function(b){if(1!==arguments.length)throw new Error("Object.create implementation only accepts one parameter.");return a.prototype=b,new a}}()),Array.isArray||(Array.isArray=function(a){return"[object Array]"===Object.prototype.toString.call(a)}),Array.prototype.indexOf||(Array.prototype.indexOf=function(a){if(null===this)throw new TypeError;var b=Object(this),c=b.length>>>0;if(0===c)return-1;var d=0;if(arguments.length>1&&(d=Number(arguments[1]),d!==d?d=0:0!==d&&d!==1/0&&d!==-(1/0)&&(d=(d>0||-1)*Math.floor(Math.abs(d)))),d>=c)return-1;for(var e=d>=0?d:Math.max(c-Math.abs(d),0);c>e;e++)if(e in b&&b[e]===a)return e;return-1}),Object.isFrozen||(Object.isFrozen=function(a){for(var b="tv4_test_frozen_key";a.hasOwnProperty(b);)b+=Math.random();try{return a[b]=!0,delete a[b],!1}catch(c){return!0}});var m={"+":!0,"#":!0,".":!0,"/":!0,";":!0,"?":!0,"&":!0},n={"*":!0};c.prototype={toString:function(){return this.template},fillFromObject:function(a){return this.fill(function(b){return a[b]})}};var o=function(a,b,c,d,e){if(this.missing=[],this.missingMap={},this.formatValidators=a?Object.create(a.formatValidators):{},this.schemas=a?Object.create(a.schemas):{},this.collectMultiple=b,this.errors=[],this.handleError=b?this.collectError:this.returnError,d&&(this.checkRecursive=!0,this.scanned=[],this.scannedFrozen=[],this.scannedFrozenSchemas=[],this.scannedFrozenValidationErrors=[],this.validatedSchemasKey="tv4_validation_id",this.validationErrorsKey="tv4_validation_errors_id"),e&&(this.trackUnknownProperties=!0,this.knownPropertyPaths={},this.unknownPropertyPaths={}),this.errorReporter=c||i("en"),"string"==typeof this.errorReporter)throw new Error("debug");if(this.definedKeywords={},a)for(var f in a.definedKeywords)this.definedKeywords[f]=a.definedKeywords[f].slice(0)};o.prototype.defineKeyword=function(a,b){this.definedKeywords[a]=this.definedKeywords[a]||[],this.definedKeywords[a].push(b)},o.prototype.createError=function(a,b,c,d,e,f,g){var h=new j(a,b,c,d,e);return h.message=this.errorReporter(h,f,g),h},o.prototype.returnError=function(a){return a},o.prototype.collectError=function(a){return a&&this.errors.push(a),null},o.prototype.prefixErrors=function(a,b,c){for(var d=a;d=p&&q>d)return this.createError(r.NUMBER_MULTIPLE_OF,{value:a,multipleOf:c},"","",null,a,b)}return null},o.prototype.validateMinMax=function(a,b){if("number"!=typeof a)return null;if(void 0!==b.minimum){if(ab.maximum)return this.createError(r.NUMBER_MAXIMUM,{value:a,maximum:b.maximum},"","/maximum",null,a,b);if(b.exclusiveMaximum&&a===b.maximum)return this.createError(r.NUMBER_MAXIMUM_EXCLUSIVE,{value:a,maximum:b.maximum},"","/exclusiveMaximum",null,a,b)}return null},o.prototype.validateNaN=function(a,b){return"number"!=typeof a?null:isNaN(a)===!0||a===1/0||a===-(1/0)?this.createError(r.NUMBER_NOT_A_NUMBER,{value:a},"","/type",null,a,b):null},o.prototype.validateString=function(a,b,c){return this.validateStringLength(a,b,c)||this.validateStringPattern(a,b,c)||null},o.prototype.validateStringLength=function(a,b){return"string"!=typeof a?null:void 0!==b.minLength&&a.lengthb.maxLength?this.createError(r.STRING_LENGTH_LONG,{length:a.length,maximum:b.maxLength},"","/maxLength",null,a,b):null},o.prototype.validateStringPattern=function(a,b){if("string"!=typeof a||"string"!=typeof b.pattern&&!(b.pattern instanceof RegExp))return null;var c;if(b.pattern instanceof RegExp)c=b.pattern;else{var d,e="",f=b.pattern.match(/^\/(.+)\/([img]*)$/);f?(d=f[1],e=f[2]):d=b.pattern,c=new RegExp(d,e)}return c.test(a)?null:this.createError(r.STRING_PATTERN,{pattern:b.pattern},"","/pattern",null,a,b)},o.prototype.validateArray=function(a,b,c){return Array.isArray(a)?this.validateArrayLength(a,b,c)||this.validateArrayUniqueItems(a,b,c)||this.validateArrayItems(a,b,c)||null:null},o.prototype.validateArrayLength=function(a,b){var c;return void 0!==b.minItems&&a.lengthb.maxItems&&(c=this.createError(r.ARRAY_LENGTH_LONG,{length:a.length,maximum:b.maxItems},"","/maxItems",null,a,b),this.handleError(c))?c:null},o.prototype.validateArrayUniqueItems=function(a,b){if(b.uniqueItems)for(var c=0;cb.maxProperties&&(c=this.createError(r.OBJECT_PROPERTIES_MAXIMUM,{propertyCount:d.length,maximum:b.maxProperties},"","/maxProperties",null,a,b),this.handleError(c))?c:null},o.prototype.validateObjectRequiredProperties=function(a,b){if(void 0!==b.required)for(var c=0;c