Merge all loose ends

Thank you for taking the initiative glad to see progress.

Here is my little contribution to ensure we pull all the loose ends together and make it clear that your fork is now the latest and not just because you had the latest commit but because it now contains all the contributions from the other forks leaving no doubt or suspicions.

Quick rundown:
This pull request now merges all the forks into one, since we haven't had an active upstream it resulted in many loose ends visible when viewing the [network tree](/kriszyp/json-schema/network) which this pull request will flatten making it evident going forward that you now have the latest master, if you will of the project.

Most of the last merges merely reverts back to the current implementations as they have been merged sometime somewhere so the commit merely makes that indicative on the [network](/kriszyp/json-schema/network) view page

The only outstanding is aarong's commit [@d0b86ab](/aarong/json-schema/commit/d0b86abcdb9df211f8c08bc818089037814d67c9) which is conflicting and I have left a message there for him to verify if he wants his changes to be taken forward with us.

So just for clarity the only changes this will have on your fork is the changes to validate.js which you haven't kept up with.  No changes to the I-D(4) xml file you have. Let me know if this apply alright as we may have to force her just a little bit =) 
I did a quick test now cloning yours and doing a pull from mine and it went smoothly so all should be well.

```diff
diff --git a/lib/validate.js b/lib/validate.js
index 97cbbf6..be4a93b 100644
--- a/lib/validate.js
+++ b/lib/validate.js
@@ -62,18 +62,29 @@ var validate = exports._validate = function(/*Any*/instance,/*Object*/schema,/*O
        }
        var errors = [];
        // validate a value against a property definition
-       function checkProp(value, schema, path,i){
+       function checkProp(value, schema, path, i){
 
                var l;
-               path += path ? typeof i == 'number' ? '[' + i + ']' : typeof i == 'undefined' ? '' : '.' + i : i;
-               function addError(message){
-                       errors.push({property:path,message:message});
+               if(typeof i !== 'undefined') {
+                       if(typeof i === 'number') {
+                               path += '[' + i + ']';
+                       } else {
+                               var escaped = i.replace(/\./g, '\\.');
+                               path += path ? ('.' + escaped) : escaped;
+                       }
+               }
+               function addError(message, value){
+                       var error = {property:path,message:message,schema:schema};
+                       if(typeof value !== 'undefined') {
+                               error.value = value;
+                       }
+                       errors.push(error);
                }
 
                if((typeof schema != 'object' || schema instanceof Array) && (path || typeof schema != 'function') && !(schema && getType(schema))){
                        if(typeof schema == 'function'){
                                if(!(value instanceof schema)){
-                                       addError("is not an instance of the class/constructor " + schema.name);
+                                       addError("is not an instance of the class/constructor " + schema.name, value);
                                }
                        }else if(schema){
                                addError("Invalid schema/property definition " + schema);
@@ -94,7 +105,7 @@ var validate = exports._validate = function(/*Any*/instance,/*Object*/schema,/*O
                                                !(value instanceof Array && type == 'array') &&
                                                !(value instanceof Date && type == 'date') &&
                                                !(type == 'integer' && value%1===0)){
-                                       return [{property:path,message:(typeof value) + " value found, but a " + type + " is required"}];
+                                       return [{property:path,message:(typeof value) + " value found, but a " + type + " is required",schema:schema,value:value}];
                                }
                                if(type instanceof Array){
                                        var unionErrors=[];
@@ -124,14 +135,14 @@ var validate = exports._validate = function(/*Any*/instance,/*Object*/schema,/*O
                }else{
                        errors = errors.concat(checkType(getType(schema),value));
                        if(schema.disallow && !checkType(schema.disallow,value).length){
-                               addError(" disallowed value was matched");
+                               addError(" disallowed value was matched", value);
                        }
                        if(value !== null){
                                if(value instanceof Array){
                                        if(schema.items){
                                                var itemsIsArray = schema.items instanceof Array;
                                                var propDef = schema.items;
-                                               for (i = 0, l = value.length; i < l; i += 1) {
+                                               for (var i = 0, l = value.length; i < l; i += 1) {
                                                        if (itemsIsArray)
                                                                propDef = schema.items[i];
                                                        if (options.coerce)
@@ -140,30 +151,40 @@ var validate = exports._validate = function(/*Any*/instance,/*Object*/schema,/*O
                                                }
                                        }
                                        if(schema.minItems && value.length < schema.minItems){
-                                               addError("There must be a minimum of " + schema.minItems + " in the array");
+                                               addError("There must be a minimum of " + schema.minItems + " in the array", value);
                                        }
                                        if(schema.maxItems && value.length > schema.maxItems){
-                                               addError("There must be a maximum of " + schema.maxItems + " in the array");
+                                               addError("There must be a maximum of " + schema.maxItems + " in the array", value);
+                                       }
+                                       if(schema.uniqueItems) {
+                                               for(var i = 0, l = value.length; i < l; i += 1) {
+                                                       for(var j = i + 1; j < l; j += 1) {
+                                                               if(compareItems(value[i], value[j])) {
+                                                                       addError("The items in the array must be unique.", value);
+                                                                       break;
+                                                               }
+                                                       }
+                                               }
                                        }
                                }else if(schema.properties || schema.additionalProperties){
                                        errors.concat(checkObj(value, schema.properties, path, schema.additionalProperties));
                                }
                                if(schema.pattern && typeof value == 'string' && !value.match(schema.pattern)){
-                                       addError("does not match the regex pattern " + schema.pattern);
+                                       addError("does not match the regex pattern " + schema.pattern, value);
                                }
                                if(schema.maxLength && typeof value == 'string' && value.length > schema.maxLength){
-                                       addError("may only be " + schema.maxLength + " characters long");
+                                       addError("may only be " + schema.maxLength + " characters long", value);
                                }
                                if(schema.minLength && typeof value == 'string' && value.length < schema.minLength){
-                                       addError("must be at least " + schema.minLength + " characters long");
+                                       addError("must be at least " + schema.minLength + " characters long", value);
                                }
                                if(typeof schema.minimum !== undefined && typeof value == typeof schema.minimum &&
                                                schema.minimum > value){
-                                       addError("must have a minimum value of " + schema.minimum);
+                                       addError("must have a minimum value of " + schema.minimum, value);
                                }
                                if(typeof schema.maximum !== undefined && typeof value == typeof schema.maximum &&
                                                schema.maximum < value){
-                                       addError("must have a maximum value of " + schema.maximum);
+                                       addError("must have a maximum value of " + schema.maximum, value);
                                }
                                if(schema['enum']){
                                        var enumer = schema['enum'];
@@ -176,12 +197,12 @@ var validate = exports._validate = function(/*Any*/instance,/*Object*/schema,/*O
                                                }
                                        }
                                        if(!found){
-                                               addError("does not have a value in the enumeration " + enumer.join(", "));
+                                               addError("does not have a value in the enumeration " + enumer.join(", "), value);
                                        }
                                }
                                if(typeof schema.maxDecimal == 'number' &&
                                        (value.toString().match(new RegExp("\\.[0-9]{" + (schema.maxDecimal + 1) + ",}")))){
-                                       addError("may only have " + schema.maxDecimal + " digits of decimal places");
+                                       addError("may only have " + schema.maxDecimal + " digits of decimal places", value);
                                }
                        }
                }
@@ -190,12 +211,20 @@ var validate = exports._validate = function(/*Any*/instance,/*Object*/schema,/*O
        // validate an object against a schema
        function checkObj(instance,objTypeDef,path,additionalProp){
 
+               function addError(message, value){
+                       var error = {property:path,message:message,schema:objTypeDef};
+                       if(typeof value !== 'undefined') {
+                               error.value = value;
+                       }
+                       errors.push(error);
+               }
+
                if(typeof objTypeDef =='object'){
                        if(typeof instance != 'object' || instance instanceof Array){
-                               errors.push({property:path,message:"an object is required"});
+                               addError("an object is required", instance);
                        }
-                       
-                       for(var i in objTypeDef){ 
+
+                       for(var i in objTypeDef){
                                if(objTypeDef.hasOwnProperty(i)){
                                        var value = instance[i];
                                        // skip _not_ specified properties
@@ -212,19 +241,19 @@ var validate = exports._validate = function(/*Any*/instance,/*Object*/schema,/*O
                                }
                        }
                }
-               for(i in instance){
+               for(var i in instance){
                        if(instance.hasOwnProperty(i) && !(i.charAt(0) == '_' && i.charAt(1) == '_') && objTypeDef && !objTypeDef[i] && additionalProp===false){
                                if (options.filter) {
                                        delete instance[i];
                                        continue;
                                } else {
-                                       errors.push({property:path,message:(typeof value) + "The property " + i +
-                                               " is not defined in the schema and the schema does not allow additional properties"});
+                                       addError("The property '" + i +
+                                               "' is not defined in the schema and the schema does not allow additional properties", instance);
                                }
                        }
                        var requires = objTypeDef && objTypeDef[i] && objTypeDef[i].requires;
                        if(requires && !(requires in instance)){
-                               errors.push({property:path,message:"the presence of the property " + i + " requires that " + requires + " also be present"});
+                               addError("the presence of the property " + i + " requires that " + requires + " also be present", instance);
                        }
                        value = instance[i];
                        if(additionalProp && (!(objTypeDef && typeof objTypeDef == 'object') || !(i in objTypeDef))){
@@ -239,6 +268,39 @@ var validate = exports._validate = function(/*Any*/instance,/*Object*/schema,/*O
                }
                return errors;
        }
+       // compare a value against another for equality (for uniqueItems)
+       function compareItems(item1, item2) {
+               if(typeof item1 !== typeof item2) {
+                       return false;
+               }
+               if(Array.isArray(item1)) {
+                       if(item1.length !== item2.length) {
+                               return false;
+                       }
+                       for(var i = 0, l = item1.length; i < l; i += 1) {
+                               if(!compareItems(item1[i], item2[i])) {
+                                       return false;
+                               }
+                       }
+                       return true;
+               }
+               if(item1 instanceof Object) {
+                       var item1Keys = Object.keys(item1);
+                       var item2Keys = Object.keys(item2);
+                       if(item1Keys.length !== item2Keys.length) {
+                               return false;
+                       }
+                       for(var i = 0, l = item1Keys.length; i < l; i += 1) {
+                               var key = item1Keys[i];
+                               if(!item2.hasOwnProperty(key) ||
+                                       !compareItems(item1[key], item2[key])) {
+                                       return false;
+                               }
+                       }
+                       return true;
+               }
+               return item1 === item2;
+       }
        if(schema){
                checkProp(instance,schema,'',_changing || '');
        }
```