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 }());