Project

General

Profile

Download (17.9 KB) Statistics
| Branch: | Tag: | Revision:

haketilo / common / jsonschema / helpers.js @ 57ce414c

1
/* SPDX-License-Identifier: MIT
2
 *
3
 * jsonschema is licensed under MIT license.
4
 *
5
 * Copyright (C) 2012-2015 Tom de Grunt <tom@degrunt.nl>
6
 *
7
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
8
 * this software and associated documentation files (the "Software"), to deal in
9
 * the Software without restriction, including without limitation the rights to
10
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
11
 * of the Software, and to permit persons to whom the Software is furnished to do
12
 * so, subject to the following conditions:
13
 *
14
 * The above copyright notice and this permission notice shall be included in all
15
 * copies or substantial portions of the Software.
16
 *
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
 * SOFTWARE.
24
 */
25

    
26
#IMPORT common/jsonschema/urllib_mock.js AS uri
27

    
28
function ValidationError (message, instance, schema, path, name, argument) {
29
  if(Array.isArray(path)){
30
    this.path = path;
31
    this.property = path.reduce(function(sum, item){
32
      return sum + makeSuffix(item);
33
    }, 'instance');
34
  }else if(path !== undefined){
35
    this.property = path;
36
  }
37
  if (message) {
38
    this.message = message;
39
  }
40
  if (schema) {
41
    var id = schema.$id || schema.id;
42
    this.schema = id || schema;
43
  }
44
  if (instance !== undefined) {
45
    this.instance = instance;
46
  }
47
  this.name = name;
48
  this.argument = argument;
49
  this.stack = this.toString();
50
};
51
#EXPORT ValidationError
52

    
53
ValidationError.prototype.toString = function toString() {
54
  return this.property + ' ' + this.message;
55
};
56

    
57
function ValidatorResult(instance, schema, options, ctx) {
58
  this.instance = instance;
59
  this.schema = schema;
60
  this.options = options;
61
  this.path = ctx.path;
62
  this.propertyPath = ctx.propertyPath;
63
  this.errors = [];
64
  this.throwError = options && options.throwError;
65
  this.throwFirst = options && options.throwFirst;
66
  this.throwAll = options && options.throwAll;
67
  this.disableFormat = options && options.disableFormat === true;
68
};
69

    
70
ValidatorResult.prototype.addError = function addError(detail) {
71
  var err;
72
  if (typeof detail == 'string') {
73
    err = new ValidationError(detail, this.instance, this.schema, this.path);
74
  } else {
75
    if (!detail) throw new Error('Missing error detail');
76
    if (!detail.message) throw new Error('Missing error message');
77
    if (!detail.name) throw new Error('Missing validator type');
78
    err = new ValidationError(detail.message, this.instance, this.schema, this.path, detail.name, detail.argument);
79
  }
80

    
81
  this.errors.push(err);
82
  if (this.throwFirst) {
83
    throw new ValidatorResultError(this);
84
  }else if(this.throwError){
85
    throw err;
86
  }
87
  return err;
88
};
89

    
90
ValidatorResult.prototype.importErrors = function importErrors(res) {
91
  if (typeof res == 'string' || (res && res.validatorType)) {
92
    this.addError(res);
93
  } else if (res && res.errors) {
94
    Array.prototype.push.apply(this.errors, res.errors);
95
  }
96
};
97

    
98
function stringizer (v,i){
99
  return i+': '+v.toString()+'\n';
100
}
101
ValidatorResult.prototype.toString = function toString(res) {
102
  return this.errors.map(stringizer).join('');
103
};
104

    
105
Object.defineProperty(ValidatorResult.prototype, "valid", { get: function() {
106
  return !this.errors.length;
107
} });
108

    
109
#EXPORT ValidatorResult
110

    
111
function ValidatorResultError(result) {
112
  if(Error.captureStackTrace){
113
    Error.captureStackTrace(this, ValidatorResultError);
114
  }
115
  this.instance = result.instance;
116
  this.schema = result.schema;
117
  this.options = result.options;
118
  this.errors = result.errors;
119
}
120
ValidatorResultError.prototype = new Error();
121
ValidatorResultError.prototype.constructor = ValidatorResultError;
122
ValidatorResultError.prototype.name = "Validation Error";
123
#EXPORT ValidatorResultError
124

    
125
/**
126
 * Describes a problem with a Schema which prevents validation of an instance
127
 * @name SchemaError
128
 * @constructor
129
 */
