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