twitst4tz

twitter statistics web application
Log | Files | Refs | README | LICENSE

validate.js (10939B)


      1 /**
      2  * JSONSchema Validator - Validates JavaScript objects using JSON Schemas
      3  *	(http://www.json.com/json-schema-proposal/)
      4  *
      5  * Copyright (c) 2007 Kris Zyp SitePen (www.sitepen.com)
      6  * Licensed under the MIT (MIT-LICENSE.txt) license.
      7 To use the validator call the validate function with an instance object and an optional schema object.
      8 If a schema is provided, it will be used to validate. If the instance object refers to a schema (self-validating),
      9 that schema will be used to validate and the schema parameter is not necessary (if both exist,
     10 both validations will occur).
     11 The validate method will return an array of validation errors. If there are no errors, then an
     12 empty list will be returned. A validation error will have two properties:
     13 "property" which indicates which property had the error
     14 "message" which indicates what the error was
     15  */
     16 (function (root, factory) {
     17     if (typeof define === 'function' && define.amd) {
     18         // AMD. Register as an anonymous module.
     19         define([], function () {
     20             return factory();
     21         });
     22     } else if (typeof module === 'object' && module.exports) {
     23         // Node. Does not work with strict CommonJS, but
     24         // only CommonJS-like environments that support module.exports,
     25         // like Node.
     26         module.exports = factory();
     27     } else {
     28         // Browser globals
     29         root.jsonSchema = factory();
     30     }
     31 }(this, function () {// setup primitive classes to be JSON Schema types
     32 var exports = validate
     33 exports.Integer = {type:"integer"};
     34 var primitiveConstructors = {
     35 	String: String,
     36 	Boolean: Boolean,
     37 	Number: Number,
     38 	Object: Object,
     39 	Array: Array,
     40 	Date: Date
     41 }
     42 exports.validate = validate;
     43 function validate(/*Any*/instance,/*Object*/schema) {
     44 		// Summary:
     45 		//  	To use the validator call JSONSchema.validate with an instance object and an optional schema object.
     46 		// 		If a schema is provided, it will be used to validate. If the instance object refers to a schema (self-validating),
     47 		// 		that schema will be used to validate and the schema parameter is not necessary (if both exist,
     48 		// 		both validations will occur).
     49 		// 		The validate method will return an object with two properties:
     50 		// 			valid: A boolean indicating if the instance is valid by the schema
     51 		// 			errors: An array of validation errors. If there are no errors, then an
     52 		// 					empty list will be returned. A validation error will have two properties:
     53 		// 						property: which indicates which property had the error
     54 		// 						message: which indicates what the error was
     55 		//
     56 		return validate(instance, schema, {changing: false});//, coerce: false, existingOnly: false});
     57 	};
     58 exports.checkPropertyChange = function(/*Any*/value,/*Object*/schema, /*String*/property) {
     59 		// Summary:
     60 		// 		The checkPropertyChange method will check to see if an value can legally be in property with the given schema
     61 		// 		This is slightly different than the validate method in that it will fail if the schema is readonly and it will
     62 		// 		not check for self-validation, it is assumed that the passed in value is already internally valid.
     63 		// 		The checkPropertyChange method will return the same object type as validate, see JSONSchema.validate for
     64 		// 		information.
     65 		//
     66 		return validate(value, schema, {changing: property || "property"});
     67 	};
     68 var validate = exports._validate = function(/*Any*/instance,/*Object*/schema,/*Object*/options) {
     69 
     70 	if (!options) options = {};
     71 	var _changing = options.changing;
     72 
     73 	function getType(schema){
     74 		return schema.type || (primitiveConstructors[schema.name] == schema && schema.name.toLowerCase());
     75 	}
     76 	var errors = [];
     77 	// validate a value against a property definition
     78 	function checkProp(value, schema, path,i){
     79 
     80 		var l;
     81 		path += path ? typeof i == 'number' ? '[' + i + ']' : typeof i == 'undefined' ? '' : '.' + i : i;
     82 		function addError(message){
     83 			errors.push({property:path,message:message});
     84 		}
     85 
     86 		if((typeof schema != 'object' || schema instanceof Array) && (path || typeof schema != 'function') && !(schema && getType(schema))){
     87 			if(typeof schema == 'function'){
     88 				if(!(value instanceof schema)){
     89 					addError("is not an instance of the class/constructor " + schema.name);
     90 				}
     91 			}else if(schema){
     92 				addError("Invalid schema/property definition " + schema);
     93 			}
     94 			return null;
     95 		}
     96 		if(_changing && schema.readonly){
     97 			addError("is a readonly field, it can not be changed");
     98 		}
     99 		if(schema['extends']){ // if it extends another schema, it must pass that schema as well
    100 			checkProp(value,schema['extends'],path,i);
    101 		}
    102 		// validate a value against a type definition
    103 		function checkType(type,value){
    104 			if(type){
    105 				if(typeof type == 'string' && type != 'any' &&
    106 						(type == 'null' ? value !== null : typeof value != type) &&
    107 						!(value instanceof Array && type == 'array') &&
    108 						!(value instanceof Date && type == 'date') &&
    109 						!(type == 'integer' && value%1===0)){
    110 					return [{property:path,message:(typeof value) + " value found, but a " + type + " is required"}];
    111 				}
    112 				if(type instanceof Array){
    113 					var unionErrors=[];
    114 					for(var j = 0; j < type.length; j++){ // a union type
    115 						if(!(unionErrors=checkType(type[j],value)).length){
    116 							break;
    117 						}
    118 					}
    119 					if(unionErrors.length){
    120 						return unionErrors;
    121 					}
    122 				}else if(typeof type == 'object'){
    123 					var priorErrors = errors;
    124 					errors = [];
    125 					checkProp(value,type,path);
    126 					var theseErrors = errors;
    127 					errors = priorErrors;
    128 					return theseErrors;
    129 				}
    130 			}
    131 			return [];
    132 		}
    133 		if(value === undefined){
    134 			if(schema.required){
    135 				addError("is missing and it is required");
    136 			}
    137 		}else{
    138 			errors = errors.concat(checkType(getType(schema),value));
    139 			if(schema.disallow && !checkType(schema.disallow,value).length){
    140 				addError(" disallowed value was matched");
    141 			}
    142 			if(value !== null){
    143 				if(value instanceof Array){
    144 					if(schema.items){
    145 						var itemsIsArray = schema.items instanceof Array;
    146 						var propDef = schema.items;
    147 						for (i = 0, l = value.length; i < l; i += 1) {
    148 							if (itemsIsArray)
    149 								propDef = schema.items[i];
    150 							if (options.coerce)
    151 								value[i] = options.coerce(value[i], propDef);
    152 							errors.concat(checkProp(value[i],propDef,path,i));
    153 						}
    154 					}
    155 					if(schema.minItems && value.length < schema.minItems){
    156 						addError("There must be a minimum of " + schema.minItems + " in the array");
    157 					}
    158 					if(schema.maxItems && value.length > schema.maxItems){
    159 						addError("There must be a maximum of " + schema.maxItems + " in the array");
    160 					}
    161 				}else if(schema.properties || schema.additionalProperties){
    162 					errors.concat(checkObj(value, schema.properties, path, schema.additionalProperties));
    163 				}
    164 				if(schema.pattern && typeof value == 'string' && !value.match(schema.pattern)){
    165 					addError("does not match the regex pattern " + schema.pattern);
    166 				}
    167 				if(schema.maxLength && typeof value == 'string' && value.length > schema.maxLength){
    168 					addError("may only be " + schema.maxLength + " characters long");
    169 				}
    170 				if(schema.minLength && typeof value == 'string' && value.length < schema.minLength){
    171 					addError("must be at least " + schema.minLength + " characters long");
    172 				}
    173 				if(typeof schema.minimum !== undefined && typeof value == typeof schema.minimum &&
    174 						schema.minimum > value){
    175 					addError("must have a minimum value of " + schema.minimum);
    176 				}
    177 				if(typeof schema.maximum !== undefined && typeof value == typeof schema.maximum &&
    178 						schema.maximum < value){
    179 					addError("must have a maximum value of " + schema.maximum);
    180 				}
    181 				if(schema['enum']){
    182 					var enumer = schema['enum'];
    183 					l = enumer.length;
    184 					var found;
    185 					for(var j = 0; j < l; j++){
    186 						if(enumer[j]===value){
    187 							found=1;
    188 							break;
    189 						}
    190 					}
    191 					if(!found){
    192 						addError("does not have a value in the enumeration " + enumer.join(", "));
    193 					}
    194 				}
    195 				if(typeof schema.maxDecimal == 'number' &&
    196 					(value.toString().match(new RegExp("\\.[0-9]{" + (schema.maxDecimal + 1) + ",}")))){
    197 					addError("may only have " + schema.maxDecimal + " digits of decimal places");
    198 				}
    199 			}
    200 		}
    201 		return null;
    202 	}
    203 	// validate an object against a schema
    204 	function checkObj(instance,objTypeDef,path,additionalProp){
    205 
    206 		if(typeof objTypeDef =='object'){
    207 			if(typeof instance != 'object' || instance instanceof Array){
    208 				errors.push({property:path,message:"an object is required"});
    209 			}
    210 			
    211 			for(var i in objTypeDef){ 
    212 				if(objTypeDef.hasOwnProperty(i)){
    213 					var value = instance[i];
    214 					// skip _not_ specified properties
    215 					if (value === undefined && options.existingOnly) continue;
    216 					var propDef = objTypeDef[i];
    217 					// set default
    218 					if(value === undefined && propDef["default"]){
    219 						value = instance[i] = propDef["default"];
    220 					}
    221 					if(options.coerce && i in instance){
    222 						value = instance[i] = options.coerce(value, propDef);
    223 					}
    224 					checkProp(value,propDef,path,i);
    225 				}
    226 			}
    227 		}
    228 		for(i in instance){
    229 			if(instance.hasOwnProperty(i) && !(i.charAt(0) == '_' && i.charAt(1) == '_') && objTypeDef && !objTypeDef[i] && additionalProp===false){
    230 				if (options.filter) {
    231 					delete instance[i];
    232 					continue;
    233 				} else {
    234 					errors.push({property:path,message:(typeof value) + "The property " + i +
    235 						" is not defined in the schema and the schema does not allow additional properties"});
    236 				}
    237 			}
    238 			var requires = objTypeDef && objTypeDef[i] && objTypeDef[i].requires;
    239 			if(requires && !(requires in instance)){
    240 				errors.push({property:path,message:"the presence of the property " + i + " requires that " + requires + " also be present"});
    241 			}
    242 			value = instance[i];
    243 			if(additionalProp && (!(objTypeDef && typeof objTypeDef == 'object') || !(i in objTypeDef))){
    244 				if(options.coerce){
    245 					value = instance[i] = options.coerce(value, additionalProp);
    246 				}
    247 				checkProp(value,additionalProp,path,i);
    248 			}
    249 			if(!_changing && value && value.$schema){
    250 				errors = errors.concat(checkProp(value,value.$schema,path,i));
    251 			}
    252 		}
    253 		return errors;
    254 	}
    255 	if(schema){
    256 		checkProp(instance,schema,'',_changing || '');
    257 	}
    258 	if(!_changing && instance && instance.$schema){
    259 		checkProp(instance,instance.$schema,'','');
    260 	}
    261 	return {valid:!errors.length,errors:errors};
    262 };
    263 exports.mustBeValid = function(result){
    264 	//	summary:
    265 	//		This checks to ensure that the result is valid and will throw an appropriate error message if it is not
    266 	// result: the result returned from checkPropertyChange or validate
    267 	if(!result.valid){
    268 		throw new TypeError(result.errors.map(function(error){return "for property " + error.property + ': ' + error.message;}).join(", \n"));
    269 	}
    270 }
    271 
    272 return exports;
    273 }));