130
function SchemaError (msg, schema) {
131
  this.message = msg;
132
  this.schema = schema;
133
  Error.call(this, msg);
134
  if(Error.captureStackTrace){
135
    Error.captureStackTrace(this, SchemaError);
136
  }
137
};
138
SchemaError.prototype = Object.create(Error.prototype,
139
  {
140
    constructor: {value: SchemaError, enumerable: false},
141
    name: {value: 'SchemaError', enumerable: false},
142
  });
143
#EXPORT SchemaError
144

    
145
function SchemaContext (schema, options, path, base, schemas) {
146
  this.schema = schema;
147
  this.options = options;
148
  if(Array.isArray(path)){
149
    this.path = path;
150
    this.propertyPath = path.reduce(function(sum, item){
151
      return sum + makeSuffix(item);
152
    }, 'instance');
153
  }else{
154
    this.propertyPath = path;
155
  }
156
  this.base = base;
157
  this.schemas = schemas;
158
};
159

    
160
SchemaContext.prototype.resolve = function resolve (target) {
161
  return uri.resolve(this.base, target);
162
};
163

    
164
SchemaContext.prototype.makeChild = function makeChild(schema, propertyName){
165
  var path = (propertyName===undefined) ? this.path : this.path.concat([propertyName]);
166
  var id = schema.$id || schema.id;
167
  var base = uri.resolve(this.base, id||'');
168
  var ctx = new SchemaContext(schema, this.options, path, base, Object.create(this.schemas));
169
  if(id && !ctx.schemas[base]){
170
    ctx.schemas[base] = schema;
171
  }
172
  return ctx;
173
};
174

    
175
#EXPORT SchemaContext
176

    
177
const FORMAT_REGEXPS = {
178
  // 7.3.1. Dates, Times, and Duration
179
  'date-time': /^\d{4}-(?:0[0-9]{1}|1[0-2]{1})-(3[01]|0[1-9]|[12][0-9])[tT ](2[0-4]|[01][0-9]):([0-5][0-9]):(60|[0-5][0-9])(\.\d+)?([zZ]|[+-]([0-5][0-9]):(60|[0-5][0-9]))$/,
180
  'date': /^\d{4}-(?:0[0-9]{1}|1[0-2]{1})-(3[01]|0[1-9]|[12][0-9])$/,
181
  'time': /^(2[0-4]|[01][0-9]):([0-5][0-9]):(60|[0-5][0-9])$/,
182
  'duration': /P(T\d+(H(\d+M(\d+S)?)?|M(\d+S)?|S)|\d+(D|M(\d+D)?|Y(\d+M(\d+D)?)?)(T\d+(H(\d+M(\d+S)?)?|M(\d+S)?|S))?|\d+W)/i,
183

    
184
  // 7.3.2. Email Addresses
185
  // TODO: fix the email production
186
  'email': /^(?:[\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])\]))$/,
