Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Release 0.4.2

  • Loading branch information...
commit 50182c5579fac8854ae7d3934e4d3c431d99185e 1 parent 95fb91d
@Baggz authored
View
4 README.md
@@ -86,8 +86,8 @@ Releases for a browser are available for download from GitHub.
| **Version** | **Description** | **Size** | **Action** |
|:------------|:----------------|:---------|:-----------|
-| `amanda.js` | *uncompressed, with comments* | 35.8 KB (7.11 KB gzipped) | [Download](https://raw.github.com/Baggz/Amanda/master/releases/latest/amanda.js) |
-| `amanda.min.js` | *compressed, without comments* | 13.6 KB (3.97 KB gzipped) | [Download](https://raw.github.com/Baggz/Amanda/master/releases/latest/amanda.min.js) |
+| `amanda.js` | *uncompressed, with comments* | 36.01 KB (7.11 KB gzipped) | [Download](https://raw.github.com/Baggz/Amanda/master/releases/latest/amanda.js) |
+| `amanda.min.js` | *compressed, without comments* | 13.62 KB (3.99 KB gzipped) | [Download](https://raw.github.com/Baggz/Amanda/master/releases/latest/amanda.min.js) |
<a name="Uage"></a>
## Usage [&uarr;](#Contents)
View
2  package.json
@@ -2,7 +2,7 @@
"name": "amanda",
"description": "JSON Schema validator",
- "version": "0.4.1",
+ "version": "0.4.2",
"author": "František Hába <hello@frantisekhaba.com>",
"devDependencies": {
View
2,014 releases/0.4.2/amanda.js
@@ -0,0 +1,2014 @@
+(function() {
+
+ /**
+ * Engines
+ * --------------------
+ */
+ var engines = {};
+
+/**
+ * DetectType
+ *
+ * @param {object} input
+ */
+var detectType = function(input) {
+ return typeof input;
+};
+
+/**
+ * Each
+ *
+ * Applies an iterator function to each item in an array or an object, in series.
+ *
+ * @param {object} list
+ * @param {function} iterator
+ * @param {function} callback
+ */
+var each = function(list, iterator, callback) {
+
+ /**
+ * SyncEach
+ *
+ * @param {object} list
+ * @param {function} iterator
+ */
+ var syncEach = function(list, iterator) {
+
+ // If the list is an array
+ if (isArray(list) && !isEmpty(list)) {
+ for (var i = 0, len = list.length; i < len; i++) {
+ iterator.apply(list, [i, list[i]]);
+ }
+ }
+
+ // If the list is an object
+ if (isObject(list) && !isEmpty(list)) {
+ for (var key in list) {
+ if (list.hasOwnProperty(key)) {
+ iterator.apply(list, [key, list[key]]);
+ }
+ }
+ }
+
+ };
+
+ /**
+ * AsyncEach
+ * @param {object} list
+ * @param {function} iterator
+ * @param {function} callback
+ */
+ var asyncEach = function(list, iterator, callback) {
+
+ var queue = [];
+
+ /**
+ * AddToQueue
+ *
+ * @param {string} key
+ * @param {string|object} value
+ */
+ var addToQueue = function(key, value) {
+ var index = queue.length + 1;
+ queue.push(function() {
+
+ var next = function(error) {
+ var fn = queue[index];
+ if (!error && fn) {
+ return fn();
+ } else if (!error && !fn) {
+ return callback();
+ } else {
+ return callback(error);
+ }
+ };
+
+ return iterator(key, value, next);
+
+ });
+ };
+
+ // If the list is an array
+ if (isArray(list) && !isEmpty(list)) {
+ for (var i = 0, len = list.length; i < len; i++) {
+ addToQueue(i, list[i]);
+ }
+
+ // If the list is an object
+ } else if (isObject(list) && !isEmpty(list)) {
+ for (var key in list) {
+ if (list.hasOwnProperty(key)) {
+ addToQueue(key, list[key]);
+ }
+ }
+
+ // If the list is not an array or an object
+ } else {
+ return callback();
+ }
+
+ // And go!
+ return queue[0]();
+
+ };
+
+ if (typeof callback === 'undefined') {
+ return syncEach.apply(this, arguments);
+ } else {
+ return asyncEach.apply(this, arguments);
+ }
+
+};
+
+/**
+ * Every
+ *
+ * @param {object} arr
+ * @param {function} iterator
+ */
+var every = function(arr, iterator) {
+ return Array.prototype.every.apply(arr, [iterator]);
+};
+
+/**
+ * Filter
+ *
+ * @param {object} arr
+ * @param {function} iterator
+ */
+var filter = function(arr, iterator, context) {
+ return Array.prototype.filter.apply(arr, [iterator, context || this]);
+};
+
+/**
+ * HasProperty
+ *
+ * @param {object} input
+ */
+var hasProperty = function(obj, property) {
+ return Object.prototype.hasOwnProperty.apply(obj, [property]);
+};
+
+/**
+ * IsArray
+ *
+ * Returns true if the passed-in object is an array.
+ *
+ * @param {object} input
+ */
+var isArray = function(input) {
+ return Object.prototype.toString.call(input) === '[object Array]';
+};
+
+/**
+ * IsBoolean
+ *
+ * @param {object} input
+ */
+var isBoolean = function(input) {
+ return typeof input === 'boolean';
+};
+
+/**
+ * IsDefined
+ *
+ * @param {object} input
+ */
+var isDefined = function(input) {
+ return typeof input !== 'undefined';
+};
+
+/**
+ * IsEmpty
+ *
+ * Returns true if the passed-in object is empty.
+ *
+ * @param {object} input
+ */
+var isEmpty = function(input) {
+
+ if (isNumber(input)) {
+ return false;
+ }
+
+ if (input === null) {
+ return true;
+ }
+
+ // If the passed-in object is an array or a string
+ if (isArray(input) || typeof input === 'string') {
+ return input.length === 0;
+ }
+
+ // If the passed-in object is an object
+ if (isObject(input)) {
+ for (var key in input) {
+ if (hasOwnProperty.call(input, key)) return false;
+ }
+ }
+
+ return true;
+
+};
+
+/**
+ * IsEqual
+ *
+ * @param {object} obj1
+ * @param {object} obj2
+ */
+var isEqual = function(obj1, obj2) {
+
+ /**
+ * Arrays
+ */
+ if (isArray(obj1, obj2)) {
+
+ if (obj1.length !== obj2.length) {
+ return false;
+ }
+
+ return every(obj1, function(value, index, context) {
+ return obj2[index] === value;
+ });
+
+ }
+
+ /**
+ * Objects
+ */
+ if (isObject(obj1, obj2)) {
+
+ var keys1 = keys(obj1),
+ keys2 = keys(obj2);
+
+ if (!isEqual(keys1, keys2)) {
+ return false;
+ }
+
+ for (key in obj1) {
+ if (!obj2[key] || obj1[key] !== obj2[key]) {
+ return false;
+ }
+ }
+
+ return true;
+
+ }
+
+ return false;
+
+};
+
+/**
+ * IsFunction
+ *
+ * @param {object} input
+ */
+var isFunction = function(input) {
+ return typeof input === 'function';
+};
+
+/**
+ * IsInteger
+ *
+ * @param {object} input
+ */
+var isInteger = function(input) {
+ return isNumber(input) && input % 1 === 0;
+};
+
+/**
+ * IsNull
+ *
+ * @param {object} input
+ */
+var isNull = function(input) {
+ return input === null;
+};
+
+/**
+ * IsNumber
+ *
+ * @param {object} input
+ */
+var isNumber = function(input) {
+ return typeof input === 'number';
+};
+
+/**
+ * IsObject
+ *
+ * Returns true if the passed-in object is an object.
+ *
+ * @param {object} input
+ */
+var isObject = function(input) {
+ return Object.prototype.toString.call(input) === '[object Object]';
+};
+
+/**
+ * IsString
+ *
+ * @param {object} input
+ */
+var isString = function(input) {
+ return typeof input === 'string';
+};
+
+/**
+ * IsUndefined
+ *
+ * @param {object} input
+ */
+var isUndefined = function(input) {
+ return typeof input === 'undefined';
+};
+
+/**
+ * Keys
+ *
+ * @param {object} obj
+ */
+var keys = function(obj) {
+ return Object.keys(obj);
+};
+
+/**
+ * Merge
+ *
+ * Copy all of the properties in the source objects over to the destination object.
+ *
+ * @param {object} obj1
+ * @param {object} obj2
+ */
+var merge = function(obj1, obj2) {
+ for (var key in obj2) {
+ if (obj2.hasOwnProperty(key) && !obj1.hasOwnProperty(key)) {
+ obj1[key] = obj2[key];
+ }
+ }
+ return obj1;
+};
+
+/**
+ * Pluck
+ *
+ * Extracts a list of property values.
+ *
+ * @param {object} list
+ * @param {string} propertyName
+ */
+var pluck = function(list, propertyName) {
+ var output = [];
+ for (var i = 0, len = list.length; i < len; i++) {
+ var property = list[i][propertyName];
+ if (output.indexOf(property) === -1) {
+ output.push(property);
+ }
+ }
+ return output;
+};
+
+/**
+ * ReturnTrue
+ */
+var returnTrue = function() {
+ return true;
+};
+
+/**
+ * Some
+ *
+ * @param {object} arr
+ * @param {function} iterator
+ */
+var some = function(arr, iterator) {
+ return Array.prototype.some.apply(arr, [iterator]);
+};
+
+(function() {
+
+/**
+ * Validation
+ *
+ * @constructor
+ * @param {object} options
+ */
+var Validation = function(options) {
+
+ // Save a reference to the ‘this’
+ var self = this;
+
+ var defaultOptions = {
+ singleError: true,
+ messages: errorMessages,
+ cache: false
+ };
+
+ each(defaultOptions, function(key, value) {
+
+ if (isObject(value) && options[key]) {
+ self[key] = merge(options[key], defaultOptions[key]);
+
+ } else if (isObject(value) && !options[key]) {
+ self[key] = merge ({}, defaultOptions[key]);
+
+ } else {
+ self[key] = (isDefined(options[key])) ? options[key] : defaultOptions[key];
+ }
+
+ });
+
+ this.errors = new ValidationError(this);
+
+};
+
+/**
+ * Attributes
+ * --------------------
+ */
+Validation.prototype.attributes = {};
+
+/**
+ * AddAttribute
+ *
+ * @param {string} attributeName
+ * @param {function} attributeFn
+ */
+Validation.prototype.addAttribute = function(attributeName, attributeFn) {
+ return Validation.prototype.attributes[attributeName] = attributeFn;
+};
+
+/**
+ * AddAttributeConstructor
+ *
+ * @param {string} attributeName
+ * @param {function} attributeConstructor
+ */
+Validation.prototype.addAttributeConstructor = function(attributeName, attributeConstructor) {
+ return Validation.prototype.attributes[attributeName] = attributeConstructor();
+};
+
+/**
+ * AdditionalProperties
+ */
+var additionalPropertiesAttribute = function additionalProperties(property, propertyValue, attributeValue, propertyAttributes, callback) {
+
+ var self = this;
+
+ /**
+ * {
+ * additionalProperties: true,
+ * ...
+ * }
+ */
+ if (attributeValue === true) {
+ return callback();
+ }
+
+ // Filter the forbidden properties
+ var propertyKeys = keys(propertyValue);
+ var forbiddenProperties = filter(propertyKeys, function(key) {
+ return !propertyAttributes.properties[key];
+ });
+
+ if (isEmpty(forbiddenProperties)) {
+ return callback();
+ }
+
+ /**
+ * {
+ * additionalProperties: false,
+ * ...
+ * }
+ */
+ if (attributeValue === false) {
+
+ forbiddenProperties.forEach(function(forbiddenProperty) {
+ this.addError({
+ property: this.joinPath(property, forbiddenProperty),
+ propertyValue: propertyValue[forbiddenProperty]
+ });
+ }, this);
+
+ return callback();
+
+ }
+
+ /**
+ * {
+ * additionalProperties: {
+ * type: 'string',
+ * ...
+ * },
+ * ...
+ * }
+ */
+ if (isObject(attributeValue)) {
+ return each(forbiddenProperties, function(index, key, callback) {
+ return self.validateSchema(
+ propertyValue[key],
+ attributeValue,
+ property + key,
+ callback
+ );
+ }, callback);
+ }
+
+};
+
+// Export
+Validation.prototype.addAttribute('additionalProperties', additionalPropertiesAttribute);
+
+/**
+ * DivisibleBy
+ */
+var divisibleByAttribute = function divisibleBy(property, propertyValue, attributeValue, propertyAttributes, callback) {
+
+ if (attributeValue === 0) {
+ throw new Error('The value of this attribute should not be 0.');
+ }
+
+ if (isNumber(propertyValue) && (propertyValue % attributeValue !== 0)) {
+ this.addError();
+ }
+
+ return callback();
+
+};
+
+// Export
+Validation.prototype.addAttribute('divisibleBy', divisibleByAttribute);
+
+/**
+ * Enum
+ */
+var enumAttribute = function(property, propertyValue, attributeValue, propertyAttributes, callback) {
+
+ if (attributeValue.indexOf(propertyValue) === -1) {
+ this.addError();
+ }
+
+ return callback();
+
+};
+
+// Export
+Validation.prototype.addAttribute('enum', enumAttribute);
+
+/**
+ * Except
+ */
+var exceptAttribute = function except(property, propertyValue, attributeValue, propertyAttributes, callback) {
+
+ if (attributeValue.indexOf(propertyValue) !== -1) {
+ this.addError();
+ }
+
+ return callback();
+
+};
+
+// Export
+Validation.prototype.addAttribute('except', exceptAttribute);
+
+/**
+ * Format
+ */
+Validation.prototype.addAttributeConstructor('format', function formatConstructor() {
+
+ // Uložíme si referenci na this
+ var self = this;
+
+ /**
+ * Formats
+ */
+ var formats = {
+
+ /**
+ * date-time
+ *
+ * This should be a date in ISO 8601 format of YYYY-MM-DDThh:mm:ssZ in UTC
+ * time. This is the recommended form of date/timestamp.
+ */
+ 'date-time': {
+ type: 'string',
+ pattern: /^\d{4}-(?:0[0-9]{1}|1[0-2]{1})-[0-9]{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/
+ },
+
+ /**
+ * date
+ *
+ * This should be a date in the format of YYYY-MM-DD. It is recommended that you
+ * use the "date-time" format instead of "date" unless you need to transfer only the date part.
+ */
+ date: function(input) {
+ if (isString(input)) {
+ return input.match(/^\d{4}-(?:0[0-9]{1}|1[0-2]{1})-[0-9]{2}$/);
+ }
+ if (isObject(input)) {
+ return Object.prototype.toString.call(input) === '[object Date]';
+ }
+ return false;
+ },
+
+ /**
+ * time
+ *
+ * This should be a time in the format of hh:mm:ss.
+ */
+ 'time': {
+ type: 'string',
+ pattern: /^\d{2}:\d{2}:\d{2}$/
+ },
+
+ /**
+ * utc-milisec
+ *
+ * This should be the difference, measured in milliseconds, between the specified
+ * time and midnight, 00:00 of January 1, 1970 UTC. The value
+ * should be a number (integer or float).
+ */
+ 'utc-milisec': {
+ type: 'number'
+ },
+
+ /**
+ * regex
+ *
+ * This should be a time in the format of hh:mm:ss.
+ */
+ regex: function(input) {
+ return input && input.test && input.exec;
+ },
+
+ /**
+ * color
+ *
+ * This is a CSS color (like "#FF0000" or "red"), based on CSS 2.1.
+ */
+ 'color': {
+ type: 'string'
+ },
+
+ /**
+ * style
+ *
+ * This is a CSS style definition (like "color: red; background-color:#FFF"), based on CSS 2.1.
+ */
+ 'style': {
+ type: 'string'
+ },
+
+ /**
+ * phone
+ *
+ * This should be a phone number.
+ */
+ 'phone': {
+ type: 'number'
+ },
+
+ /**
+ * uri
+ *
+ * This value should be a URI.
+ */
+ 'uri': {
+ type: 'string',
+ pattern: /^(?:(?:ht|f)tp(?:s?)\:\/\/|~\/|\/)?(?:\w+:\w+@)?((?:(?:[-\w\d{1-3}]+\.)+(?:com|org|cat|coop|int|pro|tel|xxx|net|gov|mil|biz|info|mobi|name|aero|jobs|edu|co\.uk|ac\.uk|it|fr|tv|museum|asia|local|travel|[a-z]{2})?)|((\b25[0-5]\b|\b[2][0-4][0-9]\b|\b[0-1]?[0-9]?[0-9]\b)(\.(\b25[0-5]\b|\b[2][0-4][0-9]\b|\b[0-1]?[0-9]?[0-9]\b)){3}))(?::[\d]{1,5})?(?:(?:(?:\/(?:[-\w~!$+|.,=]|%[a-f\d]{2})+)+|\/)+|\?|#)?(?:(?:\?(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=?(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)(?:&(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=?(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)*)*(?:#(?:[-\w~!$ |\/.,*:;=]|%[a-f\d]{2})*)?$/
+ },
+
+ /**
+ * email
+ *
+ * This should be an email address.
+ */
+ 'email': {
+ type: 'string',
+ pattern: /^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!\.)){0,61}[a-zA-Z0-9]?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!$)){0,61}[a-zA-Z0-9]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/
+ },
+
+ /**
+ * ip-address
+ *
+ * This should be an ip version 4 address.
+ */
+ 'ip-address': {
+ type: 'string',
+ pattern: /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
+ },
+
+ /**
+ * ipv6
+ *
+ * This should be an ip version 6 address.
+ */
+ 'ipv6': {
+ type: 'string',
+ pattern: /(?:(?:[a-f\d]{1,4}:)*(?:[a-f\d]{1,4}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|(?:(?:[a-f\d]{1,4}:)*[a-f\d]{1,4})?::(?:(?:[a-f\d]{1,4}:)*(?:[a-f\d]{1,4}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}))?)/
+ },
+
+ /**
+ * host-name
+ *
+ * This should be a host-name.
+ */
+ 'host-name': {
+ type: 'string'
+ }
+
+ };
+
+ /**
+ * CustomFormats
+ * --------------------
+ */
+ formats.alpha = {
+ required: true,
+ type: 'string',
+ pattern: /^[a-zA-Z]+$/
+ };
+
+ formats.alphanumeric = {
+ required: true,
+ type: ['string', 'number'],
+ pattern: /^[a-zA-Z0-9]+$/
+ };
+
+ formats.decimal = function(input) {
+ if (!isNumber(input)) return false;
+ return (input + '').match(/^[0-9]+(\.[0-9]{1,2})?$/);
+ };
+
+ formats.percentage = {
+ required: true,
+ type: ['string', 'number'],
+ pattern: /^-?[0-9]{0,2}(\.[0-9]{1,2})?$|^-?(100)(\.[0]{1,2})?$/,
+ minimum: -100,
+ maximum: 100
+ };
+
+ formats.port = {
+ required: true,
+ type: ['string', 'number'],
+ pattern: /\:\d+/
+ };
+
+ /**
+ * Aliases
+ * --------------------
+ */
+ var aliases = {
+ url: 'uri',
+ ip: 'ip-address',
+ ipv4: 'ip-address',
+ host: 'host-name',
+ hostName: 'host-name'
+ };
+
+ // Apply aliases
+ each(aliases, function(alias, format) {
+ formats[alias] = formats[format];
+ });
+
+ // Export
+ return function format(property, propertyValue, attributeValue, propertyAttributes, callback) {
+
+ /**
+ * {
+ * format: {
+ * type: 'string',
+ * pattern: /abc/
+ * ...
+ * }
+ * ...
+ * }
+ */
+ if (isObject(attributeValue)) {
+ return this.validateProperty(property, propertyValue, attributeValue, callback);
+ }
+
+ /**
+ * {
+ * format: 'lorem ipsum dolor',
+ * ...
+ * }
+ */
+ if (isString(attributeValue) && !hasProperty(formats, attributeValue)) {
+ throw new Error('The format ‘' + attributeValue + '’ is not supported.');
+ }
+
+ /**
+ * {
+ * format: 'phone',
+ * ...
+ * }
+ */
+ if (isString(attributeValue)) {
+
+ var fn = formats[attributeValue];
+
+ if (isFunction(fn)) {
+ var noError = fn(propertyValue);
+ if (!noError) {
+ this.addError();
+ }
+ return callback();
+ }
+
+ if (isObject(fn)) {
+ return this.validateProperty(property, propertyValue, fn, callback);
+ }
+
+ }
+
+ };
+
+});
+
+/**
+ * Length
+ */
+var lengthAttribute = function length(property, propertyValue, attributeValue, propertyAttributes, callback) {
+
+ if (isString(propertyValue) && propertyValue.length !== attributeValue) {
+ this.addError();
+ }
+
+ return callback();
+
+};
+
+// Export
+Validation.prototype.addAttribute('length', lengthAttribute);
+
+/**
+ * Maximum
+ */
+var maximumAttribute = function maximum(property, propertyValue, attributeValue, propertyAttributes, callback) {
+
+ if (isNumber(propertyValue)) {
+ if ((propertyAttributes.exclusiveMaximum && propertyValue >= attributeValue) || (propertyValue > attributeValue)) {
+ this.addError();
+ }
+ }
+
+ return callback();
+
+};
+
+// Export
+Validation.prototype.addAttribute('maximum', maximumAttribute);
+
+/**
+ * MaxItems
+ */
+var maxItemsAttribute = function maxItems(property, propertyValue, attributeValue, propertyAttributes, callback) {
+
+ if (isArray(propertyValue) && propertyValue.length > attributeValue) {
+ this.addError();
+ }
+
+ return callback();
+
+};
+
+// Export
+Validation.prototype.addAttribute('maxItems', maxItemsAttribute);
+
+/**
+ * MaxLength
+ */
+var maxLengthAttribute = function maxLength(property, propertyValue, attributeValue, propertyAttributes, callback) {
+
+ if (isString(propertyValue) && propertyValue.length > attributeValue) {
+ this.addError();
+ }
+
+ return callback();
+
+};
+
+// Export
+Validation.prototype.addAttribute('maxLength', maxLengthAttribute);
+
+/**
+ * Minimum
+ */
+var minimumAttribute = function minimum(property, propertyValue, attributeValue, propertyAttributes, callback) {
+
+ if (isNumber(propertyValue)) {
+ if ((propertyAttributes.exclusiveMinimum && propertyValue <= attributeValue) || (propertyValue < attributeValue)) {
+ this.addError();
+ }
+ }
+
+ return callback();
+
+};
+
+// Export
+Validation.prototype.addAttribute('minimum', minimumAttribute);
+
+
+/**
+ * MinItems
+ */
+var minItems = function minItems(property, propertyValue, attributeValue, propertyAttributes, callback) {
+
+ if (isArray(propertyValue) && propertyValue.length < attributeValue) {
+ this.addError();
+ }
+
+ return callback();
+
+};
+
+// Export
+Validation.prototype.addAttribute('minItems', minItems);
+
+/**
+ * MinLength
+ */
+var minLengthAttribute = function minLength(property, propertyValue, attributeValue, propertyAttributes, callback) {
+
+ if (isString(propertyValue) && propertyValue.length < attributeValue) {
+ this.addError();
+ }
+
+ return callback();
+
+};
+
+// Export
+Validation.prototype.addAttribute('minLength', minLengthAttribute);
+
+/**
+ * Pattern
+ */
+var patternAttribute = function pattern(property, propertyValue, attributeValue, propertyAttributes, callback) {
+
+ if (isString(propertyValue) && !propertyValue.match(attributeValue)) {
+ this.addError();
+ }
+
+ return callback();
+
+};
+
+// Export
+Validation.prototype.addAttribute('pattern', patternAttribute);
+
+(function() {
+
+ /**
+ * PatternProperties
+ */
+ var attribute = function patternProperties(property, propertyValue, attributeValue, propertyAttributes, callback) {
+
+ // Saves a reference to ‘this’
+ var self = this;
+
+ // Skip
+ if (isEmpty(attributeValue)) {
+ return callback();
+ }
+
+ var matches = {};
+ var patterns = keys(attributeValue);
+
+ each(propertyValue, function(key, value) {
+
+ each(patterns, function(index, pattern) {
+ if (key.match(new RegExp(pattern))) {
+ matches[key] = attributeValue[pattern];
+ }
+ });
+
+ });
+
+ if (isEmpty(matches)) {
+ return callback();
+ }
+
+ each(matches, function(propertyName, propertySchema, callback) {
+ return self.validateSchema(
+ propertyValue[propertyName],
+ propertySchema,
+ self.joinPath(property, propertyName),
+ callback
+ );
+ }, callback);
+
+ };
+
+ // Export
+ Validation.prototype.addAttribute('patternProperties', attribute);
+
+}());
+
+/**
+ * Required
+ */
+var requiredAttribute = function required(property, propertyValue, attributeValue, propertyAttributes, callback) {
+
+ if (attributeValue) {
+
+ var undefinedCondition = isUndefined(propertyValue);
+ var emptyCondition = (isString(propertyValue) || isArray(propertyValue) || isObject(propertyValue)) && isEmpty(propertyValue);
+
+ if (undefinedCondition || emptyCondition) {
+ this.addError();
+ }
+
+ }
+
+ return callback();
+
+};
+
+// Export
+Validation.prototype.addAttribute('required', requiredAttribute);
+
+/**
+ * Type
+ */
+var typeConstructor = function typeConstructor() {
+
+ /**
+ * Types
+ */
+ var types = {
+ 'string': isString,
+ 'number': isNumber,
+ 'function': isFunction,
+ 'boolean': isBoolean,
+ 'object': isObject,
+ 'array': isArray,
+ 'integer': isInteger,
+ 'int': isInteger,
+ 'null': isNull,
+ 'any': returnTrue
+ };
+
+ // Export
+ return function type(property, propertyValue, attributeValue, propertyAttributes, callback) {
+
+ /**
+ * {
+ * type: ['string', 'number']
+ * }
+ */
+ if (isArray(attributeValue)) {
+
+ var noError = attributeValue.some(function(type) {
+
+ if (!hasProperty(types, type)) {
+ throw new Error('Type ‘' + attributeValue + '’ is not supported.');
+ }
+
+ return types[type](propertyValue);
+
+ });
+
+ if (!noError) {
+ this.errors.addError();
+ }
+
+ return callback();
+
+ /**
+ * {
+ * type: 'string'
+ * }
+ */
+ } else {
+
+ if (!hasProperty(types, attributeValue)) {
+ throw new Error('Type ‘' + attributeValue + '’ is not supported.');
+ }
+
+ if (!types[attributeValue](propertyValue)) {
+ this.addError();
+ }
+
+ return callback();
+
+ }
+
+ };
+
+};
+
+// Export
+Validation.prototype.addAttributeConstructor('type', typeConstructor);
+
+(function() {
+
+ /**
+ * UniqueItems
+ */
+ var attribute = function uniqueItems(property, propertyValue, attributeValue, propertyAttributes, callback) {
+
+ var self = this;
+
+ each(propertyValue, function(index, value) {
+
+ if (isString(value)) {
+ if ((propertyValue.indexOf(value) < index)) {
+ self.addError();
+ }
+ }
+
+ if (isObject(value) || isArray(value)) {
+ propertyValue.forEach(function(subValue, subIndex) {
+
+ if (subIndex !== index) {
+ if (isEqual(value, subValue)) {
+ self.addError({
+ property: self.joinPath(property, subIndex)
+ });
+ }
+ }
+
+ });
+ }
+
+ });
+
+ return callback();
+
+ };
+
+ // Export
+ Validation.prototype.addAttribute('uniqueItems', attribute);
+
+}());
+
+/**
+ * Error
+ *
+ * @constructor
+ */
+var ValidationError = function(parent) {
+
+ this.length = 0;
+
+ this.errorMessages = parent.messages;
+
+};
+
+ValidationError.prototype.renderErrorMessage = function(error) {
+
+ var errorMessage = this.errorMessages[error.attributeName];
+
+ if (errorMessage && isFunction(errorMessage)) {
+ return errorMessage(
+ error.property,
+ error.propertyValue,
+ error.attributeValue
+ );
+ }
+
+ if (errorMessage && isString(errorMessage)) {
+
+ [
+ 'property',
+ 'propertyValue',
+ 'attributeValue'
+ ].forEach(function(placeholder) {
+ errorMessage = errorMessage.replace(new RegExp('{{' + placeholder + '}}', 'g'), error[placeholder]);
+ });
+
+ // Deprecated
+ errorMessage = errorMessage.replace(/{{validator}}/g, error['attributeValue']);
+
+ return errorMessage.replace(/\s+/g, ' ');
+
+ }
+
+ return error.message;
+
+};
+
+ValidationError.prototype.push = function(error) {
+
+ this[this.length] = {
+
+ property: error.property,
+ propertyValue: error.propertyValue,
+ attributeName: error.attributeName,
+ attributeValue: error.attributeValue,
+ message: this.renderErrorMessage(error),
+
+ // Deprecated
+ validator: error.attributeName,
+ validatorName: error.attributeName,
+ validatorValue: error.attributeValue
+
+ };
+
+ this.length += 1;
+
+};
+
+/**
+ * GetProperties
+ */
+ValidationError.prototype.getProperties = function() {
+ return pluck(this, 'property');
+};
+
+/**
+ * GetMessages
+ */
+ValidationError.prototype.getMessages = function() {
+ return pluck(this, 'message');
+};
+
+/**
+ * Messages
+ * --------------------
+ */
+var errorMessages = {
+
+ /**
+ * Maximum
+ *
+ * @param {string} property
+ * @param {any} propertyValue
+ * @param {string} attributeValue
+ */
+ required: function(property, propertyValue, attributeValue) {
+ return 'The ‘' + property + '’ property is required.';
+ },
+
+ /**
+ * MinLength
+ *
+ * @param {string} property
+ * @param {any} propertyValue
+ * @param {string} attributeValue
+ */
+ minLength: function(property, propertyValue, attributeValue) {
+ return [
+ 'The ' + property + ' property must be at least ' + attributeValue + ' characters.',
+ 'The length of the property is ' + propertyValue.length + '.'
+ ].join(' ');
+ },
+
+ /**
+ * MaxLength
+ *
+ * @param {string} property
+ * @param {any} propertyValue
+ * @param {string} attributeValue
+ */
+ maxLength: function(property, propertyValue, attributeValue) {
+ return [
+ 'The ' + property + ' property must not exceed ' + attributeValue + ' characters.',
+ 'The length of the property is ' + propertyValue.length + '.'
+ ].join(' ');
+ },
+
+ /**
+ * MaxLength
+ *
+ * @param {string} property
+ * @param {any} propertyValue
+ * @param {string} attributeValue
+ */
+ length: function(property, propertyValue, attributeValue) {
+ return [
+ 'The ' + property + ' property must be exactly ' + attributeValue + ' characters.',
+ 'The length of the property is ' + propertyValue.length + '.'
+ ].join(' ');
+ },
+
+ /**
+ * Format
+ *
+ * @param {string} property
+ * @param {any} propertyValue
+ * @param {string} attributeValue
+ */
+ format: function(property, propertyValue, attributeValue) {
+ return [
+ 'The ‘' + property + '’ property must be a/an ‘' + attributeValue + '’.',
+ 'The current value of the property is ‘' + propertyValue + ''
+ ].join(' ');
+ },
+
+ /**
+ * Type
+ *
+ * @param {string} property
+ * @param {any} propertyValue
+ * @param {string} attributeValue
+ */
+ type: function(property, propertyValue, attributeValue) {
+ return [
+ 'The ‘' + property + '’ property must be a/an ‘' + attributeValue + '’.',
+ 'The type of the property is ‘' + detectType(propertyValue) + ''
+ ].join(' ');
+ },
+
+ /**
+ * Except
+ *
+ * @param {string} property
+ * @param {any} propertyValue
+ * @param {string} attributeValue
+ */
+ except: function(property, propertyValue, attributeValue) {
+ return;
+ },
+
+ /**
+ * Minimum
+ *
+ * @param {string} property
+ * @param {any} propertyValue
+ * @param {string} attributeValue
+ */
+ minimum: function(property, propertyValue, attributeValue) {
+ return [
+ 'The minimum value of the ‘' + property + '’ must be ' + attributeValue + '.',
+ 'The current value of the property is ‘' + propertyValue + ''
+ ].join(' ');
+ },
+
+ /**
+ * Maximum
+ *
+ * @param {string} property
+ * @param {any} propertyValue
+ * @param {string} attributeValue
+ */
+ maximum: function(property, propertyValue, attributeValue) {
+ return [
+ 'The maximum value of the ‘' + property + '’ must be ' + attributeValue + '.',
+ 'The current value of the property is ‘' + propertyValue + '’.'
+ ].join(' ');
+ },
+
+ /**
+ * Maximum
+ *
+ * @param {string} property
+ * @param {any} propertyValue
+ * @param {string} attributeValue
+ */
+ pattern: function(property, propertyValue, attributeValue) {
+ return 'The ‘' + property + '’ does not match the ‘' + attributeValue + '’ pattern.';
+ },
+
+ /**
+ * MaxItems
+ *
+ * @param {string} property
+ * @param {any} propertyValue
+ * @param {string} attributeValue
+ */
+ maxItems: function(property, propertyValue, attributeValue) {
+ return [
+ 'The ‘' + property + '’ property must not contain more than ‘' + attributeValue + '’ items.',
+ 'Currently it contains ‘' + propertyValue.items + '’ items.'
+ ].join(' ');
+ },
+
+ /**
+ * MinItems
+ *
+ * @param {string} property
+ * @param {any} propertyValue
+ * @param {string} attributeValue
+ */
+ minItems: function(property, propertyValue, attributeValue) {
+ return [
+ 'The ‘' + property + '’ property must contain at least ‘' + attributeValue + '’ items.',
+ 'Currently it contains ‘' + propertyValue.items + '’ items.'
+ ].join(' ');
+ },
+
+ /**
+ * Maximum
+ *
+ * @param {string} property
+ * @param {any} propertyValue
+ * @param {string} attributeValue
+ */
+ divisibleBy: function(property, propertyValue, attributeValue) {
+ return 'The ‘' + property + '’ is not divisible by ‘' + attributeValue + '’.';
+ },
+
+ /**
+ * Maximum
+ *
+ * @param {string} property
+ * @param {any} propertyValue
+ * @param {string} attributeValue
+ */
+ uniqueItems: function(property, propertyValue, attributeValue) {
+ return 'All items in the ‘' + property + '’ property must be unique.';
+ },
+
+ /**
+ * Enum
+ *
+ * @param {string} property
+ * @param {any} propertyValue
+ * @param {string} attributeValue
+ */
+ 'enum': function(property, propertyValue, attributeValue) {
+ return 'Value of the ‘' + property + '’ must be ' + attributeValue.join(' or ') + '.';
+ }
+
+};
+
+/**
+ * GetProperty
+ *
+ * @param {string} property
+ * @param {object} source
+ */
+Validation.prototype.getProperty = function(property, source) {
+ var tree = property.match(/([a-zA-Z0-9\s]+)/g);
+ return tree.reduce(function(previousValue, currentValue, index) {
+ return (previousValue && previousValue[currentValue]) ? previousValue[currentValue] : undefined;
+ }, source);
+};
+
+/**
+ * JoinPath
+ *
+ * @param {string} path
+ * @param {string} property
+ */
+Validation.prototype.joinPath = function(path, property) {
+
+ // If the ‘path’ is undefined (object), convert the path to a string
+ path = path || '';
+
+ // Converts the ‘property’ to a string
+ property = property + '';
+
+ if (property.match(/^[a-zA-Z]+$/)) {
+ return (path) ? (path + '.' + property) : property;
+ } else if (property.match(/\d+/)) {
+ return path + '[' + property + ']';
+ } else {
+ return path + '["' + property + '"]';
+ }
+
+};
+
+/**
+ * Validation.validate
+ *
+ * @param {object} instance
+ * @param {object} schema
+ * @param {boolean} singleError
+ * @param {function} callback
+ */
+Validation.prototype.validate = function(instance, schema, callback) {
+
+ // Save a reference to the ‘this’
+ var self = this;
+
+ this.instance = instance;
+ this.schema = schema;
+
+ /**
+ * Basic Types
+ */
+ var basicTypes = [
+ 'string',
+ 'number',
+ 'function',
+ 'boolean',
+ 'integer',
+ 'int',
+ 'null'
+ ];
+
+ /**
+ * Object Types
+ */
+ var objectTypes = [
+ 'object',
+ 'array'
+ ];
+
+ /**
+ * CallbackProxy
+ */
+ var callbackProxy = function() {
+ if (self.errors.length !== 0) {
+ return callback(self.errors);
+ } else {
+ return callback();
+ }
+ };
+
+ /**
+ * {
+ * type: 'string',
+ * ...
+ * }
+ */
+ if (basicTypes.indexOf(schema.type) !== -1) {
+ return this.validateProperty(undefined, instance, schema, callbackProxy);
+ }
+
+ /**
+ * {
+ * type: 'object',
+ * ...
+ * }
+ */
+ if (objectTypes.indexOf(schema.type) !== -1) {
+
+ if (isString(instance)) {
+ try {
+ instance = JSON.parse(instance);
+ } catch(parseError) {
+
+ }
+ }
+
+ return this.validateSchema(instance, schema, '', callbackProxy);
+
+ }
+
+ /**
+ * {
+ * type: ???,
+ * ...
+ * }
+ */
+ if (schema.type === 'any' || !schema.type) {
+
+ if (isString(instance)) {
+ try {
+ instance = JSON.parse(instance);
+ return this.validateSchema(instance, schema, '', callbackProxy);
+ } catch(parseError2) {
+
+ }
+ }
+
+ if (isObject(instance) || isArray (instance)) {
+ return this.validateSchema(instance, schema, '', callbackProxy);
+ }
+
+ return this.validateProperty(undefined, instance, schema, callbackProxy);
+
+ }
+
+};
+
+/**
+ * Validation.validateItems
+ *
+ * @param {object} instance
+ * @param {object} schema
+ * @param {string} path
+ * @param {function} callback
+ */
+Validation.prototype.validateItems = function(instance, schema, path, callback) {
+
+ // Save a reference to the ‘this’
+ var self = this;
+
+ /**
+ * {
+ * type: 'array'
+ * items: [
+ * {
+ * type: 'string'
+ * },
+ * {
+ * type: 'number'
+ * },
+ * ...
+ * ],
+ * ...
+ * }
+ */
+ if (isArray(schema.items)) {
+
+ // Additional items are allowed
+ if (isUndefined(schema.additionalItems) || schema.additionalItems === true) {
+ return each(schema.items, function(itemIndex, itemSchema, callback) {
+ return self.validateSchema(
+ instance[itemIndex],
+ itemSchema,
+ self.joinPath(path, itemIndex),
+ callback
+ );
+ }, callback);
+ }
+
+ return each(instance, function(itemIndex, itemValue, callback) {
+
+ // The ‘additionalItems’ attribute is a schema that defines
+ // the schema of the additional items
+ if (schema.items[itemIndex] || isObject(schema.additionalItems)) {
+ return self.validateSchema(
+ itemValue,
+ schema.items[itemIndex],
+ self.joinPath(path, itemIndex),
+ callback
+ );
+ }
+
+ // Additional items are disallowed
+ if (schema.additionalItems === false) {
+ self.errors.push({
+ property: self.joinPath(path, itemIndex),
+ propertyValue: itemValue,
+ attributeName: 'additionalItems',
+ attributeValue: false
+ });
+ return callback();
+ }
+
+ }, callback);
+
+ }
+
+ /**
+ * {
+ * type: 'array'
+ * items: {
+ * type: 'string'
+ * },
+ * ...
+ * }
+ */
+ if (isObject(schema.items) && instance && !isEmpty(instance)) {
+ return each(instance, function(itemIndex, itemValue, callback) {
+ return self.validateSchema(
+ instance[itemIndex],
+ schema.items,
+ self.joinPath(path, itemIndex),
+ callback
+ );
+ }, callback);
+ } else {
+ return callback();
+ }
+
+};
+
+/**
+ * Validation.validateProperties
+ *
+ * @param {object} instance
+ * @param {object} schema
+ * @param {string} path
+ * @param {function} callback
+ */
+Validation.prototype.validateProperties = function(instance, schema, path, callback) {
+
+ // Save a reference to the ‘this’
+ var self = this;
+
+ // Goes
+ return each(schema.properties, function(property, propertyAttributes, callback) {
+
+ var isObject = propertyAttributes.type === 'object' && propertyAttributes.properties,
+ isArray = propertyAttributes.type === 'array';
+
+ // Get the value of property (instance[property])
+ var propertyValue = self.getProperty(property, instance);
+ var propertyPath = self.joinPath(path, property);
+
+ /**
+ * {
+ * type: 'object',
+ * properties: {
+ * user: {
+ * type: 'object',
+ * properties: {
+ * ...
+ * }
+ * }
+ * }
+ * }
+ */
+ if (isObject || isArray) {
+ return self.validateSchema(
+ propertyValue,
+ schema.properties[property],
+ propertyPath,
+ callback
+ );
+ } else {
+ return self.validateProperty(
+ propertyPath,
+ propertyValue,
+ propertyAttributes,
+ callback
+ );
+ }
+
+ }, callback);
+
+};
+
+/**
+ * Validation.validateProperty
+ *
+ * @param {string} propertyName
+ * @param {object} propertyAttributes
+ * @param {string|object} propertyValue
+ * @param {boolean} singleError
+ * @param {function} callback
+ */
+Validation.prototype.validateProperty = function(property, propertyValue, propertyAttributes, callback) {
+
+ // Save a reference to the ‘this’
+ var self = this;
+
+ var context = {};
+
+ [
+ 'validateItems',
+ 'validateProperties',
+ 'validateSchema',
+ 'validateProperty',
+ 'getProperty',
+ 'attributes',
+ 'errors',
+ 'joinPath'
+ ].forEach(function(key) {
+ context[key] = this[key];
+ }, self);
+
+ /**
+ * Iterator
+ *
+ * @param {string} attributeName
+ * @param {function} attributeFn
+ * @param {function} callback
+ */
+ var iterator = function(attributeName, attributeFn, callback) {
+
+ var lastLength = self.errors.length;
+
+ // Overwrite the ‘addError’ method
+ context.addError = function(message) {
+
+ if (isObject(message)) {
+ return self.errors.push({
+ property: message.property || property,
+ propertyValue: message.propertyValue || propertyValue,
+ attributeName: message.attributeName || attributeName,
+ attributeValue: message.attributeValue || propertyAttributes[attributeName],
+ message: message.message || undefined
+ });
+ }
+
+ return self.errors.push({
+ property: property,
+ propertyValue: propertyValue,
+ attributeName: attributeName,
+ attributeValue: propertyAttributes[attributeName],
+ message: message
+ });
+
+ };
+
+ /**
+ * OnComplete
+ */
+ var onComplete = function(error) {
+
+ // Deprecated
+ if (error === true || isString(error)) {
+ context.addError(error);
+ return callback(true);
+ };
+
+ if (self.errors.length > lastLength && self.singleError) {
+ return callback(true);
+ } else {
+ return callback();
+ }
+
+ };
+
+ if (isDefined(propertyAttributes[attributeName])) {
+ return attributeFn.apply(context, [
+ property,
+ propertyValue,
+ propertyAttributes[attributeName],
+ propertyAttributes,
+ onComplete
+ ]);
+ } else {
+ return callback();
+ }
+
+ };
+
+ // If it's not a required param and it's empty, skip
+ if (propertyAttributes.required !== true && isUndefined(propertyValue)) {
+ return callback();
+ }
+
+ // Validate the property
+ return each(self.attributes, iterator, callback);
+
+};
+
+/**
+ * Validation.validateSchema
+ *
+ * @param {object} instance
+ * @param {object} schema
+ * @param {string} path
+ * @param {function} callback
+ */
+Validation.prototype.validateSchema = function(instance, schema, path, callback) {
+
+ var self = this;
+
+ return self.validateProperty(path, instance, schema, function(error) {
+
+ /**
+ * {
+ * type: 'object',
+ * properties: {
+ * ...
+ * }
+ * }
+ */
+ if (schema.properties) {
+ return self.validateProperties(
+ instance,
+ schema,
+ path,
+ callback
+ );
+
+ /**
+ * {
+ * type: 'array',
+ * items: {
+ * type: 'string'
+ * ...
+ * }
+ * }
+ */
+ } else if (schema.items) {
+ return self.validateItems(
+ instance,
+ schema,
+ path,
+ callback
+ );
+
+ /**
+ * {
+ * type: 'array'
+ * }
+ * — or —
+ * {
+ * type: 'object'
+ * }
+ */
+ } else {
+ return callback();
+ }
+
+ });
+
+};
+
+
+ /**
+ * Export
+ * --------------------
+ */
+ engines.json = (function() {
+
+ /**
+ * Cache
+ */
+ var cache = [];
+ var cacheIndex = {};
+
+ return {
+
+ /**
+ * Validate
+ *
+ * @param {object} instance
+ * @param {object} schema
+ * @param {object} options
+ * @param {function} callback
+ */
+ validate: function(instance, schema, options, callback) {
+ if (typeof options === 'function') {
+ callback = options;
+ options = {};
+ }
+ return (new Validation(options)).validate(instance, schema, callback);
+ },
+
+ /**
+ * AddAttribute
+ *
+ * @param {string} attributeName
+ * @param {function} attributeFn
+ */
+ addAttribute: function(attributeName, attributeFn) {
+ return Validation.prototype.addAttribute.apply(Validation, arguments);
+ },
+
+ /**
+ * AddAttributeConstructor
+ *
+ * @param {string} attributeName
+ * @param {function} attributeConstructor
+ */
+ addAttributeConstructor: function(attributeName, attributeConstructor) {
+ return Validation.prototype.addAttributeConstructor.apply(Validation, arguments);
+ }
+
+ };
+
+ }());
+
+}());
+
+var amanda = function(engine) {
+
+ if (!hasProperty(engines, engine)) {
+ throw new Error('The ‘' + engine + '’ engine is not supported. Please use a different one.');
+ }
+
+ return engines[engine];
+
+};
+
+/**
+ * Amanda.validate
+ *
+ * This method is deprecated, please use ‘amanda('json').validate’ instead.
+ */
+amanda.validate = function(instance, schema, options, callback) {
+ var json = engines.json;
+ return json.validate.apply(json, arguments);
+};
+
+/**
+ * Amanda.addValidator
+ *
+ * This method is deprecated, please use ‘amanda('json').addValidator’ instead.
+ */
+amanda.addValidator = function(attributeName, attributeFn) {
+ var json = engines.json;
+ return json.addAttribute.apply(json, arguments);
+};
+
+/**
+ * Amanda.addAttribute
+ *
+ * This method is deprecated, please use ‘amanda('json').addAttribute’ instead.
+ */
+amanda.addAttribute = function(attributeName, attributeFn) {
+ var json = engines.json;
+ return json.addAttribute.apply(json, arguments);
+};
+
+/**
+ * Amanda.addAttributeConstructor
+ *
+ * This method is deprecated, please use ‘amanda('json').addAttributeConstructor’ instead.
+ */
+amanda.addAttributeConstructor = function(attributeName, attributeConstructor) {
+ var json = engines.json;
+ return json.addAttributeConstructor.apply(json, arguments);
+};
+
+ /**
+ * Export
+ * --------------------
+ */
+ if (typeof module !== 'undefined' && module.exports) {
+ module.exports = amanda;
+ } else if (typeof define !== 'undefined') {
+ define(function() {
+ return amanda;
+ });
+ } else {
+ this.amanda = amanda;
+ }
+
+}());
View
26 releases/0.4.2/amanda.min.js
@@ -0,0 +1,26 @@
+(function(){var p={},n=function(a,g,h){var f=function(b,f,a){var c=[],j=function(b,i){var j=c.length+1;c.push(function(){return f(b,i,function(b){var f=c[j];return!b&&f?f():!b&&!f?a():a(b)})})};if(k(b)&&!o(b))for(var i=0,g=b.length;i<g;i++)j(i,b[i]);else if(l(b)&&!o(b))for(i in b)b.hasOwnProperty(i)&&j(i,b[i]);else return a();return c[0]()};return"undefined"===typeof h?function(b,f){if(k(b)&&!o(b))for(var a=0,c=b.length;a<c;a++)f.apply(b,[a,b[a]]);if(l(b)&&!o(b))for(var j in b)b.hasOwnProperty(j)&&
+f.apply(b,[j,b[j]])}.apply(this,arguments):f.apply(this,arguments)},y=function(a,g,h){return Array.prototype.filter.apply(a,[g,h||this])},k=function(a){return"[object Array]"===Object.prototype.toString.call(a)},z=function(a){return"boolean"===typeof a},o=function(a){if(q(a))return!1;if(null===a)return!0;if(k(a)||"string"===typeof a)return 0===a.length;if(l(a))for(var g in a)if(hasOwnProperty.call(a,g))return!1;return!0},u=function(a,g){if(k(a,g))return a.length!==g.length?!1:Array.prototype.every.apply(a,
+[function(b,f){return g[f]===b}]);if(l(a,g)){var h=Object.keys(a),f=Object.keys(g);if(!u(h,f))return!1;for(key in a)if(!g[key]||a[key]!==g[key])return!1;return!0}return!1},s=function(a){return"function"===typeof a},v=function(a){return q(a)&&0===a%1},A=function(a){return null===a},q=function(a){return"number"===typeof a},l=function(a){return"[object Object]"===Object.prototype.toString.call(a)},m=function(a){return"string"===typeof a},t=function(a){return"undefined"===typeof a},w=function(a,g){for(var h in g)g.hasOwnProperty(h)&&
+!a.hasOwnProperty(h)&&(a[h]=g[h]);return a},x=function(a,g){for(var h=[],f=0,b=a.length;f<b;f++){var d=a[f][g];-1===h.indexOf(d)&&h.push(d)}return h},B=function(){return!0};(function(){var a=function(f){var b=this,d={singleError:!0,messages:h,cache:!1};n(d,function(a,c){b[a]=l(c)&&f[a]?w(f[a],d[a]):l(c)&&!f[a]?w({},d[a]):"undefined"!==typeof f[a]?f[a]:d[a]});this.errors=new g(this)};a.prototype.attributes={};a.prototype.addAttribute=function(f,b){return a.prototype.attributes[f]=b};a.prototype.addAttributeConstructor=
+function(f,b){return a.prototype.attributes[f]=b()};a.prototype.addAttribute("additionalProperties",function(f,b,d,a,c){var j=this;if(!0===d)return c();var i=Object.keys(b),i=y(i,function(b){return!a.properties[b]});if(o(i))return c();if(!1===d)return i.forEach(function(d){this.addError({property:this.joinPath(f,d),propertyValue:b[d]})},this),c();if(l(d))return n(i,function(a,c,e){return j.validateSchema(b[c],d,f+c,e)},c)});a.prototype.addAttribute("divisibleBy",function(f,b,d,a,c){if(0===d)throw Error("The value of this attribute should not be 0.");
+q(b)&&0!==b%d&&this.addError();return c()});a.prototype.addAttribute("enum",function(f,b,d,a,c){-1===d.indexOf(b)&&this.addError();return c()});a.prototype.addAttribute("except",function(f,b,d,a,c){-1!==d.indexOf(b)&&this.addError();return c()});a.prototype.addAttributeConstructor("format",function(){var f={"date-time":{type:"string",pattern:/^\d{4}-(?:0[0-9]{1}|1[0-2]{1})-[0-9]{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/},date:function(b){return m(b)?b.match(/^\d{4}-(?:0[0-9]{1}|1[0-2]{1})-[0-9]{2}$/):l(b)?"[object Date]"===
+Object.prototype.toString.call(b):!1},time:{type:"string",pattern:/^\d{2}:\d{2}:\d{2}$/},"utc-milisec":{type:"number"},regex:function(b){return b&&b.test&&b.exec},color:{type:"string"},style:{type:"string"},phone:{type:"number"},uri:{type:"string",pattern:/^(?:(?:ht|f)tp(?:s?)\:\/\/|~\/|\/)?(?:\w+:\w+@)?((?:(?:[-\w\d{1-3}]+\.)+(?:com|org|cat|coop|int|pro|tel|xxx|net|gov|mil|biz|info|mobi|name|aero|jobs|edu|co\.uk|ac\.uk|it|fr|tv|museum|asia|local|travel|[a-z]{2})?)|((\b25[0-5]\b|\b[2][0-4][0-9]\b|\b[0-1]?[0-9]?[0-9]\b)(\.(\b25[0-5]\b|\b[2][0-4][0-9]\b|\b[0-1]?[0-9]?[0-9]\b)){3}))(?::[\d]{1,5})?(?:(?:(?:\/(?:[-\w~!$+|.,=]|%[a-f\d]{2})+)+|\/)+|\?|#)?(?:(?:\?(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=?(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)(?:&(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=?(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)*)*(?:#(?:[-\w~!$ |\/.,*:;=]|%[a-f\d]{2})*)?$/},
+email:{type:"string",pattern:/^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!\.)){0,61}[a-zA-Z0-9]?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!$)){0,61}[a-zA-Z0-9]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/},"ip-address":{type:"string",pattern:/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/},ipv6:{type:"string",pattern:/(?:(?:[a-f\d]{1,4}:)*(?:[a-f\d]{1,4}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|(?:(?:[a-f\d]{1,4}:)*[a-f\d]{1,4})?::(?:(?:[a-f\d]{1,4}:)*(?:[a-f\d]{1,4}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}))?)/},
+"host-name":{type:"string"},alpha:{required:!0,type:"string",pattern:/^[a-zA-Z]+$/},alphanumeric:{required:!0,type:["string","number"],pattern:/^[a-zA-Z0-9]+$/},decimal:function(b){return!q(b)?!1:(b+"").match(/^[0-9]+(\.[0-9]{1,2})?$/)},percentage:{required:!0,type:["string","number"],pattern:/^-?[0-9]{0,2}(\.[0-9]{1,2})?$|^-?(100)(\.[0]{1,2})?$/,minimum:-100,maximum:100},port:{required:!0,type:["string","number"],pattern:/\:\d+/}};n({url:"uri",ip:"ip-address",ipv4:"ip-address",host:"host-name",hostName:"host-name"},
+function(b,a){f[b]=f[a]});return function(b,a,e,c,j){if(l(e))return this.validateProperty(b,a,e,j);if(m(e)&&!Object.prototype.hasOwnProperty.apply(f,[e]))throw Error("The format \u2018"+e+"\u2019 is not supported.");if(m(e)){e=f[e];if(s(e))return e(a)||this.addError(),j();if(l(e))return this.validateProperty(b,a,e,j)}}});a.prototype.addAttribute("length",function(f,b,a,e,c){m(b)&&b.length!==a&&this.addError();return c()});a.prototype.addAttribute("maximum",function(f,b,a,e,c){q(b)&&(e.exclusiveMaximum&&
+b>=a||b>a)&&this.addError();return c()});a.prototype.addAttribute("maxItems",function(f,b,a,e,c){k(b)&&b.length>a&&this.addError();return c()});a.prototype.addAttribute("maxLength",function(f,b,a,e,c){m(b)&&b.length>a&&this.addError();return c()});a.prototype.addAttribute("minimum",function(f,b,a,e,c){q(b)&&(e.exclusiveMinimum&&b<=a||b<a)&&this.addError();return c()});a.prototype.addAttribute("minItems",function(f,b,a,e,c){k(b)&&b.length<a&&this.addError();return c()});a.prototype.addAttribute("minLength",
+function(f,b,a,e,c){m(b)&&b.length<a&&this.addError();return c()});a.prototype.addAttribute("pattern",function(f,b,a,e,c){m(b)&&!b.match(a)&&this.addError();return c()});(function(){a.prototype.addAttribute("patternProperties",function(f,b,a,e,c){var j=this;if(o(a))return c();var i={},g=Object.keys(a);n(b,function(b){n(g,function(f,c){b.match(RegExp(c))&&(i[b]=a[c])})});if(o(i))return c();n(i,function(a,d,c){return j.validateSchema(b[a],d,j.joinPath(f,a),c)},c)})})();a.prototype.addAttribute("required",
+function(f,b,a,e,c){a&&(f=t(b),b=(m(b)||k(b)||l(b))&&o(b),(f||b)&&this.addError());return c()});a.prototype.addAttributeConstructor("type",function(){var f={string:m,number:q,"function":s,"boolean":z,object:l,array:k,integer:v,"int":v,"null":A,any:B};return function(b,a,e,c,j){if(k(e))e.some(function(b){if(!Object.prototype.hasOwnProperty.apply(f,[b]))throw Error("Type \u2018"+e+"\u2019 is not supported.");return f[b](a)})||this.errors.addError();else{if(!Object.prototype.hasOwnProperty.apply(f,[e]))throw Error("Type \u2018"+
+e+"\u2019 is not supported.");f[e](a)||this.addError()}return j()}});(function(){a.prototype.addAttribute("uniqueItems",function(f,b,a,e,c){var j=this;n(b,function(a,c){m(c)&&b.indexOf(c)<a&&j.addError();(l(c)||k(c))&&b.forEach(function(b,d){d!==a&&u(c,b)&&j.addError({property:j.joinPath(f,d)})})});return c()})})();var g=function(a){this.length=0;this.errorMessages=a.messages};g.prototype.renderErrorMessage=function(a){var b=this.errorMessages[a.attributeName];return b&&s(b)?b(a.property,a.propertyValue,
+a.attributeValue):b&&m(b)?(["property","propertyValue","attributeValue"].forEach(function(d){b=b.replace(RegExp("{{"+d+"}}","g"),a[d])}),b=b.replace(/{{validator}}/g,a.attributeValue),b.replace(/\s+/g," ")):a.message};g.prototype.push=function(a){this[this.length]={property:a.property,propertyValue:a.propertyValue,attributeName:a.attributeName,attributeValue:a.attributeValue,message:this.renderErrorMessage(a),validator:a.attributeName,validatorName:a.attributeName,validatorValue:a.attributeValue};
+this.length+=1};g.prototype.getProperties=function(){return x(this,"property")};g.prototype.getMessages=function(){return x(this,"message")};var h={required:function(a){return"The \u2018"+a+"\u2019 property is required."},minLength:function(a,b,d){return["The "+a+" property must be at least "+d+" characters.","The length of the property is "+b.length+"."].join(" ")},maxLength:function(a,b,d){return["The "+a+" property must not exceed "+d+" characters.","The length of the property is "+b.length+"."].join(" ")},
+length:function(a,b,d){return["The "+a+" property must be exactly "+d+" characters.","The length of the property is "+b.length+"."].join(" ")},format:function(a,b,d){return["The \u2018"+a+"\u2019 property must be a/an \u2018"+d+"\u2019.","The current value of the property is \u2018"+b+"\u2019"].join(" ")},type:function(a,b,d){return["The \u2018"+a+"\u2019 property must be a/an \u2018"+d+"\u2019.","The type of the property is \u2018"+typeof b+"\u2019"].join(" ")},except:function(){},minimum:function(a,
+b,d){return["The minimum value of the \u2018"+a+"\u2019 must be "+d+".","The current value of the property is \u2018"+b+"\u2019"].join(" ")},maximum:function(a,b,d){return["The maximum value of the \u2018"+a+"\u2019 must be "+d+".","The current value of the property is \u2018"+b+"\u2019."].join(" ")},pattern:function(a,b,d){return"The \u2018"+a+"\u2019 does not match the \u2018"+d+"\u2019 pattern."},maxItems:function(a,b,d){return["The \u2018"+a+"\u2019 property must not contain more than \u2018"+
+d+"\u2019 items.","Currently it contains \u2018"+b.items+"\u2019 items."].join(" ")},minItems:function(a,b,d){return["The \u2018"+a+"\u2019 property must contain at least \u2018"+d+"\u2019 items.","Currently it contains \u2018"+b.items+"\u2019 items."].join(" ")},divisibleBy:function(a,b,d){return"The \u2018"+a+"\u2019 is not divisible by \u2018"+d+"\u2019."},uniqueItems:function(a){return"All items in the \u2018"+a+"\u2019 property must be unique."},"enum":function(a,b,d){return"Value of the \u2018"+
+a+"\u2019 must be "+d.join(" or ")+"."}};a.prototype.getProperty=function(a,b){return a.match(/([a-zA-Z0-9\s]+)/g).reduce(function(b,a){return b&&b[a]?b[a]:void 0},b)};a.prototype.joinPath=function(a,b){a=a||"";b+="";return b.match(/^[a-zA-Z]+$/)?a?a+"."+b:b:b.match(/\d+/)?a+"["+b+"]":a+'["'+b+'"]'};a.prototype.validate=function(a,b,d){var e=this;this.instance=a;this.schema=b;var c=function(){return 0!==e.errors.length?d(e.errors):d()};if(-1!=="string,number,function,boolean,integer,int,null".split(",").indexOf(b.type))return this.validateProperty(void 0,
+a,b,c);if(-1!==["object","array"].indexOf(b.type)){if(m(a))try{a=JSON.parse(a)}catch(j){}return this.validateSchema(a,b,"",c)}if("any"===b.type||!b.type){if(m(a))try{return a=JSON.parse(a),this.validateSchema(a,b,"",c)}catch(i){}return l(a)||k(a)?this.validateSchema(a,b,"",c):this.validateProperty(void 0,a,b,c)}};a.prototype.validateItems=function(a,b,d,e){var c=this;return k(b.items)?t(b.additionalItems)||!0===b.additionalItems?n(b.items,function(b,e,g){return c.validateSchema(a[b],e,c.joinPath(d,
+b),g)},e):n(a,function(a,f,e){if(b.items[a]||l(b.additionalItems))return c.validateSchema(f,b.items[a],c.joinPath(d,a),e);if(!1===b.additionalItems)return c.errors.push({property:c.joinPath(d,a),propertyValue:f,attributeName:"additionalItems",attributeValue:!1}),e()},e):l(b.items)&&a&&!o(a)?n(a,function(e,i,g){return c.validateSchema(a[e],b.items,c.joinPath(d,e),g)},e):e()};a.prototype.validateProperties=function(a,b,d,e){var c=this;return n(b.properties,function(e,i,g){var h="object"===i.type&&i.properties,
+l="array"===i.type,m=c.getProperty(e,a),k=c.joinPath(d,e);return h||l?c.validateSchema(m,b.properties[e],k,g):c.validateProperty(k,m,i,g)},e)};a.prototype.validateProperty=function(a,b,d,e){var c=this,g={};"validateItems,validateProperties,validateSchema,validateProperty,getProperty,attributes,errors,joinPath".split(",").forEach(function(a){g[a]=this[a]},c);return!0!==d.required&&t(b)?e():n(c.attributes,function(e,h,k){var n=c.errors.length;g.addError=function(g){return l(g)?c.errors.push({property:g.property||
+a,propertyValue:g.propertyValue||b,attributeName:g.attributeName||e,attributeValue:g.attributeValue||d[e],message:g.message||void 0}):c.errors.push({property:a,propertyValue:b,attributeName:e,attributeValue:d[e],message:g})};return"undefined"!==typeof d[e]?h.apply(g,[a,b,d[e],d,function(a){return!0===a||m(a)?(g.addError(a),k(!0)):c.errors.length>n&&c.singleError?k(!0):k()}]):k()},e)};a.prototype.validateSchema=function(a,b,d,e){var c=this;return c.validateProperty(d,a,b,function(){return b.properties?
+c.validateProperties(a,b,d,e):b.items?c.validateItems(a,b,d,e):e()})};p.json=function(){return{validate:function(f,b,d,e){"function"===typeof d&&(e=d,d={});return(new a(d)).validate(f,b,e)},addAttribute:function(f,b){return a.prototype.addAttribute.apply(a,arguments)},addAttributeConstructor:function(f,b){return a.prototype.addAttributeConstructor.apply(a,arguments)}}}()})();var r=function(a){if(!Object.prototype.hasOwnProperty.apply(p,[a]))throw Error("The \u2018"+a+"\u2019 engine is not supported. Please use a different one.");
+return p[a]};r.validate=function(a,g,h,f){var b=p.json;return b.validate.apply(b,arguments)};r.addValidator=function(a,g){var h=p.json;return h.addAttribute.apply(h,arguments)};r.addAttribute=function(a,g){var h=p.json;return h.addAttribute.apply(h,arguments)};r.addAttributeConstructor=function(a,g){var h=p.json;return h.addAttributeConstructor.apply(h,arguments)};"undefined"!==typeof module&&module.exports?module.exports=r:"undefined"!==typeof define?define(function(){return r}):this.amanda=r})();
View
3,205 releases/latest/amanda.js
@@ -1,4 +1,4 @@
-(function () {
+(function() {
/**
* Engines
@@ -6,1911 +6,1996 @@
*/
var engines = {};
+/**
+ * DetectType
+ *
+ * @param {object} input
+ */
+var detectType = function(input) {
+ return typeof input;
+};
+
+/**
+ * Each
+ *
+ * Applies an iterator function to each item in an array or an object, in series.
+ *
+ * @param {object} list
+ * @param {function} iterator
+ * @param {function} callback
+ */
+var each = function(list, iterator, callback) {
+
/**
- * DetectType
+ * SyncEach
*
- * @param {object} input
+ * @param {object} list
+ * @param {function} iterator
*/
- var detectType = function (input) {
- return typeof input;
- };
+ var syncEach = function(list, iterator) {
+
+ // If the list is an array
+ if (isArray(list) && !isEmpty(list)) {
+ for (var i = 0, len = list.length; i < len; i++) {
+ iterator.apply(list, [i, list[i]]);
+ }
+ }
+
+ // If the list is an object
+ if (isObject(list) && !isEmpty(list)) {
+ for (var key in list) {
+ if (list.hasOwnProperty(key)) {
+ iterator.apply(list, [key, list[key]]);
+ }
+ }
+ }
+
+ };
/**
- * Each
- *
- * Applies an iterator function to each item in an array or an object, in series.
- *
+ * AsyncEach
* @param {object} list
* @param {function} iterator
* @param {function} callback
*/
- var each = function (list, iterator, callback) {
+ var asyncEach = function(list, iterator, callback) {
- /**
- * SyncEach
- *
- * @param {object} list
- * @param {function} iterator
- */
- var syncEach = function (list, iterator) {
+ var queue = [];
- // If the list is an array
- if (isArray(list) && !isEmpty(list)) {
- for (var i = 0, len = list.length; i < len; i++) {
- iterator.apply(list, [i, list[i]]);
- }
+ /**
+ * AddToQueue
+ *
+ * @param {string} key
+ * @param {string|object} value
+ */
+ var addToQueue = function(key, value) {
+ var index = queue.length + 1;
+ queue.push(function() {
+
+ var next = function(error) {
+ var fn = queue[index];
+ if (!error && fn) {
+ return fn();
+ } else if (!error && !fn) {
+ return callback();
+ } else {
+ return callback(error);
}
+ };
- // If the list is an object
- if (isObject(list) && !isEmpty(list)) {
- for (var key in list) {
- if (list.hasOwnProperty(key)) {
- iterator.apply(list, [key, list[key]]);
- }
- }
- }
+ return iterator(key, value, next);
- };
+ });
+ };
- /**
- * AsyncEach
- * @param {object} list
- * @param {function} iterator
- * @param {function} callback
- */
- var asyncEach = function (list, iterator, callback) {
-
- var queue = [];
-
- /**
- * AddToQueue
- *
- * @param {string} key
- * @param {string|object} value
- */
- var addToQueue = function (key, value) {
- var index = queue.length + 1;
- queue.push(function () {
-
- var next = function (error) {
- var fn = queue[index];
- if (!error && fn) {
- return fn();
- } else if (!error && !fn) {
- return callback();
- } else {
- return callback(error);
- }
- };
-
- return iterator(key, value, next);
+ // If the list is an array
+ if (isArray(list) && !isEmpty(list)) {
+ for (var i = 0, len = list.length; i < len; i++) {
+ addToQueue(i, list[i]);
+ }
- });
- };
+ // If the list is an object
+ } else if (isObject(list) && !isEmpty(list)) {
+ for (var key in list) {
+ if (list.hasOwnProperty(key)) {
+ addToQueue(key, list[key]);
+ }
+ }
- // If the list is an array
- if (isArray(list) && !isEmpty(list)) {
- for (var i = 0, len = list.length; i < len; i++) {
- addToQueue(i, list[i]);
- }
+ // If the list is not an array or an object
+ } else {
+ return callback();
+ }
- // If the list is an object
- } else if (isObject(list) && !isEmpty(list)) {
- for (var key in list) {
- if (list.hasOwnProperty(key)) {
- addToQueue(key, list[key]);
- }
- }
+ // And go!
+ return queue[0]();
- // If the list is not an array or an object
- } else {
- return callback();
- }
+ };
- // And go!
- return queue[0]();
+ if (typeof callback === 'undefined') {
+ return syncEach.apply(this, arguments);
+ } else {
+ return asyncEach.apply(this, arguments);
+ }
- };
+};
+
+/**
+ * Every
+ *
+ * @param {object} arr
+ * @param {function} iterator
+ */
+var every = function(arr, iterator) {
+ return Array.prototype.every.apply(arr, [iterator]);
+};
+
+/**
+ * Filter
+ *
+ * @param {object} arr
+ * @param {function} iterator
+ */
+var filter = function(arr, iterator, context) {
+ return Array.prototype.filter.apply(arr, [iterator, context || this]);
+};
+
+/**
+ * HasProperty
+ *
+ * @param {object} input
+ */
+var hasProperty = function(obj, property) {
+ return Object.prototype.hasOwnProperty.apply(obj, [property]);
+};