package/0000775000175000017500000000000011732712147011420 5ustar daviddavidpackage/schemas/0000755000175000017500000000000011727717452013051 5ustar daviddavidpackage/schemas/json-schema-draft-01/0000755000175000017500000000000011727717452016574 5ustar daviddavidpackage/schemas/json-schema-draft-01/schema.json0000644000175000017500000000512311457104136020715 0ustar daviddavid{ "$schema" : "http://json-schema.org/hyper-schema#", "id" : "http://json-schema.org/schema#", "type" : "object", "properties" : { "type" : { "type" : ["string", "array"], "items" : { "type" : ["string", {"$ref" : "#"}] }, "optional" : true, "default" : "any" }, "properties" : { "type" : "object", "additionalProperties" : {"$ref" : "#"}, "optional" : true, "default" : {} }, "items" : { "type" : [{"$ref" : "#"}, "array"], "items" : {"$ref" : "#"}, "optional" : true, "default" : {} }, "optional" : { "type" : "boolean", "optional" : true, "default" : false }, "additionalProperties" : { "type" : [{"$ref" : "#"}, "boolean"], "optional" : true, "default" : {} }, "requires" : { "type" : ["string", {"$ref" : "#"}], "optional" : true }, "minimum" : { "type" : "number", "optional" : true }, "maximum" : { "type" : "number", "optional" : true }, "minimumCanEqual" : { "type" : "boolean", "optional" : true, "requires" : "minimum", "default" : true }, "maximumCanEqual" : { "type" : "boolean", "optional" : true, "requires" : "maximum", "default" : true }, "minItems" : { "type" : "integer", "optional" : true, "minimum" : 0, "default" : 0 }, "maxItems" : { "type" : "integer", "optional" : true, "minimum" : 0 }, "pattern" : { "type" : "string", "optional" : true, "format" : "regex" }, "minLength" : { "type" : "integer", "optional" : true, "minimum" : 0, "default" : 0 }, "maxLength" : { "type" : "integer", "optional" : true }, "enum" : { "type" : "array", "optional" : true, "minItems" : 1 }, "title" : { "type" : "string", "optional" : true }, "description" : { "type" : "string", "optional" : true }, "format" : { "type" : "string", "optional" : true }, "contentEncoding" : { "type" : "string", "optional" : true }, "default" : { "type" : "any", "optional" : true }, "maxDecimal" : { "type" : "integer", "optional" : true, "minimum" : 0 }, "disallow" : { "type" : ["string", "array"], "items" : {"type" : "string"}, "optional" : true }, "extends" : { "type" : [{"$ref" : "#"}, "array"], "items" : {"$ref" : "#"}, "optional" : true, "default" : {} } }, "optional" : true, "default" : {} }package/schemas/json-schema-draft-01/json-ref.json0000644000175000017500000000064111457104136021200 0ustar daviddavid{ "$schema" : "http://json-schema.org/hyper-schema#", "id" : "http://json-schema.org/json-ref#", "items" : {"$ref" : "#"}, "additionalProperties" : {"$ref" : "#"}, "links" : [ { "href" : "{$ref}", "rel" : "full" }, { "href" : "{$schema}", "rel" : "describedby" }, { "href" : "{id}", "rel" : "self" } ], "fragmentResolution" : "dot-delimited" }package/schemas/json-schema-draft-01/links.json0000644000175000017500000000110511457104136020571 0ustar daviddavid{ "$schema" : "http://json-schema.org/hyper-schema#", "id" : "http://json-schema.org/links#", "type" : "object", "properties" : { "href" : { "type" : "string" }, "rel" : { "type" : "string" }, "method" : { "type" : "string", "default" : "GET", "optional" : true }, "enctype" : { "type" : "string", "requires" : "method", "optional" : true }, "properties" : { "type" : "object", "additionalProperties" : {"$ref" : "http://json-schema.org/hyper-schema#"}, "optional" : true } } }package/schemas/json-schema-draft-01/hyper-schema.json0000644000175000017500000000221211457104136022036 0ustar daviddavid{ "$schema" : "http://json-schema.org/hyper-schema#", "id" : "http://json-schema.org/hyper-schema#", "properties" : { "links" : { "type" : "array", "items" : {"$ref" : "http://json-schema.org/links#"}, "optional" : true }, "fragmentResolution" : { "type" : "string", "optional" : true, "default" : "dot-delimited" }, "root" : { "type" : "boolean", "optional" : true, "default" : false }, "readonly" : { "type" : "boolean", "optional" : true, "default" : false }, "pathStart" : { "type" : "string", "optional" : true, "format" : "uri" }, "mediaType" : { "type" : "string", "optional" : true, "format" : "media-type" }, "alternate" : { "type" : "array", "items" : {"$ref" : "#"}, "optional" : true } }, "links" : [ { "href" : "{$ref}", "rel" : "full" }, { "href" : "{$schema}", "rel" : "describedby" }, { "href" : "{id}", "rel" : "self" } ], "fragmentResolution" : "dot-delimited", "extends" : {"$ref" : "http://json-schema.org/schema#"} }package/schemas/json-schema-draft-01/empty-schema.json0000644000175000017500000000000211457104136022040 0ustar daviddavid{}package/schemas/json-schema-draft-02/0000755000175000017500000000000011727717452016575 5ustar daviddavidpackage/schemas/json-schema-draft-02/schema.json0000644000175000017500000000544411504242545020724 0ustar daviddavid{ "$schema" : "http://json-schema.org/hyper-schema#", "id" : "http://json-schema.org/schema#", "type" : "object", "properties" : { "type" : { "type" : ["string", "array"], "items" : { "type" : ["string", {"$ref" : "#"}] }, "optional" : true, "uniqueItems" : true, "default" : "any" }, "properties" : { "type" : "object", "additionalProperties" : {"$ref" : "#"}, "optional" : true, "default" : {} }, "items" : { "type" : [{"$ref" : "#"}, "array"], "items" : {"$ref" : "#"}, "optional" : true, "default" : {} }, "optional" : { "type" : "boolean", "optional" : true, "default" : false }, "additionalProperties" : { "type" : [{"$ref" : "#"}, "boolean"], "optional" : true, "default" : {} }, "requires" : { "type" : ["string", {"$ref" : "#"}], "optional" : true }, "minimum" : { "type" : "number", "optional" : true }, "maximum" : { "type" : "number", "optional" : true }, "minimumCanEqual" : { "type" : "boolean", "optional" : true, "requires" : "minimum", "default" : true }, "maximumCanEqual" : { "type" : "boolean", "optional" : true, "requires" : "maximum", "default" : true }, "minItems" : { "type" : "integer", "optional" : true, "minimum" : 0, "default" : 0 }, "maxItems" : { "type" : "integer", "optional" : true, "minimum" : 0 }, "uniqueItems" : { "type" : "boolean", "optional" : true, "default" : false }, "pattern" : { "type" : "string", "optional" : true, "format" : "regex" }, "minLength" : { "type" : "integer", "optional" : true, "minimum" : 0, "default" : 0 }, "maxLength" : { "type" : "integer", "optional" : true }, "enum" : { "type" : "array", "optional" : true, "minItems" : 1, "uniqueItems" : true }, "title" : { "type" : "string", "optional" : true }, "description" : { "type" : "string", "optional" : true }, "format" : { "type" : "string", "optional" : true }, "contentEncoding" : { "type" : "string", "optional" : true }, "default" : { "type" : "any", "optional" : true }, "divisibleBy" : { "type" : "number", "minimum" : 0, "minimumCanEqual" : false, "optional" : true }, "disallow" : { "type" : ["string", "array"], "items" : {"type" : "string"}, "optional" : true, "uniqueItems" : true }, "extends" : { "type" : [{"$ref" : "#"}, "array"], "items" : {"$ref" : "#"}, "optional" : true, "default" : {} } }, "optional" : true, "default" : {} }package/schemas/json-schema-draft-02/json-ref.json0000644000175000017500000000064111457104136021201 0ustar daviddavid{ "$schema" : "http://json-schema.org/hyper-schema#", "id" : "http://json-schema.org/json-ref#", "items" : {"$ref" : "#"}, "additionalProperties" : {"$ref" : "#"}, "links" : [ { "href" : "{$ref}", "rel" : "full" }, { "href" : "{$schema}", "rel" : "describedby" }, { "href" : "{id}", "rel" : "self" } ], "fragmentResolution" : "dot-delimited" }package/schemas/json-schema-draft-02/links.json0000644000175000017500000000122011457104136020570 0ustar daviddavid{ "$schema" : "http://json-schema.org/hyper-schema#", "id" : "http://json-schema.org/links#", "type" : "object", "properties" : { "href" : { "type" : "string" }, "rel" : { "type" : "string" }, "targetSchema" : {"$ref" : "http://json-schema.org/hyper-schema#"}, "method" : { "type" : "string", "default" : "GET", "optional" : true }, "enctype" : { "type" : "string", "requires" : "method", "optional" : true }, "properties" : { "type" : "object", "additionalProperties" : {"$ref" : "http://json-schema.org/hyper-schema#"}, "optional" : true } } }package/schemas/json-schema-draft-02/hyper-schema.json0000644000175000017500000000221611457104136022043 0ustar daviddavid{ "$schema" : "http://json-schema.org/hyper-schema#", "id" : "http://json-schema.org/hyper-schema#", "properties" : { "links" : { "type" : "array", "items" : {"$ref" : "http://json-schema.org/links#"}, "optional" : true }, "fragmentResolution" : { "type" : "string", "optional" : true, "default" : "slash-delimited" }, "root" : { "type" : "boolean", "optional" : true, "default" : false }, "readonly" : { "type" : "boolean", "optional" : true, "default" : false }, "pathStart" : { "type" : "string", "optional" : true, "format" : "uri" }, "mediaType" : { "type" : "string", "optional" : true, "format" : "media-type" }, "alternate" : { "type" : "array", "items" : {"$ref" : "#"}, "optional" : true } }, "links" : [ { "href" : "{$ref}", "rel" : "full" }, { "href" : "{$schema}", "rel" : "describedby" }, { "href" : "{id}", "rel" : "self" } ], "fragmentResolution" : "slash-delimited", "extends" : {"$ref" : "http://json-schema.org/schema#"} }package/schemas/json-schema-draft-02/empty-schema.json0000644000175000017500000000000211457104136022041 0ustar daviddavid{}package/schemas/json-schema-draft-03/0000755000175000017500000000000011727717452016576 5ustar daviddavidpackage/schemas/json-schema-draft-03/schema.json0000644000175000017500000000544611504242545020727 0ustar daviddavid{ "$schema" : "http://json-schema.org/draft-03/schema#", "id" : "http://json-schema.org/draft-03/schema#", "type" : "object", "properties" : { "type" : { "type" : ["string", "array"], "items" : { "type" : ["string", {"$ref" : "#"}] }, "uniqueItems" : true, "default" : "any" }, "properties" : { "type" : "object", "additionalProperties" : {"$ref" : "#"}, "default" : {} }, "patternProperties" : { "type" : "object", "additionalProperties" : {"$ref" : "#"}, "default" : {} }, "additionalProperties" : { "type" : [{"$ref" : "#"}, "boolean"], "default" : {} }, "items" : { "type" : [{"$ref" : "#"}, "array"], "items" : {"$ref" : "#"}, "default" : {} }, "additionalItems" : { "type" : [{"$ref" : "#"}, "boolean"], "default" : {} }, "required" : { "type" : "boolean", "default" : false }, "dependencies" : { "type" : "object", "additionalProperties" : { "type" : ["string", "array", {"$ref" : "#"}], "items" : { "type" : "string" } }, "default" : {} }, "minimum" : { "type" : "number" }, "maximum" : { "type" : "number" }, "exclusiveMinimum" : { "type" : "boolean", "default" : false }, "exclusiveMaximum" : { "type" : "boolean", "default" : false }, "minItems" : { "type" : "integer", "minimum" : 0, "default" : 0 }, "maxItems" : { "type" : "integer", "minimum" : 0 }, "uniqueItems" : { "type" : "boolean", "default" : false }, "pattern" : { "type" : "string", "format" : "regex" }, "minLength" : { "type" : "integer", "minimum" : 0, "default" : 0 }, "maxLength" : { "type" : "integer" }, "enum" : { "type" : "array", "minItems" : 1, "uniqueItems" : true }, "default" : { "type" : "any" }, "title" : { "type" : "string" }, "description" : { "type" : "string" }, "format" : { "type" : "string" }, "divisibleBy" : { "type" : "number", "minimum" : 0, "exclusiveMinimum" : true }, "disallow" : { "type" : ["string", "array"], "items" : { "type" : ["string", {"$ref" : "#"}] }, "uniqueItems" : true }, "extends" : { "type" : [{"$ref" : "#"}, "array"], "items" : {"$ref" : "#"}, "default" : {} }, "id" : { "type" : "string", "format" : "uri" }, "$ref" : { "type" : "string", "format" : "uri" }, "$schema" : { "type" : "string", "format" : "uri" } }, "dependencies" : { "exclusiveMinimum" : "minimum", "exclusiveMaximum" : "maximum" }, "default" : {} }package/schemas/json-schema-draft-03/json-ref.json0000644000175000017500000000067511504242545021211 0ustar daviddavid{ "$schema" : "http://json-schema.org/draft-03/hyper-schema#", "id" : "http://json-schema.org/draft-03/json-ref#", "additionalItems" : {"$ref" : "#"}, "additionalProperties" : {"$ref" : "#"}, "links" : [ { "href" : "{id}", "rel" : "self" }, { "href" : "{$ref}", "rel" : "full" }, { "href" : "{$schema}", "rel" : "describedby" } ], "fragmentResolution" : "dot-delimited" }package/schemas/json-schema-draft-03/links.json0000644000175000017500000000122111504242545020572 0ustar daviddavid{ "$schema" : "http://json-schema.org/draft-03/hyper-schema#", "id" : "http://json-schema.org/draft-03/links#", "type" : "object", "properties" : { "href" : { "type" : "string", "required" : true, "format" : "link-description-object-template" }, "rel" : { "type" : "string", "required" : true }, "targetSchema" : {"$ref" : "http://json-schema.org/draft-03/hyper-schema#"}, "method" : { "type" : "string", "default" : "GET" }, "enctype" : { "type" : "string", "requires" : "method" }, "schema" : {"$ref" : "http://json-schema.org/draft-03/hyper-schema#"} } }package/schemas/json-schema-draft-03/hyper-schema.json0000644000175000017500000000177211573505350022054 0ustar daviddavid{ "$schema" : "http://json-schema.org/draft-03/hyper-schema#", "extends" : {"$ref" : "http://json-schema.org/draft-03/schema#"}, "id" : "http://json-schema.org/draft-03/hyper-schema#", "properties" : { "links" : { "type" : "array", "items" : {"$ref" : "http://json-schema.org/draft-03/links#"} }, "fragmentResolution" : { "type" : "string", "default" : "slash-delimited" }, "root" : { "type" : "boolean", "default" : false }, "readonly" : { "type" : "boolean", "default" : false }, "contentEncoding" : { "type" : "string" }, "pathStart" : { "type" : "string", "format" : "uri" }, "mediaType" : { "type" : "string", "format" : "media-type" } }, "links" : [ { "href" : "{id}", "rel" : "self" }, { "href" : "{$ref}", "rel" : "full" }, { "href" : "{$schema}", "rel" : "describedby" } ], "fragmentResolution" : "slash-delimited" }package/tests/0000755000175000017500000000000011727717452012570 5ustar daviddavidpackage/tests/tests3b.js0000644000175000017500000002202411547163310014501 0ustar daviddavidvar env; //calls ok(true) if no error is thrown function okNoError(func, msg) { try { func(); ok(true, msg); } catch (e) { ok(false, msg + ': ' + e); } } function setupEnv () { env = env || require('../lib/jsv').JSV.createEnvironment("json-schema-draft-03"); // AddressBook example from http://relaxng.org/compact-tutorial-20030326.html env.createSchema({ "type": "object", "id": "http://example.com/addressbook.json", "description": "AddressBook example from http://relaxng.org/compact-tutorial-20030326.html", "properties": { "cards": { "type": "array", "items": { "type": "array", "items": { "type": "string" }, "minItems": 2, "maxItems": 2, "$schema":"http://json-schema.org/draft-03/schema#" }, "required": true } }, "$schema":"http://json-schema.org/draft-03/schema#" }, undefined, "http://example.com/addressbook.json"); // The referral target schema, with a canonical id. env.createSchema({ "type": "array", "id": "http://example.com/subdir/card.json", "description": "Referral target", "items": { "type": "string" }, "minItems": 2, "maxItems": 2, "$schema":"http://json-schema.org/draft-03/schema#" }, undefined, "http://example.com/subdir/card.json"); // Similar example, using $ref to factor part of the schema. env.createSchema({ "type": "object", "id": "http://example.com/addressbook_ref.json", "description": "Similar example, using $ref to factor part of the schema.", "properties": { "cards": { "type": "array", "items": { "$ref": "./subdir/card.json" }, "required": true } }, "$schema":"http://json-schema.org/draft-03/schema#" }, undefined, "http://example.com/addressbook_ref.json"); // Similar example, using extends to factor part of the schema. env.createSchema({ "type": "object", "id": "http://example.com/addressbook_extends.json", "description": "Similar example, using extends to factor part of the schema.", "properties": { "cards": { "type": "array", "items": { "extends": { "$ref": "./subdir/card.json" } }, "required": true } }, "$schema":"http://json-schema.org/draft-03/schema#" }, undefined, "http://example.com/addressbook_extends.json"); } // // // Tests // // module("Reference Tests"); test("Self Identifying Schemas", function () { setupEnv(); // While createSchema takes a URI argument, for V3 schemas the validator should be able // to use the canonical id field to find it, if it has been registered. // http://tools.ietf.org/html/draft-zyp-json-schema-03#section-5.27 env.createSchema({ "id": "http://example.com/foo.json", "$schema":"http://json-schema.org/draft-03/schema#" }); ok(env.findSchema("http://example.com/foo.json"), "found schema by id"); }); test("Forward Referral", function () { setupEnv(); // A schema with a reference in it should be accepted, even if the destination schema // hasn't been registered yet. okNoError(function () { env.createSchema({ "type": "object", "id": "http://example.com/addressbook.json", "properties": { "cards": { "type": "array", "items": { "$ref": "http://example.com/subdir/card.json" }, "required": true } }, "$schema":"http://json-schema.org/draft-03/schema#" }); }, "Can make schema with forward reference"); }); test("Validate Schema", function () { setupEnv(); var jsonschema = env.findSchema(env.getOption("latestJSONSchemaSchemaURI")); var explicit_schema = env.findSchema("http://example.com/addressbook.json"); var referring_schema = env.findSchema("http://example.com/addressbook_ref.json"); var card_schema = env.findSchema("http://example.com/subdir/card.json"); ok(explicit_schema, "explicit addressbook schema"); ok(referring_schema, "referring addressbook schema"); ok(card_schema, "card schema"); equal(jsonschema.validate(explicit_schema).errors.length, 0, 'valid explicit schema'); equal(jsonschema.validate(referring_schema).errors.length, 0, 'valid referring schema'); equal(jsonschema.validate(card_schema).errors.length, 0, 'valid referral target schema'); }); test("Simple Referral", function () { setupEnv(); var schema = { "$ref": "http://example.com/subdir/card.json" }; notEqual(env.validate({}, schema).errors.length, 0, "card must be array"); notEqual(env.validate([], schema).errors.length, 0, "card must have fields"); notEqual(env.validate(["foo"], schema).errors.length, 0, "card must have two fields"); notEqual(env.validate(["foo", {}], schema).errors.length, 0, "both fields must be string"); notEqual(env.validate(["foo", "bar", "baz"], schema).errors.length, 0, "card maxItems 2"); equal(env.validate(["foo", "bar"], schema).errors.length, 0, "card maxItems 2"); }); function validateAddressbook (test_name, schema_uri) { var schema = { "$ref": schema_uri }; test(test_name, function () { setupEnv(); notEqual(env.validate('', schema).errors.length, 0, "addressbook is object"); notEqual(env.validate({}, schema).errors.length, 0, "cards required"); notEqual(env.validate({ "cards": {}}, schema).errors.length, 0, "cards must be array"); equal(env.validate({ "cards": []}, schema).errors.length, 0, "empty array ok"); notEqual(env.validate({ "cards": [ {} ] }, schema).errors.length, 0, "cards schema is enforced on items"); notEqual(env.validate({ "cards": [['foo']]}, schema).errors.length, 0, "each card must have length 2"); notEqual(env.validate({ "cards": [ ['foo', 'bar' ], ["foo" ] ] }, schema).errors.length, 0, "second card is bad"); equal(env.validate({ "cards": [ ["foo", "bar" ] ] }, schema).errors.length, 0, "good addressbook with one card"); equal(env.validate({ "cards": [ ["foo", "bar" ], ["bar", "foo" ] ] }, schema).errors.length, 0, "good addressbook with two cards"); }); } validateAddressbook("Explicit Schema", "http://example.com/addressbook.json"); validateAddressbook("Referring Schema", "http://example.com/addressbook_ref.json"); validateAddressbook("Extends Schema", "http://example.com/addressbook_extends.json"); package/tests/qunit.js0000644000175000017500000007051111457104136014257 0ustar daviddavid/* * QUnit - A JavaScript Unit Testing Framework * * http://docs.jquery.com/QUnit * * Copyright (c) 2009 John Resig, Jörn Zaefferer * Dual licensed under the MIT (MIT-LICENSE.txt) * and GPL (GPL-LICENSE.txt) licenses. */ (function(window) { var QUnit = { // Initialize the configuration options init: function() { config = { stats: { all: 0, bad: 0 }, moduleStats: { all: 0, bad: 0 }, started: +new Date, blocking: false, autorun: false, assertions: [], filters: [], queue: [] }; var tests = id("qunit-tests"), banner = id("qunit-banner"), result = id("qunit-testresult"); if ( tests ) { tests.innerHTML = ""; } if ( banner ) { banner.className = ""; } if ( result ) { result.parentNode.removeChild( result ); } }, // call on start of module test to prepend name to all tests module: function(name, testEnvironment) { config.currentModule = name; synchronize(function() { if ( config.currentModule ) { QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all ); } config.currentModule = name; config.moduleTestEnvironment = testEnvironment; config.moduleStats = { all: 0, bad: 0 }; QUnit.moduleStart( name, testEnvironment ); }); }, asyncTest: function(testName, expected, callback) { if ( arguments.length === 2 ) { callback = expected; expected = 0; } QUnit.test(testName, expected, callback, true); }, test: function(testName, expected, callback, async) { var name = testName, testEnvironment, testEnvironmentArg; if ( arguments.length === 2 ) { callback = expected; expected = null; } // is 2nd argument a testEnvironment? if ( expected && typeof expected === 'object') { testEnvironmentArg = expected; expected = null; } if ( config.currentModule ) { name = config.currentModule + " module: " + name; } if ( !validTest(name) ) { return; } synchronize(function() { QUnit.testStart( testName ); testEnvironment = extend({ setup: function() {}, teardown: function() {} }, config.moduleTestEnvironment); if (testEnvironmentArg) { extend(testEnvironment,testEnvironmentArg); } // allow utility functions to access the current test environment QUnit.current_testEnvironment = testEnvironment; config.assertions = []; config.expected = expected; try { if ( !config.pollution ) { saveGlobal(); } testEnvironment.setup.call(testEnvironment); } catch(e) { QUnit.ok( false, "Setup failed on " + name + ": " + e.message ); } if ( async ) { QUnit.stop(); } try { callback.call(testEnvironment); } catch(e) { fail("Test " + name + " died, exception and test follows", e, callback); QUnit.ok( false, "Died on test #" + (config.assertions.length + 1) + ": " + e.message ); // else next test will carry the responsibility saveGlobal(); // Restart the tests if they're blocking if ( config.blocking ) { start(); } } }); synchronize(function() { try { checkPollution(); testEnvironment.teardown.call(testEnvironment); } catch(e) { QUnit.ok( false, "Teardown failed on " + name + ": " + e.message ); } try { QUnit.reset(); } catch(e) { fail("reset() failed, following Test " + name + ", exception and reset fn follows", e, reset); } if ( config.expected && config.expected != config.assertions.length ) { QUnit.ok( false, "Expected " + config.expected + " assertions, but " + config.assertions.length + " were run" ); } var good = 0, bad = 0, tests = id("qunit-tests"); config.stats.all += config.assertions.length; config.moduleStats.all += config.assertions.length; if ( tests ) { var ol = document.createElement("ol"); ol.style.display = "none"; for ( var i = 0; i < config.assertions.length; i++ ) { var assertion = config.assertions[i]; var li = document.createElement("li"); li.className = assertion.result ? "pass" : "fail"; li.appendChild(document.createTextNode(assertion.message || "(no message)")); ol.appendChild( li ); if ( assertion.result ) { good++; } else { bad++; config.stats.bad++; config.moduleStats.bad++; } } var b = document.createElement("strong"); b.innerHTML = name + " (" + bad + ", " + good + ", " + config.assertions.length + ")"; addEvent(b, "click", function() { var next = b.nextSibling, display = next.style.display; next.style.display = display === "none" ? "block" : "none"; }); addEvent(b, "dblclick", function(e) { var target = e && e.target ? e.target : window.event.srcElement; if ( target.nodeName.toLowerCase() === "strong" ) { var text = "", node = target.firstChild; while ( node.nodeType === 3 ) { text += node.nodeValue; node = node.nextSibling; } text = text.replace(/(^\s*|\s*$)/g, ""); if ( window.location ) { window.location.href = window.location.href.match(/^(.+?)(\?.*)?$/)[1] + "?" + encodeURIComponent(text); } } }); var li = document.createElement("li"); li.className = bad ? "fail" : "pass"; li.appendChild( b ); li.appendChild( ol ); tests.appendChild( li ); if ( bad ) { var toolbar = id("qunit-testrunner-toolbar"); if ( toolbar ) { toolbar.style.display = "block"; id("qunit-filter-pass").disabled = null; id("qunit-filter-missing").disabled = null; } } } else { for ( var i = 0; i < config.assertions.length; i++ ) { if ( !config.assertions[i].result ) { bad++; config.stats.bad++; config.moduleStats.bad++; } } } QUnit.testDone( testName, bad, config.assertions.length ); if ( !window.setTimeout && !config.queue.length ) { done(); } }); if ( window.setTimeout && !config.doneTimer ) { config.doneTimer = window.setTimeout(function(){ if ( !config.queue.length ) { done(); } else { synchronize( done ); } }, 13); } }, /** * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through. */ expect: function(asserts) { config.expected = asserts; }, /** * Asserts true. * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" ); */ ok: function(a, msg) { QUnit.log(a, msg); config.assertions.push({ result: !!a, message: msg }); }, /** * Checks that the first two arguments are equal, with an optional message. * Prints out both actual and expected values. * * Prefered to ok( actual == expected, message ) * * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." ); * * @param Object actual * @param Object expected * @param String message (optional) */ equal: function(actual, expected, message) { push(expected == actual, actual, expected, message); }, notEqual: function(actual, expected, message) { push(expected != actual, actual, expected, message); }, deepEqual: function(a, b, message) { push(QUnit.equiv(a, b), a, b, message); }, notDeepEqual: function(a, b, message) { push(!QUnit.equiv(a, b), a, b, message); }, strictEqual: function(actual, expected, message) { push(expected === actual, actual, expected, message); }, notStrictEqual: function(actual, expected, message) { push(expected !== actual, actual, expected, message); }, start: function() { // A slight delay, to avoid any current callbacks if ( window.setTimeout ) { window.setTimeout(function() { if ( config.timeout ) { clearTimeout(config.timeout); } config.blocking = false; process(); }, 13); } else { config.blocking = false; process(); } }, stop: function(timeout) { config.blocking = true; if ( timeout && window.setTimeout ) { config.timeout = window.setTimeout(function() { QUnit.ok( false, "Test timed out" ); QUnit.start(); }, timeout); } }, /** * Resets the test setup. Useful for tests that modify the DOM. */ reset: function() { if ( window.jQuery ) { jQuery("#main").html( config.fixture ); jQuery.event.global = {}; jQuery.ajaxSettings = extend({}, config.ajaxSettings); } }, /** * Trigger an event on an element. * * @example triggerEvent( document.body, "click" ); * * @param DOMElement elem * @param String type */ triggerEvent: function( elem, type, event ) { if ( document.createEvent ) { event = document.createEvent("MouseEvents"); event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView, 0, 0, 0, 0, 0, false, false, false, false, 0, null); elem.dispatchEvent( event ); } else if ( elem.fireEvent ) { elem.fireEvent("on"+type); } }, // Safe object type checking is: function( type, obj ) { return Object.prototype.toString.call( obj ) === "[object "+ type +"]"; }, // Logging callbacks done: function(failures, total) {}, log: function(result, message) {}, testStart: function(name) {}, testDone: function(name, failures, total) {}, moduleStart: function(name, testEnvironment) {}, moduleDone: function(name, failures, total) {} }; // Backwards compatibility, deprecated QUnit.equals = QUnit.equal; QUnit.same = QUnit.deepEqual; // Maintain internal state var config = { // The queue of tests to run queue: [], // block until document ready blocking: true }; // Load paramaters (function() { var location = window.location || { search: "", protocol: "file:" }, GETParams = location.search.slice(1).split('&'); for ( var i = 0; i < GETParams.length; i++ ) { GETParams[i] = decodeURIComponent( GETParams[i] ); if ( GETParams[i] === "noglobals" ) { GETParams.splice( i, 1 ); i--; config.noglobals = true; } else if ( GETParams[i].search('=') > -1 ) { GETParams.splice( i, 1 ); i--; } } // restrict modules/tests by get parameters config.filters = GETParams; // Figure out if we're running the tests from a server or not QUnit.isLocal = !!(location.protocol === 'file:'); })(); // Expose the API as global variables, unless an 'exports' // object exists, in that case we assume we're in CommonJS if ( typeof exports === "undefined" || typeof require === "undefined" ) { extend(window, QUnit); window.QUnit = QUnit; } else { extend(exports, QUnit); exports.QUnit = QUnit; } if ( typeof document === "undefined" || document.readyState === "complete" ) { config.autorun = true; } addEvent(window, "load", function() { // Initialize the config, saving the execution queue var oldconfig = extend({}, config); QUnit.init(); extend(config, oldconfig); config.blocking = false; var userAgent = id("qunit-userAgent"); if ( userAgent ) { userAgent.innerHTML = navigator.userAgent; } var toolbar = id("qunit-testrunner-toolbar"); if ( toolbar ) { toolbar.style.display = "none"; var filter = document.createElement("input"); filter.type = "checkbox"; filter.id = "qunit-filter-pass"; filter.disabled = true; addEvent( filter, "click", function() { var li = document.getElementsByTagName("li"); for ( var i = 0; i < li.length; i++ ) { if ( li[i].className.indexOf("pass") > -1 ) { li[i].style.display = filter.checked ? "none" : ""; } } }); toolbar.appendChild( filter ); var label = document.createElement("label"); label.setAttribute("for", "qunit-filter-pass"); label.innerHTML = "Hide passed tests"; toolbar.appendChild( label ); var missing = document.createElement("input"); missing.type = "checkbox"; missing.id = "qunit-filter-missing"; missing.disabled = true; addEvent( missing, "click", function() { var li = document.getElementsByTagName("li"); for ( var i = 0; i < li.length; i++ ) { if ( li[i].className.indexOf("fail") > -1 && li[i].innerHTML.indexOf('missing test - untested code is broken code') > - 1 ) { li[i].parentNode.parentNode.style.display = missing.checked ? "none" : "block"; } } }); toolbar.appendChild( missing ); label = document.createElement("label"); label.setAttribute("for", "qunit-filter-missing"); label.innerHTML = "Hide missing tests (untested code is broken code)"; toolbar.appendChild( label ); } var main = id('main'); if ( main ) { config.fixture = main.innerHTML; } if ( window.jQuery ) { config.ajaxSettings = window.jQuery.ajaxSettings; } QUnit.start(); }); function done() { if ( config.doneTimer && window.clearTimeout ) { window.clearTimeout( config.doneTimer ); config.doneTimer = null; } if ( config.queue.length ) { config.doneTimer = window.setTimeout(function(){ if ( !config.queue.length ) { done(); } else { synchronize( done ); } }, 13); return; } config.autorun = true; // Log the last module results if ( config.currentModule ) { QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all ); } var banner = id("qunit-banner"), tests = id("qunit-tests"), html = ['Tests completed in ', +new Date - config.started, ' milliseconds.
', '', config.stats.all - config.stats.bad, ' tests of ', config.stats.all, ' passed, ', config.stats.bad,' failed.'].join(''); if ( banner ) { banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass"); } if ( tests ) { var result = id("qunit-testresult"); if ( !result ) { result = document.createElement("p"); result.id = "qunit-testresult"; result.className = "result"; tests.parentNode.insertBefore( result, tests.nextSibling ); } result.innerHTML = html; } QUnit.done( config.stats.bad, config.stats.all ); } function validTest( name ) { var i = config.filters.length, run = false; if ( !i ) { return true; } while ( i-- ) { var filter = config.filters[i], not = filter.charAt(0) == '!'; if ( not ) { filter = filter.slice(1); } if ( name.indexOf(filter) !== -1 ) { return !not; } if ( not ) { run = true; } } return run; } function push(result, actual, expected, message) { message = message || (result ? "okay" : "failed"); QUnit.ok( result, result ? message + ": " + expected : message + ", expected: " + QUnit.jsDump.parse(expected) + " result: " + QUnit.jsDump.parse(actual) ); } function synchronize( callback ) { config.queue.push( callback ); if ( config.autorun && !config.blocking ) { process(); } } function process() { while ( config.queue.length && !config.blocking ) { config.queue.shift()(); } } function saveGlobal() { config.pollution = []; if ( config.noglobals ) { for ( var key in window ) { config.pollution.push( key ); } } } function checkPollution( name ) { var old = config.pollution; saveGlobal(); var newGlobals = diff( old, config.pollution ); if ( newGlobals.length > 0 ) { ok( false, "Introduced global variable(s): " + newGlobals.join(", ") ); config.expected++; } var deletedGlobals = diff( config.pollution, old ); if ( deletedGlobals.length > 0 ) { ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") ); config.expected++; } } // returns a new Array with the elements that are in a but not in b function diff( a, b ) { var result = a.slice(); for ( var i = 0; i < result.length; i++ ) { for ( var j = 0; j < b.length; j++ ) { if ( result[i] === b[j] ) { result.splice(i, 1); i--; break; } } } return result; } function fail(message, exception, callback) { if ( typeof console !== "undefined" && console.error && console.warn ) { console.error(message); console.error(exception); console.warn(callback.toString()); } else if ( window.opera && opera.postError ) { opera.postError(message, exception, callback.toString); } } function extend(a, b) { for ( var prop in b ) { a[prop] = b[prop]; } return a; } function addEvent(elem, type, fn) { if ( elem.addEventListener ) { elem.addEventListener( type, fn, false ); } else if ( elem.attachEvent ) { elem.attachEvent( "on" + type, fn ); } else { fn(); } } function id(name) { return !!(typeof document !== "undefined" && document && document.getElementById) && document.getElementById( name ); } // Test for equality any JavaScript type. // Discussions and reference: http://philrathe.com/articles/equiv // Test suites: http://philrathe.com/tests/equiv // Author: Philippe Rathé QUnit.equiv = function () { var innerEquiv; // the real equiv function var callers = []; // stack to decide between skip/abort functions // Determine what is o. function hoozit(o) { if (QUnit.is("String", o)) { return "string"; } else if (QUnit.is("Boolean", o)) { return "boolean"; } else if (QUnit.is("Number", o)) { if (isNaN(o)) { return "nan"; } else { return "number"; } } else if (typeof o === "undefined") { return "undefined"; // consider: typeof null === object } else if (o === null) { return "null"; // consider: typeof [] === object } else if (QUnit.is( "Array", o)) { return "array"; // consider: typeof new Date() === object } else if (QUnit.is( "Date", o)) { return "date"; // consider: /./ instanceof Object; // /./ instanceof RegExp; // typeof /./ === "function"; // => false in IE and Opera, // true in FF and Safari } else if (QUnit.is( "RegExp", o)) { return "regexp"; } else if (typeof o === "object") { return "object"; } else if (QUnit.is( "Function", o)) { return "function"; } else { return undefined; } } // Call the o related callback with the given arguments. function bindCallbacks(o, callbacks, args) { var prop = hoozit(o); if (prop) { if (hoozit(callbacks[prop]) === "function") { return callbacks[prop].apply(callbacks, args); } else { return callbacks[prop]; // or undefined } } } var callbacks = function () { // for string, boolean, number and null function useStrictEquality(b, a) { if (b instanceof a.constructor || a instanceof b.constructor) { // to catch short annotaion VS 'new' annotation of a declaration // e.g. var i = 1; // var j = new Number(1); return a == b; } else { return a === b; } } return { "string": useStrictEquality, "boolean": useStrictEquality, "number": useStrictEquality, "null": useStrictEquality, "undefined": useStrictEquality, "nan": function (b) { return isNaN(b); }, "date": function (b, a) { return hoozit(b) === "date" && a.valueOf() === b.valueOf(); }, "regexp": function (b, a) { return hoozit(b) === "regexp" && a.source === b.source && // the regex itself a.global === b.global && // and its modifers (gmi) ... a.ignoreCase === b.ignoreCase && a.multiline === b.multiline; }, // - skip when the property is a method of an instance (OOP) // - abort otherwise, // initial === would have catch identical references anyway "function": function () { var caller = callers[callers.length - 1]; return caller !== Object && typeof caller !== "undefined"; }, "array": function (b, a) { var i; var len; // b could be an object literal here if ( ! (hoozit(b) === "array")) { return false; } len = a.length; if (len !== b.length) { // safe and faster return false; } for (i = 0; i < len; i++) { if ( ! innerEquiv(a[i], b[i])) { return false; } } return true; }, "object": function (b, a) { var i; var eq = true; // unless we can proove it var aProperties = [], bProperties = []; // collection of strings // comparing constructors is more strict than using instanceof if ( a.constructor !== b.constructor) { return false; } // stack constructor before traversing properties callers.push(a.constructor); for (i in a) { // be strict: don't ensures hasOwnProperty and go deep aProperties.push(i); // collect a's properties if ( ! innerEquiv(a[i], b[i])) { eq = false; } } callers.pop(); // unstack, we are done for (i in b) { bProperties.push(i); // collect b's properties } // Ensures identical properties name return eq && innerEquiv(aProperties.sort(), bProperties.sort()); } }; }(); innerEquiv = function () { // can take multiple arguments var args = Array.prototype.slice.apply(arguments); if (args.length < 2) { return true; // end transition } return (function (a, b) { if (a === b) { return true; // catch the most you can } else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || hoozit(a) !== hoozit(b)) { return false; // don't lose time with error prone cases } else { return bindCallbacks(a, callbacks, [b, a]); } // apply transition with (1..n) arguments })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1)); }; return innerEquiv; }(); /** * jsDump * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php) * Date: 5/15/2008 * @projectDescription Advanced and extensible data dumping for Javascript. * @version 1.0.0 * @author Ariel Flesler * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html} */ QUnit.jsDump = (function() { function quote( str ) { return '"' + str.toString().replace(/"/g, '\\"') + '"'; }; function literal( o ) { return o + ''; }; function join( pre, arr, post ) { var s = jsDump.separator(), base = jsDump.indent(), inner = jsDump.indent(1); if ( arr.join ) arr = arr.join( ',' + s + inner ); if ( !arr ) return pre + post; return [ pre, inner + arr, base + post ].join(s); }; function array( arr ) { var i = arr.length, ret = Array(i); this.up(); while ( i-- ) ret[i] = this.parse( arr[i] ); this.down(); return join( '[', ret, ']' ); }; var reName = /^function (\w+)/; var jsDump = { parse:function( obj, type ) { //type is used mostly internally, you can fix a (custom)type in advance var parser = this.parsers[ type || this.typeOf(obj) ]; type = typeof parser; return type == 'function' ? parser.call( this, obj ) : type == 'string' ? parser : this.parsers.error; }, typeOf:function( obj ) { var type; if ( obj === null ) { type = "null"; } else if (typeof obj === "undefined") { type = "undefined"; } else if (QUnit.is("RegExp", obj)) { type = "regexp"; } else if (QUnit.is("Date", obj)) { type = "date"; } else if (QUnit.is("Function", obj)) { type = "function"; } else if (QUnit.is("Array", obj)) { type = "array"; } else if (QUnit.is("Window", obj) || QUnit.is("global", obj)) { type = "window"; } else if (QUnit.is("HTMLDocument", obj)) { type = "document"; } else if (QUnit.is("HTMLCollection", obj) || QUnit.is("NodeList", obj)) { type = "nodelist"; } else if (/^\[object HTML/.test(Object.prototype.toString.call( obj ))) { type = "node"; } else { type = typeof obj; } return type; }, separator:function() { return this.multiline ? this.HTML ? '
' : '\n' : this.HTML ? ' ' : ' '; }, indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing if ( !this.multiline ) return ''; var chr = this.indentChar; if ( this.HTML ) chr = chr.replace(/\t/g,' ').replace(/ /g,' '); return Array( this._depth_ + (extra||0) ).join(chr); }, up:function( a ) { this._depth_ += a || 1; }, down:function( a ) { this._depth_ -= a || 1; }, setParser:function( name, parser ) { this.parsers[name] = parser; }, // The next 3 are exposed so you can use them quote:quote, literal:literal, join:join, // _depth_: 1, // This is the list of parsers, to modify them, use jsDump.setParser parsers:{ window: '[Window]', document: '[Document]', error:'[ERROR]', //when no parser is found, shouldn't happen unknown: '[Unknown]', 'null':'null', undefined:'undefined', 'function':function( fn ) { var ret = 'function', name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE if ( name ) ret += ' ' + name; ret += '('; ret = [ ret, this.parse( fn, 'functionArgs' ), '){'].join(''); return join( ret, this.parse(fn,'functionCode'), '}' ); }, array: array, nodelist: array, arguments: array, object:function( map ) { var ret = [ ]; this.up(); for ( var key in map ) ret.push( this.parse(key,'key') + ': ' + this.parse(map[key]) ); this.down(); return join( '{', ret, '}' ); }, node:function( node ) { var open = this.HTML ? '<' : '<', close = this.HTML ? '>' : '>'; var tag = node.nodeName.toLowerCase(), ret = open + tag; for ( var a in this.DOMAttrs ) { var val = node[this.DOMAttrs[a]]; if ( val ) ret += ' ' + a + '=' + this.parse( val, 'attribute' ); } return ret + close + open + '/' + tag + close; }, functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function var l = fn.length; if ( !l ) return ''; var args = Array(l); while ( l-- ) args[l] = String.fromCharCode(97+l);//97 is 'a' return ' ' + args.join(', ') + ' '; }, key:quote, //object calls it internally, the key part of an item in a map functionCode:'[code]', //function calls it internally, it's the content of the function attribute:quote, //node calls it internally, it's an html attribute value string:quote, date:quote, regexp:literal, //regex number:literal, 'boolean':literal }, DOMAttrs:{//attributes to dump from nodes, name=>realName id:'id', name:'name', 'class':'className' }, HTML:true,//if true, entities are escaped ( <, >, \t, space and \n ) indentChar:' ',//indentation unit multiline:true //if true, items in a collection, are separated by a \n, else just a space. }; return jsDump; })(); })(this);package/tests/qunit.css0000644000175000017500000000577111457104136014441 0ustar daviddavidol#qunit-tests { font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial; margin:0; padding:0; list-style-position:inside; font-size: smaller; } ol#qunit-tests li{ padding:0.4em 0.5em 0.4em 2.5em; border-bottom:1px solid #fff; font-size:small; list-style-position:inside; } ol#qunit-tests li ol{ box-shadow: inset 0px 2px 13px #999; -moz-box-shadow: inset 0px 2px 13px #999; -webkit-box-shadow: inset 0px 2px 13px #999; margin-top:0.5em; margin-left:0; padding:0.5em; background-color:#fff; border-radius:15px; -moz-border-radius: 15px; -webkit-border-radius: 15px; } ol#qunit-tests li li{ border-bottom:none; margin:0.5em; background-color:#fff; list-style-position: inside; padding:0.4em 0.5em 0.4em 0.5em; } ol#qunit-tests li li.pass{ border-left:26px solid #C6E746; background-color:#fff; color:#5E740B; } ol#qunit-tests li li.fail{ border-left:26px solid #EE5757; background-color:#fff; color:#710909; } ol#qunit-tests li.pass{ background-color:#D2E0E6; color:#528CE0; } ol#qunit-tests li.fail{ background-color:#EE5757; color:#000; } ol#qunit-tests li strong { cursor:pointer; } h1#qunit-header{ background-color:#0d3349; margin:0; padding:0.5em 0 0.5em 1em; color:#fff; font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial; border-top-right-radius:15px; border-top-left-radius:15px; -moz-border-radius-topright:15px; -moz-border-radius-topleft:15px; -webkit-border-top-right-radius:15px; -webkit-border-top-left-radius:15px; text-shadow: rgba(0, 0, 0, 0.5) 4px 4px 1px; } h2#qunit-banner{ font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial; height:5px; margin:0; padding:0; } h2#qunit-banner.qunit-pass{ background-color:#C6E746; } h2#qunit-banner.qunit-fail, #qunit-testrunner-toolbar { background-color:#EE5757; } #qunit-testrunner-toolbar { font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial; padding:0; /*width:80%;*/ padding:0em 0 0.5em 2em; font-size: small; } h2#qunit-userAgent { font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial; background-color:#2b81af; margin:0; padding:0; color:#fff; font-size: small; padding:0.5em 0 0.5em 2.5em; text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; } p#qunit-testresult{ font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial; margin:0; font-size: small; color:#2b81af; border-bottom-right-radius:15px; border-bottom-left-radius:15px; -moz-border-radius-bottomright:15px; -moz-border-radius-bottomleft:15px; -webkit-border-bottom-right-radius:15px; -webkit-border-bottom-left-radius:15px; background-color:#D2E0E6; padding:0.5em 0.5em 0.5em 2.5em; } strong b.fail{ color:#710909; } strong b.pass{ color:#5E740B; }package/tests/index3b.html0000644000175000017500000000155211727717626015020 0ustar daviddavid

JSON Schema Validator Tests by dougtreder

    package/tests/tests.js0000644000175000017500000005307711512672116014271 0ustar daviddavidvar env, ENVIRONMENTS = [ "json-schema-draft-01", "json-schema-draft-02" ], curEnvId; //calls ok(true) if no error is thrown function okNoError(func, msg) { try { func(); ok(true, msg); } catch (e) { ok(false, msg + ': ' + e); } } //calls ok(true) if an error is thrown function okError(func, msg) { try { func(); ok(false, msg); } catch (e) { ok(true, msg + ': ' + e); } } // // // Tests // // for (curEnvId = 0; curEnvId < ENVIRONMENTS.length; ++curEnvId) { module(ENVIRONMENTS[curEnvId]); (function (id) { test("Acquire Validator", function () { JSV = require('../lib/jsv').JSV; env = null; ok(JSV, "JSV is loaded"); env = JSV.createEnvironment(id); ok(env, id + " environment created"); }); }(ENVIRONMENTS[curEnvId])); test("Primitive Validation", function () { equal(env.validate({}).errors.length, 0, "Object"); equal(env.validate([]).errors.length, 0, "Array"); equal(env.validate('').errors.length, 0, "String"); equal(env.validate(00).errors.length, 0, "Number"); equal(env.validate(false).errors.length, 0, "Boolean"); equal(env.validate(null).errors.length, 0, "Null"); }); test("Type Validation", function () { //simple type equal(env.validate({}, { type : 'object' }).errors.length, 0, "Object"); equal(env.validate([], { type : 'array' }).errors.length, 0, "Array"); equal(env.validate('', { type : 'string' }).errors.length, 0, "String"); equal(env.validate(00, { type : 'number' }).errors.length, 0, "Number"); equal(env.validate(00, { type : 'integer' }).errors.length, 0, "Integer"); equal(env.validate(false, { type : 'boolean' }).errors.length, 0, "Boolean"); equal(env.validate(null, { type : 'null' }).errors.length, 0, "Null"); equal(env.validate(true, { type : 'any' }).errors.length, 0, "Any"); notEqual(env.validate(null, { type : 'object' }).errors.length, 0, "Object"); notEqual(env.validate(null, { type : 'array' }).errors.length, 0, "Array"); notEqual(env.validate(null, { type : 'string' }).errors.length, 0, "String"); notEqual(env.validate(null, { type : 'number' }).errors.length, 0, "Number"); notEqual(env.validate(0.1, { type : 'integer' }).errors.length, 0, "Integer"); notEqual(env.validate(null, { type : 'boolean' }).errors.length, 0, "Boolean"); notEqual(env.validate(false, { type : 'null' }).errors.length, 0, "Null"); //union type equal(env.validate({}, { type : ['null', 'boolean', 'number', 'integer', 'string', 'array', 'object'] }).errors.length, 0, "Object"); notEqual(env.validate({}, { type : ['null', 'boolean', 'number', 'integer', 'string', 'array'] }).errors.length, 0, "Object"); //schema union type equal(env.validate({}, { type : [{ type : 'string' }, { type : 'object' }] }).errors.length, 0, "Object"); equal(env.validate(55, { type : [{ type : 'string' }, { type : 'object' }, 'number'] }).errors.length, 0, "Object"); notEqual(env.validate([], { type : ['string', { type : 'object' }] }).errors.length, 0, "Array"); }); test("Properties Validation", function () { equal(env.validate({}, { type : 'object', properties : {} }).errors.length, 0); equal(env.validate({ a : 1 }, { type : 'object', properties : { a : {}} }).errors.length, 0); equal(env.validate({ a : 1 }, { type : 'object', properties : { a : { type : 'number' }} }).errors.length, 0); equal(env.validate({ a : { b : 'two' } }, { type : 'object', properties : { a : { type : 'object', properties : { b : { type : 'string' } } }} }).errors.length, 0); }); test("Items Validation", function () { equal(env.validate([], { type : 'array', items : { type : 'string' } }).errors.length, 0); equal(env.validate(['foo'], { type : 'array', items : { type : 'string' } }).errors.length, 0); equal(env.validate(['foo', 2], { type : 'array', items : [{ type : 'string' }, { type : 'number' }] }).errors.length, 0); notEqual(env.validate([1], { type : 'array', items : { type : 'string' } }).errors.length, 0); notEqual(env.validate(['foo', 'two'], { type : 'array', items : [{ type : 'string' }, { type : 'number' }] }).errors.length, 0); }); test("Optional Validation", function () { equal(env.validate({}, { properties : { a : { optional : true } } }).errors.length, 0); equal(env.validate({ a : false }, { properties : { a : { optional : true } } }).errors.length, 0); equal(env.validate({ a : false }, { properties : { a : { optional : false } } }).errors.length, 0); notEqual(env.validate({}, { properties : { a : { optional : false } } }).errors.length, 0); notEqual(env.validate({ b : true }, { properties : { a : { optional : false } } }).errors.length, 0); notEqual(env.validate({ b : true }, { properties : { a : {} } }).errors.length, 0); }); test("AdditionalProperties Validation", function () { //object tests equal(env.validate({ a : 1, b : 2, c : 3 }, {}).errors.length, 0); equal(env.validate({ a : 1, b : 2, c : 3 }, { additionalProperties : true }).errors.length, 0); equal(env.validate({ a : 1, b : 2, c : 3 }, { properties : { a : {}, b : {} }, additionalProperties : true }).errors.length, 0); equal(env.validate({ a : 1, b : 2, c : 3 }, { properties : { a : {}, b : {}, c : {} }, additionalProperties : false }).errors.length, 0); equal(env.validate({ a : 1, b : 2, c : 3 }, { additionalProperties : { type : 'number' } }).errors.length, 0); equal(env.validate({ a : 1, b : 2, c : 3 }, { properties : { a : {}, b : {} }, additionalProperties : { type : 'number' } }).errors.length, 0); equal(env.validate({ a : 1, b : 2, c : 3 }, { properties : { a : {}, b : {}, c : {} }, additionalProperties : { type : 'string' } }).errors.length, 0); notEqual(env.validate({ a : 1, b : 2, c : 3 }, { properties : { a : {}, b : {} }, additionalProperties : false }).errors.length, 0); notEqual(env.validate({ a : 1, b : 2, c : 3 }, { properties : { a : {}, b : {} }, additionalProperties : { type : 'string' } }).errors.length, 0); //array tests equal(env.validate([1, 2, 3], {}).errors.length, 0); equal(env.validate([1, 2, 3], { additionalProperties : true }).errors.length, 0); equal(env.validate([1, 2, 3], { additionalProperties : false }).errors.length, 0); equal(env.validate([1, 2, 3], { additionalProperties : { type : 'number' } }).errors.length, 0); equal(env.validate([1, 2, 3], { additionalProperties : { type : 'string' } }).errors.length, 0); equal(env.validate(['1', '2'], { items : { type : 'string' }, additionalProperties : false }).errors.length, 0); equal(env.validate(['1', '2'], { items : [ { type : 'string' }, { type : 'string' } ], additionalProperties : false }).errors.length, 0); equal(env.validate(['1', '2', 3], { items : [ { type : 'string' }, { type : 'string' } ], additionalProperties : { type : 'number' } }).errors.length, 0); equal(env.validate(['1', '2', '3'], { items : [ { type : 'string' }, { type : 'string' }, { type : 'string' } ], additionalProperties : { type : 'number' } }).errors.length, 0); notEqual(env.validate(['1', '2', '3'], { items : [ { type : 'string' }, { type : 'string' } ], additionalProperties : false }).errors.length, 0); notEqual(env.validate(['1', '2', '3'], { items : [ { type : 'string' }, { type : 'string' } ], additionalProperties : { type : 'number' } }).errors.length, 0); }); test("Requires Validation", function () { equal(env.validate({ a : 1 }, { properties : { a : { requires : 'a' } } }).errors.length, 0); equal(env.validate({ a : 1, b : 2 }, { properties : { a : {}, b : { requires : 'a' } } }).errors.length, 0); equal(env.validate({ a : 1, b : 2 }, { properties : { a : { requires : 'b' }, b : { requires : 'a' } } }).errors.length, 0); equal(env.validate({ a : 1, b : 2 }, { properties : { b : { requires : { properties : { a : { type : 'number' } } } } } }).errors.length, 0); notEqual(env.validate({ b : 2 }, { properties : { b : { requires : 'a' } } }).errors.length, 0); notEqual(env.validate({ a : 1, b : 2 }, { properties : { a : { requires : 'b' }, b : { requires : 'c' } } }).errors.length, 0); notEqual(env.validate({ b : 2 }, { properties : { b : { requires : { properties : { b : { type : 'string' } } } } } }).errors.length, 0); }); test("Minimum/Maximum Validation", function () { equal(env.validate(0, {}).errors.length, 0); equal(env.validate(1, { minimum : 1, maximum : 10 }).errors.length, 0); equal(env.validate(5, { minimum : 1, maximum : 10 }).errors.length, 0); equal(env.validate(10, { minimum : 1, maximum : 10 }).errors.length, 0); equal(env.validate(1, { minimum : 1, maximum : 1 }).errors.length, 0); notEqual(env.validate(0, { minimum : 1, maximum : 10 }).errors.length, 0); notEqual(env.validate(11, { minimum : 1, maximum : 10 }).errors.length, 0); }); test("MinimumCanEqual/MaximumCanEqual Validation", function () { //true notEqual(env.validate(0, { minimumCanEqual : true, maximumCanEqual : true }).errors.length, 0); //illegal equal(env.validate(1, { minimum : 1, maximum : 10, minimumCanEqual : true, maximumCanEqual : true }).errors.length, 0); equal(env.validate(5, { minimum : 1, maximum : 10, minimumCanEqual : true, maximumCanEqual : true }).errors.length, 0); equal(env.validate(10, { minimum : 1, maximum : 10, minimumCanEqual : true, maximumCanEqual : true }).errors.length, 0); equal(env.validate(1, { minimum : 1, maximum : 1, minimumCanEqual : true, maximumCanEqual : true }).errors.length, 0); notEqual(env.validate(0, { minimum : 1, maximum : 10, minimumCanEqual : true, maximumCanEqual : true }).errors.length, 0); notEqual(env.validate(11, { minimum : 1, maximum : 10, minimumCanEqual : true, maximumCanEqual : true }).errors.length, 0); //false notEqual(env.validate(0, { minimumCanEqual : false, maximumCanEqual : false }).errors.length, 0); //illegal equal(env.validate(1.0001, { minimum : 1, maximum : 10, minimumCanEqual : false, maximumCanEqual : false }).errors.length, 0); equal(env.validate(5, { minimum : 1, maximum : 10, minimumCanEqual : false, maximumCanEqual : false }).errors.length, 0); equal(env.validate(9.9999, { minimum : 1, maximum : 10, minimumCanEqual : false, maximumCanEqual : false }).errors.length, 0); notEqual(env.validate(1, { minimum : 1, maximum : 10, minimumCanEqual : false, maximumCanEqual : false }).errors.length, 0); notEqual(env.validate(10, { minimum : 1, maximum : 10, minimumCanEqual : false, maximumCanEqual : false }).errors.length, 0); notEqual(env.validate(1, { minimum : 1, maximum : 1, minimumCanEqual : false, maximumCanEqual : false }).errors.length, 0); notEqual(env.validate(0, { minimum : 1, maximum : 10, minimumCanEqual : false, maximumCanEqual : false }).errors.length, 0); notEqual(env.validate(11, { minimum : 1, maximum : 10, minimumCanEqual : false, maximumCanEqual : false }).errors.length, 0); }); test("MinItems/MaxItems Validation", function () { equal(env.validate([], {}).errors.length, 0); equal(env.validate([1], { minItems : 1, maxItems : 1 }).errors.length, 0); equal(env.validate([1], { minItems : 1, maxItems : 3 }).errors.length, 0); equal(env.validate([1, 2], { minItems : 1, maxItems : 3 }).errors.length, 0); equal(env.validate([1, 2, 3], { minItems : 1, maxItems : 3 }).errors.length, 0); notEqual(env.validate([], { minItems : 1, maxItems : 0 }).errors.length, 0); notEqual(env.validate([], { minItems : 1, maxItems : 3 }).errors.length, 0); notEqual(env.validate([1, 2, 3, 4], { minItems : 1, maxItems : 3 }).errors.length, 0); }); if (curEnvId >= 1) { test("UniqueItems Validation", function () { equal(env.validate([], {}).errors.length, 0); equal(env.validate([], { uniqueItems : true }).errors.length, 0); equal(env.validate([null], { uniqueItems : true }).errors.length, 0); equal(env.validate([true, false], { uniqueItems : true }).errors.length, 0); equal(env.validate([1, 2, 3], { uniqueItems : true }).errors.length, 0); equal(env.validate(['a', 'b'], { uniqueItems : true }).errors.length, 0); equal(env.validate([[], []], { uniqueItems : true }).errors.length, 0); equal(env.validate([{}, {}], { uniqueItems : true }).errors.length, 0); notEqual(env.validate([null, null], { uniqueItems : true }).errors.length, 0); notEqual(env.validate([false, false], { uniqueItems : true }).errors.length, 0); notEqual(env.validate([1, 2, 1], { uniqueItems : true }).errors.length, 0); notEqual(env.validate(['a', 'b', 'b'], { uniqueItems : true }).errors.length, 0); }); } test("Pattern Validation", function () { equal(env.validate('', {}).errors.length, 0); equal(env.validate('', { pattern : '^$' }).errors.length, 0); equal(env.validate('today', { pattern : 'day' }).errors.length, 0); notEqual(env.validate('', { pattern : '^ $' }).errors.length, 0); notEqual(env.validate('today', { pattern : 'dam' }).errors.length, 0); notEqual(env.validate('aaaaa', { pattern : 'aa(a' }).errors.length, 0); }); test("MinLength/MaxLength Validation", function () { equal(env.validate('', {}).errors.length, 0); equal(env.validate('1', { minLength : 1, maxLength : 1 }).errors.length, 0); equal(env.validate('1', { minLength : 1, maxLength : 3 }).errors.length, 0); equal(env.validate('12', { minLength : 1, maxLength : 3 }).errors.length, 0); equal(env.validate('123', { minLength : 1, maxLength : 3 }).errors.length, 0); notEqual(env.validate('', { minLength : 1, maxLength : 0 }).errors.length, 0); notEqual(env.validate('', { minLength : 1, maxLength : 3 }).errors.length, 0); notEqual(env.validate('1234', { minLength : 1, maxLength : 3 }).errors.length, 0); }); test("Enum Validation", function () { equal(env.validate(null, {}).errors.length, 0); equal(env.validate(true, { 'enum' : [false, true] }).errors.length, 0); equal(env.validate(2, { 'enum' : [1, 2, 3] }).errors.length, 0); equal(env.validate('a', { 'enum' : ['a'] }).errors.length, 0); equal(env.validate({}, { 'properties' : { 'a' : { 'enum' : ['a'], 'optional' : true } } }).errors.length, 0); notEqual(env.validate(true, { 'enum' : ['false', 'true'] }).errors.length, 0); notEqual(env.validate(4, { 'enum' : [1, 2, 3, '4'] }).errors.length, 0); notEqual(env.validate('', { 'enum' : [] }).errors.length, 0); notEqual(env.validate({}, { 'properties' : { 'a' : { 'enum' : ['a'] } } }).errors.length, 0); }); test("Format Validation", function () { //TODO }); if (curEnvId === 0) { test("MaxDecimal Validation", function () { equal(env.validate(0, {}).errors.length, 0); equal(env.validate(0, { maxDecimal : 0 }).errors.length, 0); equal(env.validate(0, { maxDecimal : 1 }).errors.length, 0); equal(env.validate(0.22, { maxDecimal : 2 }).errors.length, 0); equal(env.validate(0.33, { maxDecimal : 3 }).errors.length, 0); notEqual(env.validate(0.1, { maxDecimal : 0 }).errors.length, 0); notEqual(env.validate(0.111, { maxDecimal : 1 }).errors.length, 0); }); } if (curEnvId >= 1) { test("DivisibleBy Validation", function () { equal(env.validate(0, {}).errors.length, 0); equal(env.validate(0, { divisibleBy : 1 }).errors.length, 0); equal(env.validate(10, { divisibleBy : 5 }).errors.length, 0); equal(env.validate(10, { divisibleBy : 10 }).errors.length, 0); equal(env.validate(0, { divisibleBy : 2.5 }).errors.length, 0); equal(env.validate(5, { divisibleBy : 2.5 }).errors.length, 0); equal(env.validate(7.5, { divisibleBy : 2.5 }).errors.length, 0); notEqual(env.validate(0, { divisibleBy : 0 }).errors.length, 0); notEqual(env.validate(7, { divisibleBy : 5 }).errors.length, 0); notEqual(env.validate(4.5, { divisibleBy : 2 }).errors.length, 0); notEqual(env.validate(7.5, { divisibleBy : 1.8 }).errors.length, 0); }); } test("Disallow Validation", function () { equal(env.validate({}, { disallow : ['null', 'boolean', 'number', 'integer', 'string', 'array'] }).errors.length, 0, "Object"); equal(env.validate([], { disallow : ['null', 'boolean', 'number', 'integer', 'string', 'object'] }).errors.length, 0, "Array"); equal(env.validate('', { disallow : ['null', 'boolean', 'number', 'integer', 'array', 'object'] }).errors.length, 0, "String"); equal(env.validate(0.1, { disallow : ['null', 'boolean', 'integer', 'string', 'array', 'object'] }).errors.length, 0, "Number"); equal(env.validate(00, { disallow : ['null', 'boolean', 'string', 'array', 'object'] }).errors.length, 0, "Integer"); equal(env.validate(false, { disallow : ['null', 'number', 'integer', 'string', 'array', 'object'] }).errors.length, 0, "Boolean"); equal(env.validate(null, { disallow : ['boolean', 'number', 'integer', 'string', 'array', 'object'] }).errors.length, 0, "Null"); notEqual(env.validate({}, { disallow : 'object' }).errors.length, 0, "Object"); notEqual(env.validate([], { disallow : 'array' }).errors.length, 0, "Array"); notEqual(env.validate('', { disallow : 'string' }).errors.length, 0, "String"); notEqual(env.validate(00, { disallow : 'integer' }).errors.length, 0, "Number"); notEqual(env.validate(0.1, { disallow : 'number' }).errors.length, 0, "Integer"); notEqual(env.validate(false, { disallow : 'boolean' }).errors.length, 0, "Boolean"); notEqual(env.validate(null, { disallow : 'null' }).errors.length, 0, "Null"); notEqual(env.validate(null, { disallow : 'any' }).errors.length, 0, "Any"); }); test("Extends Validation", function () { equal(env.validate({}, { 'extends' : {} }).errors.length, 0); equal(env.validate({}, { 'extends' : { type : 'object' } }).errors.length, 0); equal(env.validate(1, { type : 'integer', 'extends' : { type : 'number' } }).errors.length, 0); equal(env.validate({ a : 1, b : 2 }, { properties : { a : { type : 'number' } }, additionalProperties : false, 'extends' : { properties : { b : { type : 'number' } } } }).errors.length, 0); notEqual(env.validate(1, { type : 'number', 'extends' : { type : 'string' } }).errors.length, 0); //TODO: More tests }); test("JSON Schema Validation", function () { var schema = env.findSchema("http://json-schema.org/schema"); var hyperSchema = env.findSchema("http://json-schema.org/hyper-schema"); var links = env.findSchema("http://json-schema.org/links"); equal(schema.validate(schema).errors.length, 0, "schema.validate(schema)"); equal(hyperSchema.validate(schema).errors.length, 0, "hyperSchema.validate(schema)"); equal(hyperSchema.validate(hyperSchema).errors.length, 0, "hyperSchema.validate(hyperSchema)"); equal(hyperSchema.validate(links).errors.length, 0, "hyperSchema.validate(links)"); }); test("Links Validation", function () { var schema; //full equal(env.validate({ 'a' : {} }, { 'type' : 'object', 'additionalProperties' : { '$ref' : '#' } }).errors.length, 0); notEqual(env.validate({ 'a' : 1 }, { 'type' : 'object', 'additionalProperties' : { '$ref' : '#' } }).errors.length, 0); //describedby schema = env.createSchema({ "id" : "http://test.example.com/3", "properties" : { "test" : { "type" : "object" } }, "extends" : { "$ref" : "http://json-schema.org/hyper-schema" } }, null, "http://test.example.com/3"); equal(env.validate({}, { "$schema" : "http://test.example.com/3", "test" : {} }).errors.length, 0); notEqual(env.validate({}, { "$schema" : "http://test.example.com/3", "test" : 0 }).errors.length, 0); //self schema = env.createSchema({ "properties" : { "two" : { "id" : "http://test.example.com/2", "type" : "object" } } }, null, "http://not.example.com/2"); equal(env.validate({}, { "$ref" : "http://test.example.com/2" }).errors.length, 0); notEqual(env.validate(null, { "$ref" : "http://test.example.com/2" }).errors.length, 0); //links api schema = env.createSchema({ "links" : [ { "rel" : "bar", "href" : "http:{-this}#" } ] }); instance = env.createInstance("foo"); equal(schema.getLink("bar", instance), "http:foo#", "'bar' link and self reference"); }); test("PathStart Validation", function () { var instance = env.createInstance({}, "http://test.example.com/4"), schema = env.createSchema({"pathStart" : "http://test.example.com"}); equal(env.validate(instance, schema).errors.length, 0); instance = env.createInstance({}); //random URI notEqual(env.validate(instance, schema).errors.length, 0); }); test("Register Schemas", function () { var schema = env.createSchema({'type' : 'string'}, null, 'http://test.example.com/1'); equal(env.findSchema('http://json-schema.org/hyper-schema').validate(schema).errors.length, 0); equal(env.validate('', { '$ref' : 'http://test.example.com/1' }).errors.length, 0); notEqual(env.validate({}, { '$ref' : 'http://test.example.com/1' }).errors.length, 0); }); test("Complex Examples", function () { env.createSchema({ "id":"Common#", "type":"object", "properties":{ "!":{"type":"string","enum":["Common"]} }, "additionalProperties":false }, undefined, "Common#"); var report = env.validate({ "!" : "List", "list" : [ { "!" : "Text", "common" : {"!":"NotCommon"} } ], "common" : {"!":"Common"} }, { "properties":{ "!":{"type":"string","enum":["List"]}, "list":{ "type":"array", "items":{ "type":[ { "type":"object", "properties":{ "!":{"type":"string","enum":["Music"]}, "common":{"$ref":"Common#"} } }, { "type":"object", "properties":{ "!":{"type":"string","enum":["Text"]}, "common":{"$ref":"Common#"} } } ] } }, "common":{"$ref":"Common#"} } } ); notEqual(report.errors.length, 0); }); }package/tests/index.html0000644000175000017500000000166211727717626014575 0ustar daviddavid

    JSON Schema Validator Test Suite

      package/tests/tests3.js0000644000175000017500000007203111727717626014361 0ustar daviddavidvar env, DRAFTS = [ "draft-00", "draft-01", "draft-02", "draft-03" ], curDraftId; //calls ok(true) if no error is thrown function okNoError(func, msg) { try { func(); ok(true, msg); } catch (e) { ok(false, msg + ': ' + JSON.stringify(e)); } } //calls ok(true) if an error is thrown function okError(func, msg) { try { func(); ok(false, msg); } catch (e) { ok(true, msg + ': ' + JSON.stringify(e)); } } // // // Tests // // for (curDraftId = 0; curDraftId < DRAFTS.length; ++curDraftId) { module(DRAFTS[curDraftId]); (function (id) { test("Acquire Validator", function () { JSV = require('../lib/jsv').JSV; env = null; ok(JSV, "JSV is loaded"); env = JSV.createEnvironment("json-schema-draft-03"); env.setOption("defaultSchemaURI", "http://json-schema.org/" + id + "/hyper-schema#"); env.setOption("latestJSONSchemaSchemaURI", "http://json-schema.org/" + id + "/schema#"); env.setOption("latestJSONSchemaHyperSchemaURI", "http://json-schema.org/" + id + "/hyper-schema#"); env.setOption("latestJSONSchemaLinksURI", "http://json-schema.org/" + id + "/links#"); ok(env, "Environment created"); }); }(DRAFTS[curDraftId])); test("Primitive Validation", function () { equal(env.validate({}).errors.length, 0, "Object"); equal(env.validate([]).errors.length, 0, "Array"); equal(env.validate('').errors.length, 0, "String"); equal(env.validate(00).errors.length, 0, "Number"); equal(env.validate(false).errors.length, 0, "Boolean"); equal(env.validate(null).errors.length, 0, "Null"); }); test("Type Validation", function () { //simple type equal(env.validate({}, { type : 'object' }).errors.length, 0, "Object"); equal(env.validate([], { type : 'array' }).errors.length, 0, "Array"); equal(env.validate('', { type : 'string' }).errors.length, 0, "String"); equal(env.validate(00, { type : 'number' }).errors.length, 0, "Number"); equal(env.validate(00, { type : 'integer' }).errors.length, 0, "Integer"); equal(env.validate(false, { type : 'boolean' }).errors.length, 0, "Boolean"); equal(env.validate(null, { type : 'null' }).errors.length, 0, "Null"); equal(env.validate(true, { type : 'any' }).errors.length, 0, "Any"); notEqual(env.validate(null, { type : 'object' }).errors.length, 0, "Object"); notEqual(env.validate(null, { type : 'array' }).errors.length, 0, "Array"); notEqual(env.validate(null, { type : 'string' }).errors.length, 0, "String"); notEqual(env.validate(null, { type : 'number' }).errors.length, 0, "Number"); notEqual(env.validate(0.1, { type : 'integer' }).errors.length, 0, "Integer"); notEqual(env.validate(null, { type : 'boolean' }).errors.length, 0, "Boolean"); notEqual(env.validate(false, { type : 'null' }).errors.length, 0, "Null"); //union type equal(env.validate({}, { type : ['null', 'boolean', 'number', 'integer', 'string', 'array', 'object'] }).errors.length, 0, "Object"); notEqual(env.validate({}, { type : ['null', 'boolean', 'number', 'integer', 'string', 'array'] }).errors.length, 0, "Object"); //schema union type equal(env.validate({}, { type : [{ type : 'string' }, { type : 'object' }] }).errors.length, 0, "Object"); equal(env.validate(55, { type : [{ type : 'string' }, { type : 'object' }, 'number'] }).errors.length, 0, "Object"); notEqual(env.validate([], { type : ['string', { type : 'object' }] }).errors.length, 0, "Array"); }); test("Properties Validation", function () { equal(env.validate({}, { type : 'object', properties : {} }).errors.length, 0); equal(env.validate({ a : 1 }, { type : 'object', properties : { a : {}} }).errors.length, 0); equal(env.validate({ a : 1 }, { type : 'object', properties : { a : { type : 'number' }} }).errors.length, 0); equal(env.validate({ a : { b : 'two' } }, { type : 'object', properties : { a : { type : 'object', properties : { b : { type : 'string' } } }} }).errors.length, 0); notEqual(env.validate({ a : 1 }, { type : 'object', properties : { a : { type : 'string' }} }).errors.length, 0); notEqual(env.validate({ a : { b : 'two' } }, { type : 'object', properties : { a : { type : 'object', properties : { b : { type : 'number' } } }} }).errors.length, 0); }); if (curDraftId > 2) { test("PatternProperties Validation", function () { equal(env.validate(null, { patternProperties : {} }).errors.length, 0); equal(env.validate({}, { patternProperties : {} }).errors.length, 0); equal(env.validate({ a : 1 }, { patternProperties : { '[a-z]' : {}} }).errors.length, 0); equal(env.validate({ a : 1, b : 2, cc : '3' }, { patternProperties : { '^[a-z]$' : { type : 'number' }} }).errors.length, 0); equal(env.validate({ a : { b : 'two' } }, { patternProperties : { '[a-z]' : { patternProperties : { '[a-z]' : { type : 'string' } } }} }).errors.length, 0); notEqual(env.validate({ a : 1, b : 2, c : '3' }, { patternProperties : { '^[a-z]$' : { type : 'number' }} }).errors.length, 0); notEqual(env.validate({ a : { b : 'two' } }, { patternProperties : { '[a-z]' : { patternProperties : { '[a-z]' : { type : 'number' } } }} }).errors.length, 0); }); } test("AdditionalProperties Validation", function () { //object tests equal(env.validate({ a : 1, b : 2, c : 3 }, {}).errors.length, 0); equal(env.validate({ a : 1, b : 2, c : 3 }, { additionalProperties : true }).errors.length, 0); equal(env.validate({ a : 1, b : 2, c : 3 }, { properties : { a : {}, b : {} }, additionalProperties : true }).errors.length, 0); equal(env.validate({ a : 1, b : 2, c : 3 }, { properties : { a : {}, b : {}, c : {} }, additionalProperties : false }).errors.length, 0); equal(env.validate({ a : 1, b : 2, c : 3 }, { additionalProperties : { type : 'number' } }).errors.length, 0); equal(env.validate({ a : 1, b : 2, c : 3 }, { properties : { a : {}, b : {} }, additionalProperties : { type : 'number' } }).errors.length, 0); equal(env.validate({ a : 1, b : 2, c : 3 }, { properties : { a : {}, b : {}, c : {} }, additionalProperties : { type : 'string' } }).errors.length, 0); notEqual(env.validate({ a : 1, b : 2, c : 3 }, { properties : { a : {}, b : {} }, additionalProperties : false }).errors.length, 0); notEqual(env.validate({ a : 1, b : 2, c : 3 }, { properties : { a : {}, b : {} }, additionalProperties : { type : 'string' } }).errors.length, 0); //array tests equal(env.validate([1, 2, 3], {}).errors.length, 0); equal(env.validate([1, 2, 3], { additionalProperties : true }).errors.length, 0); equal(env.validate([1, 2, 3], { additionalProperties : false }).errors.length, 0); equal(env.validate([1, 2, 3], { additionalProperties : { type : 'number' } }).errors.length, 0); equal(env.validate([1, 2, 3], { additionalProperties : { type : 'string' } }).errors.length, 0); equal(env.validate(['1', '2'], { items : { type : 'string' }, additionalProperties : false }).errors.length, 0); equal(env.validate(['1', '2'], { items : [ { type : 'string' }, { type : 'string' } ], additionalProperties : false }).errors.length, 0); equal(env.validate(['1', '2', 3], { items : [ { type : 'string' }, { type : 'string' } ], additionalProperties : { type : 'number' } }).errors.length, 0); equal(env.validate(['1', '2', '3'], { items : [ { type : 'string' }, { type : 'string' }, { type : 'string' } ], additionalProperties : { type : 'number' } }).errors.length, 0); if (curDraftId < 3) { notEqual(env.validate(['1', '2', '3'], { items : [ { type : 'string' }, { type : 'string' } ], additionalProperties : false }).errors.length, 0); notEqual(env.validate(['1', '2', '3'], { items : [ { type : 'string' }, { type : 'string' } ], additionalProperties : { type : 'number' } }).errors.length, 0); } }); test("Items Validation", function () { equal(env.validate([], { type : 'array', items : { type : 'string' } }).errors.length, 0); equal(env.validate(['foo'], { type : 'array', items : { type : 'string' } }).errors.length, 0); equal(env.validate(['foo', 2], { type : 'array', items : [{ type : 'string' }, { type : 'number' }] }).errors.length, 0); notEqual(env.validate([1], { type : 'array', items : { type : 'string' } }).errors.length, 0); notEqual(env.validate(['foo', 'two'], { type : 'array', items : [{ type : 'string' }, { type : 'number' }] }).errors.length, 0); }); if (curDraftId > 2) { test("AdditionalItems Validation", function () { //array tests equal(env.validate([1, 2, 3], {}).errors.length, 0); equal(env.validate([1, 2, 3], { additionalItems : true }).errors.length, 0); equal(env.validate([1, 2, 3], { additionalItems : { type : 'number' } }).errors.length, 0); equal(env.validate(['1', '2'], { items : { type : 'string' }, additionalItems : false }).errors.length, 0); equal(env.validate(['1', '2'], { items : [ { type : 'string' }, { type : 'string' } ], additionalItems : false }).errors.length, 0); equal(env.validate(['1', '2', 3], { items : [ { type : 'string' }, { type : 'string' } ], additionalItems : { type : 'number' } }).errors.length, 0); equal(env.validate(['1', '2', '3'], { items : [ { type : 'string' }, { type : 'string' }, { type : 'string' } ], additionalItems : { type : 'number' } }).errors.length, 0); notEqual(env.validate([1, 2, 3], { additionalItems : false }).errors.length, 0); notEqual(env.validate([1, 2, 3], { additionalItems : { type : 'string' } }).errors.length, 0); notEqual(env.validate(['1', '2', '3'], { items : [ { type : 'string' }, { type : 'string' } ], additionalItems : false }).errors.length, 0); notEqual(env.validate(['1', '2', '3'], { items : [ { type : 'string' }, { type : 'string' } ], additionalItems : { type : 'number' } }).errors.length, 0); }); } (function (curDraftId) { test("Optional Validation", function () { equal(env.validate({}, { properties : { a : { optional : true } } }).errors.length, 0); equal(env.validate({ a : false }, { properties : { a : { optional : true } } }).errors.length, 0); equal(env.validate({ a : false }, { properties : { a : { optional : false } } }).errors.length, 0); notEqual(env.validate({}, { properties : { a : { optional : false } } }).errors.length, 0); notEqual(env.validate({ b : true }, { properties : { a : { optional : false } } }).errors.length, 0); if (curDraftId < 3) { notEqual(env.validate({ b : true }, { properties : { a : {} } }).errors.length, 0); } else { equal(env.validate({ b : true }, { properties : { a : {} } }).errors.length, 0); } }); }(curDraftId)); if (curDraftId > 2) { test("Required Validation", function () { equal(env.validate({ b : true }, { properties : { a : {} } }).errors.length, 0); equal(env.validate({}, { properties : { a : { required : false } } }).errors.length, 0); equal(env.validate({ a : false }, { properties : { a : { required : false } } }).errors.length, 0); equal(env.validate({ a : false }, { properties : { a : { required : true } } }).errors.length, 0); notEqual(env.validate({}, { properties : { a : { required : true } } }).errors.length, 0); notEqual(env.validate({ b : true }, { properties : { a : { required : true } } }).errors.length, 0); }); } test("Requires Validation", function () { equal(env.validate({ a : 1 }, { properties : { a : { requires : 'a' } } }).errors.length, 0); equal(env.validate({ a : 1, b : 2 }, { properties : { a : {}, b : { requires : 'a' } } }).errors.length, 0); equal(env.validate({ a : 1, b : 2 }, { properties : { a : { requires : 'b' }, b : { requires : 'a' } } }).errors.length, 0); equal(env.validate({ a : 1, b : 2 }, { properties : { b : { requires : { properties : { a : { type : 'number' } } } } } }).errors.length, 0); notEqual(env.validate({ b : 2 }, { properties : { b : { requires : 'a' } } }).errors.length, 0); notEqual(env.validate({ a : 1, b : 2 }, { properties : { a : { requires : 'b' }, b : { requires : 'c' } } }).errors.length, 0); notEqual(env.validate({ b : 2 }, { properties : { b : { requires : { properties : { b : { type : 'string' } } } } } }).errors.length, 0); }); if (curDraftId > 2) { test("Dependencies Validation", function () { equal(env.validate(null, { dependencies : {} }).errors.length, 0); equal(env.validate({ a : 1, b : 2 }, { dependencies : {} }).errors.length, 0); equal(env.validate({ a : 1, b : 2 }, { dependencies : { a : 'b' } }).errors.length, 0); equal(env.validate({ a : 1, b : 2 }, { dependencies : { c : 'd' } }).errors.length, 0); equal(env.validate({ a : 1, b : 2, c : 3 }, { dependencies : { a : ['b'], b : ['a', 'c'] } }).errors.length, 0); equal(env.validate({ a : 1, b : 2 }, { dependencies : { a : { properties : { b : { type : 'number', required : true } } } } }).errors.length, 0); notEqual(env.validate({ a : 1, b : 2 }, { dependencies : { a : 'c' } }).errors.length, 0); notEqual(env.validate({ a : 1, b : 2, c : 3 }, { dependencies : { a : ['b'], b : ['a', 'd'] } }).errors.length, 0); notEqual(env.validate({ a : 1, b : 2 }, { dependencies : { a : { properties : { b : { type : 'string', required : true } } } } }).errors.length, 0); }); } test("Minimum/Maximum Validation", function () { equal(env.validate(0, {}).errors.length, 0); equal(env.validate(1, { minimum : 1, maximum : 10 }).errors.length, 0); equal(env.validate(5, { minimum : 1, maximum : 10 }).errors.length, 0); equal(env.validate(10, { minimum : 1, maximum : 10 }).errors.length, 0); equal(env.validate(1, { minimum : 1, maximum : 1 }).errors.length, 0); notEqual(env.validate(0, { minimum : 1, maximum : 10 }).errors.length, 0); notEqual(env.validate(11, { minimum : 1, maximum : 10 }).errors.length, 0); }); test("MinimumCanEqual/MaximumCanEqual Validation", function () { //true notEqual(env.validate(0, { minimumCanEqual : true, maximumCanEqual : true }).errors.length, 0); //illegal equal(env.validate(1, { minimum : 1, maximum : 10, minimumCanEqual : true, maximumCanEqual : true }).errors.length, 0); equal(env.validate(5, { minimum : 1, maximum : 10, minimumCanEqual : true, maximumCanEqual : true }).errors.length, 0); equal(env.validate(10, { minimum : 1, maximum : 10, minimumCanEqual : true, maximumCanEqual : true }).errors.length, 0); equal(env.validate(1, { minimum : 1, maximum : 1, minimumCanEqual : true, maximumCanEqual : true }).errors.length, 0); notEqual(env.validate(0, { minimum : 1, maximum : 10, minimumCanEqual : true, maximumCanEqual : true }).errors.length, 0); notEqual(env.validate(11, { minimum : 1, maximum : 10, minimumCanEqual : true, maximumCanEqual : true }).errors.length, 0); //false notEqual(env.validate(0, { minimumCanEqual : false, maximumCanEqual : false }).errors.length, 0); //illegal equal(env.validate(1.0001, { minimum : 1, maximum : 10, minimumCanEqual : false, maximumCanEqual : false }).errors.length, 0); equal(env.validate(5, { minimum : 1, maximum : 10, minimumCanEqual : false, maximumCanEqual : false }).errors.length, 0); equal(env.validate(9.9999, { minimum : 1, maximum : 10, minimumCanEqual : false, maximumCanEqual : false }).errors.length, 0); notEqual(env.validate(1, { minimum : 1, maximum : 10, minimumCanEqual : false, maximumCanEqual : false }).errors.length, 0); notEqual(env.validate(10, { minimum : 1, maximum : 10, minimumCanEqual : false, maximumCanEqual : false }).errors.length, 0); notEqual(env.validate(1, { minimum : 1, maximum : 1, minimumCanEqual : false, maximumCanEqual : false }).errors.length, 0); notEqual(env.validate(0, { minimum : 1, maximum : 10, minimumCanEqual : false, maximumCanEqual : false }).errors.length, 0); notEqual(env.validate(11, { minimum : 1, maximum : 10, minimumCanEqual : false, maximumCanEqual : false }).errors.length, 0); }); if (curDraftId > 2) { test("ExclusiveMinimum/ExclusiveMaximum Validation", function () { //true notEqual(env.validate(0, { exclusiveMinimum : false, exclusiveMaximum : false }).errors.length, 0); //illegal equal(env.validate(1, { minimum : 1, maximum : 10, exclusiveMinimum : false, exclusiveMaximum : false }).errors.length, 0); equal(env.validate(5, { minimum : 1, maximum : 10, exclusiveMinimum : false, exclusiveMaximum : false }).errors.length, 0); equal(env.validate(10, { minimum : 1, maximum : 10, exclusiveMinimum : false, exclusiveMaximum : false }).errors.length, 0); equal(env.validate(1, { minimum : 1, maximum : 1, exclusiveMinimum : false, exclusiveMaximum : false }).errors.length, 0); notEqual(env.validate(0, { minimum : 1, maximum : 10, exclusiveMinimum : false, exclusiveMaximum : false }).errors.length, 0); notEqual(env.validate(11, { minimum : 1, maximum : 10, exclusiveMinimum : false, exclusiveMaximum : false }).errors.length, 0); //false notEqual(env.validate(0, { exclusiveMinimum : true, exclusiveMaximum : true }).errors.length, 0); //illegal equal(env.validate(1.0001, { minimum : 1, maximum : 10, exclusiveMinimum : true, exclusiveMaximum : true }).errors.length, 0); equal(env.validate(5, { minimum : 1, maximum : 10, exclusiveMinimum : true, exclusiveMaximum : true }).errors.length, 0); equal(env.validate(9.9999, { minimum : 1, maximum : 10, exclusiveMinimum : true, exclusiveMaximum : true }).errors.length, 0); notEqual(env.validate(1, { minimum : 1, maximum : 10, exclusiveMinimum : true, exclusiveMaximum : true }).errors.length, 0); notEqual(env.validate(10, { minimum : 1, maximum : 10, exclusiveMinimum : true, exclusiveMaximum : true }).errors.length, 0); notEqual(env.validate(1, { minimum : 1, maximum : 1, exclusiveMinimum : true, exclusiveMaximum : true }).errors.length, 0); notEqual(env.validate(0, { minimum : 1, maximum : 10, exclusiveMinimum : true, exclusiveMaximum : true }).errors.length, 0); notEqual(env.validate(11, { minimum : 1, maximum : 10, exclusiveMinimum : true, exclusiveMaximum : true }).errors.length, 0); }); } test("MinItems/MaxItems Validation", function () { equal(env.validate([], {}).errors.length, 0); equal(env.validate([1], { minItems : 1, maxItems : 1 }).errors.length, 0); equal(env.validate([1], { minItems : 1, maxItems : 3 }).errors.length, 0); equal(env.validate([1, 2], { minItems : 1, maxItems : 3 }).errors.length, 0); equal(env.validate([1, 2, 3], { minItems : 1, maxItems : 3 }).errors.length, 0); notEqual(env.validate([], { minItems : 1, maxItems : 0 }).errors.length, 0); notEqual(env.validate([], { minItems : 1, maxItems : 3 }).errors.length, 0); notEqual(env.validate([1, 2, 3, 4], { minItems : 1, maxItems : 3 }).errors.length, 0); }); if (curDraftId > 1) { test("UniqueItems Validation", function () { equal(env.validate([], {}).errors.length, 0); equal(env.validate([], { uniqueItems : true }).errors.length, 0); equal(env.validate([null], { uniqueItems : true }).errors.length, 0); equal(env.validate([true, false], { uniqueItems : true }).errors.length, 0); equal(env.validate([1, 2, 3], { uniqueItems : true }).errors.length, 0); equal(env.validate(['a', 'b'], { uniqueItems : true }).errors.length, 0); equal(env.validate([[], []], { uniqueItems : true }).errors.length, 0); equal(env.validate([{}, {}], { uniqueItems : true }).errors.length, 0); notEqual(env.validate([null, null], { uniqueItems : true }).errors.length, 0); notEqual(env.validate([false, false], { uniqueItems : true }).errors.length, 0); notEqual(env.validate([1, 2, 1], { uniqueItems : true }).errors.length, 0); notEqual(env.validate(['a', 'b', 'b'], { uniqueItems : true }).errors.length, 0); }); } test("Pattern Validation", function () { equal(env.validate('', {}).errors.length, 0); equal(env.validate('', { pattern : '^$' }).errors.length, 0); equal(env.validate('today', { pattern : 'day' }).errors.length, 0); notEqual(env.validate('', { pattern : '^ $' }).errors.length, 0); notEqual(env.validate('today', { pattern : 'dam' }).errors.length, 0); notEqual(env.validate('aaaaa', { pattern : 'aa(a' }).errors.length, 0); }); test("MinLength/MaxLength Validation", function () { equal(env.validate('', {}).errors.length, 0); equal(env.validate('1', { minLength : 1, maxLength : 1 }).errors.length, 0); equal(env.validate('1', { minLength : 1, maxLength : 3 }).errors.length, 0); equal(env.validate('12', { minLength : 1, maxLength : 3 }).errors.length, 0); equal(env.validate('123', { minLength : 1, maxLength : 3 }).errors.length, 0); notEqual(env.validate('', { minLength : 1, maxLength : 0 }).errors.length, 0); notEqual(env.validate('', { minLength : 1, maxLength : 3 }).errors.length, 0); notEqual(env.validate('1234', { minLength : 1, maxLength : 3 }).errors.length, 0); }); test("Enum Validation", function () { equal(env.validate(null, {}).errors.length, 0); equal(env.validate(true, { 'enum' : [false, true] }).errors.length, 0); equal(env.validate(2, { 'enum' : [1, 2, 3] }).errors.length, 0); equal(env.validate('a', { 'enum' : ['a'] }).errors.length, 0); equal(env.validate({}, { 'properties' : { 'a' : { 'enum' : ['a'], 'optional' : true, 'required' : false } } }).errors.length, 0); notEqual(env.validate(true, { 'enum' : ['false', 'true'] }).errors.length, 0); notEqual(env.validate(4, { 'enum' : [1, 2, 3, '4'] }).errors.length, 0); notEqual(env.validate('', { 'enum' : [] }).errors.length, 0); notEqual(env.validate({}, { 'properties' : { 'a' : { 'enum' : ['a'], 'optional' : false, 'required' : true } } }).errors.length, 0); }); test("Format Validation", function () { //TODO }); test("MaxDecimal Validation", function () { equal(env.validate(0, {}).errors.length, 0); equal(env.validate(0, { maxDecimal : 0 }).errors.length, 0); equal(env.validate(0, { maxDecimal : 1 }).errors.length, 0); equal(env.validate(0.22, { maxDecimal : 2 }).errors.length, 0); equal(env.validate(0.33, { maxDecimal : 3 }).errors.length, 0); notEqual(env.validate(0.1, { maxDecimal : 0 }).errors.length, 0); notEqual(env.validate(0.111, { maxDecimal : 1 }).errors.length, 0); }); if (curDraftId > 1) { test("DivisibleBy Validation", function () { equal(env.validate(0, {}).errors.length, 0); equal(env.validate(0, { divisibleBy : 1 }).errors.length, 0); equal(env.validate(10, { divisibleBy : 5 }).errors.length, 0); equal(env.validate(10, { divisibleBy : 10 }).errors.length, 0); equal(env.validate(0, { divisibleBy : 2.5 }).errors.length, 0); equal(env.validate(5, { divisibleBy : 2.5 }).errors.length, 0); equal(env.validate(7.5, { divisibleBy : 2.5 }).errors.length, 0); equal(env.validate(9.1, { divisibleBy : 1.3 }).errors.length, 0); notEqual(env.validate(0, { divisibleBy : 0 }).errors.length, 0); notEqual(env.validate(7, { divisibleBy : 5 }).errors.length, 0); notEqual(env.validate(4.5, { divisibleBy : 2 }).errors.length, 0); notEqual(env.validate(7.5, { divisibleBy : 1.8 }).errors.length, 0); }); } test("Disallow Validation", function () { equal(env.validate({}, { disallow : ['null', 'boolean', 'number', 'integer', 'string', 'array'] }).errors.length, 0, "Object"); equal(env.validate([], { disallow : ['null', 'boolean', 'number', 'integer', 'string', 'object'] }).errors.length, 0, "Array"); equal(env.validate('', { disallow : ['null', 'boolean', 'number', 'integer', 'array', 'object'] }).errors.length, 0, "String"); equal(env.validate(0.1, { disallow : ['null', 'boolean', 'integer', 'string', 'array', 'object'] }).errors.length, 0, "Number"); equal(env.validate(00, { disallow : ['null', 'boolean', 'string', 'array', 'object'] }).errors.length, 0, "Integer"); equal(env.validate(false, { disallow : ['null', 'number', 'integer', 'string', 'array', 'object'] }).errors.length, 0, "Boolean"); equal(env.validate(null, { disallow : ['boolean', 'number', 'integer', 'string', 'array', 'object'] }).errors.length, 0, "Null"); notEqual(env.validate({}, { disallow : 'object' }).errors.length, 0, "Object"); notEqual(env.validate([], { disallow : 'array' }).errors.length, 0, "Array"); notEqual(env.validate('', { disallow : 'string' }).errors.length, 0, "String"); notEqual(env.validate(00, { disallow : 'integer' }).errors.length, 0, "Number"); notEqual(env.validate(0.1, { disallow : 'number' }).errors.length, 0, "Integer"); notEqual(env.validate(false, { disallow : 'boolean' }).errors.length, 0, "Boolean"); notEqual(env.validate(null, { disallow : 'null' }).errors.length, 0, "Null"); notEqual(env.validate(null, { disallow : 'any' }).errors.length, 0, "Any"); }); test("Extends Validation", function () { equal(env.validate({}, { 'extends' : {} }).errors.length, 0); equal(env.validate({}, { 'extends' : { type : 'object' } }).errors.length, 0); equal(env.validate(1, { type : 'integer', 'extends' : { type : 'number' } }).errors.length, 0); equal(env.validate({ a : 1, b : 2 }, { properties : { a : { type : 'number' } }, additionalProperties : false, 'extends' : { properties : { b : { type : 'number' } } } }).errors.length, 0); notEqual(env.validate(1, { type : 'number', 'extends' : { type : 'string' } }).errors.length, 0); //TODO: More tests }); test("JSON Schema Validation", function () { var schema = env.findSchema(env.getOption("latestJSONSchemaSchemaURI")); var hyperSchema = env.findSchema(env.getOption("latestJSONSchemaHyperSchemaURI")); var links = env.findSchema(env.getOption("latestJSONSchemaLinksURI")); equal(schema.validate(schema).errors.length, 0, "schema.validate(schema)"); equal(hyperSchema.validate(schema).errors.length, 0, "hyperSchema.validate(schema)"); equal(hyperSchema.validate(hyperSchema).errors.length, 0, "hyperSchema.validate(hyperSchema)"); equal(hyperSchema.validate(links).errors.length, 0, "hyperSchema.validate(links)"); }); (function(curDraftId){ test("Links Validation", function () { var schema, instance; //full equal(env.validate({ 'a' : {} }, { 'type' : 'object', 'additionalProperties' : { '$ref' : '#' } }).errors.length, 0); notEqual(env.validate({ 'a' : 1 }, { 'type' : 'object', 'additionalProperties' : { '$ref' : '#' } }).errors.length, 0); //describedby okNoError(function () { schema = env.createSchema({ "id" : "http://test.example.com/3", "properties" : { "test" : { "type" : "object" } }, "extends" : { "$ref" : "http://json-schema.org/draft-03/schema#" } }, null, "http://test.example.com/3"); equal(env.validate({}, { "$schema" : "http://test.example.com/3", "test" : {} }).errors.length, 0); notEqual(env.validate({}, { "$schema" : "http://test.example.com/3", "test" : 0 }).errors.length, 0); }, "describedby schema"); //self okNoError(function () { schema = env.createSchema({ "properties" : { "two" : { "id" : "http://test.example.com/2", "type" : "object" } } }, null, "http://not.example.com/2"); equal(env.validate({}, { "$ref" : "http://test.example.com/2" }).errors.length, 0); notEqual(env.validate(null, { "$ref" : "http://test.example.com/2" }).errors.length, 0); }, "self schema"); //links api okNoError(function () { schema = env.createSchema({ "links" : [ { "rel" : "bar", "href" : "http:" + (curDraftId < 3 ? "{-this}" : "{@}") + "#" } ] }); instance = env.createInstance("foo"); equal(schema.getLink("bar", instance), "http:foo#", "'bar' link and self reference"); }, "links api schema"); //invalid reference (env.getOption("enforceReferences") ? okError : okNoError)(function () { schema = env.createSchema({ "$ref" : "asdf:qwerty" }); //should throw error }, "invalid reference"); }); }(curDraftId)); test("PathStart Validation", function () { var instance = env.createInstance({}, "http://test.example.com/4"), schema = env.createSchema({"pathStart" : "http://test.example.com"}); equal(env.validate(instance, schema).errors.length, 0); instance = env.createInstance({}); //random URI notEqual(env.validate(instance, schema).errors.length, 0); }); test("Register Schemas", function () { var schema = env.createSchema({'type' : 'string'}, null, 'http://test.example.com/1'); equal(env.findSchema(env.getOption("latestJSONSchemaHyperSchemaURI")).validate(schema).errors.length, 0); equal(env.validate('', { '$ref' : 'http://test.example.com/1' }).errors.length, 0); notEqual(env.validate({}, { '$ref' : 'http://test.example.com/1' }).errors.length, 0); }); test("Complex Examples", function () { //example 1 env.createSchema({ "id":"Common#", "type":"object", "properties":{ "!":{"type":"string","enum":["Common"]} }, "additionalProperties":false }, undefined, "Common#"); var report = env.validate({ "!" : "List", "list" : [ { "!" : "Text", "common" : {"!":"NotCommon"} } ], "common" : {"!":"Common"} }, { "properties":{ "!":{"type":"string","enum":["List"]}, "list":{ "type":"array", "items":{ "type":[ { "type":"object", "properties":{ "!":{"type":"string","enum":["Music"]}, "common":{"$ref":"Common#"} } }, { "type":"object", "properties":{ "!":{"type":"string","enum":["Text"]}, "common":{"$ref":"Common#"} } } ] } }, "common":{"$ref":"Common#"} } } ); notEqual(report.errors.length, 0); }); }package/tests/index3.html0000644000175000017500000000154011727717626014653 0ustar daviddavid

      JSON Schema Validator Test Suite

        package/docs/0000755000175000017500000000000011727717452012356 5ustar daviddavidpackage/docs/symbols/0000755000175000017500000000000011727717452014046 5ustar daviddavidpackage/docs/symbols/Report.html0000644000175000017500000004233211727717626016216 0ustar daviddavid JsDoc Reference - Report
        Class Index | File Index

        Classes


        Class Report


        Defined in: jsv.js.

        Class Summary
        Constructor Attributes Constructor Name and Description
         
        Report()
        Reports are returned from validation methods to describe the result of a validation.
        Field Summary
        Field Attributes Field Name and Description
         
        An array of ValidationError objects that define all the errors generated by the schema against the instance.
         
        If the report is generated by Environment#validate, this field is the generated instance.
         
        If the report is generated by Environment#validate, this field is the generated schema.
         
        If the report is generated by Environment#validate, this field is the schema's schema.
         
        A hash table of every instance and what schemas were validated against it.
        Method Summary
        Method Attributes Method Name and Description
         
        addError(instance, schema, attr, message, details)
        Adds a ValidationError object to the errors field.
         
        isValidatedBy(uri, schemaUri)
        Returns if an instance with the provided URI has been validated by the schema with the provided URI.
         
        registerValidation(uri, schemaUri)
        Registers that the provided instance URI has been validated by the provided schema URI.
        Class Detail
        Report()
        Reports are returned from validation methods to describe the result of a validation.
        See:
        JSONSchema#validate
        Environment#validate
        Field Detail
        {Array} errors
        An array of ValidationError objects that define all the errors generated by the schema against the instance.
        See:
        Report#addError

        {JSONInstance} instance
        If the report is generated by Environment#validate, this field is the generated instance.
        See:
        Environment#validate

        {JSONSchema} schema
        If the report is generated by Environment#validate, this field is the generated schema.
        See:
        Environment#validate

        {JSONSchema} schemaSchema
        If the report is generated by Environment#validate, this field is the schema's schema. This value is the same as calling schema.getSchema().
        See:
        Environment#validate
        JSONSchema#getSchema

        {Object} validated
        A hash table of every instance and what schemas were validated against it.

        The key of each item in the table is the URI of the instance that was validated. The value of this key is an array of strings of URIs of the schema that validated it.

        See:
        Report#registerValidation
        Report#isValidatedBy
        Method Detail
        addError(instance, schema, attr, message, details)
        Adds a ValidationError object to the errors field.
        Parameters:
        {JSONInstance|String} instance
        The instance (or instance URI) that is invalid
        {JSONSchema|String} schema
        The schema (or schema URI) that was validating the instance
        {String} attr
        The attribute that failed to validated
        {String} message
        A user-friendly message on why the schema attribute failed to validate the instance
        {Any} details
        The value of the schema attribute

        {Boolean} isValidatedBy(uri, schemaUri)
        Returns if an instance with the provided URI has been validated by the schema with the provided URI.
        Parameters:
        {String} uri
        The URI of the instance
        {String} schemaUri
        The URI of a schema
        Returns:
        {Boolean} If the instance has been validated by the schema.

        registerValidation(uri, schemaUri)
        Registers that the provided instance URI has been validated by the provided schema URI. This is recorded in the validated field.
        Parameters:
        {String} uri
        The URI of the instance that was validated
        {String} schemaUri
        The URI of the schema that validated the instance

        Documentation generated by JsDoc Toolkit 2.4.0 on Wed Sep 14 2011 18:53:24 GMT-0600 (MDT)
        package/docs/symbols/ValidationError.html0000644000175000017500000002366611727717626020060 0ustar daviddavid JsDoc Reference - ValidationError
        Class Index | File Index

        Classes


        Class ValidationError


        Defined in: jsv.js.

        Class Summary
        Constructor Attributes Constructor Name and Description
         
        Defines an error, found by a schema, with an instance.
        Field Summary
        Field Attributes Field Name and Description
         
        The name of the schema attribute that generated the error.
         
        The value of the schema attribute that generated the error.
         
        An user-friendly (English) message about what failed to validate.
         
        The URI of the schema that generated the error.
         
        uri
        The URI of the instance that has the error.
        Class Detail
        ValidationError()
        Defines an error, found by a schema, with an instance. This class can only be instantiated by Report#addError.
        See:
        Report#addError
        Field Detail
        {String} attribute
        The name of the schema attribute that generated the error.

        {Any} details
        The value of the schema attribute that generated the error.

        {String} message
        An user-friendly (English) message about what failed to validate.

        {String} schemaUri
        The URI of the schema that generated the error.

        {String} uri
        The URI of the instance that has the error.

        Documentation generated by JsDoc Toolkit 2.4.0 on Wed Sep 14 2011 18:53:24 GMT-0600 (MDT)
        package/docs/symbols/src/0000755000175000017500000000000011727717452014635 5ustar daviddavidpackage/docs/symbols/src/jsv.js.html0000644000175000017500000111157111727717626016752 0ustar daviddavid
          1 /**
          2  * JSV: JSON Schema Validator
          3  * 
          4  * @fileOverview A JavaScript implementation of a extendable, fully compliant JSON Schema validator.
          5  * @author <a href="mailto:gary.court@gmail.com">Gary Court</a>
          6  * @version 4.0
          7  * @see http://github.com/garycourt/JSV
          8  */
          9 
         10 /*
         11  * Copyright 2010 Gary Court. All rights reserved.
         12  * 
         13  * Redistribution and use in source and binary forms, with or without modification, are
         14  * permitted provided that the following conditions are met:
         15  * 
         16  *    1. Redistributions of source code must retain the above copyright notice, this list of
         17  *       conditions and the following disclaimer.
         18  * 
         19  *    2. Redistributions in binary form must reproduce the above copyright notice, this list
         20  *       of conditions and the following disclaimer in the documentation and/or other materials
         21  *       provided with the distribution.
         22  * 
         23  * THIS SOFTWARE IS PROVIDED BY GARY COURT ``AS IS'' AND ANY EXPRESS OR IMPLIED
         24  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
         25  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARY COURT OR
         26  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
         27  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
         28  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
         29  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
         30  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
         31  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
         32  * 
         33  * The views and conclusions contained in the software and documentation are those of the
         34  * authors and should not be interpreted as representing official policies, either expressed
         35  * or implied, of Gary Court or the JSON Schema specification.
         36  */
         37 
         38 /*jslint white: true, sub: true, onevar: true, undef: true, eqeqeq: true, newcap: true, immed: true, indent: 4 */
         39 
         40 var exports = exports || this,
         41 	require = require || function () {
         42 		return exports;
         43 	};
         44 
         45 (function () {
         46 	
         47 	var URI = require("./uri/uri").URI,
         48 		O = {},
         49 		I2H = "0123456789abcdef".split(""),
         50 		mapArray, filterArray, searchArray,
         51 		
         52 		JSV;
         53 	
         54 	//
         55 	// Utility functions
         56 	//
         57 	
         58 	function typeOf(o) {
         59 		return o === undefined ? "undefined" : (o === null ? "null" : Object.prototype.toString.call(o).split(" ").pop().split("]").shift().toLowerCase());
         60 	}
         61 	
         62 	/** @inner */
         63 	function F() {}
         64 	
         65 	function createObject(proto) {
         66 		F.prototype = proto || {};
         67 		return new F();
         68 	}
         69 	
         70 	function mapObject(obj, func, scope) {
         71 		var newObj = {}, key;
         72 		for (key in obj) {
         73 			if (obj[key] !== O[key]) {
         74 				newObj[key] = func.call(scope, obj[key], key, obj);
         75 			}
         76 		}
         77 		return newObj;
         78 	}
         79 	
         80 	/** @ignore */
         81 	mapArray = function (arr, func, scope) {
         82 		var x = 0, xl = arr.length, newArr = new Array(xl);
         83 		for (; x < xl; ++x) {
         84 			newArr[x] = func.call(scope, arr[x], x, arr);
         85 		}
         86 		return newArr;
         87 	};
         88 		
         89 	if (Array.prototype.map) {
         90 		/** @ignore */
         91 		mapArray = function (arr, func, scope) {
         92 			return Array.prototype.map.call(arr, func, scope);
         93 		};
         94 	}
         95 	
         96 	/** @ignore */
         97 	filterArray = function (arr, func, scope) {
         98 		var x = 0, xl = arr.length, newArr = [];
         99 		for (; x < xl; ++x) {
        100 			if (func.call(scope, arr[x], x, arr)) {
        101 				newArr[newArr.length] = arr[x];
        102 			}
        103 		}
        104 		return newArr;
        105 	};
        106 	
        107 	if (Array.prototype.filter) {
        108 		/** @ignore */
        109 		filterArray = function (arr, func, scope) {
        110 			return Array.prototype.filter.call(arr, func, scope);
        111 		};
        112 	}
        113 	
        114 	/** @ignore */
        115 	searchArray = function (arr, o) {
        116 		var x = 0, xl = arr.length;
        117 		for (; x < xl; ++x) {
        118 			if (arr[x] === o) {
        119 				return x;
        120 			}
        121 		}
        122 		return -1;
        123 	};
        124 	
        125 	if (Array.prototype.indexOf) {
        126 		/** @ignore */
        127 		searchArray = function (arr, o) {
        128 			return Array.prototype.indexOf.call(arr, o);
        129 		};
        130 	}
        131 	
        132 	function toArray(o) {
        133 		return o !== undefined && o !== null ? (o instanceof Array && !o.callee ? o : (typeof o.length !== "number" || o.split || o.setInterval || o.call ? [ o ] : Array.prototype.slice.call(o))) : [];
        134 	}
        135 	
        136 	function keys(o) {
        137 		var result = [], key;
        138 		
        139 		switch (typeOf(o)) {
        140 		case "object":
        141 			for (key in o) {
        142 				if (o[key] !== O[key]) {
        143 					result[result.length] = key;
        144 				}
        145 			}
        146 			break;
        147 		case "array":
        148 			for (key = o.length - 1; key >= 0; --key) {
        149 				result[key] = key;
        150 			}
        151 			break;
        152 		}
        153 		
        154 		return result;
        155 	}
        156 	
        157 	function pushUnique(arr, o) {
        158 		if (searchArray(arr, o) === -1) {
        159 			arr.push(o);
        160 		}
        161 		return arr;
        162 	}
        163 	
        164 	function popFirst(arr, o) {
        165 		var index = searchArray(arr, o);
        166 		if (index > -1) {
        167 			arr.splice(index, 1);
        168 		}
        169 		return arr;
        170 	}
        171 	
        172 	function randomUUID() {
        173 		return [
        174 			I2H[Math.floor(Math.random() * 0x10)],
        175 			I2H[Math.floor(Math.random() * 0x10)],
        176 			I2H[Math.floor(Math.random() * 0x10)],
        177 			I2H[Math.floor(Math.random() * 0x10)],
        178 			I2H[Math.floor(Math.random() * 0x10)],
        179 			I2H[Math.floor(Math.random() * 0x10)],
        180 			I2H[Math.floor(Math.random() * 0x10)],
        181 			I2H[Math.floor(Math.random() * 0x10)],
        182 			"-",
        183 			I2H[Math.floor(Math.random() * 0x10)],
        184 			I2H[Math.floor(Math.random() * 0x10)],
        185 			I2H[Math.floor(Math.random() * 0x10)],
        186 			I2H[Math.floor(Math.random() * 0x10)],
        187 			"-4",  //set 4 high bits of time_high field to version
        188 			I2H[Math.floor(Math.random() * 0x10)],
        189 			I2H[Math.floor(Math.random() * 0x10)],
        190 			I2H[Math.floor(Math.random() * 0x10)],
        191 			"-",
        192 			I2H[(Math.floor(Math.random() * 0x10) & 0x3) | 0x8],  //specify 2 high bits of clock sequence
        193 			I2H[Math.floor(Math.random() * 0x10)],
        194 			I2H[Math.floor(Math.random() * 0x10)],
        195 			I2H[Math.floor(Math.random() * 0x10)],
        196 			"-",
        197 			I2H[Math.floor(Math.random() * 0x10)],
        198 			I2H[Math.floor(Math.random() * 0x10)],
        199 			I2H[Math.floor(Math.random() * 0x10)],
        200 			I2H[Math.floor(Math.random() * 0x10)],
        201 			I2H[Math.floor(Math.random() * 0x10)],
        202 			I2H[Math.floor(Math.random() * 0x10)],
        203 			I2H[Math.floor(Math.random() * 0x10)],
        204 			I2H[Math.floor(Math.random() * 0x10)],
        205 			I2H[Math.floor(Math.random() * 0x10)],
        206 			I2H[Math.floor(Math.random() * 0x10)],
        207 			I2H[Math.floor(Math.random() * 0x10)],
        208 			I2H[Math.floor(Math.random() * 0x10)]
        209 		].join("");
        210 	}
        211 	
        212 	function escapeURIComponent(str) {
        213 		return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A');
        214 	}
        215 	
        216 	function formatURI(uri) {
        217 		if (typeof uri === "string" && uri.indexOf("#") === -1) {
        218 			uri += "#";
        219 		}
        220 		return uri;
        221 	}
        222 	
        223 	function stripInstances(o) {
        224 		if (o instanceof JSONInstance) {
        225 			return o.getURI();
        226 		}
        227 		
        228 		switch (typeOf(o)) {
        229 		case "undefined":
        230 		case "null":
        231 		case "boolean":
        232 		case "number":
        233 		case "string":
        234 			return o;  //do nothing
        235 		
        236 		case "object":
        237 			return mapObject(o, stripInstances);
        238 		
        239 		case "array":
        240 			return mapArray(o, stripInstances);
        241 		
        242 		default:
        243 			return o.toString();
        244 		}
        245 	}
        246 	
        247 	/**
        248 	 * The exception that is thrown when a schema fails to be created.
        249 	 * 
        250 	 * @name InitializationError
        251 	 * @class
        252 	 * @param {JSONInstance|String} instance The instance (or instance URI) that is invalid
        253 	 * @param {JSONSchema|String} schema The schema (or schema URI) that was validating the instance
        254 	 * @param {String} attr The attribute that failed to validated
        255 	 * @param {String} message A user-friendly message on why the schema attribute failed to validate the instance
        256 	 * @param {Any} details The value of the schema attribute
        257 	 */
        258 	
        259 	function InitializationError(instance, schema, attr, message, details) {
        260 		Error.call(this, message);
        261 		
        262 		this.uri = instance instanceof JSONInstance ? instance.getURI() : instance;
        263 		this.schemaUri = schema instanceof JSONInstance ? schema.getURI() : schema;
        264 		this.attribute = attr;
        265 		this.message = message;
        266 		this.description = message;  //IE
        267 		this.details = details;
        268 	}
        269 	
        270 	InitializationError.prototype = new Error();
        271 	InitializationError.prototype.constructor = InitializationError;
        272 	InitializationError.prototype.name = "InitializationError";
        273 	
        274 	/**
        275 	 * Defines an error, found by a schema, with an instance.
        276 	 * This class can only be instantiated by {@link Report#addError}. 
        277 	 * 
        278 	 * @name ValidationError
        279 	 * @class
        280 	 * @see Report#addError
        281 	 */
        282 	
        283 	/**
        284 	 * The URI of the instance that has the error.
        285 	 * 
        286 	 * @name ValidationError.prototype.uri
        287 	 * @type String
        288 	 */
        289 	
        290 	/**
        291 	 * The URI of the schema that generated the error.
        292 	 * 
        293 	 * @name ValidationError.prototype.schemaUri
        294 	 * @type String
        295 	 */
        296 	
        297 	/**
        298 	 * The name of the schema attribute that generated the error.
        299 	 * 
        300 	 * @name ValidationError.prototype.attribute
        301 	 * @type String
        302 	 */
        303 	
        304 	/**
        305 	 * An user-friendly (English) message about what failed to validate.
        306 	 * 
        307 	 * @name ValidationError.prototype.message
        308 	 * @type String
        309 	 */
        310 	
        311 	/**
        312 	 * The value of the schema attribute that generated the error.
        313 	 * 
        314 	 * @name ValidationError.prototype.details
        315 	 * @type Any
        316 	 */
        317 	
        318 	/**
        319 	 * Reports are returned from validation methods to describe the result of a validation.
        320 	 * 
        321 	 * @name Report
        322 	 * @class
        323 	 * @see JSONSchema#validate
        324 	 * @see Environment#validate
        325 	 */
        326 	
        327 	function Report() {
        328 		/**
        329 		 * An array of {@link ValidationError} objects that define all the errors generated by the schema against the instance.
        330 		 * 
        331 		 * @name Report.prototype.errors
        332 		 * @type Array
        333 		 * @see Report#addError
        334 		 */
        335 		this.errors = [];
        336 		
        337 		/**
        338 		 * A hash table of every instance and what schemas were validated against it.
        339 		 * <p>
        340 		 * The key of each item in the table is the URI of the instance that was validated.
        341 		 * The value of this key is an array of strings of URIs of the schema that validated it.
        342 		 * </p>
        343 		 * 
        344 		 * @name Report.prototype.validated
        345 		 * @type Object
        346 		 * @see Report#registerValidation
        347 		 * @see Report#isValidatedBy
        348 		 */
        349 		this.validated = {};
        350 		
        351 		/**
        352 		 * If the report is generated by {@link Environment#validate}, this field is the generated instance.
        353 		 * 
        354 		 * @name Report.prototype.instance
        355 		 * @type JSONInstance
        356 		 * @see Environment#validate
        357 		 */
        358 		
        359 		/**
        360 		 * If the report is generated by {@link Environment#validate}, this field is the generated schema.
        361 		 * 
        362 		 * @name Report.prototype.schema
        363 		 * @type JSONSchema
        364 		 * @see Environment#validate
        365 		 */
        366 		 
        367 		/**
        368 		 * If the report is generated by {@link Environment#validate}, this field is the schema's schema.
        369 		 * This value is the same as calling <code>schema.getSchema()</code>.
        370 		 * 
        371 		 * @name Report.prototype.schemaSchema
        372 		 * @type JSONSchema
        373 		 * @see Environment#validate
        374 		 * @see JSONSchema#getSchema
        375 		 */
        376 	}
        377 	
        378 	/**
        379 	 * Adds a {@link ValidationError} object to the <a href="#errors"><code>errors</code></a> field.
        380 	 * 
        381 	 * @param {JSONInstance|String} instance The instance (or instance URI) that is invalid
        382 	 * @param {JSONSchema|String} schema The schema (or schema URI) that was validating the instance
        383 	 * @param {String} attr The attribute that failed to validated
        384 	 * @param {String} message A user-friendly message on why the schema attribute failed to validate the instance
        385 	 * @param {Any} details The value of the schema attribute
        386 	 */
        387 	
        388 	Report.prototype.addError = function (instance, schema, attr, message, details) {
        389 		this.errors.push({
        390 			uri : instance instanceof JSONInstance ? instance.getURI() : instance,
        391 			schemaUri : schema instanceof JSONInstance ? schema.getURI() : schema,
        392 			attribute : attr,
        393 			message : message,
        394 			details : stripInstances(details)
        395 		});
        396 	};
        397 	
        398 	/**
        399 	 * Registers that the provided instance URI has been validated by the provided schema URI. 
        400 	 * This is recorded in the <a href="#validated"><code>validated</code></a> field.
        401 	 * 
        402 	 * @param {String} uri The URI of the instance that was validated
        403 	 * @param {String} schemaUri The URI of the schema that validated the instance
        404 	 */
        405 	
        406 	Report.prototype.registerValidation = function (uri, schemaUri) {
        407 		if (!this.validated[uri]) {
        408 			this.validated[uri] = [ schemaUri ];
        409 		} else {
        410 			this.validated[uri].push(schemaUri);
        411 		}
        412 	};
        413 	
        414 	/**
        415 	 * Returns if an instance with the provided URI has been validated by the schema with the provided URI. 
        416 	 * 
        417 	 * @param {String} uri The URI of the instance
        418 	 * @param {String} schemaUri The URI of a schema
        419 	 * @returns {Boolean} If the instance has been validated by the schema.
        420 	 */
        421 	
        422 	Report.prototype.isValidatedBy = function (uri, schemaUri) {
        423 		return !!this.validated[uri] && searchArray(this.validated[uri], schemaUri) !== -1;
        424 	};
        425 	
        426 	/**
        427 	 * A wrapper class for binding an Environment, URI and helper methods to an instance. 
        428 	 * This class is most commonly instantiated with {@link Environment#createInstance}.
        429 	 * 
        430 	 * @name JSONInstance
        431 	 * @class
        432 	 * @param {Environment} env The environment this instance belongs to
        433 	 * @param {JSONInstance|Any} json The value of the instance
        434 	 * @param {String} [uri] The URI of the instance. If undefined, the URI will be a randomly generated UUID. 
        435 	 * @param {String} [fd] The fragment delimiter for properties. If undefined, uses the environment default.
        436 	 */
        437 	
        438 	function JSONInstance(env, json, uri, fd) {
        439 		if (json instanceof JSONInstance) {
        440 			if (typeof fd !== "string") {
        441 				fd = json._fd;
        442 			}
        443 			if (typeof uri !== "string") {
        444 				uri = json._uri;
        445 			}
        446 			json = json._value;
        447 		}
        448 		
        449 		if (typeof uri !== "string") {
        450 			uri = "urn:uuid:" + randomUUID() + "#";
        451 		} else if (uri.indexOf(":") === -1) {
        452 			uri = formatURI(URI.resolve("urn:uuid:" + randomUUID() + "#", uri));
        453 		}
        454 		
        455 		this._env = env;
        456 		this._value = json;
        457 		this._uri = uri;
        458 		this._fd = fd || this._env._options["defaultFragmentDelimiter"];
        459 	}
        460 	
        461 	/**
        462 	 * Returns the environment the instance is bound to.
        463 	 * 
        464 	 * @returns {Environment} The environment of the instance
        465 	 */
        466 	
        467 	JSONInstance.prototype.getEnvironment = function () {
        468 		return this._env;
        469 	};
        470 	
        471 	/**
        472 	 * Returns the name of the type of the instance.
        473 	 * 
        474 	 * @returns {String} The name of the type of the instance
        475 	 */
        476 	
        477 	JSONInstance.prototype.getType = function () {
        478 		return typeOf(this._value);
        479 	};
        480 	
        481 	/**
        482 	 * Returns the JSON value of the instance.
        483 	 * 
        484 	 * @returns {Any} The actual JavaScript value of the instance
        485 	 */
        486 	
        487 	JSONInstance.prototype.getValue = function () {
        488 		return this._value;
        489 	};
        490 	
        491 	/**
        492 	 * Returns the URI of the instance.
        493 	 * 
        494 	 * @returns {String} The URI of the instance
        495 	 */
        496 	
        497 	JSONInstance.prototype.getURI = function () {
        498 		return this._uri;
        499 	};
        500 	
        501 	/**
        502 	 * Returns a resolved URI of a provided relative URI against the URI of the instance.
        503 	 * 
        504 	 * @param {String} uri The relative URI to resolve
        505 	 * @returns {String} The resolved URI
        506 	 */
        507 	
        508 	JSONInstance.prototype.resolveURI = function (uri) {
        509 		return formatURI(URI.resolve(this._uri, uri));
        510 	};
        511 	
        512 	/**
        513 	 * Returns an array of the names of all the properties.
        514 	 * 
        515 	 * @returns {Array} An array of strings which are the names of all the properties
        516 	 */
        517 	
        518 	JSONInstance.prototype.getPropertyNames = function () {
        519 		return keys(this._value);
        520 	};
        521 	
        522 	/**
        523 	 * Returns a {@link JSONInstance} of the value of the provided property name. 
        524 	 * 
        525 	 * @param {String} key The name of the property to fetch
        526 	 * @returns {JSONInstance} The instance of the property value
        527 	 */
        528 	
        529 	JSONInstance.prototype.getProperty = function (key) {
        530 		var value = this._value ? this._value[key] : undefined;
        531 		if (value instanceof JSONInstance) {
        532 			return value;
        533 		}
        534 		//else
        535 		return new JSONInstance(this._env, value, this._uri + this._fd + escapeURIComponent(key), this._fd);
        536 	};
        537 	
        538 	/**
        539 	 * Returns all the property instances of the target instance.
        540 	 * <p>
        541 	 * If the target instance is an Object, then the method will return a hash table of {@link JSONInstance}s of all the properties. 
        542 	 * If the target instance is an Array, then the method will return an array of {@link JSONInstance}s of all the items.
        543 	 * </p> 
        544 	 * 
        545 	 * @returns {Object|Array|undefined} The list of instances for all the properties
        546 	 */
        547 	
        548 	JSONInstance.prototype.getProperties = function () {
        549 		var type = typeOf(this._value),
        550 			self = this;
        551 		
        552 		if (type === "object") {
        553 			return mapObject(this._value, function (value, key) {
        554 				if (value instanceof JSONInstance) {
        555 					return value;
        556 				}
        557 				return new JSONInstance(self._env, value, self._uri + self._fd + escapeURIComponent(key), self._fd);
        558 			});
        559 		} else if (type === "array") {
        560 			return mapArray(this._value, function (value, key) {
        561 				if (value instanceof JSONInstance) {
        562 					return value;
        563 				}
        564 				return new JSONInstance(self._env, value, self._uri + self._fd + escapeURIComponent(key), self._fd);
        565 			});
        566 		}
        567 	};
        568 	
        569 	/**
        570 	 * Returns the JSON value of the provided property name. 
        571 	 * This method is a faster version of calling <code>instance.getProperty(key).getValue()</code>.
        572 	 * 
        573 	 * @param {String} key The name of the property
        574 	 * @returns {Any} The JavaScript value of the instance
        575 	 * @see JSONInstance#getProperty
        576 	 * @see JSONInstance#getValue
        577 	 */
        578 	
        579 	JSONInstance.prototype.getValueOfProperty = function (key) {
        580 		if (this._value) {
        581 			if (this._value[key] instanceof JSONInstance) {
        582 				return this._value[key]._value;
        583 			}
        584 			return this._value[key];
        585 		}
        586 	};
        587 	
        588 	/**
        589 	 * Return if the provided value is the same as the value of the instance.
        590 	 * 
        591 	 * @param {JSONInstance|Any} instance The value to compare
        592 	 * @returns {Boolean} If both the instance and the value match
        593 	 */
        594 	
        595 	JSONInstance.prototype.equals = function (instance) {
        596 		if (instance instanceof JSONInstance) {
        597 			return this._value === instance._value;
        598 		}
        599 		//else
        600 		return this._value === instance;
        601 	};
        602 	
        603 	/**
        604 	 * Warning: Not a generic clone function
        605 	 * Produces a JSV acceptable clone
        606 	 */
        607 	
        608 	function clone(obj, deep) {
        609 		var newObj, x;
        610 		
        611 		if (obj instanceof JSONInstance) {
        612 			obj = obj.getValue();
        613 		}
        614 		
        615 		switch (typeOf(obj)) {
        616 		case "object":
        617 			if (deep) {
        618 				newObj = {};
        619 				for (x in obj) {
        620 					if (obj[x] !== O[x]) {
        621 						newObj[x] = clone(obj[x], deep);
        622 					}
        623 				}
        624 				return newObj;
        625 			} else {
        626 				return createObject(obj);
        627 			}
        628 			break;
        629 		case "array":
        630 			if (deep) {
        631 				newObj = new Array(obj.length);
        632 				x = obj.length;
        633 				while (--x >= 0) {
        634 					newObj[x] = clone(obj[x], deep);
        635 				}
        636 				return newObj;
        637 			} else {
        638 				return Array.prototype.slice.call(obj);
        639 			}
        640 			break;
        641 		default:
        642 			return obj;
        643 		}
        644 	}
        645 	
        646 	/**
        647 	 * This class binds a {@link JSONInstance} with a {@link JSONSchema} to provided context aware methods. 
        648 	 * 
        649 	 * @name JSONSchema
        650 	 * @class
        651 	 * @param {Environment} env The environment this schema belongs to
        652 	 * @param {JSONInstance|Any} json The value of the schema
        653 	 * @param {String} [uri] The URI of the schema. If undefined, the URI will be a randomly generated UUID. 
        654 	 * @param {JSONSchema|Boolean} [schema] The schema to bind to the instance. If <code>undefined</code>, the environment's default schema will be used. If <code>true</code>, the instance's schema will be itself.
        655 	 * @extends JSONInstance
        656 	 */
        657 	
        658 	function JSONSchema(env, json, uri, schema) {
        659 		var fr;
        660 		JSONInstance.call(this, env, json, uri);
        661 		
        662 		if (schema === true) {
        663 			this._schema = this;
        664 		} else if (json instanceof JSONSchema && !(schema instanceof JSONSchema)) {
        665 			this._schema = json._schema;  //TODO: Make sure cross environments don't mess everything up
        666 		} else {
        667 			this._schema = schema instanceof JSONSchema ? schema : this._env.getDefaultSchema() || this._env.createEmptySchema();
        668 		}
        669 		
        670 		//determine fragment delimiter from schema
        671 		fr = this._schema.getValueOfProperty("fragmentResolution");
        672 		if (fr === "dot-delimited") {
        673 			this._fd = ".";
        674 		} else if (fr === "slash-delimited") {
        675 			this._fd = "/";
        676 		}
        677 		
        678 		return this.rebuild();  //this works even when called with "new"
        679 	}
        680 	
        681 	JSONSchema.prototype = createObject(JSONInstance.prototype);
        682 	
        683 	/**
        684 	 * Returns the schema of the schema.
        685 	 * 
        686 	 * @returns {JSONSchema} The schema of the schema
        687 	 */
        688 	
        689 	JSONSchema.prototype.getSchema = function () {
        690 		var uri = this._refs && this._refs["describedby"],
        691 			newSchema;
        692 		
        693 		if (uri) {
        694 			newSchema = uri && this._env.findSchema(uri);
        695 			
        696 			if (newSchema) {
        697 				if (!newSchema.equals(this._schema)) {
        698 					this._schema = newSchema;
        699 					this.rebuild();  //if the schema has changed, the context has changed - so everything must be rebuilt
        700 				}
        701 			} else if (this._env._options["enforceReferences"]) {
        702 				throw new InitializationError(this, this._schema, "{describedby}", "Unknown schema reference", uri);
        703 			}
        704 		}
        705 		
        706 		return this._schema;
        707 	};
        708 	
        709 	/**
        710 	 * Returns the value of the provided attribute name.
        711 	 * <p>
        712 	 * This method is different from {@link JSONInstance#getProperty} as the named property 
        713 	 * is converted using a parser defined by the schema's schema before being returned. This
        714 	 * makes the return value of this method attribute dependent.
        715 	 * </p>
        716 	 * 
        717 	 * @param {String} key The name of the attribute
        718 	 * @param {Any} [arg] Some attribute parsers accept special arguments for returning resolved values. This is attribute dependent.
        719 	 * @returns {JSONSchema|Any} The value of the attribute
        720 	 */
        721 	
        722 	JSONSchema.prototype.getAttribute = function (key, arg) {
        723 		var schemaProperty, parser, property, result,
        724 			schema = this.getSchema();  //we do this here to make sure the "describedby" reference has not changed, and that the attribute cache is up-to-date
        725 		
        726 		if (!arg && this._attributes && this._attributes.hasOwnProperty(key)) {
        727 			return this._attributes[key];
        728 		}
        729 		
        730 		schemaProperty = schema.getProperty("properties").getProperty(key);
        731 		parser = schemaProperty.getValueOfProperty("parser");
        732 		property = this.getProperty(key);
        733 		if (typeof parser === "function") {
        734 			result = parser(property, schemaProperty, arg);
        735 			if (!arg && this._attributes) {
        736 				this._attributes[key] = result;
        737 			}
        738 			return result;
        739 		}
        740 		//else
        741 		return property.getValue();
        742 	};
        743 	
        744 	/**
        745 	 * Returns all the attributes of the schema.
        746 	 * 
        747 	 * @returns {Object} A map of all parsed attribute values
        748 	 */
        749 	
        750 	JSONSchema.prototype.getAttributes = function () {
        751 		var properties, schemaProperties, key, schemaProperty, parser,
        752 			schema = this.getSchema();  //we do this here to make sure the "describedby" reference has not changed, and that the attribute cache is up-to-date
        753 		
        754 		if (!this._attributes && this.getType() === "object") {
        755 			properties = this.getProperties();
        756 			schemaProperties = schema.getProperty("properties");
        757 			this._attributes = {};
        758 			for (key in properties) {
        759 				if (properties[key] !== O[key]) {
        760 					schemaProperty = schemaProperties && schemaProperties.getProperty(key);
        761 					parser = schemaProperty && schemaProperty.getValueOfProperty("parser");
        762 					if (typeof parser === "function") {
        763 						this._attributes[key] = parser(properties[key], schemaProperty);
        764 					} else {
        765 						this._attributes[key] = properties[key].getValue();
        766 					}
        767 				}
        768 			}
        769 		}
        770 		
        771 		return clone(this._attributes, false);
        772 	};
        773 	
        774 	/**
        775 	 * Convenience method for retrieving a link or link object from a schema. 
        776 	 * This method is the same as calling <code>schema.getAttribute("links", [rel, instance])[0];</code>.
        777 	 * 
        778 	 * @param {String} rel The link relationship
        779 	 * @param {JSONInstance} [instance] The instance to resolve any URIs from
        780 	 * @returns {String|Object|undefined} If <code>instance</code> is provided, a string containing the resolve URI of the link is returned.
        781 	 *   If <code>instance</code> is not provided, a link object is returned with details of the link.
        782 	 *   If no link with the provided relationship exists, <code>undefined</code> is returned.
        783 	 * @see JSONSchema#getAttribute
        784 	 */
        785 	
        786 	JSONSchema.prototype.getLink = function (rel, instance) {
        787 		var schemaLinks = this.getAttribute("links", [rel, instance]);
        788 		if (schemaLinks && schemaLinks.length && schemaLinks[schemaLinks.length - 1]) {
        789 			return schemaLinks[schemaLinks.length - 1];
        790 		}
        791 	};
        792 	
        793 	/**
        794 	 * Validates the provided instance against the target schema and returns a {@link Report}.
        795 	 * 
        796 	 * @param {JSONInstance|Any} instance The instance to validate; may be a {@link JSONInstance} or any JavaScript value
        797 	 * @param {Report} [report] A {@link Report} to concatenate the result of the validation to. If <code>undefined</code>, a new {@link Report} is created. 
        798 	 * @param {JSONInstance} [parent] The parent/containing instance of the provided instance
        799 	 * @param {JSONSchema} [parentSchema] The schema of the parent/containing instance
        800 	 * @param {String} [name] The name of the parent object's property that references the instance
        801 	 * @returns {Report} The result of the validation
        802 	 */
        803 	
        804 	JSONSchema.prototype.validate = function (instance, report, parent, parentSchema, name) {
        805 		var schemaSchema = this.getSchema(),
        806 			validator = schemaSchema.getValueOfProperty("validator");
        807 		
        808 		if (!(instance instanceof JSONInstance)) {
        809 			instance = this.getEnvironment().createInstance(instance);
        810 		}
        811 		
        812 		if (!(report instanceof Report)) {
        813 			report = new Report();
        814 		}
        815 		
        816 		if (this._env._options["validateReferences"] && this._refs) {
        817 			if (this._refs["describedby"] && !this._env.findSchema(this._refs["describedby"])) {
        818 				report.addError(this, this._schema, "{describedby}", "Unknown schema reference", this._refs["describedby"]);
        819 			}
        820 			if (this._refs["full"] && !this._env.findSchema(this._refs["full"])) {
        821 				report.addError(this, this._schema, "{full}", "Unknown schema reference", this._refs["full"]);
        822 			}
        823 		}
        824 		
        825 		if (typeof validator === "function" && !report.isValidatedBy(instance.getURI(), this.getURI())) {
        826 			report.registerValidation(instance.getURI(), this.getURI());
        827 			validator(instance, this, schemaSchema, report, parent, parentSchema, name);
        828 		}
        829 		
        830 		return report;
        831 	};
        832 	
        833 	/** @inner */
        834 	function createFullLookupWrapper(func) {
        835 		return /** @inner */ function fullLookupWrapper() {
        836 			var scope = this,
        837 				stack = [],
        838 				uri = scope._refs && scope._refs["full"],
        839 				schema;
        840 			
        841 			while (uri) {
        842 				schema = scope._env.findSchema(uri);
        843 				if (schema) {
        844 					if (schema._value === scope._value) {
        845 						break;
        846 					}
        847 					scope = schema;
        848 					stack.push(uri);
        849 					uri = scope._refs && scope._refs["full"];
        850 					if (stack.indexOf(uri) > -1) {
        851 						break;  //stop infinite loop
        852 					}
        853 				} else if (scope._env._options["enforceReferences"]) {
        854 					throw new InitializationError(scope, scope._schema, "{full}", "Unknown schema reference", uri);
        855 				} else {
        856 					uri = null;
        857 				}
        858 			}
        859 			return func.apply(scope, arguments);
        860 		};
        861 	}
        862 	
        863 	/**
        864 	 * Wraps all JSONInstance methods with a function that resolves the "full" reference.
        865 	 * 
        866 	 * @inner
        867 	 */
        868 	
        869 	(function () {
        870 		var key;
        871 		for (key in JSONSchema.prototype) {
        872 			if (JSONSchema.prototype[key] !== O[key] && typeOf(JSONSchema.prototype[key]) === "function") {
        873 				JSONSchema.prototype[key] = createFullLookupWrapper(JSONSchema.prototype[key]);
        874 			}
        875 		}
        876 	}());
        877 	
        878 	/**
        879 	 * Reinitializes/re-registers/rebuilds the schema.
        880 	 * <br/>
        881 	 * This is used internally, and should only be called when a schema's private variables are modified directly.
        882 	 * 
        883 	 * @private
        884 	 * @return {JSONSchema} The newly rebuilt schema
        885 	 */
        886 	
        887 	JSONSchema.prototype.rebuild = function () {
        888 		var instance = this,
        889 			initializer = instance.getSchema().getValueOfProperty("initializer");
        890 		
        891 		//clear previous built values
        892 		instance._refs = null;
        893 		instance._attributes = null;
        894 		
        895 		if (typeof initializer === "function") {
        896 			instance = initializer(instance);
        897 		}
        898 		
        899 		//register schema
        900 		instance._env._schemas[instance._uri] = instance;
        901 		
        902 		//build & cache the rest of the schema
        903 		instance.getAttributes();
        904 		
        905 		return instance;
        906 	};
        907 	
        908 	/**
        909 	 * Set the provided reference to the given value.
        910 	 * <br/>
        911 	 * References are used for establishing soft-links to other {@link JSONSchema}s.
        912 	 * Currently, the following references are natively supported:
        913 	 * <dl>
        914 	 *   <dt><code>full</code></dt>
        915 	 *   <dd>The value is the URI to the full instance of this instance.</dd>
        916 	 *   <dt><code>describedby</code></dt>
        917 	 *   <dd>The value is the URI to the schema of this instance.</dd>
        918 	 * </dl>
        919 	 * 
        920 	 * @param {String} name The name of the reference
        921 	 * @param {String} uri The URI of the schema to refer to
        922 	 */
        923 	
        924 	JSONSchema.prototype.setReference = function (name, uri) {
        925 		if (!this._refs) {
        926 			this._refs = {};
        927 		}
        928 		this._refs[name] = this.resolveURI(uri);
        929 	};
        930 	
        931 	/**
        932 	 * Returns the value of the provided reference name.
        933 	 * 
        934 	 * @param {String} name The name of the reference
        935 	 * @return {String} The value of the provided reference name
        936 	 */
        937 	
        938 	JSONSchema.prototype.getReference = function (name) {
        939 		return this._refs && this._refs[name];
        940 	};
        941 	
        942 	/**
        943 	 * Merges two schemas/instances together.
        944 	 */
        945 	
        946 	function inherits(base, extra, extension) {
        947 		var baseType = typeOf(base),
        948 			extraType = typeOf(extra),
        949 			child, x;
        950 		
        951 		if (extraType === "undefined") {
        952 			return clone(base, true);
        953 		} else if (baseType === "undefined" || extraType !== baseType) {
        954 			return clone(extra, true);
        955 		} else if (extraType === "object") {
        956 			if (base instanceof JSONSchema) {
        957 				base = base.getAttributes();
        958 			}
        959 			if (extra instanceof JSONSchema) {
        960 				extra = extra.getAttributes();
        961 				if (extra["extends"] && extension && extra["extends"] instanceof JSONSchema) {
        962 					extra["extends"] = [ extra["extends"] ];
        963 				}
        964 			}
        965 			child = clone(base, true);  //this could be optimized as some properties get overwritten
        966 			for (x in extra) {
        967 				if (extra[x] !== O[x]) {
        968 					child[x] = inherits(base[x], extra[x], extension);
        969 				}
        970 			}
        971 			return child;
        972 		} else {
        973 			return clone(extra, true);
        974 		}
        975 	}
        976 	
        977 	/**
        978 	 * An Environment is a sandbox of schemas thats behavior is different from other environments.
        979 	 * 
        980 	 * @name Environment
        981 	 * @class
        982 	 */
        983 	
        984 	function Environment() {
        985 		this._id = randomUUID();
        986 		this._schemas = {};
        987 		this._options = {};
        988 		
        989 		this.createSchema({}, true, "urn:jsv:empty-schema#");
        990 	}
        991 	
        992 	/**
        993 	 * Returns a clone of the target environment.
        994 	 * 
        995 	 * @returns {Environment} A new {@link Environment} that is a exact copy of the target environment 
        996 	 */
        997 	
        998 	Environment.prototype.clone = function () {
        999 		var env = new Environment();
        1000 		env._schemas = createObject(this._schemas);
        1001 		env._options = createObject(this._options);
        1002 		
        1003 		return env;
        1004 	};
        1005 	
        1006 	/**
        1007 	 * Returns a new {@link JSONInstance} of the provided data.
        1008 	 * 
        1009 	 * @param {JSONInstance|Any} data The value of the instance
        1010 	 * @param {String} [uri] The URI of the instance. If undefined, the URI will be a randomly generated UUID. 
        1011 	 * @returns {JSONInstance} A new {@link JSONInstance} from the provided data
        1012 	 */
        1013 	
        1014 	Environment.prototype.createInstance = function (data, uri) {
        1015 		uri = formatURI(uri);
        1016 		
        1017 		if (data instanceof JSONInstance && (!uri || data.getURI() === uri)) {
        1018 			return data;
        1019 		}
        1020 
        1021 		return new JSONInstance(this, data, uri);
        1022 	};
        1023 	
        1024 	/**
        1025 	 * Creates a new {@link JSONSchema} from the provided data, and registers it with the environment. 
        1026 	 * 
        1027 	 * @param {JSONInstance|Any} data The value of the schema
        1028 	 * @param {JSONSchema|Boolean} [schema] The schema to bind to the instance. If <code>undefined</code>, the environment's default schema will be used. If <code>true</code>, the instance's schema will be itself.
        1029 	 * @param {String} [uri] The URI of the schema. If undefined, the URI will be a randomly generated UUID. 
        1030 	 * @returns {JSONSchema} A new {@link JSONSchema} from the provided data
        1031 	 * @throws {InitializationError} If a schema that is not registered with the environment is referenced 
        1032 	 */
        1033 	
        1034 	Environment.prototype.createSchema = function (data, schema, uri) {
        1035 		uri = formatURI(uri);
        1036 		
        1037 		if (data instanceof JSONSchema && (!uri || data._uri === uri) && (!schema || data.getSchema().equals(schema))) {
        1038 			return data;
        1039 		}
        1040 		
        1041 		return new JSONSchema(this, data, uri, schema);
        1042 	};
        1043 	
        1044 	/**
        1045 	 * Creates an empty schema.
        1046 	 * 
        1047 	 * @returns {JSONSchema} The empty schema, who's schema is itself.
        1048 	 */
        1049 	
        1050 	Environment.prototype.createEmptySchema = function () {
        1051 		return this._schemas["urn:jsv:empty-schema#"];
        1052 	};
        1053 	
        1054 	/**
        1055 	 * Returns the schema registered with the provided URI.
        1056 	 * 
        1057 	 * @param {String} uri The absolute URI of the required schema
        1058 	 * @returns {JSONSchema|undefined} The request schema, or <code>undefined</code> if not found
        1059 	 */
        1060 	
        1061 	Environment.prototype.findSchema = function (uri) {
        1062 		return this._schemas[formatURI(uri)];
        1063 	};
        1064 	
        1065 	/**
        1066 	 * Sets the specified environment option to the specified value.
        1067 	 * 
        1068 	 * @param {String} name The name of the environment option to set
        1069 	 * @param {Any} value The new value of the environment option
        1070 	 */
        1071 	
        1072 	Environment.prototype.setOption = function (name, value) {
        1073 		this._options[name] = value;
        1074 	};
        1075 	
        1076 	/**
        1077 	 * Returns the specified environment option.
        1078 	 * 
        1079 	 * @param {String} name The name of the environment option to set
        1080 	 * @returns {Any} The value of the environment option
        1081 	 */
        1082 	
        1083 	Environment.prototype.getOption = function (name) {
        1084 		return this._options[name];
        1085 	};
        1086 	
        1087 	/**
        1088 	 * Sets the default fragment delimiter of the environment.
        1089 	 * 
        1090 	 * @deprecated Use {@link Environment#setOption} with option "defaultFragmentDelimiter"
        1091 	 * @param {String} fd The fragment delimiter character
        1092 	 */
        1093 	
        1094 	Environment.prototype.setDefaultFragmentDelimiter = function (fd) {
        1095 		if (typeof fd === "string" && fd.length > 0) {
        1096 			this._options["defaultFragmentDelimiter"] = fd;
        1097 		}
        1098 	};
        1099 	
        1100 	/**
        1101 	 * Returns the default fragment delimiter of the environment.
        1102 	 * 
        1103 	 * @deprecated Use {@link Environment#getOption} with option "defaultFragmentDelimiter"
        1104 	 * @returns {String} The fragment delimiter character
        1105 	 */
        1106 	
        1107 	Environment.prototype.getDefaultFragmentDelimiter = function () {
        1108 		return this._options["defaultFragmentDelimiter"];
        1109 	};
        1110 	
        1111 	/**
        1112 	 * Sets the URI of the default schema for the environment.
        1113 	 * 
        1114 	 * @deprecated Use {@link Environment#setOption} with option "defaultSchemaURI"
        1115 	 * @param {String} uri The default schema URI
        1116 	 */
        1117 	
        1118 	Environment.prototype.setDefaultSchemaURI = function (uri) {
        1119 		if (typeof uri === "string") {
        1120 			this._options["defaultSchemaURI"] = formatURI(uri);
        1121 		}
        1122 	};
        1123 	
        1124 	/**
        1125 	 * Returns the default schema of the environment.
        1126 	 * 
        1127 	 * @returns {JSONSchema} The default schema
        1128 	 */
        1129 	
        1130 	Environment.prototype.getDefaultSchema = function () {
        1131 		return this.findSchema(this._options["defaultSchemaURI"]);
        1132 	};
        1133 	
        1134 	/**
        1135 	 * Validates both the provided schema and the provided instance, and returns a {@link Report}. 
        1136 	 * If the schema fails to validate, the instance will not be validated.
        1137 	 * 
        1138 	 * @param {JSONInstance|Any} instanceJSON The {@link JSONInstance} or JavaScript value to validate.
        1139 	 * @param {JSONSchema|Any} schemaJSON The {@link JSONSchema} or JavaScript value to use in the validation. This will also be validated againt the schema's schema.
        1140 	 * @returns {Report} The result of the validation
        1141 	 */
        1142 	
        1143 	Environment.prototype.validate = function (instanceJSON, schemaJSON) {
        1144 		var instance,
        1145 			schema,
        1146 			schemaSchema,
        1147 			report = new Report();
        1148 		
        1149 		try {
        1150 			instance = this.createInstance(instanceJSON);
        1151 			report.instance = instance;
        1152 		} catch (e) {
        1153 			report.addError(e.uri, e.schemaUri, e.attribute, e.message, e.details);
        1154 		}
        1155 		
        1156 		try {
        1157 			schema = this.createSchema(schemaJSON);
        1158 			report.schema = schema;
        1159 			
        1160 			schemaSchema = schema.getSchema();
        1161 			report.schemaSchema = schemaSchema;
        1162 		} catch (f) {
        1163 			report.addError(f.uri, f.schemaUri, f.attribute, f.message, f.details);
        1164 		}
        1165 		
        1166 		if (schemaSchema) {
        1167 			schemaSchema.validate(schema, report);
        1168 		}
        1169 			
        1170 		if (report.errors.length) {
        1171 			return report;
        1172 		}
        1173 		
        1174 		return schema.validate(instance, report);
        1175 	};
        1176 	
        1177 	/**
        1178 	 * @private
        1179 	 */
        1180 	
        1181 	Environment.prototype._checkForInvalidInstances = function (stackSize, schemaURI) {
        1182 		var result = [],
        1183 			stack = [
        1184 				[schemaURI, this._schemas[schemaURI]]
        1185 			], 
        1186 			counter = 0,
        1187 			item, uri, instance, properties, key;
        1188 		
        1189 		while (counter++ < stackSize && stack.length) {
        1190 			item = stack.shift();
        1191 			uri = item[0];
        1192 			instance = item[1];
        1193 			
        1194 			if (instance instanceof JSONSchema) {
        1195 				if (this._schemas[instance._uri] !== instance) {
        1196 					result.push("Instance " + uri + " does not match " + instance._uri);
        1197 				} else {
        1198 					//schema = instance.getSchema();
        1199 					//stack.push([uri + "/{schema}", schema]);
        1200 					
        1201 					properties = instance.getAttributes();
        1202 					for (key in properties) {
        1203 						if (properties[key] !== O[key]) {
        1204 							stack.push([uri + "/" + escapeURIComponent(key), properties[key]]);
        1205 						}
        1206 					}
        1207 				}
        1208 			} else if (typeOf(instance) === "object") {
        1209 				properties = instance;
        1210 				for (key in properties) {
        1211 					if (properties.hasOwnProperty(key)) {
        1212 						stack.push([uri + "/" + escapeURIComponent(key), properties[key]]);
        1213 					}
        1214 				}
        1215 			} else if (typeOf(instance) === "array") {
        1216 				properties = instance;
        1217 				for (key = 0; key < properties.length; ++key) {
        1218 					stack.push([uri + "/" + escapeURIComponent(key), properties[key]]);
        1219 				}
        1220 			}
        1221 		}
        1222 		
        1223 		return result.length ? result : counter;
        1224 	};
        1225 	
        1226 	/**
        1227 	 * A globaly accessible object that provides the ability to create and manage {@link Environments},
        1228 	 * as well as providing utility methods.
        1229 	 * 
        1230 	 * @namespace
        1231 	 */
        1232 	
        1233 	JSV = {
        1234 		_environments : {},
        1235 		_defaultEnvironmentID : "",
        1236 		
        1237 		/**
        1238 		 * Returns if the provide value is an instance of {@link JSONInstance}.
        1239 		 * 
        1240 		 * @param o The value to test
        1241 		 * @returns {Boolean} If the provide value is an instance of {@link JSONInstance}
        1242 		 */
        1243 		
        1244 		isJSONInstance : function (o) {
        1245 			return o instanceof JSONInstance;
        1246 		},
        1247 		
        1248 		/**
        1249 		 * Returns if the provide value is an instance of {@link JSONSchema}.
        1250 		 * 
        1251 		 * @param o The value to test
        1252 		 * @returns {Boolean} If the provide value is an instance of {@link JSONSchema}
        1253 		 */
        1254 		
        1255 		isJSONSchema : function (o) {
        1256 			return o instanceof JSONSchema;
        1257 		},
        1258 		
        1259 		/**
        1260 		 * Creates and returns a new {@link Environment} that is a clone of the environment registered with the provided ID.
        1261 		 * If no environment ID is provided, the default environment is cloned.
        1262 		 * 
        1263 		 * @param {String} [id] The ID of the environment to clone. If <code>undefined</code>, the default environment ID is used.
        1264 		 * @returns {Environment} A newly cloned {@link Environment}
        1265 		 * @throws {Error} If there is no environment registered with the provided ID
        1266 		 */
        1267 		
        1268 		createEnvironment : function (id) {
        1269 			id = id || this._defaultEnvironmentID;
        1270 			
        1271 			if (!this._environments[id]) {
        1272 				throw new Error("Unknown Environment ID");
        1273 			}
        1274 			//else
        1275 			return this._environments[id].clone();
        1276 		},
        1277 		
        1278 		Environment : Environment,
        1279 		
        1280 		/**
        1281 		 * Registers the provided {@link Environment} with the provided ID.
        1282 		 * 
        1283 		 * @param {String} id The ID of the environment
        1284 		 * @param {Environment} env The environment to register
        1285 		 */
        1286 		
        1287 		registerEnvironment : function (id, env) {
        1288 			id = id || (env || 0)._id;
        1289 			if (id && !this._environments[id] && env instanceof Environment) {
        1290 				env._id = id;
        1291 				this._environments[id] = env;
        1292 			}
        1293 		},
        1294 		
        1295 		/**
        1296 		 * Sets which registered ID is the default environment.
        1297 		 * 
        1298 		 * @param {String} id The ID of the registered environment that is default
        1299 		 * @throws {Error} If there is no registered environment with the provided ID
        1300 		 */
        1301 		
        1302 		setDefaultEnvironmentID : function (id) {
        1303 			if (typeof id === "string") {
        1304 				if (!this._environments[id]) {
        1305 					throw new Error("Unknown Environment ID");
        1306 				}
        1307 				
        1308 				this._defaultEnvironmentID = id;
        1309 			}
        1310 		},
        1311 		
        1312 		/**
        1313 		 * Returns the ID of the default environment.
        1314 		 * 
        1315 		 * @returns {String} The ID of the default environment
        1316 		 */
        1317 		
        1318 		getDefaultEnvironmentID : function () {
        1319 			return this._defaultEnvironmentID;
        1320 		},
        1321 		
        1322 		//
        1323 		// Utility Functions
        1324 		//
        1325 		
        1326 		/**
        1327 		 * Returns the name of the type of the provided value.
        1328 		 *
        1329 		 * @event //utility
        1330 		 * @param {Any} o The value to determine the type of
        1331 		 * @returns {String} The name of the type of the value
        1332 		 */
        1333 		typeOf : typeOf,
        1334 		
        1335 		/**
        1336 		 * Return a new object that inherits all of the properties of the provided object.
        1337 		 *
        1338 		 * @event //utility
        1339 		 * @param {Object} proto The prototype of the new object
        1340 		 * @returns {Object} A new object that inherits all of the properties of the provided object
        1341 		 */
        1342 		createObject : createObject,
        1343 		
        1344 		/**
        1345 		 * Returns a new object with each property transformed by the iterator.
        1346 		 *
        1347 		 * @event //utility
        1348 		 * @param {Object} obj The object to transform
        1349 		 * @param {Function} iterator A function that returns the new value of the provided property
        1350 		 * @param {Object} [scope] The value of <code>this</code> in the iterator
        1351 		 * @returns {Object} A new object with each property transformed
        1352 		 */
        1353 		mapObject : mapObject,
        1354 		
        1355 		/**
        1356 		 * Returns a new array with each item transformed by the iterator.
        1357 		 * 
        1358 		 * @event //utility
        1359 		 * @param {Array} arr The array to transform
        1360 		 * @param {Function} iterator A function that returns the new value of the provided item
        1361 		 * @param {Object} scope The value of <code>this</code> in the iterator
        1362 		 * @returns {Array} A new array with each item transformed
        1363 		 */
        1364 		mapArray : mapArray,
        1365 		
        1366 		/**
        1367 		 * Returns a new array that only contains the items allowed by the iterator.
        1368 		 *
        1369 		 * @event //utility
        1370 		 * @param {Array} arr The array to filter
        1371 		 * @param {Function} iterator The function that returns true if the provided property should be added to the array
        1372 		 * @param {Object} scope The value of <code>this</code> within the iterator
        1373 		 * @returns {Array} A new array that contains the items allowed by the iterator
        1374 		 */
        1375 		filterArray : filterArray,
        1376 		
        1377 		/**
        1378 		 * Returns the first index in the array that the provided item is located at.
        1379 		 *
        1380 		 * @event //utility
        1381 		 * @param {Array} arr The array to search
        1382 		 * @param {Any} o The item being searched for
        1383 		 * @returns {Number} The index of the item in the array, or <code>-1</code> if not found
        1384 		 */
        1385 		searchArray : searchArray,
        1386 			
        1387 		/**
        1388 		 * Returns an array representation of a value.
        1389 		 * <ul>
        1390 		 * <li>For array-like objects, the value will be casted as an Array type.</li>
        1391 		 * <li>If an array is provided, the function will simply return the same array.</li>
        1392 		 * <li>For a null or undefined value, the result will be an empty Array.</li>
        1393 		 * <li>For all other values, the value will be the first element in a new Array. </li>
        1394 		 * </ul>
        1395 		 *
        1396 		 * @event //utility
        1397 		 * @param {Any} o The value to convert into an array
        1398 		 * @returns {Array} The value as an array
        1399 		 */
        1400 		toArray : toArray,
        1401 		
        1402 		/**
        1403 		 * Returns an array of the names of all properties of an object.
        1404 		 * 
        1405 		 * @event //utility
        1406 		 * @param {Object|Array} o The object in question
        1407 		 * @returns {Array} The names of all properties
        1408 		 */
        1409 		keys : keys,
        1410 		
        1411 		/**
        1412 		 * Mutates the array by pushing the provided value onto the array only if it is not already there.
        1413 		 *
        1414 		 * @event //utility
        1415 		 * @param {Array} arr The array to modify
        1416 		 * @param {Any} o The object to add to the array if it is not already there
        1417 		 * @returns {Array} The provided array for chaining
        1418 		 */
        1419 		pushUnique : pushUnique,
        1420 		
        1421 		/**
        1422 		 * Mutates the array by removing the first item that matches the provided value in the array.
        1423 		 *
        1424 		 * @event //utility
        1425 		 * @param {Array} arr The array to modify
        1426 		 * @param {Any} o The object to remove from the array
        1427 		 * @returns {Array} The provided array for chaining
        1428 		 */
        1429 		popFirst : popFirst,
        1430 		
        1431 		/**
        1432 		 * Creates a copy of the target object.
        1433 		 * <p>
        1434 		 * This method will create a new instance of the target, and then mixin the properties of the target.
        1435 		 * If <code>deep</code> is <code>true</code>, then each property will be cloned before mixin.
        1436 		 * </p>
        1437 		 * <p><b>Warning</b>: This is not a generic clone function, as it will only properly clone objects and arrays.</p>
        1438 		 * 
        1439 		 * @event //utility
        1440 		 * @param {Any} o The value to clone 
        1441 		 * @param {Boolean} [deep=false] If each property should be recursively cloned
        1442 		 * @returns A cloned copy of the provided value
        1443 		 */
        1444 		clone : clone,
        1445 		
        1446 		/**
        1447 		 * Generates a pseudo-random UUID.
        1448 		 * 
        1449 		 * @event //utility
        1450 		 * @returns {String} A new universally unique ID
        1451 		 */
        1452 		randomUUID : randomUUID,
        1453 		
        1454 		/**
        1455 		 * Properly escapes a URI component for embedding into a URI string.
        1456 		 * 
        1457 		 * @event //utility
        1458 		 * @param {String} str The URI component to escape
        1459 		 * @returns {String} The escaped URI component
        1460 		 */
        1461 		escapeURIComponent : escapeURIComponent,
        1462 		
        1463 		/**
        1464 		 * Returns a URI that is formated for JSV. Currently, this only ensures that the URI ends with a hash tag (<code>#</code>).
        1465 		 * 
        1466 		 * @event //utility
        1467 		 * @param {String} uri The URI to format
        1468 		 * @returns {String} The URI formatted for JSV
        1469 		 */
        1470 		formatURI : formatURI,
        1471 		
        1472 		/**
        1473 		 * Merges two schemas/instance together.
        1474 		 * 
        1475 		 * @event //utility
        1476 		 * @param {JSONSchema|Any} base The old value to merge
        1477 		 * @param {JSONSchema|Any} extra The new value to merge
        1478 		 * @param {Boolean} extension If the merge is a JSON Schema extension
        1479 		 * @return {Any} The modified base value
        1480 		 */
        1481 		 
        1482 		inherits : inherits,
        1483 		
        1484 		/**
        1485 		 * @private
        1486 		 * @event //utility
        1487 		 */
        1488 		
        1489 		InitializationError : InitializationError
        1490 	};
        1491 	
        1492 	this.JSV = JSV;  //set global object
        1493 	exports.JSV = JSV;  //export to CommonJS
        1494 	
        1495 	require("./environments");  //load default environments
        1496 	
        1497 }());
        package/docs/symbols/InitializationError.html0000644000175000017500000001704711727717626020751 0ustar daviddavid JsDoc Reference - InitializationError
        Class Index | File Index

        Classes


        Class InitializationError


        Defined in: jsv.js.

        Class Summary
        Constructor Attributes Constructor Name and Description
         
        InitializationError(instance, schema, attr, message, details)
        The exception that is thrown when a schema fails to be created.
        Class Detail
        InitializationError(instance, schema, attr, message, details)
        The exception that is thrown when a schema fails to be created.
        Parameters:
        {JSONInstance|String} instance
        The instance (or instance URI) that is invalid
        {JSONSchema|String} schema
        The schema (or schema URI) that was validating the instance
        {String} attr
        The attribute that failed to validated
        {String} message
        A user-friendly message on why the schema attribute failed to validate the instance
        {Any} details
        The value of the schema attribute

        Documentation generated by JsDoc Toolkit 2.4.0 on Wed Sep 14 2011 18:53:24 GMT-0600 (MDT)
        package/docs/symbols/JSONInstance.html0000644000175000017500000004474011727717626017206 0ustar daviddavid JsDoc Reference - JSONInstance
        Class Index | File Index

        Classes


        Class JSONInstance


        Defined in: jsv.js.

        Class Summary
        Constructor Attributes Constructor Name and Description
         
        JSONInstance(env, json, uri, fd)
        A wrapper class for binding an Environment, URI and helper methods to an instance.
        Method Summary
        Method Attributes Method Name and Description
         
        equals(instance)
        Return if the provided value is the same as the value of the instance.
         
        Returns the environment the instance is bound to.
         
        Returns all the property instances of the target instance.
         
        Returns a JSONInstance of the value of the provided property name.
         
        Returns an array of the names of all the properties.
         
        Returns the name of the type of the instance.
         
        Returns the URI of the instance.
         
        Returns the JSON value of the instance.
         
        Returns the JSON value of the provided property name.
         
        Returns a resolved URI of a provided relative URI against the URI of the instance.
        Class Detail
        JSONInstance(env, json, uri, fd)
        A wrapper class for binding an Environment, URI and helper methods to an instance. This class is most commonly instantiated with Environment#createInstance.
        Parameters:
        {Environment} env
        The environment this instance belongs to
        {JSONInstance|Any} json
        The value of the instance
        {String} uri Optional
        The URI of the instance. If undefined, the URI will be a randomly generated UUID.
        {String} fd Optional
        The fragment delimiter for properties. If undefined, uses the environment default.
        Method Detail
        {Boolean} equals(instance)
        Return if the provided value is the same as the value of the instance.
        Parameters:
        {JSONInstance|Any} instance
        The value to compare
        Returns:
        {Boolean} If both the instance and the value match

        {Environment} getEnvironment()
        Returns the environment the instance is bound to.
        Returns:
        {Environment} The environment of the instance

        {Object|Array|undefined} getProperties()
        Returns all the property instances of the target instance.

        If the target instance is an Object, then the method will return a hash table of JSONInstances of all the properties. If the target instance is an Array, then the method will return an array of JSONInstances of all the items.

        Returns:
        {Object|Array|undefined} The list of instances for all the properties

        {JSONInstance} getProperty(key)
        Returns a JSONInstance of the value of the provided property name.
        Parameters:
        {String} key
        The name of the property to fetch
        Returns:
        {JSONInstance} The instance of the property value

        {Array} getPropertyNames()
        Returns an array of the names of all the properties.
        Returns:
        {Array} An array of strings which are the names of all the properties

        {String} getType()
        Returns the name of the type of the instance.
        Returns:
        {String} The name of the type of the instance

        {String} getURI()
        Returns the URI of the instance.
        Returns:
        {String} The URI of the instance

        {Any} getValue()
        Returns the JSON value of the instance.
        Returns:
        {Any} The actual JavaScript value of the instance

        {Any} getValueOfProperty(key)
        Returns the JSON value of the provided property name. This method is a faster version of calling instance.getProperty(key).getValue().
        Parameters:
        {String} key
        The name of the property
        Returns:
        {Any} The JavaScript value of the instance
        See:
        JSONInstance#getProperty
        JSONInstance#getValue

        {String} resolveURI(uri)
        Returns a resolved URI of a provided relative URI against the URI of the instance.
        Parameters:
        {String} uri
        The relative URI to resolve
        Returns:
        {String} The resolved URI

        Documentation generated by JsDoc Toolkit 2.4.0 on Wed Sep 14 2011 18:53:24 GMT-0600 (MDT)
        package/docs/symbols/_global_.html0000644000175000017500000001217511727717626016503 0ustar daviddavid JsDoc Reference - _global_
        Class Index | File Index

        Classes


        Built-In Namespace _global_


        Documentation generated by JsDoc Toolkit 2.4.0 on Wed Sep 14 2011 18:53:23 GMT-0600 (MDT)
        package/docs/symbols/Error.html0000644000175000017500000001225711727717626016037 0ustar daviddavid JsDoc Reference - Error
        Class Index | File Index

        Classes


        Built-In Namespace Error


        Documentation generated by JsDoc Toolkit 2.4.0 on Mon Aug 22 2011 14:52:01 GMT-0600 (MDT)
        package/docs/symbols/JSV.html0000644000175000017500000010757711727717626015422 0ustar daviddavid JsDoc Reference - JSV
        Class Index | File Index

        Classes


        Namespace JSV


        Defined in: jsv.js.

        Namespace Summary
        Constructor Attributes Constructor Name and Description
         
        JSV
        A globaly accessible object that provides the ability to create and manage Environments, as well as providing utility methods.
        Method Summary
        Method Attributes Method Name and Description
        <static>  
        Creates and returns a new Environment that is a clone of the environment registered with the provided ID.
        <static>  
        Returns the ID of the default environment.
        <static>  
        Returns if the provide value is an instance of JSONInstance.
        <static>  
        JSV.isJSONSchema(o)
        Returns if the provide value is an instance of JSONSchema.
        <static>  
        JSV.registerEnvironment(id, env)
        Registers the provided Environment with the provided ID.
        <static>  
        Sets which registered ID is the default environment.
        Utility Method Summary
        Utility Method Attributes Utility Method Name and Description
        <static>  
        JSV.clone(o, deep)
        Creates a copy of the target object.
        <static>  
        JSV.createObject(proto)
        Return a new object that inherits all of the properties of the provided object.
        <static>  
        Properly escapes a URI component for embedding into a URI string.
        <static>  
        JSV.filterArray(arr, iterator, scope)
        Returns a new array that only contains the items allowed by the iterator.
        <static>  
        JSV.formatURI(uri)
        Returns a URI that is formated for JSV.
        <static>  
        JSV.inherits(base, extra, extension)
        Merges two schemas/instance together.
        <static>  
        JSV.keys(o)
        Returns an array of the names of all properties of an object.
        <static>  
        JSV.mapArray(arr, iterator, scope)
        Returns a new array with each item transformed by the iterator.
        <static>  
        JSV.mapObject(obj, iterator, scope)
        Returns a new object with each property transformed by the iterator.
        <static>  
        JSV.popFirst(arr, o)
        Mutates the array by removing the first item that matches the provided value in the array.
        <static>  
        JSV.pushUnique(arr, o)
        Mutates the array by pushing the provided value onto the array only if it is not already there.
        <static>  
        JSV.randomUUID()
        Generates a pseudo-random UUID.
        <static>  
        JSV.searchArray(arr, o)
        Returns the first index in the array that the provided item is located at.
        <static>  
        JSV.toArray(o)
        Returns an array representation of a value.
        <static>  
        JSV.typeOf(o)
        Returns the name of the type of the provided value.
        Namespace Detail
        JSV
        A globaly accessible object that provides the ability to create and manage Environments, as well as providing utility methods.
        Method Detail
        <static> {Environment} JSV.createEnvironment(id)
        Creates and returns a new Environment that is a clone of the environment registered with the provided ID. If no environment ID is provided, the default environment is cloned.
        Parameters:
        {String} id Optional
        The ID of the environment to clone. If undefined, the default environment ID is used.
        Throws:
        {Error}
        If there is no environment registered with the provided ID
        Returns:
        {Environment} A newly cloned Environment

        <static> {String} JSV.getDefaultEnvironmentID()
        Returns the ID of the default environment.
        Returns:
        {String} The ID of the default environment

        <static> {Boolean} JSV.isJSONInstance(o)
        Returns if the provide value is an instance of JSONInstance.
        Parameters:
        o
        The value to test
        Returns:
        {Boolean} If the provide value is an instance of JSONInstance

        <static> {Boolean} JSV.isJSONSchema(o)
        Returns if the provide value is an instance of JSONSchema.
        Parameters:
        o
        The value to test
        Returns:
        {Boolean} If the provide value is an instance of JSONSchema

        <static> JSV.registerEnvironment(id, env)
        Registers the provided Environment with the provided ID.
        Parameters:
        {String} id
        The ID of the environment
        {Environment} env
        The environment to register

        <static> JSV.setDefaultEnvironmentID(id)
        Sets which registered ID is the default environment.
        Parameters:
        {String} id
        The ID of the registered environment that is default
        Throws:
        {Error}
        If there is no registered environment with the provided ID
        Utility Method Detail
        <static> JSV.clone(o, deep)
        Creates a copy of the target object.

        This method will create a new instance of the target, and then mixin the properties of the target. If deep is true, then each property will be cloned before mixin.

        Warning: This is not a generic clone function, as it will only properly clone objects and arrays.

        Parameters:
        {Any} o
        The value to clone
        {Boolean} deep Optional, Default: false
        If each property should be recursively cloned
        Returns:
        A cloned copy of the provided value

        <static> {Object} JSV.createObject(proto)
        Return a new object that inherits all of the properties of the provided object.
        Parameters:
        {Object} proto
        The prototype of the new object
        Returns:
        {Object} A new object that inherits all of the properties of the provided object

        <static> {String} JSV.escapeURIComponent(str)
        Properly escapes a URI component for embedding into a URI string.
        Parameters:
        {String} str
        The URI component to escape
        Returns:
        {String} The escaped URI component

        <static> {Array} JSV.filterArray(arr, iterator, scope)
        Returns a new array that only contains the items allowed by the iterator.
        Parameters:
        {Array} arr
        The array to filter
        {Function} iterator
        The function that returns true if the provided property should be added to the array
        {Object} scope
        The value of this within the iterator
        Returns:
        {Array} A new array that contains the items allowed by the iterator

        <static> {String} JSV.formatURI(uri)
        Returns a URI that is formated for JSV. Currently, this only ensures that the URI ends with a hash tag (#).
        Parameters:
        {String} uri
        The URI to format
        Returns:
        {String} The URI formatted for JSV

        <static> {Any} JSV.inherits(base, extra, extension)
        Merges two schemas/instance together.
        Parameters:
        {JSONSchema|Any} base
        The old value to merge
        {JSONSchema|Any} extra
        The new value to merge
        {Boolean} extension
        If the merge is a JSON Schema extension
        Returns:
        {Any} The modified base value

        <static> {Array} JSV.keys(o)
        Returns an array of the names of all properties of an object.
        Parameters:
        {Object|Array} o
        The object in question
        Returns:
        {Array} The names of all properties

        <static> {Array} JSV.mapArray(arr, iterator, scope)
        Returns a new array with each item transformed by the iterator.
        Parameters:
        {Array} arr
        The array to transform
        {Function} iterator
        A function that returns the new value of the provided item
        {Object} scope
        The value of this in the iterator
        Returns:
        {Array} A new array with each item transformed

        <static> {Object} JSV.mapObject(obj, iterator, scope)
        Returns a new object with each property transformed by the iterator.
        Parameters:
        {Object} obj
        The object to transform
        {Function} iterator
        A function that returns the new value of the provided property
        {Object} scope Optional
        The value of this in the iterator
        Returns:
        {Object} A new object with each property transformed

        <static> {Array} JSV.popFirst(arr, o)
        Mutates the array by removing the first item that matches the provided value in the array.
        Parameters:
        {Array} arr
        The array to modify
        {Any} o
        The object to remove from the array
        Returns:
        {Array} The provided array for chaining

        <static> {Array} JSV.pushUnique(arr, o)
        Mutates the array by pushing the provided value onto the array only if it is not already there.
        Parameters:
        {Array} arr
        The array to modify
        {Any} o
        The object to add to the array if it is not already there
        Returns:
        {Array} The provided array for chaining

        <static> {String} JSV.randomUUID()
        Generates a pseudo-random UUID.
        Returns:
        {String} A new universally unique ID

        <static> {Number} JSV.searchArray(arr, o)
        Returns the first index in the array that the provided item is located at.
        Parameters:
        {Array} arr
        The array to search
        {Any} o
        The item being searched for
        Returns:
        {Number} The index of the item in the array, or -1 if not found

        <static> {Array} JSV.toArray(o)
        Returns an array representation of a value.
        • For array-like objects, the value will be casted as an Array type.
        • If an array is provided, the function will simply return the same array.
        • For a null or undefined value, the result will be an empty Array.
        • For all other values, the value will be the first element in a new Array.
        Parameters:
        {Any} o
        The value to convert into an array
        Returns:
        {Array} The value as an array

        <static> {String} JSV.typeOf(o)
        Returns the name of the type of the provided value.
        Parameters:
        {Any} o
        The value to determine the type of
        Returns:
        {String} The name of the type of the value

        Documentation generated by JsDoc Toolkit 2.4.0 on Wed Sep 14 2011 18:53:24 GMT-0600 (MDT)
        package/docs/symbols/Environment.html0000644000175000017500000005453611727717626017260 0ustar daviddavid JsDoc Reference - Environment
        Class Index | File Index

        Classes


        Class Environment


        Defined in: jsv.js.

        Class Summary
        Constructor Attributes Constructor Name and Description
         
        An Environment is a sandbox of schemas thats behavior is different from other environments.
        Method Summary
        Method Attributes Method Name and Description
         
        Returns a clone of the target environment.
         
        Creates an empty schema.
         
        createInstance(data, uri)
        Returns a new JSONInstance of the provided data.
         
        createSchema(data, schema, uri)
        Creates a new JSONSchema from the provided data, and registers it with the environment.
         
        Returns the schema registered with the provided URI.
         
        Returns the default fragment delimiter of the environment.
         
        Returns the default schema of the environment.
         
        getOption(name)
        Returns the specified environment option.
         
        Sets the default fragment delimiter of the environment.
         
        Sets the URI of the default schema for the environment.
         
        setOption(name, value)
        Sets the specified environment option to the specified value.
         
        validate(instanceJSON, schemaJSON)
        Validates both the provided schema and the provided instance, and returns a Report.
        Class Detail
        Environment()
        An Environment is a sandbox of schemas thats behavior is different from other environments.
        Method Detail
        {Environment} clone()
        Returns a clone of the target environment.
        Returns:
        {Environment} A new Environment that is a exact copy of the target environment

        {JSONSchema} createEmptySchema()
        Creates an empty schema.
        Returns:
        {JSONSchema} The empty schema, who's schema is itself.

        {JSONInstance} createInstance(data, uri)
        Returns a new JSONInstance of the provided data.
        Parameters:
        {JSONInstance|Any} data
        The value of the instance
        {String} uri Optional
        The URI of the instance. If undefined, the URI will be a randomly generated UUID.
        Returns:
        {JSONInstance} A new JSONInstance from the provided data

        {JSONSchema} createSchema(data, schema, uri)
        Creates a new JSONSchema from the provided data, and registers it with the environment.
        Parameters:
        {JSONInstance|Any} data
        The value of the schema
        {JSONSchema|Boolean} schema Optional
        The schema to bind to the instance. If undefined, the environment's default schema will be used. If true, the instance's schema will be itself.
        {String} uri Optional
        The URI of the schema. If undefined, the URI will be a randomly generated UUID.
        Throws:
        {InitializationError}
        If a schema that is not registered with the environment is referenced
        Returns:
        {JSONSchema} A new JSONSchema from the provided data

        {JSONSchema|undefined} findSchema(uri)
        Returns the schema registered with the provided URI.
        Parameters:
        {String} uri
        The absolute URI of the required schema
        Returns:
        {JSONSchema|undefined} The request schema, or undefined if not found

        {String} getDefaultFragmentDelimiter()
        Returns the default fragment delimiter of the environment.
        Deprecated:
        Use Environment#getOption with option "defaultFragmentDelimiter"
        Returns:
        {String} The fragment delimiter character

        {JSONSchema} getDefaultSchema()
        Returns the default schema of the environment.
        Returns:
        {JSONSchema} The default schema

        {Any} getOption(name)
        Returns the specified environment option.
        Parameters:
        {String} name
        The name of the environment option to set
        Returns:
        {Any} The value of the environment option

        setDefaultFragmentDelimiter(fd)
        Sets the default fragment delimiter of the environment.
        Parameters:
        {String} fd
        The fragment delimiter character
        Deprecated:
        Use Environment#setOption with option "defaultFragmentDelimiter"

        setDefaultSchemaURI(uri)
        Sets the URI of the default schema for the environment.
        Parameters:
        {String} uri
        The default schema URI
        Deprecated:
        Use Environment#setOption with option "defaultSchemaURI"

        setOption(name, value)
        Sets the specified environment option to the specified value.
        Parameters:
        {String} name
        The name of the environment option to set
        {Any} value
        The new value of the environment option

        {Report} validate(instanceJSON, schemaJSON)
        Validates both the provided schema and the provided instance, and returns a Report. If the schema fails to validate, the instance will not be validated.
        Parameters:
        {JSONInstance|Any} instanceJSON
        The JSONInstance or JavaScript value to validate.
        {JSONSchema|Any} schemaJSON
        The JSONSchema or JavaScript value to use in the validation. This will also be validated againt the schema's schema.
        Returns:
        {Report} The result of the validation

        Documentation generated by JsDoc Toolkit 2.4.0 on Wed Sep 14 2011 18:53:24 GMT-0600 (MDT)
        package/docs/symbols/JSONSchema.html0000644000175000017500000004641211727717626016640 0ustar daviddavid JsDoc Reference - JSONSchema
        Class Index | File Index

        Classes


        Class JSONSchema


        Extends JSONInstance.

        Defined in: jsv.js.

        Class Summary
        Constructor Attributes Constructor Name and Description
         
        JSONSchema(env, json, uri, schema)
        This class binds a JSONInstance with a JSONSchema to provided context aware methods.
        Method Summary
        Method Attributes Method Name and Description
         
        getAttribute(key, arg)
        Returns the value of the provided attribute name.
         
        Returns all the attributes of the schema.
         
        getLink(rel, instance)
        Convenience method for retrieving a link or link object from a schema.
         
        Returns the value of the provided reference name.
         
        Returns the schema of the schema.
         
        setReference(name, uri)
        Set the provided reference to the given value.
         
        validate(instance, report, parent, parentSchema, name)
        Validates the provided instance against the target schema and returns a Report.
        Methods borrowed from class JSONInstance:
        equals, getEnvironment, getProperties, getProperty, getPropertyNames, getType, getURI, getValue, getValueOfProperty, resolveURI
        Class Detail
        JSONSchema(env, json, uri, schema)
        This class binds a JSONInstance with a JSONSchema to provided context aware methods.
        Parameters:
        {Environment} env
        The environment this schema belongs to
        {JSONInstance|Any} json
        The value of the schema
        {String} uri Optional
        The URI of the schema. If undefined, the URI will be a randomly generated UUID.
        {JSONSchema|Boolean} schema Optional
        The schema to bind to the instance. If undefined, the environment's default schema will be used. If true, the instance's schema will be itself.
        Method Detail
        {JSONSchema|Any} getAttribute(key, arg)
        Returns the value of the provided attribute name.

        This method is different from JSONInstance#getProperty as the named property is converted using a parser defined by the schema's schema before being returned. This makes the return value of this method attribute dependent.

        Parameters:
        {String} key
        The name of the attribute
        {Any} arg Optional
        Some attribute parsers accept special arguments for returning resolved values. This is attribute dependent.
        Returns:
        {JSONSchema|Any} The value of the attribute

        {Object} getAttributes()
        Returns all the attributes of the schema.
        Returns:
        {Object} A map of all parsed attribute values

        {String|Object|undefined} getLink(rel, instance)
        Convenience method for retrieving a link or link object from a schema. This method is the same as calling schema.getAttribute("links", [rel, instance])[0];.
        Parameters:
        {String} rel
        The link relationship
        {JSONInstance} instance Optional
        The instance to resolve any URIs from
        Returns:
        {String|Object|undefined} If instance is provided, a string containing the resolve URI of the link is returned. If instance is not provided, a link object is returned with details of the link. If no link with the provided relationship exists, undefined is returned.
        See:
        JSONSchema#getAttribute

        {String} getReference(name)
        Returns the value of the provided reference name.
        Parameters:
        {String} name
        The name of the reference
        Returns:
        {String} The value of the provided reference name

        {JSONSchema} getSchema()
        Returns the schema of the schema.
        Returns:
        {JSONSchema} The schema of the schema

        setReference(name, uri)
        Set the provided reference to the given value.
        References are used for establishing soft-links to other JSONSchemas. Currently, the following references are natively supported:
        full
        The value is the URI to the full instance of this instance.
        describedby
        The value is the URI to the schema of this instance.
        Parameters:
        {String} name
        The name of the reference
        {String} uri
        The URI of the schema to refer to

        {Report} validate(instance, report, parent, parentSchema, name)
        Validates the provided instance against the target schema and returns a Report.
        Parameters:
        {JSONInstance|Any} instance
        The instance to validate; may be a JSONInstance or any JavaScript value
        {Report} report Optional
        A Report to concatenate the result of the validation to. If undefined, a new Report is created.
        {JSONInstance} parent Optional
        The parent/containing instance of the provided instance
        {JSONSchema} parentSchema Optional
        The schema of the parent/containing instance
        {String} name Optional
        The name of the parent object's property that references the instance
        Returns:
        {Report} The result of the validation

        Documentation generated by JsDoc Toolkit 2.4.0 on Wed Sep 14 2011 18:53:24 GMT-0600 (MDT)
        package/docs/index.html0000644000175000017500000001073211727717626014361 0ustar daviddavid JsDoc Reference - Index
        Class Index | File Index

        Classes


        Class Index

        Environment


        InitializationError


        JSONInstance


        JSONSchema


        JSV


        Report


        ValidationError


        Documentation generated by JsDoc Toolkit 2.4.0 on Wed Sep 14 2011 18:53:24 GMT-0600 (MDT)
        package/docs/files.html0000644000175000017500000001025311727717626014352 0ustar daviddavid JsDoc Reference - File Index
        Class Index | File Index

        Classes


        File Index

        jsv.js

        JSV: JSON Schema Validator A JavaScript implementation of a extendable, fully compliant JSON Schema validator.
        Author:
        Gary Court
        Version:
        4.0

        Documentation generated by JsDoc Toolkit 2.4.0 on Wed Sep 14 2011 18:53:24 GMT-0600 (MDT)
        package/.project0000644000175000017500000000031711457104345013065 0ustar daviddavid JSV package/examples/0000755000175000017500000000000011727717452013244 5ustar daviddavidpackage/examples/index.html0000644000175000017500000002262011727717626015246 0ustar daviddavid JSV: JSON Schema Validator

        JSV: JSON Schema Validator

        Input
        Schema
        package/CHANGELOG.md0000644000175000017500000000332011727717626013240 0ustar daviddavid# Changelog ## 4.0 (2011/08/23) * Added referencing to JSONSchema, allows for soft-linking to other schemas. * Environment option "validateReferences" now validates references at validation time for any environment. * Environment option "enforceReferences" will cause invalid references to throw an error at creation time for any environment. * Fixed floating point errors with "divisibleBy" attribute. * Fixed typo in JSV.clone. * Fixed typo in draft-03 hyper-schema.json. * Updated uri.js to latest version. * Fixed bug with Report#addError. (Identified by cellog) * ValidationError#details is now safe to stringify. * Removed JSONSchema#createEmptySchema. All empty schemas created by Environment#createEmptySchema now have the same URI (urn:jsv:empty-schema#). * Added example HTML page. ## 3.5 (2011/03/07) * Links to unregistered schemas will now throw an error. This can be disabled by setting the environment option "validateReferences" to false. * Environment#validate now catches errors (such as InitializationErrors) and adds them to it's returned report. * Report#addError now accepts instance/schema URI strings. * Fixed bug where empty schemas would not be registered. * Added private method Environment#_checkForInvalidInstances, for testing the reliability of circular schemas. * Fixed bug where schemas in the "disallow" attribute would not validate. (Identified by henchan) ## 3.4 (2011/03/06) * Fixed bug with "id", "$ref", "$schema" attributes not resolving properly under the "http://json-schema.org/draft-03/schema#" schema. (Identified by dougtreder) * Added dougtreder's referencing tests. ## Older Older releases were not documented. See git commit history for more information.package/lib/0000755000175000017500000000000011727717452012174 5ustar daviddavidpackage/lib/uri/0000755000175000017500000000000011727717452012773 5ustar daviddavidpackage/lib/uri/uri.js0000644000175000017500000005411411727717626014140 0ustar daviddavid/** * URI.js * * @fileoverview An RFC 3986 compliant, scheme extendable URI parsing/validating/resolving library for JavaScript. * @author Gary Court * @version 1.3 * @see http://github.com/garycourt/uri-js * @license URI.js v1.3 (c) 2010 Gary Court. License: http://github.com/garycourt/uri-js */ /** * Copyright 2010 Gary Court. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY GARY COURT ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARY COURT OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of Gary Court. */ /*jslint white: true, sub: true, onevar: true, undef: true, eqeqeq: true, newcap: true, immed: true, indent: 4 */ /*global exports:true, require:true */ if (typeof exports === "undefined") { exports = {}; } if (typeof require !== "function") { require = function (id) { return exports; }; } (function () { var /** * @param {...string} sets * @return {string} */ mergeSet = function (sets) { var set = arguments[0], x = 1, nextSet = arguments[x]; while (nextSet) { set = set.slice(0, -1) + nextSet.slice(1); nextSet = arguments[++x]; } return set; }, /** * @param {string} str * @return {string} */ subexp = function (str) { return "(?:" + str + ")"; }, ALPHA$$ = "[A-Za-z]", CR$ = "[\\x0D]", DIGIT$$ = "[0-9]", DQUOTE$$ = "[\\x22]", HEXDIG$$ = mergeSet(DIGIT$$, "[A-Fa-f]"), //case-insensitive LF$$ = "[\\x0A]", SP$$ = "[\\x20]", PCT_ENCODED$ = subexp("%" + HEXDIG$$ + HEXDIG$$), GEN_DELIMS$$ = "[\\:\\/\\?\\#\\[\\]\\@]", SUB_DELIMS$$ = "[\\!\\$\\&\\'\\(\\)\\*\\+\\,\\;\\=]", RESERVED$$ = mergeSet(GEN_DELIMS$$, SUB_DELIMS$$), UNRESERVED$$ = mergeSet(ALPHA$$, DIGIT$$, "[\\-\\.\\_\\~]"), SCHEME$ = subexp(ALPHA$$ + mergeSet(ALPHA$$, DIGIT$$, "[\\+\\-\\.]") + "*"), USERINFO$ = subexp(subexp(PCT_ENCODED$ + "|" + mergeSet(UNRESERVED$$, SUB_DELIMS$$, "[\\:]")) + "*"), DEC_OCTET$ = subexp(subexp("25[0-5]") + "|" + subexp("2[0-4]" + DIGIT$$) + "|" + subexp("1" + DIGIT$$ + DIGIT$$) + "|" + subexp("[1-9]" + DIGIT$$) + "|" + DIGIT$$), IPV4ADDRESS$ = subexp(DEC_OCTET$ + "\\." + DEC_OCTET$ + "\\." + DEC_OCTET$ + "\\." + DEC_OCTET$), H16$ = subexp(HEXDIG$$ + "{1,4}"), LS32$ = subexp(subexp(H16$ + "\\:" + H16$) + "|" + IPV4ADDRESS$), IPV6ADDRESS$ = subexp(mergeSet(UNRESERVED$$, SUB_DELIMS$$, "[\\:]") + "+"), //FIXME IPVFUTURE$ = subexp("v" + HEXDIG$$ + "+\\." + mergeSet(UNRESERVED$$, SUB_DELIMS$$, "[\\:]") + "+"), IP_LITERAL$ = subexp("\\[" + subexp(IPV6ADDRESS$ + "|" + IPVFUTURE$) + "\\]"), REG_NAME$ = subexp(subexp(PCT_ENCODED$ + "|" + mergeSet(UNRESERVED$$, SUB_DELIMS$$)) + "*"), HOST$ = subexp(IP_LITERAL$ + "|" + IPV4ADDRESS$ + "|" + REG_NAME$), PORT$ = subexp(DIGIT$$ + "*"), AUTHORITY$ = subexp(subexp(USERINFO$ + "@") + "?" + HOST$ + subexp("\\:" + PORT$) + "?"), PCHAR$ = subexp(PCT_ENCODED$ + "|" + mergeSet(UNRESERVED$$, SUB_DELIMS$$, "[\\:\\@]")), SEGMENT$ = subexp(PCHAR$ + "*"), SEGMENT_NZ$ = subexp(PCHAR$ + "+"), SEGMENT_NZ_NC$ = subexp(subexp(PCT_ENCODED$ + "|" + mergeSet(UNRESERVED$$, SUB_DELIMS$$, "[\\@]")) + "+"), PATH_ABEMPTY$ = subexp(subexp("\\/" + SEGMENT$) + "*"), PATH_ABSOLUTE$ = subexp("\\/" + subexp(SEGMENT_NZ$ + PATH_ABEMPTY$) + "?"), //simplified PATH_NOSCHEME$ = subexp(SEGMENT_NZ_NC$ + PATH_ABEMPTY$), //simplified PATH_ROOTLESS$ = subexp(SEGMENT_NZ$ + PATH_ABEMPTY$), //simplified PATH_EMPTY$ = subexp(""), //simplified PATH$ = subexp(PATH_ABEMPTY$ + "|" + PATH_ABSOLUTE$ + "|" + PATH_NOSCHEME$ + "|" + PATH_ROOTLESS$ + "|" + PATH_EMPTY$), QUERY$ = subexp(subexp(PCHAR$ + "|[\\/\\?]") + "*"), FRAGMENT$ = subexp(subexp(PCHAR$ + "|[\\/\\?]") + "*"), HIER_PART$ = subexp(subexp("\\/\\/" + AUTHORITY$ + PATH_ABEMPTY$) + "|" + PATH_ABSOLUTE$ + "|" + PATH_ROOTLESS$ + "|" + PATH_EMPTY$), URI$ = subexp(SCHEME$ + "\\:" + HIER_PART$ + subexp("\\?" + QUERY$) + "?" + subexp("\\#" + FRAGMENT$) + "?"), RELATIVE_PART$ = subexp(subexp("\\/\\/" + AUTHORITY$ + PATH_ABEMPTY$) + "|" + PATH_ABSOLUTE$ + "|" + PATH_NOSCHEME$ + "|" + PATH_EMPTY$), RELATIVE_REF$ = subexp(RELATIVE_PART$ + subexp("\\?" + QUERY$) + "?" + subexp("\\#" + FRAGMENT$) + "?"), URI_REFERENCE$ = subexp(URI$ + "|" + RELATIVE_REF$), ABSOLUTE_URI$ = subexp(SCHEME$ + "\\:" + HIER_PART$ + subexp("\\?" + QUERY$) + "?"), URI_REF = new RegExp("^" + subexp("(" + URI$ + ")|(" + RELATIVE_REF$ + ")") + "$"), GENERIC_REF = new RegExp("^(" + SCHEME$ + ")\\:" + subexp(subexp("\\/\\/(" + subexp("(" + USERINFO$ + ")@") + "?(" + HOST$ + ")" + subexp("\\:(" + PORT$ + ")") + "?)") + "?(" + PATH_ABEMPTY$ + "|" + PATH_ABSOLUTE$ + "|" + PATH_ROOTLESS$ + "|" + PATH_EMPTY$ + ")") + subexp("\\?(" + QUERY$ + ")") + "?" + subexp("\\#(" + FRAGMENT$ + ")") + "?$"), RELATIVE_REF = new RegExp("^(){0}" + subexp(subexp("\\/\\/(" + subexp("(" + USERINFO$ + ")@") + "?(" + HOST$ + ")" + subexp("\\:(" + PORT$ + ")") + "?)") + "?(" + PATH_ABEMPTY$ + "|" + PATH_ABSOLUTE$ + "|" + PATH_NOSCHEME$ + "|" + PATH_EMPTY$ + ")") + subexp("\\?(" + QUERY$ + ")") + "?" + subexp("\\#(" + FRAGMENT$ + ")") + "?$"), ABSOLUTE_REF = new RegExp("^(" + SCHEME$ + ")\\:" + subexp(subexp("\\/\\/(" + subexp("(" + USERINFO$ + ")@") + "?(" + HOST$ + ")" + subexp("\\:(" + PORT$ + ")") + "?)") + "?(" + PATH_ABEMPTY$ + "|" + PATH_ABSOLUTE$ + "|" + PATH_ROOTLESS$ + "|" + PATH_EMPTY$ + ")") + subexp("\\?(" + QUERY$ + ")") + "?$"), SAMEDOC_REF = new RegExp("^" + subexp("\\#(" + FRAGMENT$ + ")") + "?$"), AUTHORITY = new RegExp("^" + subexp("(" + USERINFO$ + ")@") + "?(" + HOST$ + ")" + subexp("\\:(" + PORT$ + ")") + "?$"), NOT_SCHEME = new RegExp(mergeSet("[^]", ALPHA$$, DIGIT$$, "[\\+\\-\\.]"), "g"), NOT_USERINFO = new RegExp(mergeSet("[^\\%\\:]", UNRESERVED$$, SUB_DELIMS$$), "g"), NOT_HOST = new RegExp(mergeSet("[^\\%]", UNRESERVED$$, SUB_DELIMS$$), "g"), NOT_PATH = new RegExp(mergeSet("[^\\%\\/\\:\\@]", UNRESERVED$$, SUB_DELIMS$$), "g"), NOT_PATH_NOSCHEME = new RegExp(mergeSet("[^\\%\\/\\@]", UNRESERVED$$, SUB_DELIMS$$), "g"), NOT_QUERY = new RegExp(mergeSet("[^\\%]", UNRESERVED$$, SUB_DELIMS$$, "[\\:\\@\\/\\?]"), "g"), NOT_FRAGMENT = NOT_QUERY, ESCAPE = new RegExp(mergeSet("[^]", UNRESERVED$$, SUB_DELIMS$$), "g"), UNRESERVED = new RegExp(UNRESERVED$$, "g"), OTHER_CHARS = new RegExp(mergeSet("[^\\%]", UNRESERVED$$, RESERVED$$), "g"), PCT_ENCODEDS = new RegExp(PCT_ENCODED$ + "+", "g"), URI_PARSE = /^(?:([^:\/?#]+):)?(?:\/\/((?:([^\/?#@]*)@)?([^\/?#:]*)(?:\:(\d*))?))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?/i, RDS1 = /^\.\.?\//, RDS2 = /^\/\.(\/|$)/, RDS3 = /^\/\.\.(\/|$)/, RDS4 = /^\.\.?$/, RDS5 = /^\/?.*?(?=\/|$)/, NO_MATCH_IS_UNDEFINED = ("").match(/(){0}/)[1] === undefined, /** * @param {string} chr * @return {string} */ pctEncChar = function (chr) { var c = chr.charCodeAt(0); if (c < 128) { return "%" + c.toString(16).toUpperCase(); } else if ((c > 127) && (c < 2048)) { return "%" + ((c >> 6) | 192).toString(16).toUpperCase() + "%" + ((c & 63) | 128).toString(16).toUpperCase(); } else { return "%" + ((c >> 12) | 224).toString(16).toUpperCase() + "%" + (((c >> 6) & 63) | 128).toString(16).toUpperCase() + "%" + ((c & 63) | 128).toString(16).toUpperCase(); } }, /** * @param {string} str * @return {string} */ pctDecUnreserved = function (str) { var newStr = "", i = 0, c, s; while (i < str.length) { c = parseInt(str.substr(i + 1, 2), 16); if (c < 128) { s = String.fromCharCode(c); if (s.match(UNRESERVED)) { newStr += s; } else { newStr += str.substr(i, 3); } i += 3; } else if ((c > 191) && (c < 224)) { newStr += str.substr(i, 6); i += 6; } else { newStr += str.substr(i, 9); i += 9; } } return newStr; }, /** * @param {string} str * @return {string} */ pctDecChars = function (str) { var newStr = "", i = 0, c, c2, c3; while (i < str.length) { c = parseInt(str.substr(i + 1, 2), 16); if (c < 128) { newStr += String.fromCharCode(c); i += 3; } else if ((c > 191) && (c < 224)) { c2 = parseInt(str.substr(i + 4, 2), 16); newStr += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); i += 6; } else { c2 = parseInt(str.substr(i + 4, 2), 16); c3 = parseInt(str.substr(i + 7, 2), 16); newStr += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); i += 9; } } return newStr; }, /** * @return {string} */ typeOf = function (o) { return o === undefined ? "undefined" : (o === null ? "null" : Object.prototype.toString.call(o).split(" ").pop().split("]").shift().toLowerCase()); }, /** * @constructor * @implements URIComponents */ Components = function () { this.errors = []; }, /** @namespace */ URI = exports; /** * Components */ Components.prototype = { /** * @type String */ scheme : undefined, /** * @type String */ authority : undefined, /** * @type String */ userinfo : undefined, /** * @type String */ host : undefined, /** * @type number */ port : undefined, /** * @type string */ path : undefined, /** * @type string */ query : undefined, /** * @type string */ fragment : undefined, /** * @type string * @values "uri", "absolute", "relative", "same-document" */ reference : undefined, /** * @type Array */ errors : undefined }; /** * URI */ /** * @namespace */ URI.SCHEMES = {}; /** * @param {string} uriString * @param {Options} [options] * @returns {URIComponents} */ URI.parse = function (uriString, options) { var matches, components = new Components(), schemeHandler; uriString = uriString ? uriString.toString() : ""; options = options || {}; if (options.reference === "suffix") { uriString = (options.scheme ? options.scheme + ":" : "") + "//" + uriString; } matches = uriString.match(URI_REF); if (matches) { if (matches[1]) { //generic URI matches = uriString.match(GENERIC_REF); } else { //relative URI matches = uriString.match(RELATIVE_REF); } } if (!matches) { if (!options.tolerant) { components.errors.push("URI is not strictly valid."); } matches = uriString.match(URI_PARSE); } if (matches) { if (NO_MATCH_IS_UNDEFINED) { //store each component components.scheme = matches[1]; components.authority = matches[2]; components.userinfo = matches[3]; components.host = matches[4]; components.port = parseInt(matches[5], 10); components.path = matches[6] || ""; components.query = matches[7]; components.fragment = matches[8]; //fix port number if (isNaN(components.port)) { components.port = matches[5]; } } else { //IE FIX for improper RegExp matching //store each component components.scheme = matches[1] || undefined; components.authority = (uriString.indexOf("//") !== -1 ? matches[2] : undefined); components.userinfo = (uriString.indexOf("@") !== -1 ? matches[3] : undefined); components.host = (uriString.indexOf("//") !== -1 ? matches[4] : undefined); components.port = parseInt(matches[5], 10); components.path = matches[6] || ""; components.query = (uriString.indexOf("?") !== -1 ? matches[7] : undefined); components.fragment = (uriString.indexOf("#") !== -1 ? matches[8] : undefined); //fix port number if (isNaN(components.port)) { components.port = (uriString.match(/\/\/.*\:(?:\/|\?|\#|$)/) ? matches[4] : undefined); } } //determine reference type if (!components.scheme && !components.authority && !components.path && !components.query) { components.reference = "same-document"; } else if (!components.scheme) { components.reference = "relative"; } else if (!components.fragment) { components.reference = "absolute"; } else { components.reference = "uri"; } //check for reference errors if (options.reference && options.reference !== "suffix" && options.reference !== components.reference) { components.errors.push("URI is not a " + options.reference + " reference."); } //check if a handler for the scheme exists schemeHandler = URI.SCHEMES[(components.scheme || options.scheme || "").toLowerCase()]; if (schemeHandler && schemeHandler.parse) { //perform extra parsing schemeHandler.parse(components, options); } } else { components.errors.push("URI can not be parsed."); } return components; }; /** * @private * @param {URIComponents} components * @returns {string|undefined} */ URI._recomposeAuthority = function (components) { var uriTokens = []; if (components.userinfo !== undefined || components.host !== undefined || typeof components.port === "number") { if (components.userinfo !== undefined) { uriTokens.push(components.userinfo.toString().replace(NOT_USERINFO, pctEncChar)); uriTokens.push("@"); } if (components.host !== undefined) { uriTokens.push(components.host.toString().toLowerCase().replace(NOT_HOST, pctEncChar)); } if (typeof components.port === "number") { uriTokens.push(":"); uriTokens.push(components.port.toString(10)); } } return uriTokens.length ? uriTokens.join("") : undefined; }; /** * @param {string} input * @returns {string} */ URI.removeDotSegments = function (input) { var output = [], s; while (input.length) { if (input.match(RDS1)) { input = input.replace(RDS1, ""); } else if (input.match(RDS2)) { input = input.replace(RDS2, "/"); } else if (input.match(RDS3)) { input = input.replace(RDS3, "/"); output.pop(); } else if (input === "." || input === "..") { input = ""; } else { s = input.match(RDS5)[0]; input = input.slice(s.length); output.push(s); } } return output.join(""); }; /** * @param {URIComponents} components * @param {Options} [options] * @returns {string} */ URI.serialize = function (components, options) { var uriTokens = [], schemeHandler, s; options = options || {}; //check if a handler for the scheme exists schemeHandler = URI.SCHEMES[components.scheme || options.scheme]; if (schemeHandler && schemeHandler.serialize) { //perform extra serialization schemeHandler.serialize(components, options); } if (options.reference !== "suffix" && components.scheme) { uriTokens.push(components.scheme.toString().toLowerCase().replace(NOT_SCHEME, "")); uriTokens.push(":"); } components.authority = URI._recomposeAuthority(components); if (components.authority !== undefined) { if (options.reference !== "suffix") { uriTokens.push("//"); } uriTokens.push(components.authority); if (components.path && components.path.charAt(0) !== "/") { uriTokens.push("/"); } } if (components.path) { s = URI.removeDotSegments(components.path.toString().replace(/%2E/ig, ".")); if (components.scheme) { s = s.replace(NOT_PATH, pctEncChar); } else { s = s.replace(NOT_PATH_NOSCHEME, pctEncChar); } if (components.authority === undefined) { s = s.replace(/^\/\//, "/%2F"); //don't allow the path to start with "//" } uriTokens.push(s); } if (components.query) { uriTokens.push("?"); uriTokens.push(components.query.toString().replace(NOT_QUERY, pctEncChar)); } if (components.fragment) { uriTokens.push("#"); uriTokens.push(components.fragment.toString().replace(NOT_FRAGMENT, pctEncChar)); } return uriTokens .join('') //merge tokens into a string .replace(PCT_ENCODEDS, pctDecUnreserved) //undecode unreserved characters //.replace(OTHER_CHARS, pctEncChar) //replace non-URI characters .replace(/%[0-9A-Fa-f]{2}/g, function (str) { //uppercase percent encoded characters return str.toUpperCase(); }) ; }; /** * @param {URIComponents} base * @param {URIComponents} relative * @param {Options} [options] * @param {boolean} [skipNormalization] * @returns {URIComponents} */ URI.resolveComponents = function (base, relative, options, skipNormalization) { var target = new Components(); if (!skipNormalization) { base = URI.parse(URI.serialize(base, options), options); //normalize base components relative = URI.parse(URI.serialize(relative, options), options); //normalize relative components } options = options || {}; if (!options.tolerant && relative.scheme) { target.scheme = relative.scheme; target.authority = relative.authority; target.userinfo = relative.userinfo; target.host = relative.host; target.port = relative.port; target.path = URI.removeDotSegments(relative.path); target.query = relative.query; } else { if (relative.authority !== undefined) { target.authority = relative.authority; target.userinfo = relative.userinfo; target.host = relative.host; target.port = relative.port; target.path = URI.removeDotSegments(relative.path); target.query = relative.query; } else { if (!relative.path) { target.path = base.path; if (relative.query !== undefined) { target.query = relative.query; } else { target.query = base.query; } } else { if (relative.path.charAt(0) === "/") { target.path = URI.removeDotSegments(relative.path); } else { if (base.authority !== undefined && !base.path) { target.path = "/" + relative.path; } else if (!base.path) { target.path = relative.path; } else { target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative.path; } target.path = URI.removeDotSegments(target.path); } target.query = relative.query; } target.authority = base.authority; target.userinfo = base.userinfo; target.host = base.host; target.port = base.port; } target.scheme = base.scheme; } target.fragment = relative.fragment; return target; }; /** * @param {string} baseURI * @param {string} relativeURI * @param {Options} [options] * @returns {string} */ URI.resolve = function (baseURI, relativeURI, options) { return URI.serialize(URI.resolveComponents(URI.parse(baseURI, options), URI.parse(relativeURI, options), options, true), options); }; /** * @param {string|URIComponents} uri * @param {Options} options * @returns {string|URIComponents} */ URI.normalize = function (uri, options) { if (typeof uri === "string") { return URI.serialize(URI.parse(uri, options), options); } else if (typeOf(uri) === "object") { return URI.parse(URI.serialize(uri, options), options); } return uri; }; /** * @param {string|URIComponents} uriA * @param {string|URIComponents} uriB * @param {Options} options */ URI.equal = function (uriA, uriB, options) { if (typeof uriA === "string") { uriA = URI.serialize(URI.parse(uriA, options), options); } else if (typeOf(uriA) === "object") { uriA = URI.serialize(uriA, options); } if (typeof uriB === "string") { uriB = URI.serialize(URI.parse(uriB, options), options); } else if (typeOf(uriB) === "object") { uriB = URI.serialize(uriB, options); } return uriA === uriB; }; /** * @param {string} str * @returns {string} */ URI.escapeComponent = function (str) { return str && str.toString().replace(ESCAPE, pctEncChar); }; /** * @param {string} str * @returns {string} */ URI.unescapeComponent = function (str) { return str && str.toString().replace(PCT_ENCODEDS, pctDecChars); }; //export API exports.pctEncChar = pctEncChar; exports.pctDecChars = pctDecChars; exports.Components = Components; exports.URI = URI; //name-safe export API exports["pctEncChar"] = pctEncChar; exports["pctDecChars"] = pctDecChars; exports["Components"] = Components; exports["URI"] = { "SCHEMES" : URI.SCHEMES, "parse" : URI.parse, "removeDotSegments" : URI.removeDotSegments, "serialize" : URI.serialize, "resolveComponents" : URI.resolveComponents, "resolve" : URI.resolve, "normalize" : URI.normalize, "equal" : URI.equal, "escapeComponent" : URI.escapeComponent, "unescapeComponent" : URI.unescapeComponent }; }());package/lib/uri/schemes/0000755000175000017500000000000011727717452014422 5ustar daviddavidpackage/lib/uri/schemes/urn.js0000644000175000017500000000474011727717626015574 0ustar daviddavid(function () { var URI_NS = require("../uri"), URI = URI_NS.URI, pctEncChar = URI_NS.pctEncChar, NID$ = "(?:[0-9A-Za-z][0-9A-Za-z\\-]{1,31})", PCT_ENCODED$ = "(?:\\%[0-9A-Fa-f]{2})", TRANS$$ = "[0-9A-Za-z\\(\\)\\+\\,\\-\\.\\:\\=\\@\\;\\$\\_\\!\\*\\'\\/\\?\\#]", NSS$ = "(?:(?:" + PCT_ENCODED$ + "|" + TRANS$$ + ")+)", URN_SCHEME = new RegExp("^urn\\:(" + NID$ + ")$"), URN_PATH = new RegExp("^(" + NID$ + ")\\:(" + NSS$ + ")$"), URN_PARSE = /^([^\:]+)\:(.*)/, URN_EXCLUDED = /[\x00-\x20\\\"\&\<\>\[\]\^\`\{\|\}\~\x7F-\xFF]/g, UUID = /^[0-9A-Fa-f]{8}(?:\-[0-9A-Fa-f]{4}){3}\-[0-9A-Fa-f]{12}$/; //RFC 2141 URI.SCHEMES["urn"] = { parse : function (components, options) { var matches = components.path.match(URN_PATH), scheme, schemeHandler; if (!matches) { if (!options.tolerant) { components.errors.push("URN is not strictly valid."); } matches = components.path.match(URN_PARSE); } if (matches) { scheme = "urn:" + matches[1].toLowerCase(); schemeHandler = URI.SCHEMES[scheme]; //in order to serialize properly, //every URN must have a serializer that calls the URN serializer if (!schemeHandler) { schemeHandler = URI.SCHEMES[scheme] = {}; } if (!schemeHandler.serialize) { schemeHandler.serialize = URI.SCHEMES["urn"].serialize; } components.scheme = scheme; components.path = matches[2]; if (schemeHandler.parse) { schemeHandler.parse(components, options); } } else { components.errors.push("URN can not be parsed."); } return components; }, serialize : function (components, options) { var scheme = components.scheme || options.scheme, matches; if (scheme && scheme !== "urn") { var matches = scheme.match(URN_SCHEME); if (!matches) { matches = ["urn:" + scheme, scheme]; } components.scheme = "urn"; components.path = matches[1] + ":" + (components.path ? components.path.replace(URN_EXCLUDED, pctEncChar) : ""); } return components; } }; //RFC 4122 URI.SCHEMES["urn:uuid"] = { serialize : function (components, options) { //ensure UUID is valid if (!options.tolerant && (!components.path || !components.path.match(UUID))) { //invalid UUIDs can not have this scheme components.scheme = undefined; } return URI.SCHEMES["urn"].serialize(components, options); } }; }());package/lib/json-schema-draft-02.js0000644000175000017500000007672611727717626016303 0ustar daviddavid/** * json-schema-draft-02 Environment * * @fileOverview Implementation of the second revision of the JSON Schema specification draft. * @author Gary Court * @version 1.6 * @see http://github.com/garycourt/JSV */ /* * Copyright 2010 Gary Court. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY GARY COURT ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARY COURT OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of Gary Court or the JSON Schema specification. */ /*jslint white: true, sub: true, onevar: true, undef: true, eqeqeq: true, newcap: true, immed: true, indent: 4 */ /*global require */ (function () { var O = {}, JSV = require('./jsv').JSV, ENVIRONMENT, TYPE_VALIDATORS, SCHEMA, HYPERSCHEMA, LINKS; TYPE_VALIDATORS = { "string" : function (instance, report) { return instance.getType() === "string"; }, "number" : function (instance, report) { return instance.getType() === "number"; }, "integer" : function (instance, report) { return instance.getType() === "number" && instance.getValue() % 1 === 0; }, "boolean" : function (instance, report) { return instance.getType() === "boolean"; }, "object" : function (instance, report) { return instance.getType() === "object"; }, "array" : function (instance, report) { return instance.getType() === "array"; }, "null" : function (instance, report) { return instance.getType() === "null"; }, "any" : function (instance, report) { return true; } }; ENVIRONMENT = new JSV.Environment(); ENVIRONMENT.setOption("defaultFragmentDelimiter", "/"); ENVIRONMENT.setOption("defaultSchemaURI", "http://json-schema.org/schema#"); //updated later SCHEMA = ENVIRONMENT.createSchema({ "$schema" : "http://json-schema.org/hyper-schema#", "id" : "http://json-schema.org/schema#", "type" : "object", "properties" : { "type" : { "type" : ["string", "array"], "items" : { "type" : ["string", {"$ref" : "#"}] }, "optional" : true, "uniqueItems" : true, "default" : "any", "parser" : function (instance, self) { var parser; if (instance.getType() === "string") { return instance.getValue(); } else if (instance.getType() === "object") { return instance.getEnvironment().createSchema( instance, self.getEnvironment().findSchema(self.resolveURI("#")) ); } else if (instance.getType() === "array") { parser = self.getValueOfProperty("parser"); return JSV.mapArray(instance.getProperties(), function (prop) { return parser(prop, self); }); } //else return "any"; }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var requiredTypes = JSV.toArray(schema.getAttribute("type")), x, xl, type, subreport, typeValidators; //for instances that are required to be a certain type if (instance.getType() !== "undefined" && requiredTypes && requiredTypes.length) { typeValidators = self.getValueOfProperty("typeValidators") || {}; //ensure that type matches for at least one of the required types for (x = 0, xl = requiredTypes.length; x < xl; ++x) { type = requiredTypes[x]; if (JSV.isJSONSchema(type)) { subreport = JSV.createObject(report); subreport.errors = []; subreport.validated = JSV.clone(report.validated); if (type.validate(instance, subreport, parent, parentSchema, name).errors.length === 0) { return true; //instance matches this schema } } else { if (typeValidators[type] !== O[type] && typeof typeValidators[type] === "function") { if (typeValidators[type](instance, report)) { return true; //type is valid } } else { return true; //unknown types are assumed valid } } } //if we get to this point, type is invalid report.addError(instance, schema, "type", "Instance is not a required type", requiredTypes); return false; } //else, anything is allowed if no type is specified return true; }, "typeValidators" : TYPE_VALIDATORS }, "properties" : { "type" : "object", "additionalProperties" : {"$ref" : "#"}, "optional" : true, "default" : {}, "parser" : function (instance, self, arg) { var env = instance.getEnvironment(), selfEnv = self.getEnvironment(); if (instance.getType() === "object") { if (arg) { return env.createSchema(instance.getProperty(arg), selfEnv.findSchema(self.resolveURI("#"))); } else { return JSV.mapObject(instance.getProperties(), function (instance) { return env.createSchema(instance, selfEnv.findSchema(self.resolveURI("#"))); }); } } //else return {}; }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var propertySchemas, key; //this attribute is for object type instances only if (instance.getType() === "object") { //for each property defined in the schema propertySchemas = schema.getAttribute("properties"); for (key in propertySchemas) { if (propertySchemas[key] !== O[key] && propertySchemas[key]) { //ensure that instance property is valid propertySchemas[key].validate(instance.getProperty(key), report, instance, schema, key); } } } } }, "items" : { "type" : [{"$ref" : "#"}, "array"], "items" : {"$ref" : "#"}, "optional" : true, "default" : {}, "parser" : function (instance, self) { if (instance.getType() === "object") { return instance.getEnvironment().createSchema(instance, self.getEnvironment().findSchema(self.resolveURI("#"))); } else if (instance.getType() === "array") { return JSV.mapArray(instance.getProperties(), function (instance) { return instance.getEnvironment().createSchema(instance, self.getEnvironment().findSchema(self.resolveURI("#"))); }); } //else return instance.getEnvironment().createEmptySchema(); }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var properties, items, x, xl, itemSchema, additionalProperties; if (instance.getType() === "array") { properties = instance.getProperties(); items = schema.getAttribute("items"); additionalProperties = schema.getAttribute("additionalProperties"); if (JSV.typeOf(items) === "array") { for (x = 0, xl = properties.length; x < xl; ++x) { itemSchema = items[x] || additionalProperties; if (itemSchema !== false) { itemSchema.validate(properties[x], report, instance, schema, x); } else { report.addError(instance, schema, "additionalProperties", "Additional items are not allowed", itemSchema); } } } else { itemSchema = items || additionalProperties; for (x = 0, xl = properties.length; x < xl; ++x) { itemSchema.validate(properties[x], report, instance, schema, x); } } } } }, "optional" : { "type" : "boolean", "optional" : true, "default" : false, "parser" : function (instance, self) { return !!instance.getValue(); }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { if (instance.getType() === "undefined" && !schema.getAttribute("optional")) { report.addError(instance, schema, "optional", "Property is required", false); } }, "validationRequired" : true }, "additionalProperties" : { "type" : [{"$ref" : "#"}, "boolean"], "optional" : true, "default" : {}, "parser" : function (instance, self) { if (instance.getType() === "object") { return instance.getEnvironment().createSchema(instance, self.getEnvironment().findSchema(self.resolveURI("#"))); } else if (instance.getType() === "boolean" && instance.getValue() === false) { return false; } //else return instance.getEnvironment().createEmptySchema(); }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var additionalProperties, propertySchemas, properties, key; //we only need to check against object types as arrays do their own checking on this property if (instance.getType() === "object") { additionalProperties = schema.getAttribute("additionalProperties"); propertySchemas = schema.getAttribute("properties") || {}; properties = instance.getProperties(); for (key in properties) { if (properties[key] !== O[key] && properties[key] && !propertySchemas[key]) { if (JSV.isJSONSchema(additionalProperties)) { additionalProperties.validate(properties[key], report, instance, schema, key); } else if (additionalProperties === false) { report.addError(instance, schema, "additionalProperties", "Additional properties are not allowed", additionalProperties); } } } } } }, "requires" : { "type" : ["string", {"$ref" : "#"}], "optional" : true, "parser" : function (instance, self) { if (instance.getType() === "string") { return instance.getValue(); } else if (instance.getType() === "object") { return instance.getEnvironment().createSchema(instance, self.getEnvironment().findSchema(self.resolveURI("#"))); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var requires; if (instance.getType() !== "undefined" && parent && parent.getType() !== "undefined") { requires = schema.getAttribute("requires"); if (typeof requires === "string") { if (parent.getProperty(requires).getType() === "undefined") { report.addError(instance, schema, "requires", 'Property requires sibling property "' + requires + '"', requires); } } else if (JSV.isJSONSchema(requires)) { requires.validate(parent, report); //WATCH: A "requires" schema does not support the "requires" attribute } } } }, "minimum" : { "type" : "number", "optional" : true, "parser" : function (instance, self) { if (instance.getType() === "number") { return instance.getValue(); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var minimum, minimumCanEqual; if (instance.getType() === "number") { minimum = schema.getAttribute("minimum"); minimumCanEqual = schema.getAttribute("minimumCanEqual"); if (typeof minimum === "number" && (instance.getValue() < minimum || (minimumCanEqual === false && instance.getValue() === minimum))) { report.addError(instance, schema, "minimum", "Number is less then the required minimum value", minimum); } } } }, "maximum" : { "type" : "number", "optional" : true, "parser" : function (instance, self) { if (instance.getType() === "number") { return instance.getValue(); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var maximum, maximumCanEqual; if (instance.getType() === "number") { maximum = schema.getAttribute("maximum"); maximumCanEqual = schema.getAttribute("maximumCanEqual"); if (typeof maximum === "number" && (instance.getValue() > maximum || (maximumCanEqual === false && instance.getValue() === maximum))) { report.addError(instance, schema, "maximum", "Number is greater then the required maximum value", maximum); } } } }, "minimumCanEqual" : { "type" : "boolean", "optional" : true, "requires" : "minimum", "default" : true, "parser" : function (instance, self) { if (instance.getType() === "boolean") { return instance.getValue(); } //else return true; } }, "maximumCanEqual" : { "type" : "boolean", "optional" : true, "requires" : "maximum", "default" : true, "parser" : function (instance, self) { if (instance.getType() === "boolean") { return instance.getValue(); } //else return true; } }, "minItems" : { "type" : "integer", "optional" : true, "minimum" : 0, "default" : 0, "parser" : function (instance, self) { if (instance.getType() === "number") { return instance.getValue(); } //else return 0; }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var minItems; if (instance.getType() === "array") { minItems = schema.getAttribute("minItems"); if (typeof minItems === "number" && instance.getProperties().length < minItems) { report.addError(instance, schema, "minItems", "The number of items is less then the required minimum", minItems); } } } }, "maxItems" : { "type" : "integer", "optional" : true, "minimum" : 0, "parser" : function (instance, self) { if (instance.getType() === "number") { return instance.getValue(); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var maxItems; if (instance.getType() === "array") { maxItems = schema.getAttribute("maxItems"); if (typeof maxItems === "number" && instance.getProperties().length > maxItems) { report.addError(instance, schema, "maxItems", "The number of items is greater then the required maximum", maxItems); } } } }, "uniqueItems" : { "type" : "boolean", "optional" : true, "default" : false, "parser" : function (instance, self) { return !!instance.getValue(); }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var value, x, xl, y, yl; if (instance.getType() === "array" && schema.getAttribute("uniqueItems")) { value = instance.getProperties(); for (x = 0, xl = value.length - 1; x < xl; ++x) { for (y = x + 1, yl = value.length; y < yl; ++y) { if (value[x].equals(value[y])) { report.addError(instance, schema, "uniqueItems", "Array can only contain unique items", { x : x, y : y }); } } } } } }, "pattern" : { "type" : "string", "optional" : true, "format" : "regex", "parser" : function (instance, self) { if (instance.getType() === "string") { try { return new RegExp(instance.getValue()); } catch (e) { return e; } } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var pattern; try { pattern = schema.getAttribute("pattern"); if (pattern instanceof Error) { report.addError(instance, schema, "pattern", "Invalid pattern", pattern); } else if (instance.getType() === "string" && pattern && !pattern.test(instance.getValue())) { report.addError(instance, schema, "pattern", "String does not match pattern", pattern.toString()); } } catch (e) { report.addError(instance, schema, "pattern", "Invalid pattern", e); } } }, "minLength" : { "type" : "integer", "optional" : true, "minimum" : 0, "default" : 0, "parser" : function (instance, self) { if (instance.getType() === "number") { return instance.getValue(); } //else return 0; }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var minLength; if (instance.getType() === "string") { minLength = schema.getAttribute("minLength"); if (typeof minLength === "number" && instance.getValue().length < minLength) { report.addError(instance, schema, "minLength", "String is less then the required minimum length", minLength); } } } }, "maxLength" : { "type" : "integer", "optional" : true, "parser" : function (instance, self) { if (instance.getType() === "number") { return instance.getValue(); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var maxLength; if (instance.getType() === "string") { maxLength = schema.getAttribute("maxLength"); if (typeof maxLength === "number" && instance.getValue().length > maxLength) { report.addError(instance, schema, "maxLength", "String is greater then the required maximum length", maxLength); } } } }, "enum" : { "type" : "array", "optional" : true, "minItems" : 1, "uniqueItems" : true, "parser" : function (instance, self) { if (instance.getType() === "array") { return instance.getValue(); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var enums, x, xl; if (instance.getType() !== "undefined") { enums = schema.getAttribute("enum"); if (enums) { for (x = 0, xl = enums.length; x < xl; ++x) { if (instance.equals(enums[x])) { return true; } } report.addError(instance, schema, "enum", "Instance is not one of the possible values", enums); } } } }, "title" : { "type" : "string", "optional" : true }, "description" : { "type" : "string", "optional" : true }, "format" : { "type" : "string", "optional" : true, "parser" : function (instance, self) { if (instance.getType() === "string") { return instance.getValue(); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var format, formatValidators; if (instance.getType() === "string") { format = schema.getAttribute("format"); formatValidators = self.getValueOfProperty("formatValidators"); if (typeof format === "string" && formatValidators[format] !== O[format] && typeof formatValidators[format] === "function" && !formatValidators[format].call(this, instance, report)) { report.addError(instance, schema, "format", "String is not in the required format", format); } } }, "formatValidators" : {} }, "contentEncoding" : { "type" : "string", "optional" : true }, "default" : { "type" : "any", "optional" : true }, "divisibleBy" : { "type" : "number", "minimum" : 0, "minimumCanEqual" : false, "optional" : true, "parser" : function (instance, self) { if (instance.getType() === "number") { return instance.getValue(); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var divisor; if (instance.getType() === "number") { divisor = schema.getAttribute("divisibleBy"); if (divisor === 0) { report.addError(instance, schema, "divisibleBy", "Nothing is divisible by 0", divisor); } else if (divisor !== 1 && ((instance.getValue() / divisor) % 1) !== 0) { report.addError(instance, schema, "divisibleBy", "Number is not divisible by " + divisor, divisor); } } } }, "disallow" : { "type" : ["string", "array"], "items" : {"type" : "string"}, "optional" : true, "uniqueItems" : true, "parser" : function (instance, self) { if (instance.getType() === "string" || instance.getType() === "array") { return instance.getValue(); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var disallowedTypes = JSV.toArray(schema.getAttribute("disallow")), x, xl, key, typeValidators; //for instances that are required to be a certain type if (instance.getType() !== "undefined" && disallowedTypes && disallowedTypes.length) { typeValidators = self.getValueOfProperty("typeValidators") || {}; //ensure that type matches for at least one of the required types for (x = 0, xl = disallowedTypes.length; x < xl; ++x) { key = disallowedTypes[x]; if (typeValidators[key] !== O[key] && typeof typeValidators[key] === "function") { if (typeValidators[key](instance, report)) { report.addError(instance, schema, "disallow", "Instance is a disallowed type", disallowedTypes); return false; } } /* else { report.addError(instance, schema, "disallow", "Instance may be a disallowed type", disallowedTypes); return false; } */ } //if we get to this point, type is valid return true; } //else, everything is allowed if no disallowed types are specified return true; }, "typeValidators" : TYPE_VALIDATORS }, "extends" : { "type" : [{"$ref" : "#"}, "array"], "items" : {"$ref" : "#"}, "optional" : true, "default" : {}, "parser" : function (instance, self) { if (instance.getType() === "object") { return instance.getEnvironment().createSchema(instance, self.getEnvironment().findSchema(self.resolveURI("#"))); } else if (instance.getType() === "array") { return JSV.mapArray(instance.getProperties(), function (instance) { return instance.getEnvironment().createSchema(instance, self.getEnvironment().findSchema(self.resolveURI("#"))); }); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var extensions = schema.getAttribute("extends"), x, xl; if (extensions) { if (JSV.isJSONSchema(extensions)) { extensions.validate(instance, report, parent, parentSchema, name); } else if (JSV.typeOf(extensions) === "array") { for (x = 0, xl = extensions.length; x < xl; ++x) { extensions[x].validate(instance, report, parent, parentSchema, name); } } } } } }, "optional" : true, "default" : {}, "fragmentResolution" : "slash-delimited", "parser" : function (instance, self) { if (instance.getType() === "object") { return instance.getEnvironment().createSchema(instance, self); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var propNames = schema.getPropertyNames(), x, xl, attributeSchemas = self.getAttribute("properties"), validator; for (x in attributeSchemas) { if (attributeSchemas[x] !== O[x] && attributeSchemas[x].getValueOfProperty("validationRequired")) { JSV.pushUnique(propNames, x); } } for (x = 0, xl = propNames.length; x < xl; ++x) { if (attributeSchemas[propNames[x]] !== O[propNames[x]]) { validator = attributeSchemas[propNames[x]].getValueOfProperty("validator"); if (typeof validator === "function") { validator(instance, schema, attributeSchemas[propNames[x]], report, parent, parentSchema, name); } } } }, "initializer" : function (instance) { var link, extension, extended; //if there is a link to a different schema, set reference link = instance._schema.getLink("describedby", instance); if (link && instance._schema._uri !== link) { instance.setReference("describedby", link); } //if instance has a URI link to itself, update it's own URI link = instance._schema.getLink("self", instance); if (JSV.typeOf(link) === "string") { instance._uri = JSV.formatURI(link); } //if there is a link to the full representation, set reference link = instance._schema.getLink("full", instance); if (link && instance._uri !== link) { instance.setReference("full", link); } //extend schema extension = instance.getAttribute("extends"); if (JSV.isJSONSchema(extension)) { extended = JSV.inherits(extension, instance, true); instance = instance._env.createSchema(extended, instance._schema, instance._uri); } return instance; } }, true, "http://json-schema.org/schema#"); HYPERSCHEMA = ENVIRONMENT.createSchema(JSV.inherits(SCHEMA, ENVIRONMENT.createSchema({ "$schema" : "http://json-schema.org/hyper-schema#", "id" : "http://json-schema.org/hyper-schema#", "properties" : { "links" : { "type" : "array", "items" : {"$ref" : "links#"}, "optional" : true, "parser" : function (instance, self, arg) { var links, linkSchemaURI = self.getValueOfProperty("items")["$ref"], linkSchema = self.getEnvironment().findSchema(linkSchemaURI), linkParser = linkSchema && linkSchema.getValueOfProperty("parser"); arg = JSV.toArray(arg); if (typeof linkParser === "function") { links = JSV.mapArray(instance.getProperties(), function (link) { return linkParser(link, linkSchema); }); } else { links = JSV.toArray(instance.getValue()); } if (arg[0]) { links = JSV.filterArray(links, function (link) { return link["rel"] === arg[0]; }); } if (arg[1]) { links = JSV.mapArray(links, function (link) { var instance = arg[1], href = link["href"]; href = href.replace(/\{(.+)\}/g, function (str, p1, offset, s) { var value; if (p1 === "-this") { value = instance.getValue(); } else { value = instance.getValueOfProperty(p1); } return value !== undefined ? String(value) : ""; }); return href ? JSV.formatURI(instance.resolveURI(href)) : href; }); } return links; } }, "fragmentResolution" : { "type" : "string", "optional" : true, "default" : "slash-delimited" }, "root" : { "type" : "boolean", "optional" : true, "default" : false }, "readonly" : { "type" : "boolean", "optional" : true, "default" : false }, "pathStart" : { "type" : "string", "optional" : true, "format" : "uri", "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var pathStart; if (instance.getType() !== "undefined") { pathStart = schema.getAttribute("pathStart"); if (typeof pathStart === "string") { //TODO: Find out what pathStart is relative to if (instance.getURI().indexOf(pathStart) !== 0) { report.addError(instance, schema, "pathStart", "Instance's URI does not start with " + pathStart, pathStart); } } } } }, "mediaType" : { "type" : "string", "optional" : true, "format" : "media-type" }, "alternate" : { "type" : "array", "items" : {"$ref" : "#"}, "optional" : true } }, "links" : [ { "href" : "{$ref}", "rel" : "full" }, { "href" : "{$schema}", "rel" : "describedby" }, { "href" : "{id}", "rel" : "self" } ]//, //not needed as JSV.inherits does the job for us //"extends" : {"$ref" : "http://json-schema.org/schema#"} }, SCHEMA), true), true, "http://json-schema.org/hyper-schema#"); ENVIRONMENT.setOption("defaultSchemaURI", "http://json-schema.org/hyper-schema#"); LINKS = ENVIRONMENT.createSchema({ "$schema" : "http://json-schema.org/hyper-schema#", "id" : "http://json-schema.org/links#", "type" : "object", "properties" : { "href" : { "type" : "string" }, "rel" : { "type" : "string" }, "targetSchema" : { "$ref" : "hyper-schema#", //need this here because parsers are run before links are resolved "parser" : HYPERSCHEMA.getAttribute("parser") }, "method" : { "type" : "string", "default" : "GET", "optional" : true }, "enctype" : { "type" : "string", "requires" : "method", "optional" : true }, "properties" : { "type" : "object", "additionalProperties" : {"$ref" : "hyper-schema#"}, "optional" : true, "parser" : function (instance, self, arg) { var env = instance.getEnvironment(), selfEnv = self.getEnvironment(), additionalPropertiesSchemaURI = self.getValueOfProperty("additionalProperties")["$ref"]; if (instance.getType() === "object") { if (arg) { return env.createSchema(instance.getProperty(arg), selfEnv.findSchema(self.resolveURI(additionalPropertiesSchemaURI))); } else { return JSV.mapObject(instance.getProperties(), function (instance) { return env.createSchema(instance, selfEnv.findSchema(self.resolveURI(additionalPropertiesSchemaURI))); }); } } } } }, "parser" : function (instance, self) { var selfProperties = self.getProperty("properties"); if (instance.getType() === "object") { return JSV.mapObject(instance.getProperties(), function (property, key) { var propertySchema = selfProperties.getProperty(key), parser = propertySchema && propertySchema.getValueOfProperty("parser"); if (typeof parser === "function") { return parser(property, propertySchema); } //else return property.getValue(); }); } return instance.getValue(); } }, HYPERSCHEMA, "http://json-schema.org/links#"); JSV.registerEnvironment("json-schema-draft-02", ENVIRONMENT); if (!JSV.getDefaultEnvironmentID() || JSV.getDefaultEnvironmentID() === "json-schema-draft-01") { JSV.setDefaultEnvironmentID("json-schema-draft-02"); } }());package/lib/environments.js0000644000175000017500000000015211504242545015244 0ustar daviddavidrequire("./json-schema-draft-01"); require("./json-schema-draft-02"); require("./json-schema-draft-03");package/lib/jsv.js0000644000175000017500000013222511727717626013344 0ustar daviddavid/** * JSV: JSON Schema Validator * * @fileOverview A JavaScript implementation of a extendable, fully compliant JSON Schema validator. * @author Gary Court * @version 4.0 * @see http://github.com/garycourt/JSV */ /* * Copyright 2010 Gary Court. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY GARY COURT ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARY COURT OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of Gary Court or the JSON Schema specification. */ /*jslint white: true, sub: true, onevar: true, undef: true, eqeqeq: true, newcap: true, immed: true, indent: 4 */ var exports = exports || this, require = require || function () { return exports; }; (function () { var URI = require("./uri/uri").URI, O = {}, I2H = "0123456789abcdef".split(""), mapArray, filterArray, searchArray, JSV; // // Utility functions // function typeOf(o) { return o === undefined ? "undefined" : (o === null ? "null" : Object.prototype.toString.call(o).split(" ").pop().split("]").shift().toLowerCase()); } /** @inner */ function F() {} function createObject(proto) { F.prototype = proto || {}; return new F(); } function mapObject(obj, func, scope) { var newObj = {}, key; for (key in obj) { if (obj[key] !== O[key]) { newObj[key] = func.call(scope, obj[key], key, obj); } } return newObj; } /** @ignore */ mapArray = function (arr, func, scope) { var x = 0, xl = arr.length, newArr = new Array(xl); for (; x < xl; ++x) { newArr[x] = func.call(scope, arr[x], x, arr); } return newArr; }; if (Array.prototype.map) { /** @ignore */ mapArray = function (arr, func, scope) { return Array.prototype.map.call(arr, func, scope); }; } /** @ignore */ filterArray = function (arr, func, scope) { var x = 0, xl = arr.length, newArr = []; for (; x < xl; ++x) { if (func.call(scope, arr[x], x, arr)) { newArr[newArr.length] = arr[x]; } } return newArr; }; if (Array.prototype.filter) { /** @ignore */ filterArray = function (arr, func, scope) { return Array.prototype.filter.call(arr, func, scope); }; } /** @ignore */ searchArray = function (arr, o) { var x = 0, xl = arr.length; for (; x < xl; ++x) { if (arr[x] === o) { return x; } } return -1; }; if (Array.prototype.indexOf) { /** @ignore */ searchArray = function (arr, o) { return Array.prototype.indexOf.call(arr, o); }; } function toArray(o) { return o !== undefined && o !== null ? (o instanceof Array && !o.callee ? o : (typeof o.length !== "number" || o.split || o.setInterval || o.call ? [ o ] : Array.prototype.slice.call(o))) : []; } function keys(o) { var result = [], key; switch (typeOf(o)) { case "object": for (key in o) { if (o[key] !== O[key]) { result[result.length] = key; } } break; case "array": for (key = o.length - 1; key >= 0; --key) { result[key] = key; } break; } return result; } function pushUnique(arr, o) { if (searchArray(arr, o) === -1) { arr.push(o); } return arr; } function popFirst(arr, o) { var index = searchArray(arr, o); if (index > -1) { arr.splice(index, 1); } return arr; } function randomUUID() { return [ I2H[Math.floor(Math.random() * 0x10)], I2H[Math.floor(Math.random() * 0x10)], I2H[Math.floor(Math.random() * 0x10)], I2H[Math.floor(Math.random() * 0x10)], I2H[Math.floor(Math.random() * 0x10)], I2H[Math.floor(Math.random() * 0x10)], I2H[Math.floor(Math.random() * 0x10)], I2H[Math.floor(Math.random() * 0x10)], "-", I2H[Math.floor(Math.random() * 0x10)], I2H[Math.floor(Math.random() * 0x10)], I2H[Math.floor(Math.random() * 0x10)], I2H[Math.floor(Math.random() * 0x10)], "-4", //set 4 high bits of time_high field to version I2H[Math.floor(Math.random() * 0x10)], I2H[Math.floor(Math.random() * 0x10)], I2H[Math.floor(Math.random() * 0x10)], "-", I2H[(Math.floor(Math.random() * 0x10) & 0x3) | 0x8], //specify 2 high bits of clock sequence I2H[Math.floor(Math.random() * 0x10)], I2H[Math.floor(Math.random() * 0x10)], I2H[Math.floor(Math.random() * 0x10)], "-", I2H[Math.floor(Math.random() * 0x10)], I2H[Math.floor(Math.random() * 0x10)], I2H[Math.floor(Math.random() * 0x10)], I2H[Math.floor(Math.random() * 0x10)], I2H[Math.floor(Math.random() * 0x10)], I2H[Math.floor(Math.random() * 0x10)], I2H[Math.floor(Math.random() * 0x10)], I2H[Math.floor(Math.random() * 0x10)], I2H[Math.floor(Math.random() * 0x10)], I2H[Math.floor(Math.random() * 0x10)], I2H[Math.floor(Math.random() * 0x10)], I2H[Math.floor(Math.random() * 0x10)] ].join(""); } function escapeURIComponent(str) { return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A'); } function formatURI(uri) { if (typeof uri === "string" && uri.indexOf("#") === -1) { uri += "#"; } return uri; } function stripInstances(o) { if (o instanceof JSONInstance) { return o.getURI(); } switch (typeOf(o)) { case "undefined": case "null": case "boolean": case "number": case "string": return o; //do nothing case "object": return mapObject(o, stripInstances); case "array": return mapArray(o, stripInstances); default: return o.toString(); } } /** * The exception that is thrown when a schema fails to be created. * * @name InitializationError * @class * @param {JSONInstance|String} instance The instance (or instance URI) that is invalid * @param {JSONSchema|String} schema The schema (or schema URI) that was validating the instance * @param {String} attr The attribute that failed to validated * @param {String} message A user-friendly message on why the schema attribute failed to validate the instance * @param {Any} details The value of the schema attribute */ function InitializationError(instance, schema, attr, message, details) { Error.call(this, message); this.uri = instance instanceof JSONInstance ? instance.getURI() : instance; this.schemaUri = schema instanceof JSONInstance ? schema.getURI() : schema; this.attribute = attr; this.message = message; this.description = message; //IE this.details = details; } InitializationError.prototype = new Error(); InitializationError.prototype.constructor = InitializationError; InitializationError.prototype.name = "InitializationError"; /** * Defines an error, found by a schema, with an instance. * This class can only be instantiated by {@link Report#addError}. * * @name ValidationError * @class * @see Report#addError */ /** * The URI of the instance that has the error. * * @name ValidationError.prototype.uri * @type String */ /** * The URI of the schema that generated the error. * * @name ValidationError.prototype.schemaUri * @type String */ /** * The name of the schema attribute that generated the error. * * @name ValidationError.prototype.attribute * @type String */ /** * An user-friendly (English) message about what failed to validate. * * @name ValidationError.prototype.message * @type String */ /** * The value of the schema attribute that generated the error. * * @name ValidationError.prototype.details * @type Any */ /** * Reports are returned from validation methods to describe the result of a validation. * * @name Report * @class * @see JSONSchema#validate * @see Environment#validate */ function Report() { /** * An array of {@link ValidationError} objects that define all the errors generated by the schema against the instance. * * @name Report.prototype.errors * @type Array * @see Report#addError */ this.errors = []; /** * A hash table of every instance and what schemas were validated against it. *

        * The key of each item in the table is the URI of the instance that was validated. * The value of this key is an array of strings of URIs of the schema that validated it. *

        * * @name Report.prototype.validated * @type Object * @see Report#registerValidation * @see Report#isValidatedBy */ this.validated = {}; /** * If the report is generated by {@link Environment#validate}, this field is the generated instance. * * @name Report.prototype.instance * @type JSONInstance * @see Environment#validate */ /** * If the report is generated by {@link Environment#validate}, this field is the generated schema. * * @name Report.prototype.schema * @type JSONSchema * @see Environment#validate */ /** * If the report is generated by {@link Environment#validate}, this field is the schema's schema. * This value is the same as calling schema.getSchema(). * * @name Report.prototype.schemaSchema * @type JSONSchema * @see Environment#validate * @see JSONSchema#getSchema */ } /** * Adds a {@link ValidationError} object to the errors field. * * @param {JSONInstance|String} instance The instance (or instance URI) that is invalid * @param {JSONSchema|String} schema The schema (or schema URI) that was validating the instance * @param {String} attr The attribute that failed to validated * @param {String} message A user-friendly message on why the schema attribute failed to validate the instance * @param {Any} details The value of the schema attribute */ Report.prototype.addError = function (instance, schema, attr, message, details) { this.errors.push({ uri : instance instanceof JSONInstance ? instance.getURI() : instance, schemaUri : schema instanceof JSONInstance ? schema.getURI() : schema, attribute : attr, message : message, details : stripInstances(details) }); }; /** * Registers that the provided instance URI has been validated by the provided schema URI. * This is recorded in the validated field. * * @param {String} uri The URI of the instance that was validated * @param {String} schemaUri The URI of the schema that validated the instance */ Report.prototype.registerValidation = function (uri, schemaUri) { if (!this.validated[uri]) { this.validated[uri] = [ schemaUri ]; } else { this.validated[uri].push(schemaUri); } }; /** * Returns if an instance with the provided URI has been validated by the schema with the provided URI. * * @param {String} uri The URI of the instance * @param {String} schemaUri The URI of a schema * @returns {Boolean} If the instance has been validated by the schema. */ Report.prototype.isValidatedBy = function (uri, schemaUri) { return !!this.validated[uri] && searchArray(this.validated[uri], schemaUri) !== -1; }; /** * A wrapper class for binding an Environment, URI and helper methods to an instance. * This class is most commonly instantiated with {@link Environment#createInstance}. * * @name JSONInstance * @class * @param {Environment} env The environment this instance belongs to * @param {JSONInstance|Any} json The value of the instance * @param {String} [uri] The URI of the instance. If undefined, the URI will be a randomly generated UUID. * @param {String} [fd] The fragment delimiter for properties. If undefined, uses the environment default. */ function JSONInstance(env, json, uri, fd) { if (json instanceof JSONInstance) { if (typeof fd !== "string") { fd = json._fd; } if (typeof uri !== "string") { uri = json._uri; } json = json._value; } if (typeof uri !== "string") { uri = "urn:uuid:" + randomUUID() + "#"; } else if (uri.indexOf(":") === -1) { uri = formatURI(URI.resolve("urn:uuid:" + randomUUID() + "#", uri)); } this._env = env; this._value = json; this._uri = uri; this._fd = fd || this._env._options["defaultFragmentDelimiter"]; } /** * Returns the environment the instance is bound to. * * @returns {Environment} The environment of the instance */ JSONInstance.prototype.getEnvironment = function () { return this._env; }; /** * Returns the name of the type of the instance. * * @returns {String} The name of the type of the instance */ JSONInstance.prototype.getType = function () { return typeOf(this._value); }; /** * Returns the JSON value of the instance. * * @returns {Any} The actual JavaScript value of the instance */ JSONInstance.prototype.getValue = function () { return this._value; }; /** * Returns the URI of the instance. * * @returns {String} The URI of the instance */ JSONInstance.prototype.getURI = function () { return this._uri; }; /** * Returns a resolved URI of a provided relative URI against the URI of the instance. * * @param {String} uri The relative URI to resolve * @returns {String} The resolved URI */ JSONInstance.prototype.resolveURI = function (uri) { return formatURI(URI.resolve(this._uri, uri)); }; /** * Returns an array of the names of all the properties. * * @returns {Array} An array of strings which are the names of all the properties */ JSONInstance.prototype.getPropertyNames = function () { return keys(this._value); }; /** * Returns a {@link JSONInstance} of the value of the provided property name. * * @param {String} key The name of the property to fetch * @returns {JSONInstance} The instance of the property value */ JSONInstance.prototype.getProperty = function (key) { var value = this._value ? this._value[key] : undefined; if (value instanceof JSONInstance) { return value; } //else return new JSONInstance(this._env, value, this._uri + this._fd + escapeURIComponent(key), this._fd); }; /** * Returns all the property instances of the target instance. *

        * If the target instance is an Object, then the method will return a hash table of {@link JSONInstance}s of all the properties. * If the target instance is an Array, then the method will return an array of {@link JSONInstance}s of all the items. *

        * * @returns {Object|Array|undefined} The list of instances for all the properties */ JSONInstance.prototype.getProperties = function () { var type = typeOf(this._value), self = this; if (type === "object") { return mapObject(this._value, function (value, key) { if (value instanceof JSONInstance) { return value; } return new JSONInstance(self._env, value, self._uri + self._fd + escapeURIComponent(key), self._fd); }); } else if (type === "array") { return mapArray(this._value, function (value, key) { if (value instanceof JSONInstance) { return value; } return new JSONInstance(self._env, value, self._uri + self._fd + escapeURIComponent(key), self._fd); }); } }; /** * Returns the JSON value of the provided property name. * This method is a faster version of calling instance.getProperty(key).getValue(). * * @param {String} key The name of the property * @returns {Any} The JavaScript value of the instance * @see JSONInstance#getProperty * @see JSONInstance#getValue */ JSONInstance.prototype.getValueOfProperty = function (key) { if (this._value) { if (this._value[key] instanceof JSONInstance) { return this._value[key]._value; } return this._value[key]; } }; /** * Return if the provided value is the same as the value of the instance. * * @param {JSONInstance|Any} instance The value to compare * @returns {Boolean} If both the instance and the value match */ JSONInstance.prototype.equals = function (instance) { if (instance instanceof JSONInstance) { return this._value === instance._value; } //else return this._value === instance; }; /** * Warning: Not a generic clone function * Produces a JSV acceptable clone */ function clone(obj, deep) { var newObj, x; if (obj instanceof JSONInstance) { obj = obj.getValue(); } switch (typeOf(obj)) { case "object": if (deep) { newObj = {}; for (x in obj) { if (obj[x] !== O[x]) { newObj[x] = clone(obj[x], deep); } } return newObj; } else { return createObject(obj); } break; case "array": if (deep) { newObj = new Array(obj.length); x = obj.length; while (--x >= 0) { newObj[x] = clone(obj[x], deep); } return newObj; } else { return Array.prototype.slice.call(obj); } break; default: return obj; } } /** * This class binds a {@link JSONInstance} with a {@link JSONSchema} to provided context aware methods. * * @name JSONSchema * @class * @param {Environment} env The environment this schema belongs to * @param {JSONInstance|Any} json The value of the schema * @param {String} [uri] The URI of the schema. If undefined, the URI will be a randomly generated UUID. * @param {JSONSchema|Boolean} [schema] The schema to bind to the instance. If undefined, the environment's default schema will be used. If true, the instance's schema will be itself. * @extends JSONInstance */ function JSONSchema(env, json, uri, schema) { var fr; JSONInstance.call(this, env, json, uri); if (schema === true) { this._schema = this; } else if (json instanceof JSONSchema && !(schema instanceof JSONSchema)) { this._schema = json._schema; //TODO: Make sure cross environments don't mess everything up } else { this._schema = schema instanceof JSONSchema ? schema : this._env.getDefaultSchema() || this._env.createEmptySchema(); } //determine fragment delimiter from schema fr = this._schema.getValueOfProperty("fragmentResolution"); if (fr === "dot-delimited") { this._fd = "."; } else if (fr === "slash-delimited") { this._fd = "/"; } return this.rebuild(); //this works even when called with "new" } JSONSchema.prototype = createObject(JSONInstance.prototype); /** * Returns the schema of the schema. * * @returns {JSONSchema} The schema of the schema */ JSONSchema.prototype.getSchema = function () { var uri = this._refs && this._refs["describedby"], newSchema; if (uri) { newSchema = uri && this._env.findSchema(uri); if (newSchema) { if (!newSchema.equals(this._schema)) { this._schema = newSchema; this.rebuild(); //if the schema has changed, the context has changed - so everything must be rebuilt } } else if (this._env._options["enforceReferences"]) { throw new InitializationError(this, this._schema, "{describedby}", "Unknown schema reference", uri); } } return this._schema; }; /** * Returns the value of the provided attribute name. *

        * This method is different from {@link JSONInstance#getProperty} as the named property * is converted using a parser defined by the schema's schema before being returned. This * makes the return value of this method attribute dependent. *

        * * @param {String} key The name of the attribute * @param {Any} [arg] Some attribute parsers accept special arguments for returning resolved values. This is attribute dependent. * @returns {JSONSchema|Any} The value of the attribute */ JSONSchema.prototype.getAttribute = function (key, arg) { var schemaProperty, parser, property, result, schema = this.getSchema(); //we do this here to make sure the "describedby" reference has not changed, and that the attribute cache is up-to-date if (!arg && this._attributes && this._attributes.hasOwnProperty(key)) { return this._attributes[key]; } schemaProperty = schema.getProperty("properties").getProperty(key); parser = schemaProperty.getValueOfProperty("parser"); property = this.getProperty(key); if (typeof parser === "function") { result = parser(property, schemaProperty, arg); if (!arg && this._attributes) { this._attributes[key] = result; } return result; } //else return property.getValue(); }; /** * Returns all the attributes of the schema. * * @returns {Object} A map of all parsed attribute values */ JSONSchema.prototype.getAttributes = function () { var properties, schemaProperties, key, schemaProperty, parser, schema = this.getSchema(); //we do this here to make sure the "describedby" reference has not changed, and that the attribute cache is up-to-date if (!this._attributes && this.getType() === "object") { properties = this.getProperties(); schemaProperties = schema.getProperty("properties"); this._attributes = {}; for (key in properties) { if (properties[key] !== O[key]) { schemaProperty = schemaProperties && schemaProperties.getProperty(key); parser = schemaProperty && schemaProperty.getValueOfProperty("parser"); if (typeof parser === "function") { this._attributes[key] = parser(properties[key], schemaProperty); } else { this._attributes[key] = properties[key].getValue(); } } } } return clone(this._attributes, false); }; /** * Convenience method for retrieving a link or link object from a schema. * This method is the same as calling schema.getAttribute("links", [rel, instance])[0];. * * @param {String} rel The link relationship * @param {JSONInstance} [instance] The instance to resolve any URIs from * @returns {String|Object|undefined} If instance is provided, a string containing the resolve URI of the link is returned. * If instance is not provided, a link object is returned with details of the link. * If no link with the provided relationship exists, undefined is returned. * @see JSONSchema#getAttribute */ JSONSchema.prototype.getLink = function (rel, instance) { var schemaLinks = this.getAttribute("links", [rel, instance]); if (schemaLinks && schemaLinks.length && schemaLinks[schemaLinks.length - 1]) { return schemaLinks[schemaLinks.length - 1]; } }; /** * Validates the provided instance against the target schema and returns a {@link Report}. * * @param {JSONInstance|Any} instance The instance to validate; may be a {@link JSONInstance} or any JavaScript value * @param {Report} [report] A {@link Report} to concatenate the result of the validation to. If undefined, a new {@link Report} is created. * @param {JSONInstance} [parent] The parent/containing instance of the provided instance * @param {JSONSchema} [parentSchema] The schema of the parent/containing instance * @param {String} [name] The name of the parent object's property that references the instance * @returns {Report} The result of the validation */ JSONSchema.prototype.validate = function (instance, report, parent, parentSchema, name) { var schemaSchema = this.getSchema(), validator = schemaSchema.getValueOfProperty("validator"); if (!(instance instanceof JSONInstance)) { instance = this.getEnvironment().createInstance(instance); } if (!(report instanceof Report)) { report = new Report(); } if (this._env._options["validateReferences"] && this._refs) { if (this._refs["describedby"] && !this._env.findSchema(this._refs["describedby"])) { report.addError(this, this._schema, "{describedby}", "Unknown schema reference", this._refs["describedby"]); } if (this._refs["full"] && !this._env.findSchema(this._refs["full"])) { report.addError(this, this._schema, "{full}", "Unknown schema reference", this._refs["full"]); } } if (typeof validator === "function" && !report.isValidatedBy(instance.getURI(), this.getURI())) { report.registerValidation(instance.getURI(), this.getURI()); validator(instance, this, schemaSchema, report, parent, parentSchema, name); } return report; }; /** @inner */ function createFullLookupWrapper(func) { return /** @inner */ function fullLookupWrapper() { var scope = this, stack = [], uri = scope._refs && scope._refs["full"], schema; while (uri) { schema = scope._env.findSchema(uri); if (schema) { if (schema._value === scope._value) { break; } scope = schema; stack.push(uri); uri = scope._refs && scope._refs["full"]; if (stack.indexOf(uri) > -1) { break; //stop infinite loop } } else if (scope._env._options["enforceReferences"]) { throw new InitializationError(scope, scope._schema, "{full}", "Unknown schema reference", uri); } else { uri = null; } } return func.apply(scope, arguments); }; } /** * Wraps all JSONInstance methods with a function that resolves the "full" reference. * * @inner */ (function () { var key; for (key in JSONSchema.prototype) { if (JSONSchema.prototype[key] !== O[key] && typeOf(JSONSchema.prototype[key]) === "function") { JSONSchema.prototype[key] = createFullLookupWrapper(JSONSchema.prototype[key]); } } }()); /** * Reinitializes/re-registers/rebuilds the schema. *
        * This is used internally, and should only be called when a schema's private variables are modified directly. * * @private * @return {JSONSchema} The newly rebuilt schema */ JSONSchema.prototype.rebuild = function () { var instance = this, initializer = instance.getSchema().getValueOfProperty("initializer"); //clear previous built values instance._refs = null; instance._attributes = null; if (typeof initializer === "function") { instance = initializer(instance); } //register schema instance._env._schemas[instance._uri] = instance; //build & cache the rest of the schema instance.getAttributes(); return instance; }; /** * Set the provided reference to the given value. *
        * References are used for establishing soft-links to other {@link JSONSchema}s. * Currently, the following references are natively supported: *
        *
        full
        *
        The value is the URI to the full instance of this instance.
        *
        describedby
        *
        The value is the URI to the schema of this instance.
        *
        * * @param {String} name The name of the reference * @param {String} uri The URI of the schema to refer to */ JSONSchema.prototype.setReference = function (name, uri) { if (!this._refs) { this._refs = {}; } this._refs[name] = this.resolveURI(uri); }; /** * Returns the value of the provided reference name. * * @param {String} name The name of the reference * @return {String} The value of the provided reference name */ JSONSchema.prototype.getReference = function (name) { return this._refs && this._refs[name]; }; /** * Merges two schemas/instances together. */ function inherits(base, extra, extension) { var baseType = typeOf(base), extraType = typeOf(extra), child, x; if (extraType === "undefined") { return clone(base, true); } else if (baseType === "undefined" || extraType !== baseType) { return clone(extra, true); } else if (extraType === "object") { if (base instanceof JSONSchema) { base = base.getAttributes(); } if (extra instanceof JSONSchema) { extra = extra.getAttributes(); if (extra["extends"] && extension && extra["extends"] instanceof JSONSchema) { extra["extends"] = [ extra["extends"] ]; } } child = clone(base, true); //this could be optimized as some properties get overwritten for (x in extra) { if (extra[x] !== O[x]) { child[x] = inherits(base[x], extra[x], extension); } } return child; } else { return clone(extra, true); } } /** * An Environment is a sandbox of schemas thats behavior is different from other environments. * * @name Environment * @class */ function Environment() { this._id = randomUUID(); this._schemas = {}; this._options = {}; this.createSchema({}, true, "urn:jsv:empty-schema#"); } /** * Returns a clone of the target environment. * * @returns {Environment} A new {@link Environment} that is a exact copy of the target environment */ Environment.prototype.clone = function () { var env = new Environment(); env._schemas = createObject(this._schemas); env._options = createObject(this._options); return env; }; /** * Returns a new {@link JSONInstance} of the provided data. * * @param {JSONInstance|Any} data The value of the instance * @param {String} [uri] The URI of the instance. If undefined, the URI will be a randomly generated UUID. * @returns {JSONInstance} A new {@link JSONInstance} from the provided data */ Environment.prototype.createInstance = function (data, uri) { uri = formatURI(uri); if (data instanceof JSONInstance && (!uri || data.getURI() === uri)) { return data; } return new JSONInstance(this, data, uri); }; /** * Creates a new {@link JSONSchema} from the provided data, and registers it with the environment. * * @param {JSONInstance|Any} data The value of the schema * @param {JSONSchema|Boolean} [schema] The schema to bind to the instance. If undefined, the environment's default schema will be used. If true, the instance's schema will be itself. * @param {String} [uri] The URI of the schema. If undefined, the URI will be a randomly generated UUID. * @returns {JSONSchema} A new {@link JSONSchema} from the provided data * @throws {InitializationError} If a schema that is not registered with the environment is referenced */ Environment.prototype.createSchema = function (data, schema, uri) { uri = formatURI(uri); if (data instanceof JSONSchema && (!uri || data._uri === uri) && (!schema || data.getSchema().equals(schema))) { return data; } return new JSONSchema(this, data, uri, schema); }; /** * Creates an empty schema. * * @returns {JSONSchema} The empty schema, who's schema is itself. */ Environment.prototype.createEmptySchema = function () { return this._schemas["urn:jsv:empty-schema#"]; }; /** * Returns the schema registered with the provided URI. * * @param {String} uri The absolute URI of the required schema * @returns {JSONSchema|undefined} The request schema, or undefined if not found */ Environment.prototype.findSchema = function (uri) { return this._schemas[formatURI(uri)]; }; /** * Sets the specified environment option to the specified value. * * @param {String} name The name of the environment option to set * @param {Any} value The new value of the environment option */ Environment.prototype.setOption = function (name, value) { this._options[name] = value; }; /** * Returns the specified environment option. * * @param {String} name The name of the environment option to set * @returns {Any} The value of the environment option */ Environment.prototype.getOption = function (name) { return this._options[name]; }; /** * Sets the default fragment delimiter of the environment. * * @deprecated Use {@link Environment#setOption} with option "defaultFragmentDelimiter" * @param {String} fd The fragment delimiter character */ Environment.prototype.setDefaultFragmentDelimiter = function (fd) { if (typeof fd === "string" && fd.length > 0) { this._options["defaultFragmentDelimiter"] = fd; } }; /** * Returns the default fragment delimiter of the environment. * * @deprecated Use {@link Environment#getOption} with option "defaultFragmentDelimiter" * @returns {String} The fragment delimiter character */ Environment.prototype.getDefaultFragmentDelimiter = function () { return this._options["defaultFragmentDelimiter"]; }; /** * Sets the URI of the default schema for the environment. * * @deprecated Use {@link Environment#setOption} with option "defaultSchemaURI" * @param {String} uri The default schema URI */ Environment.prototype.setDefaultSchemaURI = function (uri) { if (typeof uri === "string") { this._options["defaultSchemaURI"] = formatURI(uri); } }; /** * Returns the default schema of the environment. * * @returns {JSONSchema} The default schema */ Environment.prototype.getDefaultSchema = function () { return this.findSchema(this._options["defaultSchemaURI"]); }; /** * Validates both the provided schema and the provided instance, and returns a {@link Report}. * If the schema fails to validate, the instance will not be validated. * * @param {JSONInstance|Any} instanceJSON The {@link JSONInstance} or JavaScript value to validate. * @param {JSONSchema|Any} schemaJSON The {@link JSONSchema} or JavaScript value to use in the validation. This will also be validated againt the schema's schema. * @returns {Report} The result of the validation */ Environment.prototype.validate = function (instanceJSON, schemaJSON) { var instance, schema, schemaSchema, report = new Report(); try { instance = this.createInstance(instanceJSON); report.instance = instance; } catch (e) { report.addError(e.uri, e.schemaUri, e.attribute, e.message, e.details); } try { schema = this.createSchema(schemaJSON); report.schema = schema; schemaSchema = schema.getSchema(); report.schemaSchema = schemaSchema; } catch (f) { report.addError(f.uri, f.schemaUri, f.attribute, f.message, f.details); } if (schemaSchema) { schemaSchema.validate(schema, report); } if (report.errors.length) { return report; } return schema.validate(instance, report); }; /** * @private */ Environment.prototype._checkForInvalidInstances = function (stackSize, schemaURI) { var result = [], stack = [ [schemaURI, this._schemas[schemaURI]] ], counter = 0, item, uri, instance, properties, key; while (counter++ < stackSize && stack.length) { item = stack.shift(); uri = item[0]; instance = item[1]; if (instance instanceof JSONSchema) { if (this._schemas[instance._uri] !== instance) { result.push("Instance " + uri + " does not match " + instance._uri); } else { //schema = instance.getSchema(); //stack.push([uri + "/{schema}", schema]); properties = instance.getAttributes(); for (key in properties) { if (properties[key] !== O[key]) { stack.push([uri + "/" + escapeURIComponent(key), properties[key]]); } } } } else if (typeOf(instance) === "object") { properties = instance; for (key in properties) { if (properties.hasOwnProperty(key)) { stack.push([uri + "/" + escapeURIComponent(key), properties[key]]); } } } else if (typeOf(instance) === "array") { properties = instance; for (key = 0; key < properties.length; ++key) { stack.push([uri + "/" + escapeURIComponent(key), properties[key]]); } } } return result.length ? result : counter; }; /** * A globaly accessible object that provides the ability to create and manage {@link Environments}, * as well as providing utility methods. * * @namespace */ JSV = { _environments : {}, _defaultEnvironmentID : "", /** * Returns if the provide value is an instance of {@link JSONInstance}. * * @param o The value to test * @returns {Boolean} If the provide value is an instance of {@link JSONInstance} */ isJSONInstance : function (o) { return o instanceof JSONInstance; }, /** * Returns if the provide value is an instance of {@link JSONSchema}. * * @param o The value to test * @returns {Boolean} If the provide value is an instance of {@link JSONSchema} */ isJSONSchema : function (o) { return o instanceof JSONSchema; }, /** * Creates and returns a new {@link Environment} that is a clone of the environment registered with the provided ID. * If no environment ID is provided, the default environment is cloned. * * @param {String} [id] The ID of the environment to clone. If undefined, the default environment ID is used. * @returns {Environment} A newly cloned {@link Environment} * @throws {Error} If there is no environment registered with the provided ID */ createEnvironment : function (id) { id = id || this._defaultEnvironmentID; if (!this._environments[id]) { throw new Error("Unknown Environment ID"); } //else return this._environments[id].clone(); }, Environment : Environment, /** * Registers the provided {@link Environment} with the provided ID. * * @param {String} id The ID of the environment * @param {Environment} env The environment to register */ registerEnvironment : function (id, env) { id = id || (env || 0)._id; if (id && !this._environments[id] && env instanceof Environment) { env._id = id; this._environments[id] = env; } }, /** * Sets which registered ID is the default environment. * * @param {String} id The ID of the registered environment that is default * @throws {Error} If there is no registered environment with the provided ID */ setDefaultEnvironmentID : function (id) { if (typeof id === "string") { if (!this._environments[id]) { throw new Error("Unknown Environment ID"); } this._defaultEnvironmentID = id; } }, /** * Returns the ID of the default environment. * * @returns {String} The ID of the default environment */ getDefaultEnvironmentID : function () { return this._defaultEnvironmentID; }, // // Utility Functions // /** * Returns the name of the type of the provided value. * * @event //utility * @param {Any} o The value to determine the type of * @returns {String} The name of the type of the value */ typeOf : typeOf, /** * Return a new object that inherits all of the properties of the provided object. * * @event //utility * @param {Object} proto The prototype of the new object * @returns {Object} A new object that inherits all of the properties of the provided object */ createObject : createObject, /** * Returns a new object with each property transformed by the iterator. * * @event //utility * @param {Object} obj The object to transform * @param {Function} iterator A function that returns the new value of the provided property * @param {Object} [scope] The value of this in the iterator * @returns {Object} A new object with each property transformed */ mapObject : mapObject, /** * Returns a new array with each item transformed by the iterator. * * @event //utility * @param {Array} arr The array to transform * @param {Function} iterator A function that returns the new value of the provided item * @param {Object} scope The value of this in the iterator * @returns {Array} A new array with each item transformed */ mapArray : mapArray, /** * Returns a new array that only contains the items allowed by the iterator. * * @event //utility * @param {Array} arr The array to filter * @param {Function} iterator The function that returns true if the provided property should be added to the array * @param {Object} scope The value of this within the iterator * @returns {Array} A new array that contains the items allowed by the iterator */ filterArray : filterArray, /** * Returns the first index in the array that the provided item is located at. * * @event //utility * @param {Array} arr The array to search * @param {Any} o The item being searched for * @returns {Number} The index of the item in the array, or -1 if not found */ searchArray : searchArray, /** * Returns an array representation of a value. * * * @event //utility * @param {Any} o The value to convert into an array * @returns {Array} The value as an array */ toArray : toArray, /** * Returns an array of the names of all properties of an object. * * @event //utility * @param {Object|Array} o The object in question * @returns {Array} The names of all properties */ keys : keys, /** * Mutates the array by pushing the provided value onto the array only if it is not already there. * * @event //utility * @param {Array} arr The array to modify * @param {Any} o The object to add to the array if it is not already there * @returns {Array} The provided array for chaining */ pushUnique : pushUnique, /** * Mutates the array by removing the first item that matches the provided value in the array. * * @event //utility * @param {Array} arr The array to modify * @param {Any} o The object to remove from the array * @returns {Array} The provided array for chaining */ popFirst : popFirst, /** * Creates a copy of the target object. *

        * This method will create a new instance of the target, and then mixin the properties of the target. * If deep is true, then each property will be cloned before mixin. *

        *

        Warning: This is not a generic clone function, as it will only properly clone objects and arrays.

        * * @event //utility * @param {Any} o The value to clone * @param {Boolean} [deep=false] If each property should be recursively cloned * @returns A cloned copy of the provided value */ clone : clone, /** * Generates a pseudo-random UUID. * * @event //utility * @returns {String} A new universally unique ID */ randomUUID : randomUUID, /** * Properly escapes a URI component for embedding into a URI string. * * @event //utility * @param {String} str The URI component to escape * @returns {String} The escaped URI component */ escapeURIComponent : escapeURIComponent, /** * Returns a URI that is formated for JSV. Currently, this only ensures that the URI ends with a hash tag (#). * * @event //utility * @param {String} uri The URI to format * @returns {String} The URI formatted for JSV */ formatURI : formatURI, /** * Merges two schemas/instance together. * * @event //utility * @param {JSONSchema|Any} base The old value to merge * @param {JSONSchema|Any} extra The new value to merge * @param {Boolean} extension If the merge is a JSON Schema extension * @return {Any} The modified base value */ inherits : inherits, /** * @private * @event //utility */ InitializationError : InitializationError }; this.JSV = JSV; //set global object exports.JSV = JSV; //export to CommonJS require("./environments"); //load default environments }());package/lib/json-schema-draft-01.js0000644000175000017500000007505111727717626016270 0ustar daviddavid/** * json-schema-draft-01 Environment * * @fileOverview Implementation of the first revision of the JSON Schema specification draft. * @author Gary Court * @version 1.6 * @see http://github.com/garycourt/JSV */ /* * Copyright 2010 Gary Court. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY GARY COURT ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARY COURT OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of Gary Court or the JSON Schema specification. */ /*jslint white: true, sub: true, onevar: true, undef: true, eqeqeq: true, newcap: true, immed: true, indent: 4 */ /*global require */ (function () { var O = {}, JSV = require('./jsv').JSV, ENVIRONMENT, TYPE_VALIDATORS, SCHEMA, HYPERSCHEMA, LINKS; TYPE_VALIDATORS = { "string" : function (instance, report) { return instance.getType() === "string"; }, "number" : function (instance, report) { return instance.getType() === "number"; }, "integer" : function (instance, report) { return instance.getType() === "number" && instance.getValue() % 1 === 0; }, "boolean" : function (instance, report) { return instance.getType() === "boolean"; }, "object" : function (instance, report) { return instance.getType() === "object"; }, "array" : function (instance, report) { return instance.getType() === "array"; }, "null" : function (instance, report) { return instance.getType() === "null"; }, "any" : function (instance, report) { return true; } }; ENVIRONMENT = new JSV.Environment(); ENVIRONMENT.setOption("defaultFragmentDelimiter", "."); ENVIRONMENT.setOption("defaultSchemaURI", "http://json-schema.org/schema#"); //updated later SCHEMA = ENVIRONMENT.createSchema({ "$schema" : "http://json-schema.org/hyper-schema#", "id" : "http://json-schema.org/schema#", "type" : "object", "properties" : { "type" : { "type" : ["string", "array"], "items" : { "type" : ["string", {"$ref" : "#"}] }, "optional" : true, "uniqueItems" : true, "default" : "any", "parser" : function (instance, self) { var parser; if (instance.getType() === "string") { return instance.getValue(); } else if (instance.getType() === "object") { return instance.getEnvironment().createSchema( instance, self.getEnvironment().findSchema(self.resolveURI("#")) ); } else if (instance.getType() === "array") { parser = self.getValueOfProperty("parser"); return JSV.mapArray(instance.getProperties(), function (prop) { return parser(prop, self); }); } //else return "any"; }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var requiredTypes = JSV.toArray(schema.getAttribute("type")), x, xl, type, subreport, typeValidators; //for instances that are required to be a certain type if (instance.getType() !== "undefined" && requiredTypes && requiredTypes.length) { typeValidators = self.getValueOfProperty("typeValidators") || {}; //ensure that type matches for at least one of the required types for (x = 0, xl = requiredTypes.length; x < xl; ++x) { type = requiredTypes[x]; if (JSV.isJSONSchema(type)) { subreport = JSV.createObject(report); subreport.errors = []; subreport.validated = JSV.clone(report.validated); if (type.validate(instance, subreport, parent, parentSchema, name).errors.length === 0) { return true; //instance matches this schema } } else { if (typeValidators[type] !== O[type] && typeof typeValidators[type] === "function") { if (typeValidators[type](instance, report)) { return true; //type is valid } } else { return true; //unknown types are assumed valid } } } //if we get to this point, type is invalid report.addError(instance, schema, "type", "Instance is not a required type", requiredTypes); return false; } //else, anything is allowed if no type is specified return true; }, "typeValidators" : TYPE_VALIDATORS }, "properties" : { "type" : "object", "additionalProperties" : {"$ref" : "#"}, "optional" : true, "default" : {}, "parser" : function (instance, self, arg) { var env = instance.getEnvironment(), selfEnv = self.getEnvironment(); if (instance.getType() === "object") { if (arg) { return env.createSchema(instance.getProperty(arg), selfEnv.findSchema(self.resolveURI("#"))); } else { return JSV.mapObject(instance.getProperties(), function (instance) { return env.createSchema(instance, selfEnv.findSchema(self.resolveURI("#"))); }); } } //else return {}; }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var propertySchemas, key; //this attribute is for object type instances only if (instance.getType() === "object") { //for each property defined in the schema propertySchemas = schema.getAttribute("properties"); for (key in propertySchemas) { if (propertySchemas[key] !== O[key] && propertySchemas[key]) { //ensure that instance property is valid propertySchemas[key].validate(instance.getProperty(key), report, instance, schema, key); } } } } }, "items" : { "type" : [{"$ref" : "#"}, "array"], "items" : {"$ref" : "#"}, "optional" : true, "default" : {}, "parser" : function (instance, self) { if (instance.getType() === "object") { return instance.getEnvironment().createSchema(instance, self.getEnvironment().findSchema(self.resolveURI("#"))); } else if (instance.getType() === "array") { return JSV.mapArray(instance.getProperties(), function (instance) { return instance.getEnvironment().createSchema(instance, self.getEnvironment().findSchema(self.resolveURI("#"))); }); } //else return instance.getEnvironment().createEmptySchema(); }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var properties, items, x, xl, itemSchema, additionalProperties; if (instance.getType() === "array") { properties = instance.getProperties(); items = schema.getAttribute("items"); additionalProperties = schema.getAttribute("additionalProperties"); if (JSV.typeOf(items) === "array") { for (x = 0, xl = properties.length; x < xl; ++x) { itemSchema = items[x] || additionalProperties; if (itemSchema !== false) { itemSchema.validate(properties[x], report, instance, schema, x); } else { report.addError(instance, schema, "additionalProperties", "Additional items are not allowed", itemSchema); } } } else { itemSchema = items || additionalProperties; for (x = 0, xl = properties.length; x < xl; ++x) { itemSchema.validate(properties[x], report, instance, schema, x); } } } } }, "optional" : { "type" : "boolean", "optional" : true, "default" : false, "parser" : function (instance, self) { return !!instance.getValue(); }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { if (instance.getType() === "undefined" && !schema.getAttribute("optional")) { report.addError(instance, schema, "optional", "Property is required", false); } }, "validationRequired" : true }, "additionalProperties" : { "type" : [{"$ref" : "#"}, "boolean"], "optional" : true, "default" : {}, "parser" : function (instance, self) { if (instance.getType() === "object") { return instance.getEnvironment().createSchema(instance, self.getEnvironment().findSchema(self.resolveURI("#"))); } else if (instance.getType() === "boolean" && instance.getValue() === false) { return false; } //else return instance.getEnvironment().createEmptySchema(); }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var additionalProperties, propertySchemas, properties, key; //we only need to check against object types as arrays do their own checking on this property if (instance.getType() === "object") { additionalProperties = schema.getAttribute("additionalProperties"); propertySchemas = schema.getAttribute("properties") || {}; properties = instance.getProperties(); for (key in properties) { if (properties[key] !== O[key] && properties[key] && !propertySchemas[key]) { if (JSV.isJSONSchema(additionalProperties)) { additionalProperties.validate(properties[key], report, instance, schema, key); } else if (additionalProperties === false) { report.addError(instance, schema, "additionalProperties", "Additional properties are not allowed", additionalProperties); } } } } } }, "requires" : { "type" : ["string", {"$ref" : "#"}], "optional" : true, "parser" : function (instance, self) { if (instance.getType() === "string") { return instance.getValue(); } else if (instance.getType() === "object") { return instance.getEnvironment().createSchema(instance, self.getEnvironment().findSchema(self.resolveURI("#"))); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var requires; if (instance.getType() !== "undefined" && parent && parent.getType() !== "undefined") { requires = schema.getAttribute("requires"); if (typeof requires === "string") { if (parent.getProperty(requires).getType() === "undefined") { report.addError(instance, schema, "requires", 'Property requires sibling property "' + requires + '"', requires); } } else if (JSV.isJSONSchema(requires)) { requires.validate(parent, report); //WATCH: A "requires" schema does not support the "requires" attribute } } } }, "minimum" : { "type" : "number", "optional" : true, "parser" : function (instance, self) { if (instance.getType() === "number") { return instance.getValue(); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var minimum, minimumCanEqual; if (instance.getType() === "number") { minimum = schema.getAttribute("minimum"); minimumCanEqual = schema.getAttribute("minimumCanEqual"); if (typeof minimum === "number" && (instance.getValue() < minimum || (minimumCanEqual === false && instance.getValue() === minimum))) { report.addError(instance, schema, "minimum", "Number is less then the required minimum value", minimum); } } } }, "maximum" : { "type" : "number", "optional" : true, "parser" : function (instance, self) { if (instance.getType() === "number") { return instance.getValue(); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var maximum, maximumCanEqual; if (instance.getType() === "number") { maximum = schema.getAttribute("maximum"); maximumCanEqual = schema.getAttribute("maximumCanEqual"); if (typeof maximum === "number" && (instance.getValue() > maximum || (maximumCanEqual === false && instance.getValue() === maximum))) { report.addError(instance, schema, "maximum", "Number is greater then the required maximum value", maximum); } } } }, "minimumCanEqual" : { "type" : "boolean", "optional" : true, "requires" : "minimum", "default" : true, "parser" : function (instance, self) { if (instance.getType() === "boolean") { return instance.getValue(); } //else return true; } }, "maximumCanEqual" : { "type" : "boolean", "optional" : true, "requires" : "maximum", "default" : true, "parser" : function (instance, self) { if (instance.getType() === "boolean") { return instance.getValue(); } //else return true; } }, "minItems" : { "type" : "integer", "optional" : true, "minimum" : 0, "default" : 0, "parser" : function (instance, self) { if (instance.getType() === "number") { return instance.getValue(); } //else return 0; }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var minItems; if (instance.getType() === "array") { minItems = schema.getAttribute("minItems"); if (typeof minItems === "number" && instance.getProperties().length < minItems) { report.addError(instance, schema, "minItems", "The number of items is less then the required minimum", minItems); } } } }, "maxItems" : { "type" : "integer", "optional" : true, "minimum" : 0, "parser" : function (instance, self) { if (instance.getType() === "number") { return instance.getValue(); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var maxItems; if (instance.getType() === "array") { maxItems = schema.getAttribute("maxItems"); if (typeof maxItems === "number" && instance.getProperties().length > maxItems) { report.addError(instance, schema, "maxItems", "The number of items is greater then the required maximum", maxItems); } } } }, "pattern" : { "type" : "string", "optional" : true, "format" : "regex", "parser" : function (instance, self) { if (instance.getType() === "string") { try { return new RegExp(instance.getValue()); } catch (e) { return e; } } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var pattern; try { pattern = schema.getAttribute("pattern"); if (pattern instanceof Error) { report.addError(instance, schema, "pattern", "Invalid pattern", pattern); } else if (instance.getType() === "string" && pattern && !pattern.test(instance.getValue())) { report.addError(instance, schema, "pattern", "String does not match pattern", pattern.toString()); } } catch (e) { report.addError(instance, schema, "pattern", "Invalid pattern", e); } } }, "minLength" : { "type" : "integer", "optional" : true, "minimum" : 0, "default" : 0, "parser" : function (instance, self) { if (instance.getType() === "number") { return instance.getValue(); } //else return 0; }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var minLength; if (instance.getType() === "string") { minLength = schema.getAttribute("minLength"); if (typeof minLength === "number" && instance.getValue().length < minLength) { report.addError(instance, schema, "minLength", "String is less then the required minimum length", minLength); } } } }, "maxLength" : { "type" : "integer", "optional" : true, "parser" : function (instance, self) { if (instance.getType() === "number") { return instance.getValue(); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var maxLength; if (instance.getType() === "string") { maxLength = schema.getAttribute("maxLength"); if (typeof maxLength === "number" && instance.getValue().length > maxLength) { report.addError(instance, schema, "maxLength", "String is greater then the required maximum length", maxLength); } } } }, "enum" : { "type" : "array", "optional" : true, "minItems" : 1, "uniqueItems" : true, "parser" : function (instance, self) { if (instance.getType() === "array") { return instance.getValue(); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var enums, x, xl; if (instance.getType() !== "undefined") { enums = schema.getAttribute("enum"); if (enums) { for (x = 0, xl = enums.length; x < xl; ++x) { if (instance.equals(enums[x])) { return true; } } report.addError(instance, schema, "enum", "Instance is not one of the possible values", enums); } } } }, "title" : { "type" : "string", "optional" : true }, "description" : { "type" : "string", "optional" : true }, "format" : { "type" : "string", "optional" : true, "parser" : function (instance, self) { if (instance.getType() === "string") { return instance.getValue(); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var format, formatValidators; if (instance.getType() === "string") { format = schema.getAttribute("format"); formatValidators = self.getValueOfProperty("formatValidators"); if (typeof format === "string" && formatValidators[format] !== O[format] && typeof formatValidators[format] === "function" && !formatValidators[format].call(this, instance, report)) { report.addError(instance, schema, "format", "String is not in the required format", format); } } }, "formatValidators" : {} }, "contentEncoding" : { "type" : "string", "optional" : true }, "default" : { "type" : "any", "optional" : true }, "maxDecimal" : { "type" : "integer", "optional" : true, "minimum" : 0, "parser" : function (instance, self) { if (instance.getType() === "number") { return instance.getValue(); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var maxDecimal, decimals; if (instance.getType() === "number") { maxDecimal = schema.getAttribute("maxDecimal"); if (typeof maxDecimal === "number") { decimals = instance.getValue().toString(10).split('.')[1]; if (decimals && decimals.length > maxDecimal) { report.addError(instance, schema, "maxDecimal", "The number of decimal places is greater then the allowed maximum", maxDecimal); } } } } }, "disallow" : { "type" : ["string", "array"], "items" : {"type" : "string"}, "optional" : true, "uniqueItems" : true, "parser" : function (instance, self) { if (instance.getType() === "string" || instance.getType() === "array") { return instance.getValue(); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var disallowedTypes = JSV.toArray(schema.getAttribute("disallow")), x, xl, key, typeValidators; //for instances that are required to be a certain type if (instance.getType() !== "undefined" && disallowedTypes && disallowedTypes.length) { typeValidators = self.getValueOfProperty("typeValidators") || {}; //ensure that type matches for at least one of the required types for (x = 0, xl = disallowedTypes.length; x < xl; ++x) { key = disallowedTypes[x]; if (typeValidators[key] !== O[key] && typeof typeValidators[key] === "function") { if (typeValidators[key](instance, report)) { report.addError(instance, schema, "disallow", "Instance is a disallowed type", disallowedTypes); return false; } } /* else { report.addError(instance, schema, "disallow", "Instance may be a disallowed type", disallowedTypes); return false; } */ } //if we get to this point, type is valid return true; } //else, everything is allowed if no disallowed types are specified return true; }, "typeValidators" : TYPE_VALIDATORS }, "extends" : { "type" : [{"$ref" : "#"}, "array"], "items" : {"$ref" : "#"}, "optional" : true, "default" : {}, "parser" : function (instance, self) { if (instance.getType() === "object") { return instance.getEnvironment().createSchema(instance, self.getEnvironment().findSchema(self.resolveURI("#"))); } else if (instance.getType() === "array") { return JSV.mapArray(instance.getProperties(), function (instance) { return instance.getEnvironment().createSchema(instance, self.getEnvironment().findSchema(self.resolveURI("#"))); }); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var extensions = schema.getAttribute("extends"), x, xl; if (extensions) { if (JSV.isJSONSchema(extensions)) { extensions.validate(instance, report, parent, parentSchema, name); } else if (JSV.typeOf(extensions) === "array") { for (x = 0, xl = extensions.length; x < xl; ++x) { extensions[x].validate(instance, report, parent, parentSchema, name); } } } } } }, "optional" : true, "default" : {}, "fragmentResolution" : "dot-delimited", "parser" : function (instance, self) { if (instance.getType() === "object") { return instance.getEnvironment().createSchema(instance, self); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var propNames = schema.getPropertyNames(), x, xl, attributeSchemas = self.getAttribute("properties"), validator; for (x in attributeSchemas) { if (attributeSchemas[x] !== O[x] && attributeSchemas[x].getValueOfProperty("validationRequired")) { JSV.pushUnique(propNames, x); } } for (x = 0, xl = propNames.length; x < xl; ++x) { if (attributeSchemas[propNames[x]] !== O[propNames[x]]) { validator = attributeSchemas[propNames[x]].getValueOfProperty("validator"); if (typeof validator === "function") { validator(instance, schema, attributeSchemas[propNames[x]], report, parent, parentSchema, name); } } } }, "initializer" : function (instance) { var link, extension, extended; //if there is a link to a different schema, set reference link = instance._schema.getLink("describedby", instance); if (link && instance._schema._uri !== link) { instance.setReference("describedby", link); } //if instance has a URI link to itself, update it's own URI link = instance._schema.getLink("self", instance); if (JSV.typeOf(link) === "string") { instance._uri = JSV.formatURI(link); } //if there is a link to the full representation, set reference link = instance._schema.getLink("full", instance); if (link && instance._uri !== link) { instance.setReference("full", link); } //extend schema extension = instance.getAttribute("extends"); if (JSV.isJSONSchema(extension)) { extended = JSV.inherits(extension, instance, true); instance = instance._env.createSchema(extended, instance._schema, instance._uri); } return instance; } }, true, "http://json-schema.org/schema#"); HYPERSCHEMA = ENVIRONMENT.createSchema(JSV.inherits(SCHEMA, ENVIRONMENT.createSchema({ "$schema" : "http://json-schema.org/hyper-schema#", "id" : "http://json-schema.org/hyper-schema#", "properties" : { "links" : { "type" : "array", "items" : {"$ref" : "links#"}, "optional" : true, "parser" : function (instance, self, arg) { var links, linkSchemaURI = self.getValueOfProperty("items")["$ref"], linkSchema = self.getEnvironment().findSchema(linkSchemaURI), linkParser = linkSchema && linkSchema.getValueOfProperty("parser"); arg = JSV.toArray(arg); if (typeof linkParser === "function") { links = JSV.mapArray(instance.getProperties(), function (link) { return linkParser(link, linkSchema); }); } else { links = JSV.toArray(instance.getValue()); } if (arg[0]) { links = JSV.filterArray(links, function (link) { return link["rel"] === arg[0]; }); } if (arg[1]) { links = JSV.mapArray(links, function (link) { var instance = arg[1], href = link["href"]; href = href.replace(/\{(.+)\}/g, function (str, p1, offset, s) { var value; if (p1 === "-this") { value = instance.getValue(); } else { value = instance.getValueOfProperty(p1); } return value !== undefined ? String(value) : ""; }); return href ? JSV.formatURI(instance.resolveURI(href)) : href; }); } return links; } }, "fragmentResolution" : { "type" : "string", "optional" : true, "default" : "dot-delimited" }, "root" : { "type" : "boolean", "optional" : true, "default" : false }, "readonly" : { "type" : "boolean", "optional" : true, "default" : false }, "pathStart" : { "type" : "string", "optional" : true, "format" : "uri", "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var pathStart; if (instance.getType() !== "undefined") { pathStart = schema.getAttribute("pathStart"); if (typeof pathStart === "string") { //TODO: Find out what pathStart is relative to if (instance.getURI().indexOf(pathStart) !== 0) { report.addError(instance, schema, "pathStart", "Instance's URI does not start with " + pathStart, pathStart); } } } } }, "mediaType" : { "type" : "string", "optional" : true, "format" : "media-type" }, "alternate" : { "type" : "array", "items" : {"$ref" : "#"}, "optional" : true } }, "links" : [ { "href" : "{$ref}", "rel" : "full" }, { "href" : "{$schema}", "rel" : "describedby" }, { "href" : "{id}", "rel" : "self" } ]//, //not needed as JSV.inherits does the job for us //"extends" : {"$ref" : "http://json-schema.org/schema#"} }, SCHEMA), true), true, "http://json-schema.org/hyper-schema#"); ENVIRONMENT.setOption("defaultSchemaURI", "http://json-schema.org/hyper-schema#"); LINKS = ENVIRONMENT.createSchema({ "$schema" : "http://json-schema.org/hyper-schema#", "id" : "http://json-schema.org/links#", "type" : "object", "properties" : { "href" : { "type" : "string" }, "rel" : { "type" : "string" }, "method" : { "type" : "string", "default" : "GET", "optional" : true }, "enctype" : { "type" : "string", "requires" : "method", "optional" : true }, "properties" : { "type" : "object", "additionalProperties" : {"$ref" : "hyper-schema#"}, "optional" : true, "parser" : function (instance, self, arg) { var env = instance.getEnvironment(), selfEnv = self.getEnvironment(), additionalPropertiesSchemaURI = self.getValueOfProperty("additionalProperties")["$ref"]; if (instance.getType() === "object") { if (arg) { return env.createSchema(instance.getProperty(arg), selfEnv.findSchema(self.resolveURI(additionalPropertiesSchemaURI))); } else { return JSV.mapObject(instance.getProperties(), function (instance) { return env.createSchema(instance, selfEnv.findSchema(self.resolveURI(additionalPropertiesSchemaURI))); }); } } } } }, "parser" : function (instance, self) { var selfProperties = self.getProperty("properties"); if (instance.getType() === "object") { return JSV.mapObject(instance.getProperties(), function (property, key) { var propertySchema = selfProperties.getProperty(key), parser = propertySchema && propertySchema.getValueOfProperty("parser"); if (typeof parser === "function") { return parser(property, propertySchema); } //else return property.getValue(); }); } return instance.getValue(); } }, HYPERSCHEMA, "http://json-schema.org/links#"); JSV.registerEnvironment("json-schema-draft-00", ENVIRONMENT); JSV.registerEnvironment("json-schema-draft-01", JSV.createEnvironment("json-schema-draft-00")); if (!JSV.getDefaultEnvironmentID()) { JSV.setDefaultEnvironmentID("json-schema-draft-01"); } }());package/lib/json-schema-draft-03.js0000644000175000017500000014464411727717626016277 0ustar daviddavid/** * json-schema-draft-03 Environment * * @fileOverview Implementation of the third revision of the JSON Schema specification draft. * @author Gary Court * @version 1.4 * @see http://github.com/garycourt/JSV */ /* * Copyright 2010 Gary Court. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY GARY COURT ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARY COURT OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of Gary Court or the JSON Schema specification. */ /*jslint white: true, sub: true, onevar: true, undef: true, eqeqeq: true, newcap: true, immed: true, indent: 4 */ /*global require */ (function () { var O = {}, JSV = require('./jsv').JSV, TYPE_VALIDATORS, ENVIRONMENT, SCHEMA_00_JSON, HYPERSCHEMA_00_JSON, LINKS_00_JSON, SCHEMA_00, HYPERSCHEMA_00, LINKS_00, SCHEMA_01_JSON, HYPERSCHEMA_01_JSON, LINKS_01_JSON, SCHEMA_01, HYPERSCHEMA_01, LINKS_01, SCHEMA_02_JSON, HYPERSCHEMA_02_JSON, LINKS_02_JSON, SCHEMA_02, HYPERSCHEMA_02, LINKS_02, SCHEMA_03_JSON, HYPERSCHEMA_03_JSON, LINKS_03_JSON, SCHEMA_03, HYPERSCHEMA_03, LINKS_03; TYPE_VALIDATORS = { "string" : function (instance, report) { return instance.getType() === "string"; }, "number" : function (instance, report) { return instance.getType() === "number"; }, "integer" : function (instance, report) { return instance.getType() === "number" && instance.getValue() % 1 === 0; }, "boolean" : function (instance, report) { return instance.getType() === "boolean"; }, "object" : function (instance, report) { return instance.getType() === "object"; }, "array" : function (instance, report) { return instance.getType() === "array"; }, "null" : function (instance, report) { return instance.getType() === "null"; }, "any" : function (instance, report) { return true; } }; ENVIRONMENT = new JSV.Environment(); ENVIRONMENT.setOption("validateReferences", true); ENVIRONMENT.setOption("enforceReferences", false); ENVIRONMENT.setOption("strict", false); // // draft-00 // SCHEMA_00_JSON = { "$schema" : "http://json-schema.org/draft-00/hyper-schema#", "id" : "http://json-schema.org/draft-00/schema#", "type" : "object", "properties" : { "type" : { "type" : ["string", "array"], "items" : { "type" : ["string", {"$ref" : "#"}] }, "optional" : true, "uniqueItems" : true, "default" : "any", "parser" : function (instance, self) { var parser; if (instance.getType() === "string") { return instance.getValue(); } else if (instance.getType() === "object") { return instance.getEnvironment().createSchema( instance, self.getEnvironment().findSchema(self.resolveURI("#")) ); } else if (instance.getType() === "array") { parser = self.getValueOfProperty("parser"); return JSV.mapArray(instance.getProperties(), function (prop) { return parser(prop, self); }); } //else return "any"; }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var requiredTypes = JSV.toArray(schema.getAttribute("type")), x, xl, type, subreport, typeValidators; //for instances that are required to be a certain type if (instance.getType() !== "undefined" && requiredTypes && requiredTypes.length) { typeValidators = self.getValueOfProperty("typeValidators") || {}; //ensure that type matches for at least one of the required types for (x = 0, xl = requiredTypes.length; x < xl; ++x) { type = requiredTypes[x]; if (JSV.isJSONSchema(type)) { subreport = JSV.createObject(report); subreport.errors = []; subreport.validated = JSV.clone(report.validated); if (type.validate(instance, subreport, parent, parentSchema, name).errors.length === 0) { return true; //instance matches this schema } } else { if (typeValidators[type] !== O[type] && typeof typeValidators[type] === "function") { if (typeValidators[type](instance, report)) { return true; //type is valid } } else { return true; //unknown types are assumed valid } } } //if we get to this point, type is invalid report.addError(instance, schema, "type", "Instance is not a required type", requiredTypes); return false; } //else, anything is allowed if no type is specified return true; }, "typeValidators" : TYPE_VALIDATORS }, "properties" : { "type" : "object", "additionalProperties" : {"$ref" : "#"}, "optional" : true, "default" : {}, "parser" : function (instance, self, arg) { var env = instance.getEnvironment(), selfEnv = self.getEnvironment(); if (instance.getType() === "object") { if (arg) { return env.createSchema(instance.getProperty(arg), selfEnv.findSchema(self.resolveURI("#"))); } else { return JSV.mapObject(instance.getProperties(), function (instance) { return env.createSchema(instance, selfEnv.findSchema(self.resolveURI("#"))); }); } } //else return {}; }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var propertySchemas, key; //this attribute is for object type instances only if (instance.getType() === "object") { //for each property defined in the schema propertySchemas = schema.getAttribute("properties"); for (key in propertySchemas) { if (propertySchemas[key] !== O[key] && propertySchemas[key]) { //ensure that instance property is valid propertySchemas[key].validate(instance.getProperty(key), report, instance, schema, key); } } } } }, "items" : { "type" : [{"$ref" : "#"}, "array"], "items" : {"$ref" : "#"}, "optional" : true, "default" : {}, "parser" : function (instance, self) { if (instance.getType() === "object") { return instance.getEnvironment().createSchema(instance, self.getEnvironment().findSchema(self.resolveURI("#"))); } else if (instance.getType() === "array") { return JSV.mapArray(instance.getProperties(), function (instance) { return instance.getEnvironment().createSchema(instance, self.getEnvironment().findSchema(self.resolveURI("#"))); }); } //else return instance.getEnvironment().createEmptySchema(); }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var properties, items, x, xl, itemSchema, additionalProperties; if (instance.getType() === "array") { properties = instance.getProperties(); items = schema.getAttribute("items"); additionalProperties = schema.getAttribute("additionalProperties"); if (JSV.typeOf(items) === "array") { for (x = 0, xl = properties.length; x < xl; ++x) { itemSchema = items[x] || additionalProperties; if (itemSchema !== false) { itemSchema.validate(properties[x], report, instance, schema, x); } else { report.addError(instance, schema, "additionalProperties", "Additional items are not allowed", itemSchema); } } } else { itemSchema = items || additionalProperties; for (x = 0, xl = properties.length; x < xl; ++x) { itemSchema.validate(properties[x], report, instance, schema, x); } } } } }, "optional" : { "type" : "boolean", "optional" : true, "default" : false, "parser" : function (instance, self) { return !!instance.getValue(); }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { if (instance.getType() === "undefined" && !schema.getAttribute("optional")) { report.addError(instance, schema, "optional", "Property is required", false); } }, "validationRequired" : true }, "additionalProperties" : { "type" : [{"$ref" : "#"}, "boolean"], "optional" : true, "default" : {}, "parser" : function (instance, self) { if (instance.getType() === "object") { return instance.getEnvironment().createSchema(instance, self.getEnvironment().findSchema(self.resolveURI("#"))); } else if (instance.getType() === "boolean" && instance.getValue() === false) { return false; } //else return instance.getEnvironment().createEmptySchema(); }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var additionalProperties, propertySchemas, properties, key; //we only need to check against object types as arrays do their own checking on this property if (instance.getType() === "object") { additionalProperties = schema.getAttribute("additionalProperties"); propertySchemas = schema.getAttribute("properties") || {}; properties = instance.getProperties(); for (key in properties) { if (properties[key] !== O[key] && properties[key] && propertySchemas[key] === O[key]) { if (JSV.isJSONSchema(additionalProperties)) { additionalProperties.validate(properties[key], report, instance, schema, key); } else if (additionalProperties === false) { report.addError(instance, schema, "additionalProperties", "Additional properties are not allowed", additionalProperties); } } } } } }, "requires" : { "type" : ["string", {"$ref" : "#"}], "optional" : true, "parser" : function (instance, self) { if (instance.getType() === "string") { return instance.getValue(); } else if (instance.getType() === "object") { return instance.getEnvironment().createSchema(instance, self.getEnvironment().findSchema(self.resolveURI("#"))); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var requires; if (instance.getType() !== "undefined" && parent && parent.getType() !== "undefined") { requires = schema.getAttribute("requires"); if (typeof requires === "string") { if (parent.getProperty(requires).getType() === "undefined") { report.addError(instance, schema, "requires", 'Property requires sibling property "' + requires + '"', requires); } } else if (JSV.isJSONSchema(requires)) { requires.validate(parent, report); //WATCH: A "requires" schema does not support the "requires" attribute } } } }, "minimum" : { "type" : "number", "optional" : true, "parser" : function (instance, self) { if (instance.getType() === "number") { return instance.getValue(); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var minimum, minimumCanEqual; if (instance.getType() === "number") { minimum = schema.getAttribute("minimum"); minimumCanEqual = schema.getAttribute("minimumCanEqual"); if (typeof minimum === "number" && (instance.getValue() < minimum || (minimumCanEqual === false && instance.getValue() === minimum))) { report.addError(instance, schema, "minimum", "Number is less then the required minimum value", minimum); } } } }, "maximum" : { "type" : "number", "optional" : true, "parser" : function (instance, self) { if (instance.getType() === "number") { return instance.getValue(); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var maximum, maximumCanEqual; if (instance.getType() === "number") { maximum = schema.getAttribute("maximum"); maximumCanEqual = schema.getAttribute("maximumCanEqual"); if (typeof maximum === "number" && (instance.getValue() > maximum || (maximumCanEqual === false && instance.getValue() === maximum))) { report.addError(instance, schema, "maximum", "Number is greater then the required maximum value", maximum); } } } }, "minimumCanEqual" : { "type" : "boolean", "optional" : true, "requires" : "minimum", "default" : true, "parser" : function (instance, self) { if (instance.getType() === "boolean") { return instance.getValue(); } //else return true; } }, "maximumCanEqual" : { "type" : "boolean", "optional" : true, "requires" : "maximum", "default" : true, "parser" : function (instance, self) { if (instance.getType() === "boolean") { return instance.getValue(); } //else return true; } }, "minItems" : { "type" : "integer", "optional" : true, "minimum" : 0, "default" : 0, "parser" : function (instance, self) { if (instance.getType() === "number") { return instance.getValue(); } //else return 0; }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var minItems; if (instance.getType() === "array") { minItems = schema.getAttribute("minItems"); if (typeof minItems === "number" && instance.getProperties().length < minItems) { report.addError(instance, schema, "minItems", "The number of items is less then the required minimum", minItems); } } } }, "maxItems" : { "type" : "integer", "optional" : true, "minimum" : 0, "parser" : function (instance, self) { if (instance.getType() === "number") { return instance.getValue(); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var maxItems; if (instance.getType() === "array") { maxItems = schema.getAttribute("maxItems"); if (typeof maxItems === "number" && instance.getProperties().length > maxItems) { report.addError(instance, schema, "maxItems", "The number of items is greater then the required maximum", maxItems); } } } }, "pattern" : { "type" : "string", "optional" : true, "format" : "regex", "parser" : function (instance, self) { if (instance.getType() === "string") { try { return new RegExp(instance.getValue()); } catch (e) { return e; } } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var pattern; try { pattern = schema.getAttribute("pattern"); if (pattern instanceof Error) { report.addError(schema, self, "pattern", "Invalid pattern", schema.getValueOfProperty("pattern")); } else if (instance.getType() === "string" && pattern && !pattern.test(instance.getValue())) { report.addError(instance, schema, "pattern", "String does not match pattern", pattern.toString()); } } catch (e) { report.addError(schema, self, "pattern", "Invalid pattern", schema.getValueOfProperty("pattern")); } } }, "minLength" : { "type" : "integer", "optional" : true, "minimum" : 0, "default" : 0, "parser" : function (instance, self) { if (instance.getType() === "number") { return instance.getValue(); } //else return 0; }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var minLength; if (instance.getType() === "string") { minLength = schema.getAttribute("minLength"); if (typeof minLength === "number" && instance.getValue().length < minLength) { report.addError(instance, schema, "minLength", "String is less then the required minimum length", minLength); } } } }, "maxLength" : { "type" : "integer", "optional" : true, "parser" : function (instance, self) { if (instance.getType() === "number") { return instance.getValue(); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var maxLength; if (instance.getType() === "string") { maxLength = schema.getAttribute("maxLength"); if (typeof maxLength === "number" && instance.getValue().length > maxLength) { report.addError(instance, schema, "maxLength", "String is greater then the required maximum length", maxLength); } } } }, "enum" : { "type" : "array", "optional" : true, "minItems" : 1, "uniqueItems" : true, "parser" : function (instance, self) { if (instance.getType() === "array") { return instance.getValue(); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var enums, x, xl; if (instance.getType() !== "undefined") { enums = schema.getAttribute("enum"); if (enums) { for (x = 0, xl = enums.length; x < xl; ++x) { if (instance.equals(enums[x])) { return true; } } report.addError(instance, schema, "enum", "Instance is not one of the possible values", enums); } } } }, "title" : { "type" : "string", "optional" : true }, "description" : { "type" : "string", "optional" : true }, "format" : { "type" : "string", "optional" : true, "parser" : function (instance, self) { if (instance.getType() === "string") { return instance.getValue(); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var format, formatValidators; if (instance.getType() === "string") { format = schema.getAttribute("format"); formatValidators = self.getValueOfProperty("formatValidators"); if (typeof format === "string" && formatValidators[format] !== O[format] && typeof formatValidators[format] === "function" && !formatValidators[format].call(this, instance, report)) { report.addError(instance, schema, "format", "String is not in the required format", format); } } }, "formatValidators" : {} }, "contentEncoding" : { "type" : "string", "optional" : true }, "default" : { "type" : "any", "optional" : true }, "maxDecimal" : { "type" : "integer", "optional" : true, "minimum" : 0, "parser" : function (instance, self) { if (instance.getType() === "number") { return instance.getValue(); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var maxDecimal, decimals; if (instance.getType() === "number") { maxDecimal = schema.getAttribute("maxDecimal"); if (typeof maxDecimal === "number") { decimals = instance.getValue().toString(10).split('.')[1]; if (decimals && decimals.length > maxDecimal) { report.addError(instance, schema, "maxDecimal", "The number of decimal places is greater then the allowed maximum", maxDecimal); } } } } }, "disallow" : { "type" : ["string", "array"], "items" : {"type" : "string"}, "optional" : true, "uniqueItems" : true, "parser" : function (instance, self) { if (instance.getType() === "string" || instance.getType() === "array") { return instance.getValue(); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var disallowedTypes = JSV.toArray(schema.getAttribute("disallow")), x, xl, key, typeValidators, subreport; //for instances that are required to be a certain type if (instance.getType() !== "undefined" && disallowedTypes && disallowedTypes.length) { typeValidators = self.getValueOfProperty("typeValidators") || {}; //ensure that type matches for at least one of the required types for (x = 0, xl = disallowedTypes.length; x < xl; ++x) { key = disallowedTypes[x]; if (JSV.isJSONSchema(key)) { //this is supported draft-03 and on subreport = JSV.createObject(report); subreport.errors = []; subreport.validated = JSV.clone(report.validated); if (key.validate(instance, subreport, parent, parentSchema, name).errors.length === 0) { //instance matches this schema report.addError(instance, schema, "disallow", "Instance is a disallowed type", disallowedTypes); return false; } } else if (typeValidators[key] !== O[key] && typeof typeValidators[key] === "function") { if (typeValidators[key](instance, report)) { report.addError(instance, schema, "disallow", "Instance is a disallowed type", disallowedTypes); return false; } } /* else { report.addError(instance, schema, "disallow", "Instance may be a disallowed type", disallowedTypes); return false; } */ } //if we get to this point, type is valid return true; } //else, everything is allowed if no disallowed types are specified return true; }, "typeValidators" : TYPE_VALIDATORS }, "extends" : { "type" : [{"$ref" : "#"}, "array"], "items" : {"$ref" : "#"}, "optional" : true, "default" : {}, "parser" : function (instance, self) { if (instance.getType() === "object") { return instance.getEnvironment().createSchema(instance, self.getEnvironment().findSchema(self.resolveURI("#"))); } else if (instance.getType() === "array") { return JSV.mapArray(instance.getProperties(), function (instance) { return instance.getEnvironment().createSchema(instance, self.getEnvironment().findSchema(self.resolveURI("#"))); }); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var extensions = schema.getAttribute("extends"), x, xl; if (extensions) { if (JSV.isJSONSchema(extensions)) { extensions.validate(instance, report, parent, parentSchema, name); } else if (JSV.typeOf(extensions) === "array") { for (x = 0, xl = extensions.length; x < xl; ++x) { extensions[x].validate(instance, report, parent, parentSchema, name); } } } } } }, "optional" : true, "default" : {}, "fragmentResolution" : "dot-delimited", "parser" : function (instance, self) { if (instance.getType() === "object") { return instance.getEnvironment().createSchema(instance, self); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var propNames = schema.getPropertyNames(), x, xl, attributeSchemas = self.getAttribute("properties"), strict = instance.getEnvironment().getOption("strict"), validator; for (x in attributeSchemas) { if (attributeSchemas[x] !== O[x]) { if (attributeSchemas[x].getValueOfProperty("validationRequired")) { JSV.pushUnique(propNames, x); } if (strict && attributeSchemas[x].getValueOfProperty("deprecated")) { JSV.popFirst(propNames, x); } } } for (x = 0, xl = propNames.length; x < xl; ++x) { if (attributeSchemas[propNames[x]] !== O[propNames[x]]) { validator = attributeSchemas[propNames[x]].getValueOfProperty("validator"); if (typeof validator === "function") { validator(instance, schema, attributeSchemas[propNames[x]], report, parent, parentSchema, name); } } } } }; HYPERSCHEMA_00_JSON = { "$schema" : "http://json-schema.org/draft-00/hyper-schema#", "id" : "http://json-schema.org/draft-00/hyper-schema#", "properties" : { "links" : { "type" : "array", "items" : {"$ref" : "links#"}, "optional" : true, "parser" : function (instance, self, arg) { var links, linkSchemaURI = self.getValueOfProperty("items")["$ref"], linkSchema = self.getEnvironment().findSchema(linkSchemaURI), linkParser = linkSchema && linkSchema.getValueOfProperty("parser"), selfReferenceVariable; arg = JSV.toArray(arg); if (typeof linkParser === "function") { links = JSV.mapArray(instance.getProperties(), function (link) { return linkParser(link, linkSchema); }); } else { links = JSV.toArray(instance.getValue()); } if (arg[0]) { links = JSV.filterArray(links, function (link) { return link["rel"] === arg[0]; }); } if (arg[1]) { selfReferenceVariable = self.getValueOfProperty("selfReferenceVariable"); links = JSV.mapArray(links, function (link) { var instance = arg[1], href = link["href"]; href = href.replace(/\{(.+)\}/g, function (str, p1, offset, s) { var value; if (p1 === selfReferenceVariable) { value = instance.getValue(); } else { value = instance.getValueOfProperty(p1); } return value !== undefined ? String(value) : ""; }); return href ? JSV.formatURI(instance.resolveURI(href)) : href; }); } return links; }, "selfReferenceVariable" : "-this" }, "fragmentResolution" : { "type" : "string", "optional" : true, "default" : "dot-delimited" }, "root" : { "type" : "boolean", "optional" : true, "default" : false }, "readonly" : { "type" : "boolean", "optional" : true, "default" : false }, "pathStart" : { "type" : "string", "optional" : true, "format" : "uri", "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var pathStart; if (instance.getType() !== "undefined") { pathStart = schema.getAttribute("pathStart"); if (typeof pathStart === "string") { //TODO: Find out what pathStart is relative to if (instance.getURI().indexOf(pathStart) !== 0) { report.addError(instance, schema, "pathStart", "Instance's URI does not start with " + pathStart, pathStart); } } } } }, "mediaType" : { "type" : "string", "optional" : true, "format" : "media-type" }, "alternate" : { "type" : "array", "items" : {"$ref" : "#"}, "optional" : true } }, "links" : [ { "href" : "{$ref}", "rel" : "full" }, { "href" : "{$schema}", "rel" : "describedby" }, { "href" : "{id}", "rel" : "self" } ], "initializer" : function (instance) { var link, extension, extended; //if there is a link to a different schema, set reference link = instance._schema.getLink("describedby", instance); if (link && instance._schema._uri !== link) { instance.setReference("describedby", link); } //if instance has a URI link to itself, update it's own URI link = instance._schema.getLink("self", instance); if (JSV.typeOf(link) === "string") { instance._uri = JSV.formatURI(link); } //if there is a link to the full representation, set reference link = instance._schema.getLink("full", instance); if (link && instance._uri !== link) { instance.setReference("full", link); } //extend schema extension = instance.getAttribute("extends"); if (JSV.isJSONSchema(extension)) { extended = JSV.inherits(extension, instance, true); instance = instance._env.createSchema(extended, instance._schema, instance._uri); } return instance; } //not needed as JSV.inherits does the job for us //"extends" : {"$ref" : "http://json-schema.org/schema#"} }; LINKS_00_JSON = { "$schema" : "http://json-schema.org/draft-00/hyper-schema#", "id" : "http://json-schema.org/draft-00/links#", "type" : "object", "properties" : { "href" : { "type" : "string" }, "rel" : { "type" : "string" }, "method" : { "type" : "string", "default" : "GET", "optional" : true }, "enctype" : { "type" : "string", "requires" : "method", "optional" : true }, "properties" : { "type" : "object", "additionalProperties" : {"$ref" : "hyper-schema#"}, "optional" : true, "parser" : function (instance, self, arg) { var env = instance.getEnvironment(), selfEnv = self.getEnvironment(), additionalPropertiesSchemaURI = self.getValueOfProperty("additionalProperties")["$ref"]; if (instance.getType() === "object") { if (arg) { return env.createSchema(instance.getProperty(arg), selfEnv.findSchema(self.resolveURI(additionalPropertiesSchemaURI))); } else { return JSV.mapObject(instance.getProperties(), function (instance) { return env.createSchema(instance, selfEnv.findSchema(self.resolveURI(additionalPropertiesSchemaURI))); }); } } } } }, "parser" : function (instance, self) { var selfProperties = self.getProperty("properties"); if (instance.getType() === "object") { return JSV.mapObject(instance.getProperties(), function (property, key) { var propertySchema = selfProperties.getProperty(key), parser = propertySchema && propertySchema.getValueOfProperty("parser"); if (typeof parser === "function") { return parser(property, propertySchema); } //else return property.getValue(); }); } return instance.getValue(); } }; ENVIRONMENT.setOption("defaultFragmentDelimiter", "."); ENVIRONMENT.setOption("defaultSchemaURI", "http://json-schema.org/draft-00/schema#"); //updated later SCHEMA_00 = ENVIRONMENT.createSchema(SCHEMA_00_JSON, true, "http://json-schema.org/draft-00/schema#"); HYPERSCHEMA_00 = ENVIRONMENT.createSchema(JSV.inherits(SCHEMA_00, ENVIRONMENT.createSchema(HYPERSCHEMA_00_JSON, true, "http://json-schema.org/draft-00/hyper-schema#"), true), true, "http://json-schema.org/draft-00/hyper-schema#"); ENVIRONMENT.setOption("defaultSchemaURI", "http://json-schema.org/draft-00/hyper-schema#"); LINKS_00 = ENVIRONMENT.createSchema(LINKS_00_JSON, HYPERSCHEMA_00, "http://json-schema.org/draft-00/links#"); // // draft-01 // SCHEMA_01_JSON = JSV.inherits(SCHEMA_00_JSON, { "$schema" : "http://json-schema.org/draft-01/hyper-schema#", "id" : "http://json-schema.org/draft-01/schema#" }); HYPERSCHEMA_01_JSON = JSV.inherits(HYPERSCHEMA_00_JSON, { "$schema" : "http://json-schema.org/draft-01/hyper-schema#", "id" : "http://json-schema.org/draft-01/hyper-schema#" }); LINKS_01_JSON = JSV.inherits(LINKS_00_JSON, { "$schema" : "http://json-schema.org/draft-01/hyper-schema#", "id" : "http://json-schema.org/draft-01/links#" }); ENVIRONMENT.setOption("defaultSchemaURI", "http://json-schema.org/draft-01/schema#"); //update later SCHEMA_01 = ENVIRONMENT.createSchema(SCHEMA_01_JSON, true, "http://json-schema.org/draft-01/schema#"); HYPERSCHEMA_01 = ENVIRONMENT.createSchema(JSV.inherits(SCHEMA_01, ENVIRONMENT.createSchema(HYPERSCHEMA_01_JSON, true, "http://json-schema.org/draft-01/hyper-schema#"), true), true, "http://json-schema.org/draft-01/hyper-schema#"); ENVIRONMENT.setOption("defaultSchemaURI", "http://json-schema.org/draft-01/hyper-schema#"); LINKS_01 = ENVIRONMENT.createSchema(LINKS_01_JSON, HYPERSCHEMA_01, "http://json-schema.org/draft-01/links#"); // // draft-02 // SCHEMA_02_JSON = JSV.inherits(SCHEMA_01_JSON, { "$schema" : "http://json-schema.org/draft-02/hyper-schema#", "id" : "http://json-schema.org/draft-02/schema#", "properties" : { "uniqueItems" : { "type" : "boolean", "optional" : true, "default" : false, "parser" : function (instance, self) { return !!instance.getValue(); }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var value, x, xl, y, yl; if (instance.getType() === "array" && schema.getAttribute("uniqueItems")) { value = instance.getProperties(); for (x = 0, xl = value.length - 1; x < xl; ++x) { for (y = x + 1, yl = value.length; y < yl; ++y) { if (value[x].equals(value[y])) { report.addError(instance, schema, "uniqueItems", "Array can only contain unique items", { x : x, y : y }); } } } } } }, "maxDecimal" : { "deprecated" : true }, "divisibleBy" : { "type" : "number", "minimum" : 0, "minimumCanEqual" : false, "optional" : true, "parser" : function (instance, self) { if (instance.getType() === "number") { return instance.getValue(); } }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var divisor, value, digits; if (instance.getType() === "number") { divisor = schema.getAttribute("divisibleBy"); if (divisor === 0) { report.addError(instance, schema, "divisibleBy", "Nothing is divisible by 0", divisor); } else if (divisor !== 1) { value = instance.getValue(); digits = Math.max((value.toString().split(".")[1] || " ").length, (divisor.toString().split(".")[1] || " ").length); digits = parseFloat(((value / divisor) % 1).toFixed(digits)); //cut out floating point errors if (0 < digits && digits < 1) { report.addError(instance, schema, "divisibleBy", "Number is not divisible by " + divisor, divisor); } } } } } }, "fragmentResolution" : "slash-delimited" }); HYPERSCHEMA_02_JSON = JSV.inherits(HYPERSCHEMA_01_JSON, { "id" : "http://json-schema.org/draft-02/hyper-schema#", "properties" : { "fragmentResolution" : { "default" : "slash-delimited" } } }); LINKS_02_JSON = JSV.inherits(LINKS_01_JSON, { "$schema" : "http://json-schema.org/draft-02/hyper-schema#", "id" : "http://json-schema.org/draft-02/links#", "properties" : { "targetSchema" : { "$ref" : "hyper-schema#", //need this here because parsers are run before links are resolved "parser" : HYPERSCHEMA_01.getAttribute("parser") } } }); ENVIRONMENT.setOption("defaultFragmentDelimiter", "/"); ENVIRONMENT.setOption("defaultSchemaURI", "http://json-schema.org/draft-02/schema#"); //update later SCHEMA_02 = ENVIRONMENT.createSchema(SCHEMA_02_JSON, true, "http://json-schema.org/draft-02/schema#"); HYPERSCHEMA_02 = ENVIRONMENT.createSchema(JSV.inherits(SCHEMA_02, ENVIRONMENT.createSchema(HYPERSCHEMA_02_JSON, true, "http://json-schema.org/draft-02/hyper-schema#"), true), true, "http://json-schema.org/draft-02/hyper-schema#"); ENVIRONMENT.setOption("defaultSchemaURI", "http://json-schema.org/draft-02/hyper-schema#"); LINKS_02 = ENVIRONMENT.createSchema(LINKS_02_JSON, HYPERSCHEMA_02, "http://json-schema.org/draft-02/links#"); // // draft-03 // function getMatchedPatternProperties(instance, schema, report, self) { var matchedProperties = {}, patternProperties, pattern, regexp, properties, key; if (instance.getType() === "object") { patternProperties = schema.getAttribute("patternProperties"); properties = instance.getProperties(); for (pattern in patternProperties) { if (patternProperties[pattern] !== O[pattern]) { regexp = null; try { regexp = new RegExp(pattern); } catch (e) { if (report) { report.addError(schema, self, "patternProperties", "Invalid pattern", pattern); } } if (regexp) { for (key in properties) { if (properties[key] !== O[key] && regexp.test(key)) { matchedProperties[key] = matchedProperties[key] ? JSV.pushUnique(matchedProperties[key], patternProperties[pattern]) : [ patternProperties[pattern] ]; } } } } } } return matchedProperties; } SCHEMA_03_JSON = JSV.inherits(SCHEMA_02_JSON, { "$schema" : "http://json-schema.org/draft-03/schema#", "id" : "http://json-schema.org/draft-03/schema#", "properties" : { "patternProperties" : { "type" : "object", "additionalProperties" : {"$ref" : "#"}, "default" : {}, "parser" : SCHEMA_02.getValueOfProperty("properties")["properties"]["parser"], "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var matchedProperties, key, x; if (instance.getType() === "object") { matchedProperties = getMatchedPatternProperties(instance, schema, report, self); for (key in matchedProperties) { if (matchedProperties[key] !== O[key]) { x = matchedProperties[key].length; while (x--) { matchedProperties[key][x].validate(instance.getProperty(key), report, instance, schema, key); } } } } } }, "additionalProperties" : { "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var additionalProperties, propertySchemas, properties, matchedProperties, key; if (instance.getType() === "object") { additionalProperties = schema.getAttribute("additionalProperties"); propertySchemas = schema.getAttribute("properties") || {}; properties = instance.getProperties(); matchedProperties = getMatchedPatternProperties(instance, schema); for (key in properties) { if (properties[key] !== O[key] && properties[key] && propertySchemas[key] === O[key] && matchedProperties[key] === O[key]) { if (JSV.isJSONSchema(additionalProperties)) { additionalProperties.validate(properties[key], report, instance, schema, key); } else if (additionalProperties === false) { report.addError(instance, schema, "additionalProperties", "Additional properties are not allowed", additionalProperties); } } } } } }, "items" : { "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var properties, items, x, xl, itemSchema, additionalItems; if (instance.getType() === "array") { properties = instance.getProperties(); items = schema.getAttribute("items"); additionalItems = schema.getAttribute("additionalItems"); if (JSV.typeOf(items) === "array") { for (x = 0, xl = properties.length; x < xl; ++x) { itemSchema = items[x] || additionalItems; if (itemSchema !== false) { itemSchema.validate(properties[x], report, instance, schema, x); } else { report.addError(instance, schema, "additionalItems", "Additional items are not allowed", itemSchema); } } } else { itemSchema = items || additionalItems; for (x = 0, xl = properties.length; x < xl; ++x) { itemSchema.validate(properties[x], report, instance, schema, x); } } } } }, "additionalItems" : { "type" : [{"$ref" : "#"}, "boolean"], "default" : {}, "parser" : SCHEMA_02.getValueOfProperty("properties")["additionalProperties"]["parser"], "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var additionalItems, properties, x, xl; //only validate if the "items" attribute is undefined if (instance.getType() === "array" && schema.getProperty("items").getType() === "undefined") { additionalItems = schema.getAttribute("additionalItems"); properties = instance.getProperties(); if (additionalItems !== false) { for (x = 0, xl = properties.length; x < xl; ++x) { additionalItems.validate(properties[x], report, instance, schema, x); } } else if (properties.length) { report.addError(instance, schema, "additionalItems", "Additional items are not allowed", additionalItems); } } } }, "optional" : { "validationRequired" : false, "deprecated" : true }, "required" : { "type" : "boolean", "default" : false, "parser" : function (instance, self) { return !!instance.getValue(); }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { if (instance.getType() === "undefined" && schema.getAttribute("required")) { report.addError(instance, schema, "required", "Property is required", true); } } }, "requires" : { "deprecated" : true }, "dependencies" : { "type" : "object", "additionalProperties" : { "type" : ["string", "array", {"$ref" : "#"}], "items" : { "type" : "string" } }, "default" : {}, "parser" : function (instance, self, arg) { function parseProperty(property) { var type = property.getType(); if (type === "string" || type === "array") { return property.getValue(); } else if (type === "object") { return property.getEnvironment().createSchema(property, self.getEnvironment().findSchema(self.resolveURI("#"))); } } if (instance.getType() === "object") { if (arg) { return parseProperty(instance.getProperty(arg)); } else { return JSV.mapObject(instance.getProperties(), parseProperty); } } //else return {}; }, "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var dependencies, key, dependency, type, x, xl; if (instance.getType() === "object") { dependencies = schema.getAttribute("dependencies"); for (key in dependencies) { if (dependencies[key] !== O[key] && instance.getProperty(key).getType() !== "undefined") { dependency = dependencies[key]; type = JSV.typeOf(dependency); if (type === "string") { if (instance.getProperty(dependency).getType() === "undefined") { report.addError(instance, schema, "dependencies", 'Property "' + key + '" requires sibling property "' + dependency + '"', dependencies); } } else if (type === "array") { for (x = 0, xl = dependency.length; x < xl; ++x) { if (instance.getProperty(dependency[x]).getType() === "undefined") { report.addError(instance, schema, "dependencies", 'Property "' + key + '" requires sibling property "' + dependency[x] + '"', dependencies); } } } else if (JSV.isJSONSchema(dependency)) { dependency.validate(instance, report); } } } } } }, "minimumCanEqual" : { "deprecated" : true }, "maximumCanEqual" : { "deprecated" : true }, "exclusiveMinimum" : { "type" : "boolean", "default" : false, "parser" : function (instance, self) { return !!instance.getValue(); } }, "exclusiveMaximum" : { "type" : "boolean", "default" : false, "parser" : function (instance, self) { return !!instance.getValue(); } }, "minimum" : { "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var minimum, exclusiveMinimum; if (instance.getType() === "number") { minimum = schema.getAttribute("minimum"); exclusiveMinimum = schema.getAttribute("exclusiveMinimum") || (!instance.getEnvironment().getOption("strict") && !schema.getAttribute("minimumCanEqual")); if (typeof minimum === "number" && (instance.getValue() < minimum || (exclusiveMinimum === true && instance.getValue() === minimum))) { report.addError(instance, schema, "minimum", "Number is less then the required minimum value", minimum); } } } }, "maximum" : { "validator" : function (instance, schema, self, report, parent, parentSchema, name) { var maximum, exclusiveMaximum; if (instance.getType() === "number") { maximum = schema.getAttribute("maximum"); exclusiveMaximum = schema.getAttribute("exclusiveMaximum") || (!instance.getEnvironment().getOption("strict") && !schema.getAttribute("maximumCanEqual")); if (typeof maximum === "number" && (instance.getValue() > maximum || (exclusiveMaximum === true && instance.getValue() === maximum))) { report.addError(instance, schema, "maximum", "Number is greater then the required maximum value", maximum); } } } }, "contentEncoding" : { "deprecated" : true }, "divisibleBy" : { "exclusiveMinimum" : true }, "disallow" : { "items" : { "type" : ["string", {"$ref" : "#"}] }, "parser" : SCHEMA_02_JSON["properties"]["type"]["parser"] }, "id" : { "type" : "string", "format" : "uri" }, "$ref" : { "type" : "string", "format" : "uri" }, "$schema" : { "type" : "string", "format" : "uri" } }, "dependencies" : { "exclusiveMinimum" : "minimum", "exclusiveMaximum" : "maximum" }, "initializer" : function (instance) { var link, extension, extended, schemaLink = instance.getValueOfProperty("$schema"), refLink = instance.getValueOfProperty("$ref"), idLink = instance.getValueOfProperty("id"); //if there is a link to a different schema, set reference if (schemaLink) { link = instance.resolveURI(schemaLink); instance.setReference("describedby", link); } //if instance has a URI link to itself, update it's own URI if (idLink) { link = instance.resolveURI(idLink); if (JSV.typeOf(link) === "string") { instance._uri = JSV.formatURI(link); } } //if there is a link to the full representation, set reference if (refLink) { link = instance.resolveURI(refLink); instance.setReference("full", link); } //extend schema extension = instance.getAttribute("extends"); if (JSV.isJSONSchema(extension)) { extended = JSV.inherits(extension, instance, true); instance = instance._env.createSchema(extended, instance._schema, instance._uri); } return instance; } }); HYPERSCHEMA_03_JSON = JSV.inherits(HYPERSCHEMA_02_JSON, { "$schema" : "http://json-schema.org/draft-03/hyper-schema#", "id" : "http://json-schema.org/draft-03/hyper-schema#", "properties" : { "links" : { "selfReferenceVariable" : "@" }, "root" : { "deprecated" : true }, "contentEncoding" : { "deprecated" : false //moved from core to hyper }, "alternate" : { "deprecated" : true } } }); LINKS_03_JSON = JSV.inherits(LINKS_02_JSON, { "$schema" : "http://json-schema.org/draft-03/hyper-schema#", "id" : "http://json-schema.org/draft-03/links#", "properties" : { "href" : { "required" : true, "format" : "link-description-object-template" }, "rel" : { "required" : true }, "properties" : { "deprecated" : true }, "schema" : {"$ref" : "http://json-schema.org/draft-03/hyper-schema#"} } }); ENVIRONMENT.setOption("defaultSchemaURI", "http://json-schema.org/draft-03/schema#"); //update later SCHEMA_03 = ENVIRONMENT.createSchema(SCHEMA_03_JSON, true, "http://json-schema.org/draft-03/schema#"); HYPERSCHEMA_03 = ENVIRONMENT.createSchema(JSV.inherits(SCHEMA_03, ENVIRONMENT.createSchema(HYPERSCHEMA_03_JSON, true, "http://json-schema.org/draft-03/hyper-schema#"), true), true, "http://json-schema.org/draft-03/hyper-schema#"); ENVIRONMENT.setOption("defaultSchemaURI", "http://json-schema.org/draft-03/hyper-schema#"); LINKS_03 = ENVIRONMENT.createSchema(LINKS_03_JSON, true, "http://json-schema.org/draft-03/links#"); ENVIRONMENT.setOption("latestJSONSchemaSchemaURI", "http://json-schema.org/draft-03/schema#"); ENVIRONMENT.setOption("latestJSONSchemaHyperSchemaURI", "http://json-schema.org/draft-03/hyper-schema#"); ENVIRONMENT.setOption("latestJSONSchemaLinksURI", "http://json-schema.org/draft-03/links#"); // //Latest JSON Schema // //Hack, but WAY faster then instantiating a new schema ENVIRONMENT._schemas["http://json-schema.org/schema#"] = SCHEMA_03; ENVIRONMENT._schemas["http://json-schema.org/hyper-schema#"] = HYPERSCHEMA_03; ENVIRONMENT._schemas["http://json-schema.org/links#"] = LINKS_03; // //register environment // JSV.registerEnvironment("json-schema-draft-03", ENVIRONMENT); if (!JSV.getDefaultEnvironmentID() || JSV.getDefaultEnvironmentID() === "json-schema-draft-01" || JSV.getDefaultEnvironmentID() === "json-schema-draft-02") { JSV.setDefaultEnvironmentID("json-schema-draft-03"); } }());package/package.json0000644000175000017500000000141111727717626013714 0ustar daviddavid{ "name" : "JSV", "version" : "4.0.0", "description" : "A JavaScript implementation of a extendable, fully compliant JSON Schema validator.", "homepage" : "http://github.com/garycourt/JSV", "author" : "Gary Court ", "licenses" : [ { "type" : "FreeBSD", "url" : "http://github.com/garycourt/JSV/raw/master/jsv.js" } ], "maintainers" : [ { "name" : "Gary Court", "email" : "gary.court@gmail.com" } ], "bugs" : { "mail" : "gary.court@gmail.com", "web" : "http://github.com/garycourt/JSV/issues" }, "repositories" : [ { "type" : "git", "url" : "git://github.com/garycourt/JSV.git" } ], "dependencies" : [], "main" : "lib/jsv.js", "keywords" : ["json", "schema", "validator"] } package/README.md0000644000175000017500000002073211727717626012714 0ustar daviddavidJSV: JSON Schema Validator ========================== JSV is a JavaScript implementation of a extendable, fully compliant JSON Schema validator with the following features: * The fastest extendable JSON validator available! * Complete implementation of all current JSON Schema draft revisions. * Supports creating individual environments (sandboxes) that validate using a particular schema specification. * Provides an intuitive API for creating new validating schema attributes, or whole new custom schema schemas. * Supports `self`, `full` and `describedby` hyper links. * Validates itself, and is bootstrapped from the JSON Schema schemas. * Includes over 1100 unit tests for testing all parts of the specifications. * Works in all ECMAScript 3 environments, including all web browsers and Node.js. * Licensed under the FreeBSD License, a very open license. ## It's a what? **JSON** (an acronym for **JavaScript Object Notation**) is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate. It is based on a subset of JavaScript/ECMA-262 3rd Edition. JSON is a text format that is completely language independent but uses conventions that are familiar to programmers of the C-family of languages. (C, C++, C#, Java, JavaScript, Perl, Python, ...) These properties make JSON an ideal data-interchange language. \[[json.org](http://json.org)\] **JSON Schema** is a JSON media type for defining the structure of JSON data. JSON Schema provides a contract for what JSON data is required for a given application and how to interact with it. JSON Schema is intended to define validation, documentation, hyperlink navigation, and interaction control of JSON data. \[[draft-zyp-json-schema-02](http://tools.ietf.org/html/draft-zyp-json-schema-02)\] A **JSON validator** is a program that takes JSON data and, with a provided schema, will ensure that the provided JSON is structured in the way defined by the schema. This ensures that if validation has passed, the JSON instance is guaranteed to be in the expected format. It will also provide an explanation on why a particular instance failed validation. ## Example Here's an example on how to validate some JSON with JSV: var JSV = require("./jsv").JSV; var json = {}; var schema = {"type" : "object"}; var env = JSV.createEnvironment(); var report = env.validate(json, schema); if (report.errors.length === 0) { //JSON is valid against the schema } Another example; for the following test: env.validate({ a : 1 }, { type : 'object', properties : { a : { type : 'string' }} }); The generated report would look like: { errors : [ { message : "Instance is not a required type", uri : "urn:uuid:74b843b5-3aa4-44e9-b7bc-f555936fa823#/a", schemaUri : "urn:uuid:837fdefe-3bd4-4993-9a20-38a6a0624d5a#/properties/a", attribute : "type", details : ["string"] } ], validated : { "urn:uuid:74b843b5-3aa4-44e9-b7bc-f555936fa823#" : ["urn:uuid:837fdefe-3bd4-4993-9a20-38a6a0624d5a#"], "urn:uuid:74b843b5-3aa4-44e9-b7bc-f555936fa823#/a" : ["urn:uuid:837fdefe-3bd4-4993-9a20-38a6a0624d5a#/properties/a"], //... }, instance : [JSONInstance object], schema : [JSONSchema object], schemaSchema : [JSONSchema object] } ## Environments & JSON Schema support There is no one way to validate JSON, just like there is no one way to validate XML. Even the JSON Schema specification has gone through several revisions which are not 100% backwards compatible with each other. To solve the issue of using numerous schemas already written to older specifications, JSV provides customizable environments to validate your JSON within. When creating an environment, you can optionally specify how you want that environment to behave. For example, this allows you to specify which version of the JSON Schema you would like the environment to behave like. JSV already provides the following environments: * `json-schema-draft-03` A complete implementation of the [third revision](http://tools.ietf.org/html/draft-zyp-json-schema-03) of the JSON Schema specification. This is the same as the second revision, except: * "optional" has been replaced by "required" * "requires" has been replaced by "dependencies" * "minimumCanEqual"/"maximumCanEqual" has been replaced by "exclusiveMinimum"/"exclusiveMaximum" * "self"/"full"/"describedby" links have been moved to the core schema, and are provided by "id"/"$ref"/"$schema" * Adds the attributes "patternSchema" and "additionalItems" * Deprecates the attributes "root" and "alternate" * Schemas are now versioned under the URIs "http://json-schema.org/draft-XX/", where XX is the draft number In addition to this, all schemas from the previous versions of the JSON Schema draft are included in this environment, and are backwards compatible (where possible) with it's previous version. This backwards compatibility can be disabled with the environment option `strict` is set to `true`. This is currently the default environment. * `json-schema-draft-02` A complete implementation of the [second revision](http://tools.ietf.org/html/draft-zyp-json-schema-02) of the JSON Schema specification. This is the same as the first revision, except adds: * "targetSchema" attribute * slash-delimited fragment identifiers, which is now the default behavior * `json-schema-draft-01` A complete implementation of the [first revision](http://tools.ietf.org/html/draft-zyp-json-schema-01) of the JSON Schema specification, which is exactly the same as the [original draft](http://tools.ietf.org/html/draft-zyp-json-schema-00). Users with JSON Schemas written for JSV < v2.1 should use this environment for it's dot-delimited fragment identifiers. Environments can also be customized and registered for multiple reuse. (See the section on *Extending Environments* below) ## Validation API The following methods are used to validate JSON data: ### JSV.createEnvironment(*environmentID?*) *->* *<Environment>* * *environmentID* *<String>* *(optional)* The ID of the environment to clone a new Environment from Creates an new environment that is a copy of the Environment registered with the provided `environmentID`. If no ID is provided, the latest registered JSON Schema is used as the template. See the above section on *Environments* for the default available Environment IDs. ### *<Environment>*.validate(*json*, *schema*) *->* *<Report>* * *json* *<Any|JSONInstance>* The JSON data to validate * *schema* *<Object|JSONInstance|JSONSchema>* The schema to validate the JSON with Validates both the schema and the JSON, and returns a report of the validation. ### *<Report>*.errors *<Array>* An array of error objects from the validation process; each object represents a restriction check that failed. If the array is empty, the validation passed. The error objects have the following schema: { "type" : "object", "properties" : { "message" : { "description" : "A user-friendly error message about what failed to validate.", "type" : "string" }, "uri" : { "description" : "URI of the instance that failed to validate.", "type" : "string", "format" : "uri" }, "schemaUri" : { "description" : "URI of the schema instance that reported the error.", "type" : "string", "format" : "uri" }, "attribute" : { "description" : "The attribute of the schema instance that failed to validate.", "type" : "string" }, "details" : { "description" : "The value of the schema attribute that failed to validate.", "type" : "any" } } } ## API Documentation There are many more APIs available for JSV, too many to detail here. The complete API and documentation can be found in the `docs` directory. ## Extending Environments JSV provides an API for extending available schemas, adding new attributes and validation to currently existing schemas, and creating whole new Environments with unique behaviors. In fact, in the [eat-your-own-dog-food](http://en.wikipedia.org/wiki/Eating_your_own_dog_food) approach, all the default JSON Schema environments available are implemented using this API. Details and instruction on this feature will be provided at a later date. ## Unit Tests Open `tests/index.html` and `tests/index3.html` in your web browser to run the unit tests. ## License JSV is licensed under the FreeBSD License.