187
  'idn-email': /^("(?:[!#-\[\]-\u{10FFFF}]|\\[\t -\u{10FFFF}])*"|[!#-'*+\-/-9=?A-Z\^-\u{10FFFF}](?:\.?[!#-'*+\-/-9=?A-Z\^-\u{10FFFF}])*)@([!#-'*+\-/-9=?A-Z\^-\u{10FFFF}](?:\.?[!#-'*+\-/-9=?A-Z\^-\u{10FFFF}])*|\[[!-Z\^-\u{10FFFF}]*\])$/u,
188

    
189
  // 7.3.3. Hostnames
190

    
191
  // 7.3.4. IP Addresses
192
  'ip-address': /^(?:(?: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]?)$/,
193
  // FIXME whitespace is invalid
194
  'ipv6': /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/,
195

    
196
  // 7.3.5. Resource Identifiers
197
  // TODO: A more accurate regular expression for "uri" goes:
198
  // [A-Za-z][+\-.0-9A-Za-z]*:((/(/((%[0-9A-Fa-f]{2}|[!$&-.0-9;=A-Z_a-z~])+|(\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~]+)?|[.0-:A-Fa-f]+)\])?)(:\d*)?)?)?#(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~])*|(/(/((%[0-9A-Fa-f]{2}|[!$&-.0-9;=A-Z_a-z~])+|(\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~]+)?|[.0-:A-Fa-f]+)\])?)(:\d*)?[/?]|[!$&-.0-;=?-Z_a-z~])|/?%[0-9A-Fa-f]{2}|[!$&-.0-;=?-Z_a-z~])(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~])*(#(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~])*)?|/(/((%[0-9A-Fa-f]{2}|[!$&-.0-9;=A-Z_a-z~])+(:\d*)?|(\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~]+)?|[.0-:A-Fa-f]+)\])?:\d*|\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~]+)?|[.0-:A-Fa-f]+)\])?)?)?
199
  'uri': /^[a-zA-Z][a-zA-Z0-9+.-]*:[^\s]*$/,
200
  'uri-reference': /^(((([A-Za-z][+\-.0-9A-Za-z]*(:%[0-9A-Fa-f]{2}|:[!$&-.0-;=?-Z_a-z~]|[/?])|\?)(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~])*|([A-Za-z][+\-.0-9A-Za-z]*:?)?)|([A-Za-z][+\-.0-9A-Za-z]*:)?\/((%[0-9A-Fa-f]{2}|\/((%[0-9A-Fa-f]{2}|[!$&-.0-9;=A-Z_a-z~])+|(\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~]+)?|[.0-:A-Fa-f]+)\])?)(:\d*)?[/?]|[!$&-.0-;=?-Z_a-z~])(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~])*|(\/((%[0-9A-Fa-f]{2}|[!$&-.0-9;=A-Z_a-z~])+|(\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~]+)?|[.0-:A-Fa-f]+)\])?)(:\d*)?)?))#(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~])*|(([A-Za-z][+\-.0-9A-Za-z]*)?%[0-9A-Fa-f]{2}|[!$&-.0-9;=@_~]|[A-Za-z][+\-.0-9A-Za-z]*[!$&-*,;=@_~])(%[0-9A-Fa-f]{2}|[!$&-.0-9;=@-Z_a-z~])*((([/?](%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~])*)?#|[/?])(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~])*)?|([A-Za-z][+\-.0-9A-Za-z]*(:%[0-9A-Fa-f]{2}|:[!$&-.0-;=?-Z_a-z~]|[/?])|\?)(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~])*|([A-Za-z][+\-.0-9A-Za-z]*:)?\/((%[0-9A-Fa-f]{2}|\/((%[0-9A-Fa-f]{2}|[!$&-.0-9;=A-Z_a-z~])+|(\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~]+)?|[.0-:A-Fa-f]+)\])?)(:\d*)?[/?]|[!$&-.0-;=?-Z_a-z~])(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~])*|\/((%[0-9A-Fa-f]{2}|[!$&-.0-9;=A-Z_a-z~])+(:\d*)?|(\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~]+)?|[.0-:A-Fa-f]+)\])?:\d*|\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~]+)?|[.0-:A-Fa-f]+)\])?)?|[A-Za-z][+\-.0-9A-Za-z]*:?)?$/,
201
  'iri': /^[a-zA-Z][a-zA-Z0-9+.-]*:[^\s]*$/,
