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