Spaces:
Sleeping
Sleeping
/** | |
* json-schema-draft-01 Environment | |
* | |
* @fileOverview Implementation of the first revision of the JSON Schema specification draft. | |
* @author <a href="mailto:gary.court@gmail.com">Gary Court</a> | |
* @version 1.7.1 | |
* @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 than 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 than 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 than 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 than the required maximum", maxItems); | |
} | |
} | |
} | |
}, | |
"pattern" : { | |
"type" : "string", | |
"optional" : true, | |
"format" : "regex", | |
"parser" : function (instance, self) { | |
if (instance.getType() === "string") { | |
return instance.getValue(); | |
} | |
}, | |
"validator" : function (instance, schema, self, report, parent, parentSchema, name) { | |
var pattern; | |
try { | |
pattern = new RegExp(schema.getAttribute("pattern")); | |
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 than 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 than 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 than 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"); | |
} | |
}()); |