202
  'iri-reference': /^(((([A-Za-z][+\-.0-9A-Za-z]*(:%[0-9A-Fa-f]{2}|:[!$&-.0-;=?-Z_a-z~-\u{10FFFF}]|[/?])|\?)(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~-\u{10FFFF}])*|([A-Za-z][+\-.0-9A-Za-z]*:?)?)|([A-Za-z][+\-.0-9A-Za-z]*:)?\/((%[0-9A-Fa-f]{2}|\/((%[0-9A-Fa-f]{2}|[!$&-.0-9;=A-Z_a-z~-\u{10FFFF}])+|(\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~-\u{10FFFF}]+)?|[.0-:A-Fa-f]+)\])?)(:\d*)?[/?]|[!$&-.0-;=?-Z_a-z~-\u{10FFFF}])(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~-\u{10FFFF}])*|(\/((%[0-9A-Fa-f]{2}|[!$&-.0-9;=A-Z_a-z~-\u{10FFFF}])+|(\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~-\u{10FFFF}]+)?|[.0-:A-Fa-f]+)\])?)(:\d*)?)?))#(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~-\u{10FFFF}])*|(([A-Za-z][+\-.0-9A-Za-z]*)?%[0-9A-Fa-f]{2}|[!$&-.0-9;=@_~-\u{10FFFF}]|[A-Za-z][+\-.0-9A-Za-z]*[!$&-*,;=@_~-\u{10FFFF}])(%[0-9A-Fa-f]{2}|[!$&-.0-9;=@-Z_a-z~-\u{10FFFF}])*((([/?](%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~-\u{10FFFF}])*)?#|[/?])(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~-\u{10FFFF}])*)?|([A-Za-z][+\-.0-9A-Za-z]*(:%[0-9A-Fa-f]{2}|:[!$&-.0-;=?-Z_a-z~-\u{10FFFF}]|[/?])|\?)(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~-\u{10FFFF}])*|([A-Za-z][+\-.0-9A-Za-z]*:)?\/((%[0-9A-Fa-f]{2}|\/((%[0-9A-Fa-f]{2}|[!$&-.0-9;=A-Z_a-z~-\u{10FFFF}])+|(\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~-\u{10FFFF}]+)?|[.0-:A-Fa-f]+)\])?)(:\d*)?[/?]|[!$&-.0-;=?-Z_a-z~-\u{10FFFF}])(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~-\u{10FFFF}])*|\/((%[0-9A-Fa-f]{2}|[!$&-.0-9;=A-Z_a-z~-\u{10FFFF}])+(:\d*)?|(\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~-\u{10FFFF}]+)?|[.0-:A-Fa-f]+)\])?:\d*|\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~-\u{10FFFF}]+)?|[.0-:A-Fa-f]+)\])?)?|[A-Za-z][+\-.0-9A-Za-z]*:?)?$/u,
203
  'uuid': /^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i,
204

    
205
  // 7.3.6. uri-template
