You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							271 lines
						
					
					
						
							11 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							271 lines
						
					
					
						
							11 KiB
						
					
					
				
								/**
							 | 
						|
								 * JSONSchema Validator - Validates JavaScript objects using JSON Schemas
							 | 
						|
								 *	(http://www.json.com/json-schema-proposal/)
							 | 
						|
								 * Licensed under AFL-2.1 OR BSD-3-Clause
							 | 
						|
								To use the validator call the validate function with an instance object and an optional schema object.
							 | 
						|
								If a schema is provided, it will be used to validate. If the instance object refers to a schema (self-validating),
							 | 
						|
								that schema will be used to validate and the schema parameter is not necessary (if both exist,
							 | 
						|
								both validations will occur).
							 | 
						|
								The validate method will return an array of validation errors. If there are no errors, then an
							 | 
						|
								empty list will be returned. A validation error will have two properties:
							 | 
						|
								"property" which indicates which property had the error
							 | 
						|
								"message" which indicates what the error was
							 | 
						|
								 */
							 | 
						|
								(function (root, factory) {
							 | 
						|
								    if (typeof define === 'function' && define.amd) {
							 | 
						|
								        // AMD. Register as an anonymous module.
							 | 
						|
								        define([], function () {
							 | 
						|
								            return factory();
							 | 
						|
								        });
							 | 
						|
								    } else if (typeof module === 'object' && module.exports) {
							 | 
						|
								        // Node. Does not work with strict CommonJS, but
							 | 
						|
								        // only CommonJS-like environments that support module.exports,
							 | 
						|
								        // like Node.
							 | 
						|
								        module.exports = factory();
							 | 
						|
								    } else {
							 | 
						|
								        // Browser globals
							 | 
						|
								        root.jsonSchema = factory();
							 | 
						|
								    }
							 | 
						|
								}(this, function () {// setup primitive classes to be JSON Schema types
							 | 
						|
								var exports = validate
							 | 
						|
								exports.Integer = {type:"integer"};
							 | 
						|
								var primitiveConstructors = {
							 | 
						|
									String: String,
							 | 
						|
									Boolean: Boolean,
							 | 
						|
									Number: Number,
							 | 
						|
									Object: Object,
							 | 
						|
									Array: Array,
							 | 
						|
									Date: Date
							 | 
						|
								}
							 | 
						|
								exports.validate = validate;
							 | 
						|
								function validate(/*Any*/instance,/*Object*/schema) {
							 | 
						|
										// Summary:
							 | 
						|
										//  	To use the validator call JSONSchema.validate with an instance object and an optional schema object.
							 | 
						|
										// 		If a schema is provided, it will be used to validate. If the instance object refers to a schema (self-validating),
							 | 
						|
										// 		that schema will be used to validate and the schema parameter is not necessary (if both exist,
							 | 
						|
										// 		both validations will occur).
							 | 
						|
										// 		The validate method will return an object with two properties:
							 | 
						|
										// 			valid: A boolean indicating if the instance is valid by the schema
							 | 
						|
										// 			errors: An array of validation errors. If there are no errors, then an
							 | 
						|
										// 					empty list will be returned. A validation error will have two properties:
							 | 
						|
										// 						property: which indicates which property had the error
							 | 
						|
										// 						message: which indicates what the error was
							 | 
						|
										//
							 | 
						|
										return validate(instance, schema, {changing: false});//, coerce: false, existingOnly: false});
							 | 
						|
									};
							 | 
						|
								exports.checkPropertyChange = function(/*Any*/value,/*Object*/schema, /*String*/property) {
							 | 
						|
										// Summary:
							 | 
						|
										// 		The checkPropertyChange method will check to see if an value can legally be in property with the given schema
							 | 
						|
										// 		This is slightly different than the validate method in that it will fail if the schema is readonly and it will
							 | 
						|
										// 		not check for self-validation, it is assumed that the passed in value is already internally valid.
							 | 
						|
										// 		The checkPropertyChange method will return the same object type as validate, see JSONSchema.validate for
							 | 
						|
										// 		information.
							 | 
						|
										//
							 | 
						|
										return validate(value, schema, {changing: property || "property"});
							 | 
						|
									};
							 | 
						|
								var validate = exports._validate = function(/*Any*/instance,/*Object*/schema,/*Object*/options) {
							 | 
						|
								
							 | 
						|
									if (!options) options = {};
							 | 
						|
									var _changing = options.changing;
							 | 
						|
								
							 | 
						|
									function getType(schema){
							 | 
						|
										return schema.type || (primitiveConstructors[schema.name] == schema && schema.name.toLowerCase());
							 | 
						|
									}
							 | 
						|
									var errors = [];
							 | 
						|
									// validate a value against a property definition
							 | 
						|
									function checkProp(value, schema, path,i){
							 | 
						|
								
							 | 
						|
										var l;
							 | 
						|
										path += path ? typeof i == 'number' ? '[' + i + ']' : typeof i == 'undefined' ? '' : '.' + i : i;
							 | 
						|
										function addError(message){
							 | 
						|
											errors.push({property:path,message:message});
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										if((typeof schema != 'object' || schema instanceof Array) && (path || typeof schema != 'function') && !(schema && getType(schema))){
							 | 
						|
											if(typeof schema == 'function'){
							 | 
						|
												if(!(value instanceof schema)){
							 | 
						|
													addError("is not an instance of the class/constructor " + schema.name);
							 | 
						|
												}
							 | 
						|
											}else if(schema){
							 | 
						|
												addError("Invalid schema/property definition " + schema);
							 | 
						|
											}
							 | 
						|
											return null;
							 | 
						|
										}
							 | 
						|
										if(_changing && schema.readonly){
							 | 
						|
											addError("is a readonly field, it can not be changed");
							 | 
						|
										}
							 | 
						|
										if(schema['extends']){ // if it extends another schema, it must pass that schema as well
							 | 
						|
											checkProp(value,schema['extends'],path,i);
							 | 
						|
										}
							 | 
						|
										// validate a value against a type definition
							 | 
						|
										function checkType(type,value){
							 | 
						|
											if(type){
							 | 
						|
												if(typeof type == 'string' && type != 'any' &&
							 | 
						|
														(type == 'null' ? value !== null : typeof value != type) &&
							 | 
						|
														!(value instanceof Array && type == 'array') &&
							 | 
						|
														!(value instanceof Date && type == 'date') &&
							 | 
						|
														!(type == 'integer' && value%1===0)){
							 | 
						|
													return [{property:path,message:value + " - " + (typeof value) + " value found, but a " + type + " is required"}];
							 | 
						|
												}
							 | 
						|
												if(type instanceof Array){
							 | 
						|
													var unionErrors=[];
							 | 
						|
													for(var j = 0; j < type.length; j++){ // a union type
							 | 
						|
														if(!(unionErrors=checkType(type[j],value)).length){
							 | 
						|
															break;
							 | 
						|
														}
							 | 
						|
													}
							 | 
						|
													if(unionErrors.length){
							 | 
						|
														return unionErrors;
							 | 
						|
													}
							 | 
						|
												}else if(typeof type == 'object'){
							 | 
						|
													var priorErrors = errors;
							 | 
						|
													errors = [];
							 | 
						|
													checkProp(value,type,path);
							 | 
						|
													var theseErrors = errors;
							 | 
						|
													errors = priorErrors;
							 | 
						|
													return theseErrors;
							 | 
						|
												}
							 | 
						|
											}
							 | 
						|
											return [];
							 | 
						|
										}
							 | 
						|
										if(value === undefined){
							 | 
						|
											if(schema.required){
							 | 
						|
												addError("is missing and it is required");
							 | 
						|
											}
							 | 
						|
										}else{
							 | 
						|
											errors = errors.concat(checkType(getType(schema),value));
							 | 
						|
											if(schema.disallow && !checkType(schema.disallow,value).length){
							 | 
						|
												addError(" disallowed value was matched");
							 | 
						|
											}
							 | 
						|
											if(value !== null){
							 | 
						|
												if(value instanceof Array){
							 | 
						|
													if(schema.items){
							 | 
						|
														var itemsIsArray = schema.items instanceof Array;
							 | 
						|
														var propDef = schema.items;
							 | 
						|
														for (i = 0, l = value.length; i < l; i += 1) {
							 | 
						|
															if (itemsIsArray)
							 | 
						|
																propDef = schema.items[i];
							 | 
						|
															if (options.coerce)
							 | 
						|
																value[i] = options.coerce(value[i], propDef);
							 | 
						|
															errors.concat(checkProp(value[i],propDef,path,i));
							 | 
						|
														}
							 | 
						|
													}
							 | 
						|
													if(schema.minItems && value.length < schema.minItems){
							 | 
						|
														addError("There must be a minimum of " + schema.minItems + " in the array");
							 | 
						|
													}
							 | 
						|
													if(schema.maxItems && value.length > schema.maxItems){
							 | 
						|
														addError("There must be a maximum of " + schema.maxItems + " in the array");
							 | 
						|
													}
							 | 
						|
												}else if(schema.properties || schema.additionalProperties){
							 | 
						|
													errors.concat(checkObj(value, schema.properties, path, schema.additionalProperties));
							 | 
						|
												}
							 | 
						|
												if(schema.pattern && typeof value == 'string' && !value.match(schema.pattern)){
							 | 
						|
													addError("does not match the regex pattern " + schema.pattern);
							 | 
						|
												}
							 | 
						|
												if(schema.maxLength && typeof value == 'string' && value.length > schema.maxLength){
							 | 
						|
													addError("may only be " + schema.maxLength + " characters long");
							 | 
						|
												}
							 | 
						|
												if(schema.minLength && typeof value == 'string' && value.length < schema.minLength){
							 | 
						|
													addError("must be at least " + schema.minLength + " characters long");
							 | 
						|
												}
							 | 
						|
												if(typeof schema.minimum !== 'undefined' && typeof value == typeof schema.minimum &&
							 | 
						|
														schema.minimum > value){
							 | 
						|
													addError("must have a minimum value of " + schema.minimum);
							 | 
						|
												}
							 | 
						|
												if(typeof schema.maximum !== 'undefined' && typeof value == typeof schema.maximum &&
							 | 
						|
														schema.maximum < value){
							 | 
						|
													addError("must have a maximum value of " + schema.maximum);
							 | 
						|
												}
							 | 
						|
												if(schema['enum']){
							 | 
						|
													var enumer = schema['enum'];
							 | 
						|
													l = enumer.length;
							 | 
						|
													var found;
							 | 
						|
													for(var j = 0; j < l; j++){
							 | 
						|
														if(enumer[j]===value){
							 | 
						|
															found=1;
							 | 
						|
															break;
							 | 
						|
														}
							 | 
						|
													}
							 | 
						|
													if(!found){
							 | 
						|
														addError("does not have a value in the enumeration " + enumer.join(", "));
							 | 
						|
													}
							 | 
						|
												}
							 | 
						|
												if(typeof schema.maxDecimal == 'number' &&
							 | 
						|
													(value.toString().match(new RegExp("\\.[0-9]{" + (schema.maxDecimal + 1) + ",}")))){
							 | 
						|
													addError("may only have " + schema.maxDecimal + " digits of decimal places");
							 | 
						|
												}
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
										return null;
							 | 
						|
									}
							 | 
						|
									// validate an object against a schema
							 | 
						|
									function checkObj(instance,objTypeDef,path,additionalProp){
							 | 
						|
								
							 | 
						|
										if(typeof objTypeDef =='object'){
							 | 
						|
											if(typeof instance != 'object' || instance instanceof Array){
							 | 
						|
												errors.push({property:path,message:"an object is required"});
							 | 
						|
											}
							 | 
						|
											
							 | 
						|
											for(var i in objTypeDef){ 
							 | 
						|
												if(objTypeDef.hasOwnProperty(i) && i != '__proto__' && i != 'constructor'){
							 | 
						|
													var value = instance.hasOwnProperty(i) ? instance[i] : undefined;
							 | 
						|
													// skip _not_ specified properties
							 | 
						|
													if (value === undefined && options.existingOnly) continue;
							 | 
						|
													var propDef = objTypeDef[i];
							 | 
						|
													// set default
							 | 
						|
													if(value === undefined && propDef["default"]){
							 | 
						|
														value = instance[i] = propDef["default"];
							 | 
						|
													}
							 | 
						|
													if(options.coerce && i in instance){
							 | 
						|
														value = instance[i] = options.coerce(value, propDef);
							 | 
						|
													}
							 | 
						|
													checkProp(value,propDef,path,i);
							 | 
						|
												}
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
										for(i in instance){
							 | 
						|
											if(instance.hasOwnProperty(i) && !(i.charAt(0) == '_' && i.charAt(1) == '_') && objTypeDef && !objTypeDef[i] && additionalProp===false){
							 | 
						|
												if (options.filter) {
							 | 
						|
													delete instance[i];
							 | 
						|
													continue;
							 | 
						|
												} else {
							 | 
						|
													errors.push({property:path,message:"The property " + i +
							 | 
						|
														" is not defined in the schema and the schema does not allow additional properties"});
							 | 
						|
												}
							 | 
						|
											}
							 | 
						|
											var requires = objTypeDef && objTypeDef[i] && objTypeDef[i].requires;
							 | 
						|
											if(requires && !(requires in instance)){
							 | 
						|
												errors.push({property:path,message:"the presence of the property " + i + " requires that " + requires + " also be present"});
							 | 
						|
											}
							 | 
						|
											value = instance[i];
							 | 
						|
											if(additionalProp && (!(objTypeDef && typeof objTypeDef == 'object') || !(i in objTypeDef))){
							 | 
						|
												if(options.coerce){
							 | 
						|
													value = instance[i] = options.coerce(value, additionalProp);
							 | 
						|
												}
							 | 
						|
												checkProp(value,additionalProp,path,i);
							 | 
						|
											}
							 | 
						|
											if(!_changing && value && value.$schema){
							 | 
						|
												errors = errors.concat(checkProp(value,value.$schema,path,i));
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
										return errors;
							 | 
						|
									}
							 | 
						|
									if(schema){
							 | 
						|
										checkProp(instance,schema,'',_changing || '');
							 | 
						|
									}
							 | 
						|
									if(!_changing && instance && instance.$schema){
							 | 
						|
										checkProp(instance,instance.$schema,'','');
							 | 
						|
									}
							 | 
						|
									return {valid:!errors.length,errors:errors};
							 | 
						|
								};
							 | 
						|
								exports.mustBeValid = function(result){
							 | 
						|
									//	summary:
							 | 
						|
									//		This checks to ensure that the result is valid and will throw an appropriate error message if it is not
							 | 
						|
									// result: the result returned from checkPropertyChange or validate
							 | 
						|
									if(!result.valid){
							 | 
						|
										throw new TypeError(result.errors.map(function(error){return "for property " + error.property + ': ' + error.message;}).join(", \n"));
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								return exports;
							 | 
						|
								}));
							 | 
						|
								
							 |