206
  'uri-template': /(%[0-9a-f]{2}|[!#$&(-;=?@\[\]_a-z~]|\{[!#&+,./;=?@|]?(%[0-9a-f]{2}|[0-9_a-z])(\.?(%[0-9a-f]{2}|[0-9_a-z]))*(:[1-9]\d{0,3}|\*)?(,(%[0-9a-f]{2}|[0-9_a-z])(\.?(%[0-9a-f]{2}|[0-9_a-z]))*(:[1-9]\d{0,3}|\*)?)*\})*/iu,
207

    
208
  // 7.3.7. JSON Pointers
209
  'json-pointer': /^(\/([\x00-\x2e0-@\[-}\x7f]|~[01])*)*$/iu,
210
  'relative-json-pointer': /^\d+(#|(\/([\x00-\x2e0-@\[-}\x7f]|~[01])*)*)$/iu,
211

    
212
  // hostname regex from: http://stackoverflow.com/a/1420225/5628
213
  'hostname': /^(?=.{1,255}$)[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?)*\.?$/,
214
  'host-name': /^(?=.{1,255}$)[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?)*\.?$/,
215

    
216
  'utc-millisec': function (input) {
217
    return (typeof input === 'string') && parseFloat(input) === parseInt(input, 10) && !isNaN(input);
218
  },
219

    
220
  // 7.3.8. regex
221
  'regex': function (input) {
222
    var result = true;
223
    try {
224
      new RegExp(input);
225
    } catch (e) {
226
      result = false;
227
    }
228
    return result;
229
  },
230

    
231
  // Other definitions
232
  // "style" was removed from JSON Schema in draft-4 and is deprecated
233
  'style': /[\r\n\t ]*[^\r\n\t ][^:]*:[\r\n\t ]*[^\r\n\t ;]*[\r\n\t ]*;?/,
234
  // "color" was removed from JSON Schema in draft-4 and is deprecated
235
  'color': /^(#?([0-9A-Fa-f]{3}){1,2}\b|aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow|(rgb\(\s*\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\b\s*,\s*\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\b\s*,\s*\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\b\s*\))|(rgb\(\s*(\d?\d%|100%)+\s*,\s*(\d?\d%|100%)+\s*,\s*(\d?\d%|100%)+\s*\)))$/,
236
  'phone': /^\+(?:[0-9] ?){6,14}[0-9]$/,
237
  'alpha': /^[a-zA-Z]+$/,
238
  'alphanumeric': /^[a-zA-Z0-9]+$/,
239
};
240

    
241
FORMAT_REGEXPS.regexp = FORMAT_REGEXPS.regex;
242
FORMAT_REGEXPS.pattern = FORMAT_REGEXPS.regex;
243
FORMAT_REGEXPS.ipv4 = FORMAT_REGEXPS['ip-address'];
244

    
245
#EXPORT FORMAT_REGEXPS
246

    
247
function isFormat (input, format, validator) {
248
  if (typeof input === 'string' && FORMAT_REGEXPS[format] !== undefined) {
249
    if (FORMAT_REGEXPS[format] instanceof RegExp) {
250
      return FORMAT_REGEXPS[format].test(input);
251
    }
252
    if (typeof FORMAT_REGEXPS[format] === 'function') {
253
      return FORMAT_REGEXPS[format](input);
254
    }
255
  } else if (validator && validator.customFormats &&
256
      typeof validator.customFormats[format] === 'function') {
257
    return validator.customFormats[format](input);
258
  }
259
  return true;
260
};
261

    
262
#EXPORT isFormat
263

    
264
function makeSuffix (key) {
265
  key = key.toString();
266
  // This function could be capable of outputting valid a ECMAScript string, but the
267
  // resulting code for testing which form to use would be tens of thousands of characters long
268
  // That means this will use the name form for some illegal forms
269
  if (!key.match(/[.\s\[\]]/) && !key.match(/^[\d]/)) {
270
    return '.' + key;
271
  }
272
  if (key.match(/^\d+$/)) {
273
    return '[' + key + ']';
274
  }
275
  return '[' + JSON.stringify(key) + ']';
276
};
277
#EXPORT makeSuffix
278

    
279
function deepCompareStrict (a, b) {
280
  if (typeof a !== typeof b) {
281
    return false;
282
  }
283
  if (Array.isArray(a)) {
284
    if (!Array.isArray(b)) {
285
      return false;
286
    }
287
    if (a.length !== b.length) {
288
      return false;
289
    }
290
    return a.every(function (v, i) {
291
      return deepCompareStrict(a[i], b[i]);
292
    });
293
  }
294
  if (typeof a === 'object') {
295
    if (!a || !b) {
296
      return a === b;
297
    }
298
    var aKeys = Object.keys(a);
299
    var bKeys = Object.keys(b);
300
    if (aKeys.length !== bKeys.length) {
301
      return false;
302
    }
303
    return aKeys.every(function (v) {
304
      return deepCompareStrict(a[v], b[v]);
305
    });
306
  }
307
  return a === b;
308
};
309
#EXPORT deepCompareStrict
310

    
311
function deepMerger (target, dst, e, i) {
312
  if (typeof e === 'object') {
313
    dst[i] = deepMerge(target[i], e);
314
  } else {
315
    if (target.indexOf(e) === -1) {
316
      dst.push(e);
317
    }
318
  }
319
}
320

    
321
function copyist (src, dst, key) {
322
  dst[key] = src[key];
323
}
324

    
325
function copyistWithDeepMerge (target, src, dst, key) {
326
  if (typeof src[key] !== 'object' || !src[key]) {
327
    dst[key] = src[key];
328
  }
329
  else {
330
    if (!target[key]) {
331
      dst[key] = src[key];
332
    } else {
333
      dst[key] = deepMerge(target[key], src[key]);
334
    }
335
  }
336
}
337

    
338
function deepMerge (target, src) {
339
  var array = Array.isArray(src);
340
  var dst = array && [] || {};
341

    
342
  if (array) {
343
    target = target || [];
344
    dst = dst.concat(target);
345
    src.forEach(deepMerger.bind(null, target, dst));
346
  } else {
347
    if (target && typeof target === 'object') {
348
      Object.keys(target).forEach(copyist.bind(null, target, dst));
349
    }
350
    Object.keys(src).forEach(copyistWithDeepMerge.bind(null, target, src, dst));
351
  }
352

    
353
  return dst;
354
}
355
#EXPORT deepMerge
356

    
357
/**
358
 * Validates instance against the provided schema
359
 * Implements URI+JSON Pointer encoding, e.g. "%7e"="~0"=>"~", "~1"="%2f"=>"/"
360
 * @param o
361
 * @param s The path to walk o along
362
 * @return any
363
 */
364
function objectGetPath(o, s) {
365
  var parts = s.split('/').slice(1);
366
  var k;
367
  while (typeof (k=parts.shift()) == 'string') {
368
    var n = decodeURIComponent(k.replace(/~0/,'~').replace(/~1/g,'/'));
369
    if (!(n in o)) return;
370
    o = o[n];
371
  }
372
  return o;
373
};
374
#EXPORT objectGetPath
375

    
376
function pathEncoder (v) {
377
  return '/'+encodeURIComponent(v).replace(/~/g,'%7E');
378
}
379
/**
380
 * Accept an Array of property names and return a JSON Pointer URI fragment
381
 * @param Array a
382
 * @return {String}
383
 */
384
function encodePointer(a){
385
  // ~ must be encoded explicitly because hacks
386
  // the slash is encoded by encodeURIComponent
387
  return a.map(pathEncoder).join('');
388
};
389
#EXPORT  encodePointer  AS encodePath
390

    
391

    
392
/**
393
 * Calculate the number of decimal places a number uses
394
 * We need this to get correct results out of multipleOf and divisibleBy
395
 * when either figure is has decimal places, due to IEEE-754 float issues.
396
 * @param number
397
 * @returns {number}
398
 */
399
function getDecimalPlaces(number) {
400

    
401
  var decimalPlaces = 0;
402
  if (isNaN(number)) return decimalPlaces;
403

    
404
  if (typeof number !== 'number') {
405
    number = Number(number);
406
  }
407

    
408
  var parts = number.toString().split('e');
409
  if (parts.length === 2) {
410
    if (parts[1][0] !== '-') {
411
      return decimalPlaces;
412
    } else {
413
      decimalPlaces = Number(parts[1].slice(1));
414
    }
415
  }
416

    
417
  var decimalParts = parts[0].split('.');
418
  if (decimalParts.length === 2) {
419
    decimalPlaces += decimalParts[1].length;
420
  }
421

    
422
  return decimalPlaces;
423
};
424
#EXPORT getDecimalPlaces
425

    
426
function isSchema(val){
427
  return (typeof val === 'object' && val) || (typeof val === 'boolean');
428
};
429
#EXPORT isSchema
(2-2/5)