1// This is a build of https://github.com/garycourt/JSV at 0aa11852537069b0830569ef1eab11a36b65b3ab with jsv.js, schema03 and URI.js appended. 2// The build contains a few lines of custom code to handle format validation. 3// Custom code is wrapped in comments that start with "JOSHFIRE" 4(function(global, require) { 5var exports = {}; 6 7 8 9/** 10 * URI.js 11 * 12 * @fileoverview An RFC 3986 compliant, scheme extendable URI parsing/validating/resolving library for JavaScript. 13 * @author <a href="mailto:[email protected]">Gary Court</a> 14 * @version 1.2 15 * @see http://github.com/garycourt/uri-js 16 * @license URI.js v1.2 (c) 2010 Gary Court. License: http://github.com/garycourt/uri-js 17 */ 18 19/** 20 * Copyright 2010 Gary Court. All rights reserved. 21 * 22 * Redistribution and use in source and binary forms, with or without modification, are 23 * permitted provided that the following conditions are met: 24 * 25 * 1. Redistributions of source code must retain the above copyright notice, this list of 26 * conditions and the following disclaimer. 27 * 28 * 2. Redistributions in binary form must reproduce the above copyright notice, this list 29 * of conditions and the following disclaimer in the documentation and/or other materials 30 * provided with the distribution. 31 * 32 * THIS SOFTWARE IS PROVIDED BY GARY COURT ``AS IS'' AND ANY EXPRESS OR IMPLIED 33 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 34 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARY COURT OR 35 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 36 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 37 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 38 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 39 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 40 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 41 * 42 * The views and conclusions contained in the software and documentation are those of the 43 * authors and should not be interpreted as representing official policies, either expressed 44 * or implied, of Gary Court. 45 */ 46 47/*jslint white: true, sub: true, onevar: true, undef: true, eqeqeq: true, newcap: true, immed: true, indent: 4 */ 48/*global exports:true, require:true */ 49 50if (typeof exports === "undefined") { 51 exports = {}; 52} 53if (typeof require !== "function") { 54 require = function (id) { 55 return exports; 56 }; 57} 58(function () { 59 var 60 /** 61 * @param {...string} sets 62 * @return {string} 63 */ 64 mergeSet = function (sets) { 65 var set = arguments[0], 66 x = 1, 67 nextSet = arguments[x]; 68 69 while (nextSet) { 70 set = set.slice(0, -1) + nextSet.slice(1); 71 nextSet = arguments[++x]; 72 } 73 74 return set; 75 }, 76 77 /** 78 * @param {string} str 79 * @return {string} 80 */ 81 subexp = function (str) { 82 return "(?:" + str + ")"; 83 }, 84 85 ALPHA$$ = "[A-Za-z]", 86 CR$ = "[\\x0D]", 87 DIGIT$$ = "[0-9]", 88 DQUOTE$$ = "[\\x22]", 89 HEXDIG$$ = mergeSet(DIGIT$$, "[A-Fa-f]"), //case-insensitive 90 LF$$ = "[\\x0A]", 91 SP$$ = "[\\x20]", 92 PCT_ENCODED$ = subexp("%" + HEXDIG$$ + HEXDIG$$), 93 GEN_DELIMS$$ = "[\\:\\/\\?\\#\\[\\]\\@]", 94 SUB_DELIMS$$ = "[\\!\\$\\&\\'\\(\\)\\*\\+\\,\\;\\=]", 95 RESERVED$$ = mergeSet(GEN_DELIMS$$, SUB_DELIMS$$), 96 UNRESERVED$$ = mergeSet(ALPHA$$, DIGIT$$, "[\\-\\.\\_\\~]"), 97 SCHEME$ = subexp(ALPHA$$ + mergeSet(ALPHA$$, DIGIT$$, "[\\+\\-\\.]") + "*"), 98 USERINFO$ = subexp(subexp(PCT_ENCODED$ + "|" + mergeSet(UNRESERVED$$, SUB_DELIMS$$, "[\\:]")) + "*"), 99 DEC_OCTET$ = subexp(subexp("25[0-5]") + "|" + subexp("2[0-4]" + DIGIT$$) + "|" + subexp("1" + DIGIT$$ + DIGIT$$) + "|" + subexp("[1-9]" + DIGIT$$) + "|" + DIGIT$$), 100 IPV4ADDRESS$ = subexp(DEC_OCTET$ + "\\." + DEC_OCTET$ + "\\." + DEC_OCTET$ + "\\." + DEC_OCTET$), 101 H16$ = subexp(HEXDIG$$ + "{1,4}"), 102 LS32$ = subexp(subexp(H16$ + "\\:" + H16$) + "|" + IPV4ADDRESS$),
103 IPV6ADDRESS$ = subexp(mergeSet(UNRESERVED$$, SUB_DELIMS$$, "[\\:]") + "+"), //FIXME 104 IPVFUTURE$ = subexp("v" + HEXDIG$$ + "+\\." + mergeSet(UNRESERVED$$, SUB_DELIMS$$, "[\\:]") + "+"), 105 IP_LITERAL$ = subexp("\\[" + subexp(IPV6ADDRESS$ + "|" + IPVFUTURE$) + "\\]"), 106 REG_NAME$ = subexp(subexp(PCT_ENCODED$ + "|" + mergeSet(UNRESERVED$$, SUB_DELIMS$$)) + "*"), 107 HOST$ = subexp(IP_LITERAL$ + "|" + IPV4ADDRESS$ + "|" + REG_NAME$), 108 PORT$ = subexp(DIGIT$$ + "*"), 109 AUTHORITY$ = subexp(subexp(USERINFO$ + "@") + "?" + HOST$ + subexp("\\:" + PORT$) + "?"), 110 PCHAR$ = subexp(PCT_ENCODED$ + "|" + mergeSet(UNRESERVED$$, SUB_DELIMS$$, "[\\:\\@]")), 111 SEGMENT$ = subexp(PCHAR$ + "*"), 112 SEGMENT_NZ$ = subexp(PCHAR$ + "+"), 113 SEGMENT_NZ_NC$ = subexp(subexp(PCT_ENCODED$ + "|" + mergeSet(UNRESERVED$$, SUB_DELIMS$$, "[\\@]")) + "+"), 114 PATH_ABEMPTY$ = subexp(subexp("\\/" + SEGMENT$) + "*"), 115 PATH_ABSOLUTE$ = subexp("\\/" + subexp(SEGMENT_NZ$ + PATH_ABEMPTY$) + "?"), //simplified 116 PATH_NOSCHEME$ = subexp(SEGMENT_NZ_NC$ + PATH_ABEMPTY$), //simplified 117 PATH_ROOTLESS$ = subexp(SEGMENT_NZ$ + PATH_ABEMPTY$), //simplified 118 PATH_EMPTY$ = subexp(""), //simplified 119 PATH$ = subexp(PATH_ABEMPTY$ + "|" + PATH_ABSOLUTE$ + "|" + PATH_NOSCHEME$ + "|" + PATH_ROOTLESS$ + "|" + PATH_EMPTY$), 120 QUERY$ = subexp(subexp(PCHAR$ + "|[\\/\\?]") + "*"), 121 FRAGMENT$ = subexp(subexp(PCHAR$ + "|[\\/\\?]") + "*"), 122 HIER_PART$ = subexp(subexp("\\/\\/" + AUTHORITY$ + PATH_ABEMPTY$) + "|" + PATH_ABSOLUTE$ + "|" + PATH_ROOTLESS$ + "|" + PATH_EMPTY$), 123 URI$ = subexp(SCHEME$ + "\\:" + HIER_PART$ + subexp("\\?" + QUERY$) + "?" + subexp("\\#" + FRAGMENT$) + "?"), 124 RELATIVE_PART$ = subexp(subexp("\\/\\/" + AUTHORITY$ + PATH_ABEMPTY$) + "|" + PATH_ABSOLUTE$ + "|" + PATH_NOSCHEME$ + "|" + PATH_EMPTY$), 125 RELATIVE_REF$ = subexp(RELATIVE_PART$ + subexp("\\?" + QUERY$) + "?" + subexp("\\#" + FRAGMENT$) + "?"), 126 URI_REFERENCE$ = subexp(URI$ + "|" + RELATIVE_REF$), 127 ABSOLUTE_URI$ = subexp(SCHEME$ + "\\:" + HIER_PART$ + subexp("\\?" + QUERY$) + "?"), 128 129 URI_REF = new RegExp("^" + subexp("(" + URI$ + ")|(" + RELATIVE_REF$ + ")") + "$"), 130 GENERIC_REF = new RegExp("^(" + SCHEME$ + ")\\:" + subexp(subexp("\\/\\/(" + subexp("(" + USERINFO$ + ")@") + "?(" + HOST$ + ")" + subexp("\\:(" + PORT$ + ")") + "?)") + "?(" + PATH_ABEMPTY$ + "|" + PATH_ABSOLUTE$ + "|" + PATH_ROOTLESS$ + "|" + PATH_EMPTY$ + ")") + subexp("\\?(" + QUERY$ + ")") + "?" + subexp("\\#(" + FRAGMENT$ + ")") + "?$"), 131 RELATIVE_REF = new RegExp("^(){0}" + subexp(subexp("\\/\\/(" + subexp("(" + USERINFO$ + ")@") + "?(" + HOST$ + ")" + subexp("\\:(" + PORT$ + ")") + "?)") + "?(" + PATH_ABEMPTY$ + "|" + PATH_ABSOLUTE$ + "|" + PATH_NOSCHEME$ + "|" + PATH_EMPTY$ + ")") + subexp("\\?(" + QUERY$ + ")") + "?" + subexp("\\#(" + FRAGMENT$ + ")") + "?$"), 132 ABSOLUTE_REF = new RegExp("^(" + SCHEME$ + ")\\:" + subexp(subexp("\\/\\/(" + subexp("(" + USERINFO$ + ")@") + "?(" + HOST$ + ")" + subexp("\\:(" + PORT$ + ")") + "?)") + "?(" + PATH_ABEMPTY$ + "|" + PATH_ABSOLUTE$ + "|" + PATH_ROOTLESS$ + "|" + PATH_EMPTY$ + ")") + subexp("\\?(" + QUERY$ + ")") + "?$"), 133 SAMEDOC_REF = new RegExp("^" + subexp("\\#(" + FRAGMENT$ + ")") + "?$"), 134 AUTHORITY = new RegExp("^" + subexp("(" + USERINFO$ + ")@") + "?(" + HOST$ + ")" + subexp("\\:(" + PORT$ + ")") + "?$"), 135 136 NOT_SCHEME = new RegExp(mergeSet("[^]", ALPHA$$, DIGIT$$, "[\\+\\-\\.]"), "g"), 137 NOT_USERINFO = new RegExp(mergeSet("[^\\%\\:]", UNRESERVED$$, SUB_DELIMS$$), "g"), 138 NOT_HOST = new RegExp(mergeSet("[^\\%]", UNRESERVED$$, SUB_DELIMS$$), "g"), 139 NOT_PATH = new RegExp(mergeSet("[^\\%\\/\\:\\@]", UNRESERVED$$, SUB_DELIMS$$), "g"), 140 NOT_PATH_NOSCHEME = new RegExp(mergeSet("[^\\%\\/\\@]", UNRESERVED$$, SUB_DELIMS$$), "g"), 141 NOT_QUERY = new RegExp(mergeSet("[^\\%]", UNRESERVED$$, SUB_DELIMS$$, "[\\:\\@\\/\\?]"), "g"), 142 NOT_FRAGMENT = NOT_QUERY, 143 ESCAPE = new RegExp(mergeSet("[^]", UNRESERVED$$, SUB_DELIMS$$), "g"), 144 UNRESERVED = new RegExp(UNRESERVED$$, "g"), 145 OTHER_CHARS = new RegExp(mergeSet("[^\\%]", UNRESERVED$$, RESERVED$$), "g"), 146 PCT_ENCODEDS = new RegExp(PCT_ENCODED$ + "+", "g"), 147 URI_PARSE = /^(?:([^:\/?#]+):)?(?:\/\/((?:([^\/?#@]*)@)?([^\/?#:]*)(?:\:(\d*))?))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?/i, 148 RDS1 = /^\.\.?\//, 149 RDS2 = /^\/\.(\/|$)/, 150 RDS3 = /^\/\.\.(\/|$)/, 151 RDS4 = /^\.\.?$/, 152 RDS5 = /^\/?.*?(?=\/|$)/, 153 NO_MATCH_IS_UNDEFINED = ("").match(/(){0}
153/)[1] === undefined, 154 155 /** 156 * @param {string} chr 157 * @return {string} 158 */ 159 pctEncChar = function (chr) { 160 var c = chr.charCodeAt(0); 161 162 if (c < 128) { 163 return "%" + c.toString(16).toUpperCase(); 164 } 165 else if ((c > 127) && (c < 2048)) { 166 return "%" + ((c >> 6) | 192).toString(16).toUpperCase() + "%" + ((c & 63) | 128).toString(16).toUpperCase(); 167 } 168 else { 169 return "%" + ((c >> 12) | 224).toString(16).toUpperCase() + "%" + (((c >> 6) & 63) | 128).toString(16).toUpperCase() + "%" + ((c & 63) | 128).toString(16).toUpperCase(); 170 } 171 }, 172 173 /** 174 * @param {string} str 175 * @return {string} 176 */ 177 pctDecUnreserved = function (str) { 178 var newStr = "", 179 i = 0, 180 c, s; 181 182 while (i < str.length) { 183 c = parseInt(str.substr(i + 1, 2), 16); 184 185 if (c < 128) { 186 s = String.fromCharCode(c); 187 if (s.match(UNRESERVED)) { 188 newStr += s; 189 } else { 190 newStr += str.substr(i, 3); 191 } 192 i += 3; 193 } 194 else if ((c > 191) && (c < 224)) { 195 newStr += str.substr(i, 6); 196 i += 6; 197 } 198 else { 199 newStr += str.substr(i, 9); 200 i += 9; 201 } 202 } 203 204 return newStr; 205 }, 206 207 /** 208 * @param {string} str 209 * @return {string} 210 */ 211 pctDecChars = function (str) { 212 var newStr = "", 213 i = 0, 214 c, c2, c3; 215 216 while (i < str.length) { 217 c = parseInt(str.substr(i + 1, 2), 16); 218 219 if (c < 128) { 220 newStr += String.fromCharCode(c); 221 i += 3; 222 } 223 else if ((c > 191) && (c < 224)) { 224 c2 = parseInt(str.substr(i + 4, 2), 16); 225 newStr += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); 226 i += 6; 227 } 228 else { 229 c2 = parseInt(str.substr(i + 4, 2), 16); 230 c3 = parseInt(str.substr(i + 7, 2), 16); 231 newStr += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); 232 i += 9; 233 } 234 } 235 236 return newStr; 237 }, 238 239 /** 240 * @return {string} 241 */ 242 typeOf = function (o) { 243 return o === undefined ? "undefined" : (o === null ? "null" : Object.prototype.toString.call(o).split(" ").pop().split("]").shift().toLowerCase()); 244 }, 245 246 /** 247 * @constructor 248 * @implements URIComponents 249 */ 250 Components = function () { 251 this.errors = []; 252 }, 253 254 /** @namespace */ 255 URI = {}; 256 257 /** 258 * Components 259 */ 260 261 Components.prototype = { 262 /**
263 * @type String 264 */ 265 266 scheme : undefined, 267 268 /** 269 * @type String 270 */ 271 272 authority : undefined, 273 274 /** 275 * @type String 276 */ 277 278 userinfo : undefined, 279 280 /** 281 * @type String 282 */ 283 284 host : undefined, 285 286 /** 287 * @type number 288 */ 289 290 port : undefined, 291 292 /** 293 * @type string 294 */ 295 296 path : undefined, 297 298 /** 299 * @type string 300 */ 301 302 query : undefined, 303 304 /** 305 * @type string 306 */ 307 308 fragment : undefined, 309 310 /** 311 * @type string 312 * @values "uri", "absolute", "relative", "same-document" 313 */ 314 315 reference : undefined, 316 317 /** 318 * @type Array 319 */ 320 321 errors : undefined 322 }; 323 324 /** 325 * URI 326 */ 327 328 /** 329 * @namespace 330 */ 331 332 URI.SCHEMES = {}; 333 334 /** 335 * @param {string} uriString 336 * @param {Options} [options] 337 * @returns {URIComponents} 338 */ 339 340 URI.parse = function (uriString, options) { 341 var matches, 342 components = new Components(), 343 schemeHandler; 344 345 uriString = uriString ? uriString.toString() : ""; 346 options = options || {}; 347 348 if (options.reference === "suffix") { 349 uriString = (options.scheme ? options.scheme + ":" : "") + "//" + uriString; 350 } 351 352 matches = uriString.match(URI_REF); 353 354 if (matches) { 355 if (matches[1]) { 356 //generic URI 357 matches = uriString.match(GENERIC_REF); 358 } else { 359 //relative URI 360 matches = uriString.match(RELATIVE_REF); 361 } 362 } 363 364 if (!matches) { 365 if (!options.tolerant) { 366 components.errors.push("URI is not strictly valid."); 367 } 368 matches = uriString.match(URI_PARSE); 369 } 370 371 if (matches) { 372 if (NO_MATCH_IS_UNDEFINED) { 373 //store each component 374 components.scheme = matches[1]; 375 components.authority = matches[2]; 376 components.userinfo = matches[3]; 377 components.host = matches[4]; 378 components.port = parseInt(matches[5], 10); 379 components.path = matches[6] || ""; 380 components.query = matches[7]; 381 components.fragment = matches[8]; 382 383 //fix port number 384 if (isNaN(components.port)) { 385 components.port = matches[5]; 386 } 387 } else { //IE FIX for improper RegExp matching 388 //store each component 389 components.scheme = matches[1] || undefined; 390 components.authority = (uriString.indexOf("//") !== -1 ? matches[2] : undefined); 391 components.userinfo = (uriString.indexOf("@") !== -1 ? matches[3] : undefined); 392 components.host = (uriString.indexOf("//") !== -1 ? matches[4] : undefined); 393 components.port = parseInt(matches[5], 10); 394 components.path = matches[6] || ""; 395 components.query = (uriString.indexOf("?") !== -1 ? matches[7] : undefined); 396 components.fragment = (uriString.indexOf("#") !== -1 ? matches[8] : undefined); 397 398 //fix port number 399 if (isNaN(components.port)) { 400 components.port = (uriString.match(/\/\/.*\:(?:\/|\?|\#|$)/) ? matches[4] : undefined); 401 } 402 } 403 404 //determine reference type 405 if (!components.scheme && !components.authority && !components.path && !components.query) { 406 components.reference = "same-document"; 407 } else if (!components.scheme) { 408 components.reference = "relative"; 409 } else if (!components.fragment) { 410 components.reference = "absolute"; 411 } else { 412 components.reference = "uri"; 413 } 414 415 //check for reference errors 416 if (options.reference && options.reference !== "suffix" && options.reference !== components.reference) { 417 components.errors.push("URI is not a " + options.reference + " reference."); 418 } 419 420 //check if a handler for the scheme exists 421 schemeHandler = URI.SCHEMES[components.scheme || options.scheme]; 422 if (schemeHandler && schemeHandler.parse) { 423 //perform extra parsing 424 schemeHandler.parse(components, options); 425 } 426 } else { 427 components.errors.push("URI can not be parsed."); 428 } 429 430 return components; 431 }; 432 433 /** 434 * @private 435 * @param {URIComponents} components 436 * @returns {string|undefined} 437 */ 438 439 URI._recomposeAuthority = function (components) { 440 var uriTokens = []; 441 442 if (components.userinfo !== undefined || components.host !== undefined || typeof components.port === "number") { 443 if (components.userinfo !== undefined) { 444 uriTokens.push(components.userinfo.toString().replace(NOT_USERINFO, pctEncChar)); 445 uriTokens.push("@"); 446 } 447 if (components.host !== undefined) { 448 uriTokens.push(components.host.toString().toLowerCase().replace(NOT_HOST, pctEncChar)); 449 } 450 if (typeof components.port === "number") { 451 uriTokens.push(":"); 452 uriTokens.push(components.port.toString(10)); 453 } 454 } 455 456 return uriTokens.length ? uriTokens.join("") : undefined; 457 }; 458 459 /** 460 * @param {string} input 461 * @returns {string} 462 */ 463 464 URI.removeDotSegments = function (input) { 465 var output = [], s; 466 467 while (input.length) { 468 if (input.match(RDS1)) { 469 input = input.replace(RDS1, ""); 470 } else if (input.match(RDS2)) { 471 input = input.replace(RDS2, "/"); 472 } else if (input.match(RDS3)) { 473 input = input.replace(RDS3, "/"); 474 output.pop(); 475 } else if (input === "." || input === "..") { 476 input = ""; 477 } else { 478 s = input.match(RDS5)[0]; 479 input = input.slice(s.length); 480 output.push(s); 481 } 482 } 483 484 return output.join(""); 485 }; 486 487 /** 488 * @param {URIComponents} components 489 * @param {Options} [options] 490 * @returns {string} 491 */ 492 493 URI.serialize = function (components, options) { 494 var uriTokens = [], 495 schemeHandler,
496 s; 497 options = options || {}; 498 499 //check if a handler for the scheme exists 500 schemeHandler = URI.SCHEMES[components.scheme || options.scheme]; 501 if (schemeHandler && schemeHandler.serialize) { 502 //perform extra serialization 503 schemeHandler.serialize(components, options); 504 } 505 506 if (options.reference !== "suffix" && components.scheme) { 507 uriTokens.push(components.scheme.toString().toLowerCase().replace(NOT_SCHEME, "")); 508 uriTokens.push(":"); 509 } 510 511 components.authority = URI._recomposeAuthority(components); 512 if (components.authority !== undefined) { 513 if (options.reference !== "suffix") { 514 uriTokens.push("//"); 515 } 516 517 uriTokens.push(components.authority); 518 519 if (components.path && components.path.charAt(0) !== "/") { 520 uriTokens.push("/"); 521 } 522 } 523 524 if (components.path) { 525 s = URI.removeDotSegments(components.path.toString().replace(/%2E/ig, ".")); 526 527 if (components.scheme) { 528 s = s.replace(NOT_PATH, pctEncChar); 529 } else { 530 s = s.replace(NOT_PATH_NOSCHEME, pctEncChar); 531 } 532 533 if (components.authority === undefined) { 534 s = s.replace(/^\/\//, "/%2F"); //don't allow the path to start with "//" 535 } 536 uriTokens.push(s); 537 } 538 539 if (components.query) { 540 uriTokens.push("?"); 541 uriTokens.push(components.query.toString().replace(NOT_QUERY, pctEncChar)); 542 } 543 544 if (components.fragment) { 545 uriTokens.push("#"); 546 uriTokens.push(components.fragment.toString().replace(NOT_FRAGMENT, pctEncChar)); 547 } 548 549 return uriTokens 550 .join('') //merge tokens into a string 551 .replace(PCT_ENCODEDS, pctDecUnreserved) //undecode unreserved characters 552 //.replace(OTHER_CHARS, pctEncChar) //replace non-URI characters 553 .replace(/%[0-9A-Fa-f]{2}/g, function (str) { //uppercase percent encoded characters 554 return str.toUpperCase(); 555 }) 556 ; 557 }; 558 559 /** 560 * @param {URIComponents} base 561 * @param {URIComponents} relative 562 * @param {Options} [options] 563 * @param {boolean} [skipNormalization] 564 * @returns {URIComponents} 565 */ 566 567 URI.resolveComponents = function (base, relative, options, skipNormalization) { 568 var target = new Components(); 569 570 if (!skipNormalization) { 571 base = URI.parse(URI.serialize(base, options), options); //normalize base components 572 relative = URI.parse(URI.serialize(relative, options), options); //normalize relative components 573 } 574 options = options || {}; 575 576 if (!options.tolerant && relative.scheme) { 577 target.scheme = relative.scheme; 578 target.authority = relative.authority; 579 target.userinfo = relative.userinfo; 580 target.host = relative.host; 581 target.port = relative.port; 582 target.path = URI.removeDotSegments(relative.path); 583 target.query = relative.query; 584 } else { 585 if (relative.authority !== undefined) { 586 target.authority = relative.authority; 587 target.userinfo = relative.userinfo; 588 target.host = relative.host; 589 target.port = relative.port; 590 target.path = URI.removeDotSegments(relative.path); 591 target.query = relative.query; 592 } else { 593 if (!relative.path) { 594 target.path = base.path; 595 if (relative.query !== undefined) { 596 target.query = relative.query; 597 } else { 598 target.query = base.query; 599 } 600 } else { 601 if (relative.path.charAt(0) === "/") { 602 target.path = URI.removeDotSegments(relative.path); 603 } else { 604 if (base.authority !== undefined && !base.path) { 605 target.path = "/" + relative.path; 606 } else if (!base.path) { 607 target.path = relative.path; 608 } else { 609 target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative.path; 610 } 611 target.path = URI.removeDotSegments(target.path); 612 } 613 target.query = relative.query; 614 } 615 target.authority = base.authority; 616 target.userinfo = base.userinfo; 617 target.host = base.host; 618 target.port = base.port; 619 } 620 target.scheme = base.scheme; 621 } 622 623 target.fragment = relative.fragment; 624 625 return target; 626 }; 627 628 /** 629 * @param {string} baseURI 630 * @param {string} relativeURI 631 * @param {Options} [options] 632 * @returns {string} 633 */ 634 635 URI.resolve = function (baseURI, relativeURI, options) {
636 return URI.serialize(URI.resolveComponents(URI.parse(baseURI, options), URI.parse(relativeURI, options), options, true), options); 637 }; 638 639 /** 640 * @param {string|URIComponents} uri 641 * @param {Options} options 642 * @returns {string|URIComponents} 643 */ 644 645 URI.normalize = function (uri, options) { 646 if (typeof uri === "string") { 647 return URI.serialize(URI.parse(uri, options), options); 648 } else if (typeOf(uri) === "object") { 649 return URI.parse(URI.serialize(uri, options), options); 650 } 651 652 return uri; 653 }; 654 655 /** 656 * @param {string|URIComponents} uriA 657 * @param {string|URIComponents} uriB 658 * @param {Options} options 659 */ 660 661 URI.equal = function (uriA, uriB, options) { 662 if (typeof uriA === "string") { 663 uriA = URI.serialize(URI.parse(uriA, options), options); 664 } else if (typeOf(uriA) === "object") { 665 uriA = URI.serialize(uriA, options); 666 } 667 668 if (typeof uriB === "string") { 669 uriB = URI.serialize(URI.parse(uriB, options), options); 670 } else if (typeOf(uriB) === "object") { 671 uriB = URI.serialize(uriB, options); 672 } 673 674 return uriA === uriB; 675 }; 676 677 /** 678 * @param {string} str 679 * @returns {string} 680 */ 681 682 URI.escapeComponent = function (str) { 683 return str && str.toString().replace(ESCAPE, pctEncChar); 684 }; 685 686 /** 687 * @param {string} str 688 * @returns {string} 689 */ 690 691 URI.unescapeComponent = function (str) { 692 return str && str.toString().replace(PCT_ENCODEDS, pctDecChars); 693 }; 694 695 //export API 696 exports.Components = Components; 697 exports.URI = URI; 698 699 //name-safe export API 700 exports["URI"] = { 701 "SCHEMES" : URI.SCHEMES, 702 "parse" : URI.parse, 703 "removeDotSegments" : URI.removeDotSegments, 704 "serialize" : URI.serialize, 705 "resolveComponents" : URI.resolveComponents, 706 "resolve" : URI.resolve, 707 "normalize" : URI.normalize, 708 "equal" : URI.equal, 709 "escapeComponent" : URI.escapeComponent, 710 "unescapeComponent" : URI.unescapeComponent 711 }; 712 713}()); 714 715 716 717 /** 718 * JSV: JSON Schema Validator 719 * 720 * @fileOverview A JavaScript implementation of a extendable, fully compliant JSON Schema validator. 721 * @author <a href="mailto:[email protected]">Gary Court</a> 722 * @version 3.5 723 * @see http://github.com/garycourt/JSV 724 */ 725 726/* 727 * Copyright 2010 Gary Court. All rights reserved. 728 * 729 * Redistribution and use in source and binary forms, with or without modification, are 730 * permitted provided that the following conditions are met: 731 * 732 * 1. Redistributions of source code must retain the above copyright notice, this list of 733 * conditions and the following disclaimer. 734 * 735 * 2. Redistributions in binary form must reproduce the above copyright notice, this list 736 * of conditions and the following disclaimer in the documentation and/or other materials 737 * provided with the distribution. 738 * 739 * THIS SOFTWARE IS PROVIDED BY GARY COURT ``AS IS'' AND ANY EXPRESS OR IMPLIED 740 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 741 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARY COURT OR 742 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 743 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 744 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 745 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 746 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 747 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 748 * 749 * The views and conclusions contained in the software and documentation are those of the 750 * authors and should not be interpreted as representing official policies, either expressed 751 * or implied, of Gary Court or the JSON Schema specification. 752 */ 753 754/*jslint white: true, sub: true, onevar: true, undef: true, eqeqeq: true, newcap: true, immed: true, indent: 4 */ 755 756var exports = exports || this, 757 require = require || function () { 758 return exports; 759 }; 760 761(function () { 762 763 var URI = require("./uri/uri").URI, 764 O = {}, 765 I2H = "0123456789abcdef".split(""), 766 mapArray, filterArray, searchArray, 767 768 JSV; 769 770 // 771 // Utility functions 772 // 773 774 function typeOf(o) { 775 return o === undefined ? "undefined" : (o === null ? "null" : Object.prototype.toString.call(o).split(" ").pop().split("]").shift().toLowerCase()); 776 } 777 778 /** @inner */ 779 function F() {} 780 781 function createObject(proto) { 782 F.prototype = proto || {}; 783 return new F(); 784 } 785 786 function mapObject(obj, func, scope) { 787 var newObj = {}, key; 788 for (key in obj) { 789 if (obj[key] !== O[key]) { 790 newObj[key] = func.call(scope, obj[key], key, obj); 791 } 792 } 793 return newObj; 794 } 795 796 /** @ignore */ 797 mapArray = function (arr, func, scope) { 798 var x = 0, xl = arr.length, newArr = new Array(xl); 799 for (; x < xl; ++x) { 800 newArr[x] = func.call(scope, arr[x], x, arr); 801 } 802 return newArr; 803 }; 804 805 if (Array.prototype.map) { 806 /** @ignore */ 807 mapArray = function (arr, func, scope) { 808 return Array.prototype.map.call(arr, func, scope); 809 }; 810 } 811 812 /** @ignore */ 813 filterArray = function (arr, func, scope) { 814 var x = 0, xl = arr.length, newArr = []; 815 for (; x < xl; ++x) { 816 if (func.call(scope, arr[x], x, arr)) { 817 newArr[newArr.length] = arr[x]; 818 } 819 } 820 return newArr; 821 }; 822 823 if (Array.prototype.filter) { 824 /** @ignore */ 825 filterArray = function (arr, func, scope) { 826 return Array.prototype.filter.call(arr, func, scope); 827 }; 828 } 829 830 /** @ignore */ 831 searchArray = function (arr, o) { 832 var x = 0, xl = arr.length; 833 for (; x < xl; ++x) { 834 if (arr[x] === o) { 835 return x; 836 } 837 } 838 return -1; 839 }; 840 841 if (Array.prototype.indexOf) { 842 /** @ignore */ 843 searchArray = function (arr, o) { 844 return Array.prototype.indexOf.call(arr, o); 845 }; 846 } 847 848 function toArray(o) { 849 return o !== undefined && o !== null ? (o instanceof Array && !o.callee ? o : (typeof o.length !== "number" || o.split || o.setInterval || o.call ? [ o ] : Array.prototype.slice.call(o))) : []; 850 } 851 852 function keys(o) { 853 var result = [], key; 854
855 switch (typeOf(o)) { 856 case "object": 857 for (key in o) { 858 if (o[key] !== O[key]) { 859 result[result.length] = key; 860 } 861 } 862 break; 863 case "array": 864 for (key = o.length - 1; key >= 0; --key) { 865 result[key] = key; 866 } 867 break; 868 } 869 870 return result; 871 } 872 873 function pushUnique(arr, o) { 874 if (searchArray(arr, o) === -1) { 875 arr.push(o); 876 } 877 return arr; 878 } 879 880 function popFirst(arr, o) { 881 var index = searchArray(arr, o); 882 if (index > -1) { 883 arr.splice(index, 1); 884 } 885 return arr; 886 } 887 888 function randomUUID() { 889 return [ 890 I2H[Math.floor(Math.random() * 0x10)], 891 I2H[Math.floor(Math.random() * 0x10)], 892 I2H[Math.floor(Math.random() * 0x10)], 893 I2H[Math.floor(Math.random() * 0x10)], 894 I2H[Math.floor(Math.random() * 0x10)], 895 I2H[Math.floor(Math.random() * 0x10)], 896 I2H[Math.floor(Math.random() * 0x10)], 897 I2H[Math.floor(Math.random() * 0x10)], 898 "-", 899 I2H[Math.floor(Math.random() * 0x10)], 900 I2H[Math.floor(Math.random() * 0x10)], 901 I2H[Math.floor(Math.random() * 0x10)], 902 I2H[Math.floor(Math.random() * 0x10)], 903 "-4", //set 4 high bits of time_high field to version 904 I2H[Math.floor(Math.random() * 0x10)], 905 I2H[Math.floor(Math.random() * 0x10)], 906 I2H[Math.floor(Math.random() * 0x10)], 907 "-", 908 I2H[(Math.floor(Math.random() * 0x10) & 0x3) | 0x8], //specify 2 high bits of clock sequence 909 I2H[Math.floor(Math.random() * 0x10)], 910 I2H[Math.floor(Math.random() * 0x10)], 911 I2H[Math.floor(Math.random() * 0x10)], 912 "-", 913 I2H[Math.floor(Math.random() * 0x10)], 914 I2H[Math.floor(Math.random() * 0x10)], 915 I2H[Math.floor(Math.random() * 0x10)], 916 I2H[Math.floor(Math.random() * 0x10)], 917 I2H[Math.floor(Math.random() * 0x10)], 918 I2H[Math.floor(Math.random() * 0x10)], 919 I2H[Math.floor(Math.random() * 0x10)], 920 I2H[Math.floor(Math.random() * 0x10)], 921 I2H[Math.floor(Math.random() * 0x10)], 922 I2H[Math.floor(Math.random() * 0x10)], 923 I2H[Math.floor(Math.random() * 0x10)], 924 I2H[Math.floor(Math.random() * 0x10)] 925 ].join(""); 926 } 927 928 function escapeURIComponent(str) { 929 return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A'); 930 } 931 932 function formatURI(uri) { 933 if (typeof uri === "string" && uri.indexOf("#") === -1) { 934 uri += "#"; 935 } 936 return uri; 937 } 938 939 /** 940 * Defines an error, found by a schema, with an instance. 941 * This class can only be instantiated by {@link Report#addError}. 942 * 943 * @name ValidationError 944 * @class 945 * @see Report#addError 946 */ 947 948 /** 949 * The URI of the instance that has the error. 950 * 951 * @name ValidationError.prototype.uri
952 * @type String 953 */ 954 955 /** 956 * The URI of the schema that generated the error. 957 * 958 * @name ValidationError.prototype.schemaUri 959 * @type String 960 */ 961 962 /** 963 * The name of the schema attribute that generated the error. 964 * 965 * @name ValidationError.prototype.attribute 966 * @type String 967 */ 968 969 /** 970 * An user-friendly (English) message about what failed to validate. 971 * 972 * @name ValidationError.prototype.message 973 * @type String 974 */ 975 976 /** 977 * The value of the schema attribute that generated the error. 978 * 979 * @name ValidationError.prototype.details 980 * @type Any 981 */ 982 983 /** 984 * Reports are returned from validation methods to describe the result of a validation. 985 * 986 * @name Report 987 * @class 988 * @see JSONSchema#validate 989 * @see Environment#validate 990 */ 991 992 function Report() { 993 /** 994 * An array of {@link ValidationError} objects that define all the errors generated by the schema against the instance. 995 * 996 * @name Report.prototype.errors 997 * @type Array 998 * @see Report#addError 999 */ 1000 this.errors = []; 1001 1002 /** 1003 * A hash table of every instance and what schemas were validated against it. 1004 * <p> 1005 * The key of each item in the table is the URI of the instance that was validated. 1006 * The value of this key is an array of strings of URIs of the schema that validated it. 1007 * </p> 1008 * 1009 * @name Report.prototype.validated 1010 * @type Object 1011 * @see Report#registerValidation 1012 * @see Report#isValidatedBy 1013 */ 1014 this.validated = {}; 1015 1016 /** 1017 * If the report is generated by {@link Environment#validate}, this field is the generated instance. 1018 * 1019 * @name Report.prototype.instance 1020 * @type JSONInstance 1021 * @see Environment#validate 1022 */ 1023 1024 /** 1025 * If the report is generated by {@link Environment#validate}, this field is the generated schema. 1026 * 1027 * @name Report.prototype.schema 1028 * @type JSONSchema 1029 * @see Environment#validate 1030 */ 1031 1032 /** 1033 * If the report is generated by {@link Environment#validate}, this field is the schema's schema. 1034 * This value is the same as calling <code>schema.getSchema()</code>. 1035 * 1036 * @name Report.prototype.schemaSchema 1037 * @type JSONSchema 1038 * @see Environment#validate 1039 * @see JSONSchema#getSchema 1040 */ 1041 } 1042 1043 /** 1044 * Adds a {@link ValidationError} object to the <a href="#errors"><code>errors</code></a> field. 1045 * 1046 * @param {JSONInstance|String} instance The instance (or instance URI) that is invalid 1047 * @param {JSONSchema|String} schema The schema (or schema URI) that was validating the instance 1048 * @param {String} attr The attribute that failed to validated 1049 * @param {String} message A user-friendly message on why the schema attribute failed to validate the instance 1050 * @param {Any} details The value of the schema attribute 1051 */ 1052 1053 Report.prototype.addError = function (instance, schema, attr, message, details) { 1054 this.errors.push({ 1055 uri : instance instanceof JSONInstance ? instance.getURI() : instance, 1056 schemaUri : schema instanceof JSONInstance ? schema.getURI() : schema, 1057 attribute : attr, 1058 message : message, 1059 details : details 1060 }); 1061 }; 1062 1063 /** 1064 * Registers that the provided instance URI has been validated by the provided schema URI. 1065 * This is recorded in the <a href="#validated"><code>validated</code></a> field. 1066 * 1067 * @param {String} uri The URI of the instance that was validated 1068 * @param {String} schemaUri The URI of the schema that validated the instance 1069 */ 1070 1071 Report.prototype.registerValidation = function (uri, schemaUri) { 1072 if (!this.validated[uri]) { 1073 this.validated[uri] = [ schemaUri ]; 1074 } else { 1075 this.validated[uri].push(schemaUri); 1076 } 1077 }; 1078 1079 /** 1080 * Returns if an instance with the provided URI has been validated by the schema with the provided URI. 1081 * 1082 * @param {String} uri The URI of the instance 1083 * @param {String} schemaUri The URI of a schema 1084 * @returns {Boolean} If the instance has been validated by the schema. 1085 */ 1086 1087 Report.prototype.isValidatedBy = function (uri, schemaUri) { 1088 return !!this.validated[uri] && searchArray(this.validated[uri], schemaUri) !== -1; 1089 }; 1090 1091 /** 1092 * A wrapper class for binding an Environment, URI and helper methods to an instance. 1093 * This class is most commonly instantiated with {@link Environment#createInstance}. 1094 * 1095 * @name JSONInstance 1096 * @class 1097 * @param {Environment} env The environment this instance belongs to 1098 * @param {JSONInstance|Any} json The value of the instance 1099 * @param {String} [uri] The URI of the instance. If undefined, the URI will be a randomly generated UUID. 1100 * @param {String} [fd] The fragment delimiter for properties. If undefined, uses the environment default. 1101 */ 1102 1103 function JSONInstance(env, json, uri, fd) { 1104 if (json instanceof JSONInstance) { 1105 if (typeof fd !== "string") { 1106 fd = json._fd; 1107 } 1108 if (typeof uri !== "string") { 1109 uri = json._uri; 1110 } 1111 json = json._value; 1112 } 1113 1114 if (typeof uri !== "string") { 1115 uri = "urn:uuid:" + randomUUID() + "#"; 1116 } else if (uri.indexOf(":") === -1) { 1117 uri = formatURI(URI.resolve("urn:uuid:" + randomUUID() + "#", uri)); 1118 } 1119 1120 this._env = env; 1121 this._value = json; 1122 this._uri = uri; 1123 this._fd = fd || this._env._options["defaultFragmentDelimiter"]; 1124 } 1125 1126 /** 1127 * Returns the environment the instance is bound to. 1128 * 1129 * @returns {Environment} The environment of the instance 1130 */ 1131 1132 JSONInstance.prototype.getEnvironment = function () { 1133 return this._env; 1134 }; 1135 1136 /** 1137 * Returns the name of the type of the instance. 1138 * 1139 * @returns {String} The name of the type of the instance 1140 */ 1141 1142 JSONInstance.prototype.getType = function () { 1143 return typeOf(this._value); 1144 }; 1145 1146 /** 1147 * Returns the JSON value of the instance. 1148 * 1149 * @returns {Any} The actual JavaScript value of the instance 1150 */ 1151 1152 JSONInstance.prototype.getValue = function () { 1153 return this._value; 1154 }; 1155 1156 /** 1157 * Returns the URI of the instance. 1158 * 1159 * @returns {String} The URI of the instance 1160 */ 1161 1162 JSONInstance.prototype.getURI = function () { 1163 return this._uri; 1164 }; 1165 1166 /** 1167 * Returns a resolved URI of a provided relative URI against the URI of the instance. 1168 * 1169 * @param {String} uri The relative URI to resolve 1170 * @returns {String} The resolved URI 1171 */ 1172 1173 JSONInstance.prototype.resolveURI = function (uri) { 1174 return formatURI(URI.resolve(this._uri, uri)); 1175 }; 1176 1177 /** 1178 * Returns an array of the names of all the properties. 1179 * 1180 * @returns {Array}
1180 An array of strings which are the names of all the properties 1181 */ 1182 1183 JSONInstance.prototype.getPropertyNames = function () { 1184 return keys(this._value); 1185 }; 1186 1187 /** 1188 * Returns a {@link JSONInstance} of the value of the provided property name. 1189 * 1190 * @param {String} key The name of the property to fetch 1191 * @returns {JSONInstance} The instance of the property value 1192 */ 1193 1194 JSONInstance.prototype.getProperty = function (key) { 1195 var value = this._value ? this._value[key] : undefined; 1196 if (value instanceof JSONInstance) { 1197 return value; 1198 } 1199 //else 1200 return new JSONInstance(this._env, value, this._uri + this._fd + escapeURIComponent(key), this._fd); 1201 }; 1202 1203 /** 1204 * Returns all the property instances of the target instance. 1205 * <p> 1206 * If the target instance is an Object, then the method will return a hash table of {@link JSONInstance}s of all the properties. 1207 * If the target instance is an Array, then the method will return an array of {@link JSONInstance}s of all the items. 1208 * </p> 1209 * 1210 * @returns {Object|Array|undefined} The list of instances for all the properties 1211 */ 1212 1213 JSONInstance.prototype.getProperties = function () { 1214 var type = typeOf(this._value), 1215 self = this; 1216 1217 if (type === "object") { 1218 return mapObject(this._value, function (value, key) { 1219 if (value instanceof JSONInstance) { 1220 return value; 1221 } 1222 return new JSONInstance(self._env, value, self._uri + self._fd + escapeURIComponent(key), self._fd); 1223 }); 1224 } else if (type === "array") { 1225 return mapArray(this._value, function (value, key) { 1226 if (value instanceof JSONInstance) { 1227 return value; 1228 } 1229 return new JSONInstance(self._env, value, self._uri + self._fd + escapeURIComponent(key), self._fd); 1230 }); 1231 } 1232 }; 1233 1234 /** 1235 * Returns the JSON value of the provided property name. 1236 * This method is a faster version of calling <code>instance.getProperty(key).getValue()</code>. 1237 * 1238 * @param {String} key The name of the property 1239 * @returns {Any} The JavaScript value of the instance 1240 * @see JSONInstance#getProperty 1241 * @see JSONInstance#getValue 1242 */ 1243 1244 JSONInstance.prototype.getValueOfProperty = function (key) { 1245 if (this._value) { 1246 if (this._value[key] instanceof JSONInstance) { 1247 return this._value[key]._value; 1248 } 1249 return this._value[key]; 1250 } 1251 }; 1252 1253 /** 1254 * Return if the provided value is the same as the value of the instance. 1255 * 1256 * @param {JSONInstance|Any} instance The value to compare 1257 * @returns {Boolean} If both the instance and the value match 1258 */ 1259 1260 JSONInstance.prototype.equals = function (instance) { 1261 if (instance instanceof JSONInstance) { 1262 return this._value === instance._value; 1263 } 1264 //else 1265 return this._value === instance; 1266 }; 1267 1268 /** 1269 * Warning: Not a generic clone function 1270 * Produces a JSV acceptable clone 1271 */ 1272 1273 function clone(obj, deep) { 1274 var newObj, x; 1275 1276 if (obj instanceof JSONInstance) { 1277 obj = obj.getValue(); 1278 } 1279 1280 switch (typeOf(obj)) { 1281 case "object": 1282 if (deep) { 1283 newObj = {}; 1284 for (x in obj) { 1285 if (obj[x] !== O[x]) { 1286 newObj[x] = clone(obj[x], deep); 1287 } 1288 } 1289 return newObj; 1290 } else { 1291 return createObject(obj); 1292 } 1293 break; 1294 case "array": 1295 if (deep) { 1296 newObj = new Array(obj.length); 1297 x = obj.length; 1298 while (--x >= 0) { 1299 newObj[x] = clone(obj[x], deep); 1300 } 1301 return newObj; 1302 } else { 1303 return Array.prototype.slice.call(obj); 1304 } 1305 break; 1306 default: 1307 return obj; 1308 } 1309 } 1310 1311 /** 1312 * This class binds a {@link JSONInstance} with a {@link JSONSchema} to provided context aware methods. 1313 * 1314 * @name JSONSchema 1315 * @class 1316 * @param {Environment} env The environment this schema belongs to 1317 * @param {JSONInstance|Any} json The value of the schema 1318 * @param {String} [uri] The URI of the schema. If undefined, the URI will be a randomly generated UUID. 1319 * @param {JSONSchema|Boolean} [schema] The schema to bind to the instance. If <code>undefined</code>, the environment's default schema will be used. If <code>true</code>, the instance's schema will be itself. 1320 * @extends JSONInstance 1321 */ 1322 1323 function JSONSchema(env, json, uri, schema) { 1324 var fr; 1325 JSONInstance.call(this, env, json, uri); 1326 1327 if (schema === true) { 1328 this._schema = this; 1329 } else if (json instanceof JSONSchema && !(schema instanceof JSONSchema)) { 1330 this._schema = json._schema; //TODO: Make sure cross environments don't mess everything up 1331 } else { 1332 this._schema = schema instanceof JSONSchema ? schema : this._env.getDefaultSchema() || JSONSchema.createEmptySchema(this._env); 1333 } 1334 1335 //determine fragment delimiter from schema 1336 fr = this._schema.getValueOfProperty("fragmentResolution"); 1337 if (fr === "dot-delimited") { 1338 this._fd = "."; 1339 } else if (fr === "slash-delimited") { 1340 this._fd = "/"; 1341 } 1342 } 1343
1344 JSONSchema.prototype = createObject(JSONInstance.prototype); 1345 1346 /** 1347 * Creates an empty schema. 1348 * 1349 * @param {Environment} env The environment of the schema 1350 * @returns {JSONSchema} The empty schema, who's schema is itself. 1351 */ 1352 1353 JSONSchema.createEmptySchema = function (env) { 1354 var schema = createObject(JSONSchema.prototype); 1355 JSONInstance.call(schema, env, {}, undefined, undefined); 1356 schema._schema = schema; 1357 return schema; 1358 }; 1359 1360 /** 1361 * Returns the schema of the schema. 1362 * 1363 * @returns {JSONSchema} The schema of the schema 1364 */ 1365 1366 JSONSchema.prototype.getSchema = function () { 1367 return this._schema; 1368 }; 1369 1370 /** 1371 * Returns the value of the provided attribute name. 1372 * <p> 1373 * This method is different from {@link JSONInstance#getProperty} as the named property 1374 * is converted using a parser defined by the schema's schema before being returned. This 1375 * makes the return value of this method attribute dependent. 1376 * </p> 1377 * 1378 * @param {String} key The name of the attribute 1379 * @param {Any} [arg] Some attribute parsers accept special arguments for returning resolved values. This is attribute dependent. 1380 * @returns {JSONSchema|Any} The value of the attribute 1381 */ 1382 1383 JSONSchema.prototype.getAttribute = function (key, arg) { 1384 var schemaProperty, parser, property, result; 1385 1386 if (!arg && this._attributes && this._attributes.hasOwnProperty(key)) { 1387 return this._attributes[key]; 1388 } 1389 1390 schemaProperty = this._schema.getProperty("properties").getProperty(key); 1391 parser = schemaProperty.getValueOfProperty("parser"); 1392 property = this.getProperty(key); 1393 if (typeof parser === "function") { 1394 result = parser(property, schemaProperty, arg); 1395 if (!arg && this._attributes) { 1396 this._attributes[key] = result; 1397 } 1398 return result; 1399 } 1400 //else 1401 return property.getValue(); 1402 }; 1403 1404 /** 1405 * Returns all the attributes of the schema. 1406 * 1407 * @returns {Object} A map of all parsed attribute values 1408 */ 1409 1410 JSONSchema.prototype.getAttributes = function () { 1411 var properties, schemaProperties, key, schemaProperty, parser; 1412 1413 if (!this._attributes && this.getType() === "object") { 1414 properties = this.getProperties(); 1415 schemaProperties = this._schema.getProperty("properties"); 1416 this._attributes = {}; 1417 for (key in properties) { 1418 if (properties[key] !== O[key]) { 1419 schemaProperty = schemaProperties && schemaProperties.getProperty(key); 1420 parser = schemaProperty && schemaProperty.getValueOfProperty("parser"); 1421 if (typeof parser === "function") { 1422 this._attributes[key] = parser(properties[key], schemaProperty); 1423 } else { 1424 this._attributes[key] = properties[key].getValue(); 1425 } 1426 } 1427 } 1428 } 1429 1430 return clone(this._attributes, false); 1431 }; 1432 1433 /** 1434 * Convenience method for retrieving a link or link object from a schema. 1435 * This method is the same as calling <code>schema.getAttribute("links", [rel, instance])[0];</code>. 1436 * 1437 * @param {String} rel The link relationship 1438 * @param {JSONInstance} [instance] The instance to resolve any URIs from 1439 * @returns {String|Object|undefined} If <code>instance</code> is provided, a string containing the resolve URI of the link is returned. 1440 * If <code>instance</code> is not provided, a link object is returned with details of the link. 1441 * If no link with the provided relationship exists, <code>undefined</code> is returned. 1442 * @see JSONSchema#getAttribute 1443 */ 1444 1445 JSONSchema.prototype.getLink = function (rel, instance) { 1446 var schemaLinks = this.getAttribute("links", [rel, instance]); 1447 if (schemaLinks && schemaLinks.length && schemaLinks[schemaLinks.length - 1]) { 1448 return schemaLinks[schemaLinks.length - 1]; 1449 } 1450 }; 1451 1452 /** 1453 * Validates the provided instance against the target schema and returns a {@link Report}. 1454 * 1455 * @param {JSONInstance|Any} instance The instance to validate; may be a {@link JSONInstance} or any JavaScript value 1456 * @param {Report} [report] A {@link Report} to concatenate the result of the validation to. If <code>undefined</code>, a new {@link Report} is created. 1457 * @param {JSONInstance} [parent] The parent/containing instance of the provided instance 1458 * @param {JSONSchema} [parentSchema] The schema of the parent/containing instance 1459 * @param {String} [name] The name of the parent object's property that references the instance 1460 * @returns {Report} The result of the validation 1461 */ 1462 1463 JSONSchema.prototype.validate = function (instance, report, parent, parentSchema, name) {
1464 var validator = this._schema.getValueOfProperty("validator"); 1465 1466 if (!(instance instanceof JSONInstance)) { 1467 instance = this.getEnvironment().createInstance(instance); 1468 } 1469 1470 if (!(report instanceof Report)) { 1471 report = new Report(); 1472 } 1473 1474 if (typeof validator === "function" && !report.isValidatedBy(instance.getURI(), this.getURI())) { 1475 report.registerValidation(instance.getURI(), this.getURI()); 1476 validator(instance, this, this._schema, report, parent, parentSchema, name); 1477 } 1478 1479 return report; 1480 }; 1481 1482 /** 1483 * Merges two schemas/instances together. 1484 */ 1485 1486 function inherits(base, extra, extension) { 1487 var baseType = typeOf(base), 1488 extraType = typeOf(extra), 1489 child, x; 1490 1491 if (extraType === "undefined") { 1492 return clone(base, true); 1493 } else if (baseType === "undefined" || extraType !== baseType) { 1494 return clone(extra, true); 1495 } else if (extraType === "object") { 1496 if (base instanceof JSONSchema) { 1497 base = base.getAttributes(); 1498 } 1499 if (extra instanceof JSONSchema) { 1500 extra = extra.getAttributes(); 1501 if (extra["extends"] && extension && extra["extends"] instanceof JSONSchema) { 1502 extra["extends"] = [ extra["extends"] ]; 1503 } 1504 } 1505 child = clone(base, true); //this could be optimized as some properties get overwritten 1506 for (x in extra) { 1507 if (extra[x] !== O[x]) { 1508 child[x] = inherits(base[x], extra[x], extension); 1509 } 1510 } 1511 return child; 1512 } else { 1513 return clone(extra, true); 1514 } 1515 } 1516 1517 /** 1518 * An Environment is a sandbox of schemas thats behavior is different from other environments. 1519 * 1520 * @name Environment 1521 * @class 1522 */ 1523 1524 function Environment() { 1525 this._id = randomUUID(); 1526 this._schemas = {}; 1527 this._options = {}; 1528 } 1529 1530 /** 1531 * Returns a clone of the target environment. 1532 * 1533 * @returns {Environment} A new {@link Environment} that is a exact copy of the target environment 1534 */ 1535 1536 Environment.prototype.clone = function () { 1537 var env = new Environment(); 1538 env._schemas = createObject(this._schemas); 1539 env._options = createObject(this._options); 1540 1541 return env; 1542 }; 1543 1544 /** 1545 * Returns a new {@link JSONInstance} of the provided data. 1546 * 1547 * @param {JSONInstance|Any} data The value of the instance 1548 * @param {String} [uri] The URI of the instance. If undefined, the URI will be a randomly generated UUID. 1549 * @returns {JSONInstance} A new {@link JSONInstance} from the provided data 1550 */ 1551 1552 Environment.prototype.createInstance = function (data, uri) { 1553 var instance; 1554 uri = formatURI(uri); 1555 1556 if (data instanceof JSONInstance && (!uri || data.getURI() === uri)) { 1557 return data; 1558 } 1559 //else 1560 instance = new JSONInstance(this, data, uri); 1561 1562 return instance; 1563 }; 1564 1565 /** 1566 * Creates a new {@link JSONSchema} from the provided data, and registers it with the environment. 1567 * 1568 * @param {JSONInstance|Any} data The value of the schema 1569 * @param {JSONSchema|Boolean} [schema] The schema to bind to the instance. If <code>undefined</code>, the environment's default schema will be used. If <code>true</code>, the instance's schema will be itself. 1570 * @param {String} [uri] The URI of the schema. If undefined, the URI will be a randomly generated UUID. 1571 * @returns {JSONSchema} A new {@link JSONSchema} from the provided data 1572 * @throws {InitializationError} If a schema that is not registered with the environment is referenced 1573 */ 1574 1575 Environment.prototype.createSchema = function (data, schema, uri) { 1576 var instance, 1577 initializer; 1578 uri = formatURI(uri); 1579 1580 if (data instanceof JSONSchema && (!uri || data._uri === uri) && (!schema || data._schema.equals(schema))) { 1581 return data; 1582 } 1583 1584 instance = new JSONSchema(this, data, uri, schema); 1585 1586 initializer = instance.getSchema().getValueOfProperty("initializer"); 1587 if (typeof initializer === "function") { 1588 instance = initializer(instance); 1589 } 1590 1591 //register schema 1592 this._schemas[instance._uri] = instance; 1593 this._schemas[uri] = instance; 1594 1595 //build & cache the rest of the schema 1596 instance.getAttributes(); 1597 1598 return instance; 1599 }; 1600 1601 /** 1602 * Creates an empty schema. 1603 * 1604 * @param {Environment} env The environment of the schema 1605 * @returns {JSONSchema}
1605 The empty schema, who's schema is itself. 1606 */ 1607 1608 Environment.prototype.createEmptySchema = function () { 1609 var schema = JSONSchema.createEmptySchema(this); 1610 this._schemas[schema._uri] = schema; 1611 return schema; 1612 }; 1613 1614 /** 1615 * Returns the schema registered with the provided URI. 1616 * 1617 * @param {String} uri The absolute URI of the required schema 1618 * @returns {JSONSchema|undefined} The request schema, or <code>undefined</code> if not found 1619 */ 1620 1621 Environment.prototype.findSchema = function (uri) { 1622 return this._schemas[formatURI(uri)]; 1623 }; 1624 1625 /** 1626 * Sets the specified environment option to the specified value. 1627 * 1628 * @param {String} name The name of the environment option to set 1629 * @param {Any} value The new value of the environment option 1630 */ 1631 1632 Environment.prototype.setOption = function (name, value) { 1633 this._options[name] = value; 1634 }; 1635 1636 /** 1637 * Returns the specified environment option. 1638 * 1639 * @param {String} name The name of the environment option to set 1640 * @returns {Any} The value of the environment option 1641 */ 1642 1643 Environment.prototype.getOption = function (name) { 1644 return this._options[name]; 1645 }; 1646 1647 /** 1648 * Sets the default fragment delimiter of the environment. 1649 * 1650 * @deprecated Use {@link Environment#setOption} with option "defaultFragmentDelimiter" 1651 * @param {String} fd The fragment delimiter character 1652 */ 1653 1654 Environment.prototype.setDefaultFragmentDelimiter = function (fd) { 1655 if (typeof fd === "string" && fd.length > 0) { 1656 this._options["defaultFragmentDelimiter"] = fd; 1657 } 1658 }; 1659 1660 /** 1661 * Returns the default fragment delimiter of the environment. 1662 * 1663 * @deprecated Use {@link Environment#getOption} with option "defaultFragmentDelimiter" 1664 * @returns {String} The fragment delimiter character 1665 */ 1666 1667 Environment.prototype.getDefaultFragmentDelimiter = function () { 1668 return this._options["defaultFragmentDelimiter"]; 1669 }; 1670 1671 /** 1672 * Sets the URI of the default schema for the environment. 1673 * 1674 * @deprecated Use {@link Environment#setOption} with option "defaultSchemaURI" 1675 * @param {String} uri The default schema URI 1676 */ 1677 1678 Environment.prototype.setDefaultSchemaURI = function (uri) { 1679 if (typeof uri === "string") { 1680 this._options["defaultSchemaURI"] = formatURI(uri); 1681 } 1682 }; 1683 1684 /** 1685 * Returns the default schema of the environment. 1686 * 1687 * @returns {JSONSchema} The default schema 1688 */ 1689 1690 Environment.prototype.getDefaultSchema = function () { 1691 return this.findSchema(this._options["defaultSchemaURI"]); 1692 }; 1693 1694 /** 1695 * Validates both the provided schema and the provided instance, and returns a {@link Report}. 1696 * If the schema fails to validate, the instance will not be validated. 1697 * 1698 * @param {JSONInstance|Any} instanceJSON The {@link JSONInstance} or JavaScript value to validate. 1699 * @param {JSONSchema|Any} schemaJSON The {@link JSONSchema} or JavaScript value to use in the validation. This will also be validated againt the schema's schema. 1700 * @returns {Report} The result of the validation 1701 */ 1702 1703 Environment.prototype.validate = function (instanceJSON, schemaJSON) { 1704 var instance, 1705 schema, 1706 schemaSchema, 1707 report = new Report(); 1708 1709 try { 1710 instance = this.createInstance(instanceJSON); 1711 report.instance = instance; 1712 } catch (e) { 1713 report.addError(e.uri, e.schemaUri, e.attribute, e.message, e.details); 1714 } 1715 1716 try { 1717 schema = this.createSchema(schemaJSON); 1718 report.schema = schema; 1719 1720 schemaSchema = schema.getSchema(); 1721 report.schemaSchema = schemaSchema; 1722 } catch (e) { 1723 report.addError(e.uri, e.schemaUri, e.attribute, e.message, e.details); 1724 } 1725 1726 if (schemaSchema) { 1727 schemaSchema.validate(schema, report); 1728 } 1729 1730 if (report.errors.length) { 1731 return report; 1732 } 1733 1734 return schema.validate(instance, report); 1735 }; 1736 1737 /** 1738 * @private 1739 */ 1740 1741 Environment.prototype._checkForInvalidInstances = function (stackSize, schemaURI) { 1742 var result = [], 1743 stack = [ 1744 [schemaURI, this._schemas[schemaURI]] 1745 ], 1746 counter = 0, 1747 item, uri, instance, schema, properties, key; 1748 1749 while (counter++ < stackSize && stack.length) { 1750 item = stack.shift(); 1751 uri = item[0]; 1752 instance = item[1]; 1753 1754 if (instance instanceof JSONSchema) { 1755 if (this._schemas[instance._uri] !== instance) { 1756 result.push("Instance " + uri + " does not match " + instance._uri); 1757 } else { 1758 //schema = instance.getSchema(); 1759 //stack.push([uri + "/{schema}", schema]); 1760 1761 properties = instance.getAttributes(); 1762 for (key in properties) { 1763 if (properties[key] !== O[key]) { 1764 stack.push([uri + "/" + escapeURIComponent(key), properties[key]]); 1765 } 1766 } 1767 } 1768 } else if (typeOf(instance) === "object") { 1769 properties = instance; 1770 for (key in properties) { 1771 if (properties.hasOwnProperty(key)) { 1772 stack.push([uri + "/" + escapeURIComponent(key), properties[key]]); 1773 } 1774 } 1775 } else if (typeOf(instance) === "array") { 1776 properties = instance; 1777 for (key = 0; key < properties.length; ++key) { 1778 stack.push([uri + "/" + escapeURIComponent(key), properties[key]]); 1779 } 1780 } 1781 } 1782 1783 return result.length ? result : counter; 1784 }; 1785 1786 /** 1787 * A globaly accessible object that provides the ability to create and manage {@link Environments}, 1788 * as well as providing utility methods. 1789 * 1790 * @namespace 1791 */ 1792 1793 JSV = { 1794 _environments : {}, 1795 _defaultEnvironmentID : "", 1796 1797 /** 1798 * Returns if the provide value is an instance of {@link JSONInstance}. 1799 * 1800 * @param o The value to test 1801 * @returns {Boolean} If the provide value is an instance of {@link JSONInstance} 1802 */ 1803 1804 isJSONInstance : function (o) { 1805 return o instanceof JSONInstance; 1806 }, 1807 1808 /** 1809 * Returns if the provide value is an instance of {@link JSONSchema}. 1810 * 1811 * @param o The value to test 1812 * @returns {Boolean} If the provide value is an instance of {@link JSONSchema} 1813 */ 1814 1815 isJSONSchema : function (o) { 1816 return o instanceof JSONSchema; 1817 }, 1818 1819 /** 1820 * Creates and returns a new {@link Environment} that is a clone of the environment registered with the provided ID. 1821 * If no environment ID is provided, the default environment is cloned. 1822 * 1823 * @param {String} [id] The ID of the environment to clone. If <code>undefined</code>, the default environment ID is used. 1824 * @returns {Environment} A newly cloned {@link Environment} 1825 * @throws {Error} If there is no environment registered with the provided ID 1826 */ 1827 1828 createEnvironment : function (id) { 1829 id = id || this._defaultEnvironmentID; 1830 1831 if (!this._environments[id]) { 1832 throw new Error("Unknown Environment ID"); 1833 } 1834 //else 1835 return this._environments[id].clone(); 1836 }, 1837 1838 Environment : Environment, 1839 1840 /** 1841 * Registers the provided {@link Environment} with the provided ID. 1842 * 1843 * @param {String} id The ID of the environment 1844 * @param {Environment} env The environment to register 1845 */ 1846 1847 registerEnvironment : function (id, env) { 1848 id = id || (env || 0)._id; 1849 if (id && !this._environments[id] && env instanceof Environment) { 1850 env._id = id; 1851 this._environments[id] = env; 1852 } 1853 }, 1854 1855 /** 1856 * Sets which registered ID is the default environment. 1857 * 1858 * @param {String} id The ID of the registered environment that is default 1859 * @throws {Error} If there is no registered environment with the provided ID 1860 */ 1861 1862 setDefaultEnvironmentID : function (id) { 1863 if (typeof id === "string") { 1864 if (!this._environments[id]) { 1865 throw new Error("Unknown Environment ID"); 1866 } 1867 1868 this._defaultEnvironmentID = id; 1869 } 1870 }, 1871 1872 /** 1873 * Returns the ID of the default environment. 1874 * 1875 * @returns {String} The ID of the default environment 1876 */ 1877 1878 getDefaultEnvironmentID : function () { 1879 return this._defaultEnvironmentID; 1880 }, 1881 1882 // 1883 // Utility Functions 1884 // 1885 1886 /** 1887 * Returns the name of the type of the provided value. 1888 * 1889 * @event //utility 1890 * @param {Any} o The value to determine the type of 1891 * @returns {String} The name of the type of the value 1892 */ 1893 typeOf : typeOf, 1894 1895 /** 1896 * Return a new object that inherits all of the properties of the provided object. 1897 * 1898 * @event //utility 1899 * @param {Object} proto The prototype of the new object 1900 * @returns {Object} A new object that inherits all of the properties of the provided object 1901 */ 1902 createObject : createObject, 1903 1904 /** 1905 * Returns a new object with each property transformed by the iterator. 1906 * 1907 * @event //utility 1908 * @param {Object} obj The object to transform 1909 * @param {Function} iterator A function that returns the new value of the provided property 1910 * @param {Object} [scope] The value of <code>this</code> in the iterator 1911 * @returns {Object} A new object with each property transformed 1912 */ 1913 mapObject : mapObject, 1914 1915 /** 1916 * Returns a new array with each item transformed by the iterator. 1917 * 1918 * @event //utility 1919 * @param {Array} arr The array to transform 1920 * @param {Function} iterator A function that returns the new value of the provided item 1921 * @param {Object} scope The value of <code>this</code> in the iterator 1922 * @returns {Array} A new array with each item transformed 1923 */ 1924 mapArray : mapArray, 1925 1926 /** 1927 * Returns a new array that only contains the items allowed by the iterator. 1928 * 1929 * @event //utility 1930 * @param {Array} arr The array to filter 1931 * @param {Function} iterator The function that returns true if the provided property should be added to the array 1932 * @param {Object} scope The value of <code>this</code> within the iterator 1933 * @returns {Array} A new array that contains the items allowed by the iterator 1934 */ 1935 filterArray : filterArray, 1936 1937 /** 1938 * Returns the first index in the array that the provided item is located at. 1939 * 1940 * @event //utility 1941 * @param {Array} arr The array to search 1942 * @param {Any} o The item being searched for 1943 * @returns {Number} The index of the item in the array, or <code>-1</code> if not found 1944 */ 1945 searchArray : searchArray, 1946 1947 /** 1948 * Returns an array representation of a value. 1949 * <ul> 1950 * <li>For array-like objects, the value will be casted as an Array type.</li> 1951 * <li>If an array is provided, the function will simply return the same array.</li> 1952 * <li>For a null or undefined value, the result will be an empty Array.</li> 1953 * <li>For all other values, the value will be the first element in a new Array. </li> 1954 * </ul> 1955 * 1956 * @event //utility 1957 * @param {Any} o The value to convert into an array 1958 * @returns {Array} The value as an array 1959 */ 1960 toArray : toArray, 1961 1962 /** 1963 * Returns an array of the names of all properties of an object. 1964 * 1965 * @event //utility 1966 * @param {Object|Array} o The object in question 1967 * @returns {Array} The names of all properties 1968 */ 1969 keys : keys, 1970 1971 /**
1972 * Mutates the array by pushing the provided value onto the array only if it is not already there. 1973 * 1974 * @event //utility 1975 * @param {Array} arr The array to modify 1976 * @param {Any} o The object to add to the array if it is not already there 1977 * @returns {Array} The provided array for chaining 1978 */ 1979 pushUnique : pushUnique, 1980 1981 /** 1982 * Mutates the array by removing the first item that matches the provided value in the array. 1983 * 1984 * @event //utility 1985 * @param {Array} arr The array to modify 1986 * @param {Any} o The object to remove from the array 1987 * @returns {Array} The provided array for chaining 1988 */ 1989 popFirst : popFirst, 1990 1991 /** 1992 * Creates a copy of the target object. 1993 * <p> 1994 * This method will create a new instance of the target, and then mixin the properties of the target. 1995 * If <code>deep</code> is <code>true</code>, then each property will be cloned before mixin. 1996 * </p> 1997 * <p><b>Warning</b>: This is not a generic clone function, as it will only properly clone objects and arrays.</p> 1998 * 1999 * @event //utility 2000 * @param {Any} o The value to clone 2001 * @param {Boolean} [deep=false] If each property should be recursively cloned 2002 * @returns A cloned copy of the provided value 2003 */ 2004 clone : clone, 2005 2006 /** 2007 * Generates a pseudo-random UUID. 2008 * 2009 * @event //utility 2010 * @returns {String} A new universally unique ID 2011 */ 2012 randomUUID : randomUUID, 2013 2014 /** 2015 * Properly escapes a URI component for embedding into a URI string. 2016 * 2017 * @event //utility 2018 * @param {String} str The URI component to escape 2019 * @returns {String} The escaped URI component 2020 */ 2021 escapeURIComponent : escapeURIComponent, 2022 2023 /** 2024 * Returns a URI that is formated for JSV. Currently, this only ensures that the URI ends with a hash tag (<code>#</code>). 2025 * 2026 * @event //utility 2027 * @param {String} uri The URI to format 2028 * @returns {String} The URI formatted for JSV 2029 */ 2030 formatURI : formatURI, 2031 2032 /** 2033 * Merges two schemas/instance together. 2034 * 2035 * @event //utility 2036 * @param {JSONSchema|Any} base The old value to merge 2037 * @param {JSONSchema|Any} extra The new value to merge 2038 * @param {Boolean} extension If the merge is a JSON Schema extension 2039 * @return {Any} The modified base value 2040 */ 2041 2042 inherits : inherits 2043 }; 2044 2045 this.JSV = JSV; //set global object 2046 exports.JSV = JSV; //export to CommonJS 2047 2048 require("./environments"); //load default environments 2049 2050}()); 2051 2052 2053 2054 2055 2056/** 2057 * json-schema-draft-03 Environment 2058 * 2059 * @fileOverview Implementation of the third revision of the JSON Schema specification draft. 2060 * @author <a href="mailto:[email protected]">Gary Court</a> 2061 * @version 1.3 2062 * @see http://github.com/garycourt/JSV 2063 */ 2064 2065/* 2066 * Copyright 2010 Gary Court. All rights reserved. 2067 * 2068 * Redistribution and use in source and binary forms, with or without modification, are 2069 * permitted provided that the following conditions are met: 2070 * 2071 * 1. Redistributions of source code must retain the above copyright notice, this list of 2072 * conditions and the following disclaimer. 2073 * 2074 * 2. Redistributions in binary form must reproduce the above copyright notice, this list 2075 * of conditions and the following disclaimer in the documentation and/or other materials 2076 * provided with the distribution. 2077 * 2078 * THIS SOFTWARE IS PROVIDED BY GARY COURT ``AS IS'' AND ANY EXPRESS OR IMPLIED 2079 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 2080 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARY COURT OR 2081 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2082 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 2083 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 2084 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 2085 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 2086 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2087 *
2088 * The views and conclusions contained in the software and documentation are those of the 2089 * authors and should not be interpreted as representing official policies, either expressed 2090 * or implied, of Gary Court or the JSON Schema specification. 2091 */ 2092 2093/*jslint white: true, sub: true, onevar: true, undef: true, eqeqeq: true, newcap: true, immed: true, indent: 4 */ 2094/*global require */ 2095 2096(function () { 2097 var O = {}, 2098 JSV = require('./jsv').JSV, 2099 InitializationError, 2100 TYPE_VALIDATORS, 2101 ENVIRONMENT, 2102 SCHEMA_00_JSON, 2103 HYPERSCHEMA_00_JSON, 2104 LINKS_00_JSON, 2105 SCHEMA_00, 2106 HYPERSCHEMA_00, 2107 LINKS_00, 2108 SCHEMA_01_JSON, 2109 HYPERSCHEMA_01_JSON, 2110 LINKS_01_JSON, 2111 SCHEMA_01, 2112 HYPERSCHEMA_01, 2113 LINKS_01, 2114 SCHEMA_02_JSON, 2115 HYPERSCHEMA_02_JSON, 2116 LINKS_02_JSON, 2117 SCHEMA_02, 2118 HYPERSCHEMA_02, 2119 LINKS_02, 2120 SCHEMA_03_JSON, 2121 HYPERSCHEMA_03_JSON, 2122 LINKS_03_JSON, 2123 SCHEMA_03, 2124 HYPERSCHEMA_03, 2125 LINKS_03; 2126 2127 InitializationError = function InitializationError(instance, schema, attr, message, details) { 2128 Error.call(this, message); 2129 2130 this.uri = instance.getURI(); 2131 this.schemaUri = schema.getURI(); 2132 this.attribute = attr; 2133 this.message = message; 2134 this.description = message; //IE 2135 this.details = details; 2136 } 2137 InitializationError.prototype = new Error(); 2138 InitializationError.prototype.constructor = InitializationError; 2139 InitializationError.prototype.name = "InitializationError"; 2140 2141 TYPE_VALIDATORS = { 2142 "string" : function (instance, report) { 2143 return instance.getType() === "string"; 2144 }, 2145 2146 "number" : function (instance, report) { 2147 return instance.getType() === "number"; 2148 }, 2149 2150 "integer" : function (instance, report) { 2151 return instance.getType() === "number" && instance.getValue() % 1 === 0; 2152 }, 2153 2154 "boolean" : function (instance, report) { 2155 return instance.getType() === "boolean"; 2156 }, 2157 2158 "object" : function (instance, report) { 2159 return instance.getType() === "object"; 2160 }, 2161 2162 "array" : function (instance, report) { 2163 return instance.getType() === "array"; 2164 }, 2165 2166 "null" : function (instance, report) { 2167 return instance.getType() === "null"; 2168 }, 2169 2170 "any" : function (instance, report) { 2171 return true; 2172 } 2173 }; 2174 2175 ENVIRONMENT = new JSV.Environment(); 2176 ENVIRONMENT.setOption("strict", false); 2177 ENVIRONMENT.setOption("validateReferences", false); //updated later 2178 2179 // 2180 // draft-00 2181 // 2182 2183 SCHEMA_00_JSON = { 2184 "$schema" : "http://json-schema.org/draft-00/hyper-schema#", 2185 "id" : "http://json-schema.org/draft-00/schema#", 2186 "type" : "object", 2187 2188 "properties" : { 2189 "type" : { 2190 "type" : ["string", "array"], 2191 "items" : { 2192 "type" : ["string", {"$ref" : "#"}] 2193 }, 2194 "optional" : true, 2195 "uniqueItems" : true, 2196 "default" : "any", 2197 2198 "parser" : function (instance, self) { 2199 var parser; 2200 2201 if (instance.getType() === "string") { 2202 return instance.getValue(); 2203 } else if (instance.getType() === "object") { 2204 return instance.getEnvironment().createSchema( 2205 instance, 2206 self.getEnvironment().findSchema(self.resolveURI("#")) 2207 ); 2208 } else if (instance.getType() === "array") { 2209 parser = self.getValueOfProperty("parser"); 2210 return JSV.mapArray(instance.getProperties(), function (prop) { 2211 return parser(prop, self); 2212 }); 2213 } 2214 //else 2215 return "any"; 2216 }, 2217 2218 "validator" : function (instance, schema, self, report, parent, parentSchema, name) { 2219 var requiredTypes = JSV.toArray(schema.getAttribute("type")), 2220 x, xl, type, subreport, typeValidators; 2221 2222 //for instances that are required to be a certain type 2223 if (instance.getType() !== "undefined" && requiredTypes && requiredTypes.length) { 2224 typeValidators = self.getValueOfProperty("typeValidators") || {}; 2225 2226 //ensure that type matches for at least one of the required types 2227 for (x = 0, xl = requiredTypes.length; x < xl; ++x) { 2228 type = requiredTypes[x]; 2229 if (JSV.isJSONSchema(type)) { 2230 subreport = JSV.createObject(report); 2231 subreport.errors = []; 2232 subreport.validated = JSV.clone(report.validated); 2233 if (type.validate(instance, subreport, parent, parentSchema, name).errors.length === 0) { 2234 return true;
2234 //instance matches this schema 2235 } 2236 } else { 2237 if (typeValidators[type] !== O[type] && typeof typeValidators[type] === "function") { 2238 if (typeValidators[type](instance, report)) { 2239 return true; //type is valid 2240 } 2241 } else { 2242 return true; //unknown types are assumed valid 2243 } 2244 } 2245 } 2246 2247 //if we get to this point, type is invalid 2248 report.addError(instance, schema, "type", "Instance is not a required type", requiredTypes); 2249 return false; 2250 } 2251 //else, anything is allowed if no type is specified 2252 return true; 2253 }, 2254 2255 "typeValidators" : TYPE_VALIDATORS 2256 }, 2257 2258 "properties" : { 2259 "type" : "object", 2260 "additionalProperties" : {"$ref" : "#"}, 2261 "optional" : true, 2262 "default" : {}, 2263 2264 "parser" : function (instance, self, arg) { 2265 var env = instance.getEnvironment(), 2266 selfEnv = self.getEnvironment(); 2267 if (instance.getType() === "object") { 2268 if (arg) { 2269 return env.createSchema(instance.getProperty(arg), selfEnv.findSchema(self.resolveURI("#"))); 2270 } else { 2271 return JSV.mapObject(instance.getProperties(), function (instance) { 2272 return env.createSchema(instance, selfEnv.findSchema(self.resolveURI("#"))); 2273 }); 2274 } 2275 } 2276 //else 2277 return {}; 2278 }, 2279 2280 "validator" : function (instance, schema, self, report, parent, parentSchema, name) { 2281 var propertySchemas, key; 2282 //this attribute is for object type instances only 2283 if (instance.getType() === "object") { 2284 //for each property defined in the schema 2285 propertySchemas = schema.getAttribute("properties"); 2286 for (key in propertySchemas) { 2287 if (propertySchemas[key] !== O[key] && propertySchemas[key]) { 2288 //ensure that instance property is valid 2289 propertySchemas[key].validate(instance.getProperty(key), report, instance, schema, key); 2290 } 2291 } 2292 } 2293 } 2294 }, 2295 2296 "items" : { 2297 "type" : [{"$ref" : "#"}, "array"], 2298 "items" : {"$ref" : "#"}, 2299 "optional" : true, 2300 "default" : {}, 2301 2302 "parser" : function (instance, self) { 2303 if (instance.getType() === "object") { 2304 return instance.getEnvironment().createSchema(instance, self.getEnvironment().findSchema(self.resolveURI("#"))); 2305 } else if (instance.getType() === "array") { 2306 return JSV.mapArray(instance.getProperties(), function (instance) { 2307 return instance.getEnvironment().createSchema(instance, self.getEnvironment().findSchema(self.resolveURI("#"))); 2308 }); 2309 } 2310 //else 2311 return instance.getEnvironment().createEmptySchema(); 2312 }, 2313 2314 "validator" : function (instance, schema, self, report, parent, parentSchema, name) { 2315 var properties, items, x, xl, itemSchema, additionalProperties; 2316 2317 if (instance.getType() === "array") { 2318 properties = instance.getProperties(); 2319 items = schema.getAttribute("items"); 2320 additionalProperties = schema.getAttribute("additionalProperties"); 2321 2322 if (JSV.typeOf(items) === "array") { 2323 for (x = 0, xl = properties.length; x < xl; ++x) { 2324 itemSchema = items[x] || additionalProperties; 2325 if (itemSchema !== false) { 2326 itemSchema.validate(properties[x], report, instance, schema, x); 2327 } else { 2328 report.addError(instance, schema, "additionalProperties", "Additional items are not allowed", itemSchema); 2329 } 2330 } 2331 } else { 2332 itemSchema = items || additionalProperties; 2333 for (x = 0, xl = properties.length; x < xl; ++x) { 2334 itemSchema.validate(properties[x], report, instance, schema, x); 2335 } 2336 } 2337 } 2338 } 2339 }, 2340 2341 "optional" : { 2342 "type" : "boolean", 2343 "optional" : true, 2344 "default" : false, 2345 2346 "parser" : function (instance, self) { 2347 return !!instance.getValue(); 2348 }, 2349 2350 "validator" : function (instance, schema, self, report, parent, parentSchema, name) { 2351 if (instance.getType() === "undefined" && !schema.getAttribute("optional")) { 2352 report.addError(instance, schema, "optional", "Property is required", false); 2353 } 2354 }, 2355 2356 "validationRequired" : true 2357 }, 2358 2359 "additionalProperties" : { 2360 "type" : [{"$ref" : "#"}, "boolean"], 2361 "optional" : true, 2362 "default" : {}, 2363 2364 "parser" : function (instance, self) { 2365 if (instance.getType() === "object") { 2366 return instance.getEnvironment().createSchema(instance, self.getEnvironment().findSchema(self.resolveURI("#"))); 2367 } else if (instance.getType() === "boolean" && instance.getValue() === false) { 2368 return false;
2369 } 2370 //else 2371 return instance.getEnvironment().createEmptySchema(); 2372 }, 2373 2374 "validator" : function (instance, schema, self, report, parent, parentSchema, name) { 2375 var additionalProperties, propertySchemas, properties, key; 2376 //we only need to check against object types as arrays do their own checking on this property 2377 if (instance.getType() === "object") { 2378 additionalProperties = schema.getAttribute("additionalProperties"); 2379 propertySchemas = schema.getAttribute("properties") || {}; 2380 properties = instance.getProperties(); 2381 for (key in properties) { 2382 if (properties[key] !== O[key] && properties[key] && propertySchemas[key] === O[key]) { 2383 if (JSV.isJSONSchema(additionalProperties)) { 2384 additionalProperties.validate(properties[key], report, instance, schema, key); 2385 } else if (additionalProperties === false) { 2386 report.addError(instance, schema, "additionalProperties", "Additional properties are not allowed", additionalProperties); 2387 } 2388 } 2389 } 2390 } 2391 } 2392 }, 2393 2394 "requires" : { 2395 "type" : ["string", {"$ref" : "#"}], 2396 "optional" : true, 2397 2398 "parser" : function (instance, self) { 2399 if (instance.getType() === "string") { 2400 return instance.getValue(); 2401 } else if (instance.getType() === "object") { 2402 return instance.getEnvironment().createSchema(instance, self.getEnvironment().findSchema(self.resolveURI("#"))); 2403 } 2404 }, 2405 2406 "validator" : function (instance, schema, self, report, parent, parentSchema, name) { 2407 var requires; 2408 if (instance.getType() !== "undefined" && parent && parent.getType() !== "undefined") { 2409 requires = schema.getAttribute("requires"); 2410 if (typeof requires === "string") { 2411 if (parent.getProperty(requires).getType() === "undefined") { 2412 report.addError(instance, schema, "requires", 'Property requires sibling property "' + requires + '"', requires); 2413 } 2414 } else if (JSV.isJSONSchema(requires)) { 2415 requires.validate(parent, report); //WATCH: A "requires" schema does not support the "requires" attribute 2416 } 2417 } 2418 } 2419 }, 2420 2421 "minimum" : { 2422 "type" : "number", 2423 "optional" : true, 2424 2425 "parser" : function (instance, self) { 2426 if (instance.getType() === "number") { 2427 return instance.getValue(); 2428 } 2429 }, 2430 2431 "validator" : function (instance, schema, self, report, parent, parentSchema, name) { 2432 var minimum, minimumCanEqual; 2433 if (instance.getType() === "number") { 2434 minimum = schema.getAttribute("minimum"); 2435 minimumCanEqual = schema.getAttribute("minimumCanEqual"); 2436 if (typeof minimum === "number" && (instance.getValue() < minimum || (minimumCanEqual === false && instance.getValue() === minimum))) { 2437 report.addError(instance, schema, "minimum", "Number is less then the required minimum value", minimum); 2438 } 2439 } 2440 } 2441 }, 2442 2443 "maximum" : { 2444 "type" : "number", 2445 "optional" : true, 2446 2447 "parser" : function (instance, self) { 2448 if (instance.getType() === "number") { 2449 return instance.getValue(); 2450 } 2451 }, 2452 2453 "validator" : function (instance, schema, self, report, parent, parentSchema, name) { 2454 var maximum, maximumCanEqual; 2455 if (instance.getType() === "number") { 2456 maximum = schema.getAttribute("maximum"); 2457 maximumCanEqual = schema.getAttribute("maximumCanEqual"); 2458 if (typeof maximum === "number" && (instance.getValue() > maximum || (maximumCanEqual === false && instance.getValue() === maximum))) { 2459 report.addError(instance, schema, "maximum", "Number is greater then the required maximum value", maximum); 2460 } 2461 } 2462 } 2463 }, 2464 2465 "minimumCanEqual" : { 2466 "type" : "boolean", 2467 "optional" : true, 2468 "requires" : "minimum", 2469 "default" : true, 2470 2471 "parser" : function (instance, self) { 2472 if (instance.getType() === "boolean") { 2473 return instance.getValue(); 2474 } 2475 //else 2476 return true; 2477 } 2478 }, 2479 2480 "maximumCanEqual" : { 2481 "type" : "boolean", 2482 "optional" : true, 2483 "requires" : "maximum", 2484 "default" : true, 2485 2486 "parser" : function (instance, self) { 2487 if (instance.getType() === "boolean") { 2488 return instance.getValue(); 2489 } 2490 //else 2491 return true; 2492 } 2493 }, 2494 2495 "minItems" : { 2496 "type" : "integer", 2497 "optional" : true, 2498 "minimum" : 0, 2499 "default" : 0, 2500 2501 "parser" : function (instance, self) { 2502 if (instance.getType() === "number") { 2503 return instance.getValue(); 2504 } 2505 //else 2506 return 0; 2507 }, 2508 2509 "validator" : function (instance, schema, self, report, parent, parentSchema, name) { 2510 var minItems; 2511 if (instance.getType() === "array") { 2512 minItems = schema.getAttribute("minItems");
2513 if (typeof minItems === "number" && instance.getProperties().length < minItems) { 2514 report.addError(instance, schema, "minItems", "The number of items is less then the required minimum", minItems); 2515 } 2516 } 2517 } 2518 }, 2519 2520 "maxItems" : { 2521 "type" : "integer", 2522 "optional" : true, 2523 "minimum" : 0, 2524 2525 "parser" : function (instance, self) { 2526 if (instance.getType() === "number") { 2527 return instance.getValue(); 2528 } 2529 }, 2530 2531 "validator" : function (instance, schema, self, report, parent, parentSchema, name) { 2532 var maxItems; 2533 if (instance.getType() === "array") { 2534 maxItems = schema.getAttribute("maxItems"); 2535 if (typeof maxItems === "number" && instance.getProperties().length > maxItems) { 2536 report.addError(instance, schema, "maxItems", "The number of items is greater then the required maximum", maxItems); 2537 } 2538 } 2539 } 2540 }, 2541 2542 "pattern" : { 2543 "type" : "string", 2544 "optional" : true, 2545 "format" : "regex", 2546 2547 "parser" : function (instance, self) { 2548 if (instance.getType() === "string") { 2549 try { 2550 return new RegExp(instance.getValue()); 2551 } catch (e) { 2552 return e; 2553 } 2554 } 2555 }, 2556 2557 "validator" : function (instance, schema, self, report, parent, parentSchema, name) { 2558 var pattern; 2559 try { 2560 pattern = schema.getAttribute("pattern"); 2561 if (pattern instanceof Error) { 2562 report.addError(schema, self, "pattern", "Invalid pattern", schema.getValueOfProperty("pattern")); 2563 } else if (instance.getType() === "string" && pattern && !pattern.test(instance.getValue())) { 2564 report.addError(instance, schema, "pattern", "String does not match pattern", pattern.toString()); 2565 } 2566 } catch (e) { 2567 report.addError(schema, self, "pattern", "Invalid pattern", schema.getValueOfProperty("pattern")); 2568 } 2569 } 2570 }, 2571 2572 "minLength" : { 2573 "type" : "integer", 2574 "optional" : true, 2575 "minimum" : 0, 2576 "default" : 0, 2577 2578 "parser" : function (instance, self) { 2579 if (instance.getType() === "number") { 2580 return instance.getValue(); 2581 } 2582 //else 2583 return 0; 2584 }, 2585 2586 "validator" : function (instance, schema, self, report, parent, parentSchema, name) { 2587 var minLength; 2588 if (instance.getType() === "string") { 2589 minLength = schema.getAttribute("minLength"); 2590 if (typeof minLength === "number" && instance.getValue().length < minLength) { 2591 report.addError(instance, schema, "minLength", "String is less then the required minimum length", minLength); 2592 } 2593 } 2594 } 2595 }, 2596 2597 "maxLength" : { 2598 "type" : "integer", 2599 "optional" : true, 2600 2601 "parser" : function (instance, self) { 2602 if (instance.getType() === "number") { 2603 return instance.getValue(); 2604 } 2605 }, 2606 2607 "validator" : function (instance, schema, self, report, parent, parentSchema, name) { 2608 var maxLength; 2609 if (instance.getType() === "string") { 2610 maxLength = schema.getAttribute("maxLength"); 2611 if (typeof maxLength === "number" && instance.getValue().length > maxLength) { 2612 report.addError(instance, schema, "maxLength", "String is greater then the required maximum length", maxLength); 2613 } 2614 } 2615 } 2616 }, 2617 2618 "enum" : { 2619 "type" : "array", 2620 "optional" : true, 2621 "minItems" : 1, 2622 "uniqueItems" : true, 2623 2624 "parser" : function (instance, self) { 2625 if (instance.getType() === "array") { 2626 return instance.getValue(); 2627 } 2628 }, 2629 2630 "validator" : function (instance, schema, self, report, parent, parentSchema, name) { 2631 var enums, x, xl; 2632 if (instance.getType() !== "undefined") { 2633 enums = schema.getAttribute("enum"); 2634 if (enums) { 2635 for (x = 0, xl = enums.length; x < xl; ++x) { 2636 if (instance.equals(enums[x])) { 2637 return true; 2638 } 2639 } 2640 report.addError(instance, schema, "enum", "Instance is not one of the possible values", enums); 2641 } 2642 } 2643 } 2644 }, 2645 2646 "title" : { 2647 "type" : "string", 2648 "optional" : true 2649 }, 2650 2651 "description" : { 2652 "type" : "string", 2653 "optional" : true 2654 }, 2655 2656 "format" : { 2657 "type" : "string", 2658 "optional" : true, 2659 2660 "parser" : function (instance, self) { 2661 if (instance.getType() === "string") { 2662 return instance.getValue(); 2663 } 2664 }, 2665 2666 "validator" : function (instance, schema, self, report, parent, parentSchema, name) { 2667 var format, formatValidators; 2668 if (instance.getType() === "string") { 2669 format = schema.getAttribute("format"); 2670 formatValidators = self.getValueOfProperty("formatValidators"); 2671 if (typeof format === "string" && formatValidators[format] !== O[format] && typeof formatValidators[format] === "function" && !formatValidators[format].call(this, instance, report)) { 2672 report.addError(instance, schema, "format", "String is not in the required format", format); 2673 } 2674 } 2675 }, 2676 2677 // JOSHFIRE: added a couple of format validators for "uri" and "email" 2678 "formatValidators" : { 2679 "uri": function (instance, report) { 2680 // Regular expression from @diegoperini taken from: 2681 // http://mathiasbynens.be/demo/url-regex 2682 // ... and only slightly adjusted for use in JavaScript 2683 var reUri = /^(?:(?:https?):\/\/)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]+\-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]+\-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/i; 2684 return !!reUri.test(instance._value); 2685 }, 2686 "email": function (instance, report) { 2687 // Regular expression taken from: 2688 // http://www.regular-expressions.info/email.html 2689 var reEmail = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i; 2690 return !!reEmail.test(instance._value); 2691 } 2692 } 2693 // JOSHFIRE: end of custom code 2694 // "formatValidators": {} 2695 }, 2696 2697 "contentEncoding" : { 2698 "type" : "string", 2699 "optional" : true 2700 }, 2701 2702 "default" : { 2703 "type" : "any", 2704 "optional" : true 2705 }, 2706 2707 "maxDecimal" : { 2708 "type" : "integer", 2709 "optional" : true, 2710 "minimum" : 0, 2711 2712 "parser" : function (instance, self) { 2713 if (instance.getType() === "number") { 2714 return instance.getValue(); 2715 } 2716 }, 2717 2718 "validator" : function (instance, schema, self, report, parent, parentSchema, name) { 2719 var maxDecimal, decimals; 2720 if (instance.getType() === "number") { 2721 maxDecimal = schema.getAttribute("maxDecimal"); 2722 if (typeof maxDecimal === "number") { 2723 decimals = instance.getValue().toString(10).split('.')[1]; 2724 if (decimals && decimals.length > maxDecimal) { 2725 report.addError(instance, schema, "maxDecimal", "The number of decimal places is greater then the allowed maximum", maxDecimal); 2726 } 2727 } 2728 } 2729 } 2730 }, 2731 2732 "disallow" : { 2733 "type" : ["string", "array"], 2734 "items" : {"type" : "string"}, 2735 "optional" : true, 2736 "uniqueItems" : true, 2737 2738 "parser" : function (instance, self) { 2739 if (instance.getType() === "string" || instance.getType() === "array") { 2740 return instance.getValue(); 2741 } 2742 }, 2743 2744 "validator" : function (instance, schema, self, report, parent, parentSchema, name) { 2745 var disallowedTypes = JSV.toArray(schema.getAttribute("disallow")), 2746 x, xl, key, typeValidators, subreport; 2747 2748 //for instances that are required to be a certain type 2749 if (instance.getType() !== "undefined" && disallowedTypes && disallowedTypes.length) { 2750 typeValidators = self.getValueOfProperty("typeValidators") || {}; 2751 2752 //ensure that type matches for at least one of the required types 2753 for (x = 0, xl = disallowedTypes.length; x < xl; ++x) { 2754 key = disallowedTypes[x]; 2755 if (JSV.isJSONSchema(key)) { //this is supported draft-03 and on 2756 subreport = JSV.createObject(report); 2757 subreport.errors = []; 2758 subreport.validated = JSV.clone(report.validated); 2759 if (key.validate(instance, subreport, parent, parentSchema, name).errors.length === 0) {
2760 //instance matches this schema 2761 report.addError(instance, schema, "disallow", "Instance is a disallowed type", disallowedTypes); 2762 return false; 2763 } 2764 } else if (typeValidators[key] !== O[key] && typeof typeValidators[key] === "function") { 2765 if (typeValidators[key](instance, report)) { 2766 report.addError(instance, schema, "disallow", "Instance is a disallowed type", disallowedTypes); 2767 return false; 2768 } 2769 } 2770 /* 2771 else { 2772 report.addError(instance, schema, "disallow", "Instance may be a disallowed type", disallowedTypes); 2773 return false; 2774 } 2775 */ 2776 } 2777 2778 //if we get to this point, type is valid 2779 return true; 2780 } 2781 //else, everything is allowed if no disallowed types are specified 2782 return true; 2783 }, 2784 2785 "typeValidators" : TYPE_VALIDATORS 2786 }, 2787 2788 "extends" : { 2789 "type" : [{"$ref" : "#"}, "array"], 2790 "items" : {"$ref" : "#"}, 2791 "optional" : true, 2792 "default" : {}, 2793 2794 "parser" : function (instance, self) { 2795 if (instance.getType() === "object") { 2796 return instance.getEnvironment().createSchema(instance, self.getEnvironment().findSchema(self.resolveURI("#"))); 2797 } else if (instance.getType() === "array") { 2798 return JSV.mapArray(instance.getProperties(), function (instance) { 2799 return instance.getEnvironment().createSchema(instance, self.getEnvironment().findSchema(self.resolveURI("#"))); 2800 }); 2801 } 2802 }, 2803 2804 "validator" : function (instance, schema, self, report, parent, parentSchema, name) { 2805 var extensions = schema.getAttribute("extends"), x, xl; 2806 if (extensions) { 2807 if (JSV.isJSONSchema(extensions)) { 2808 extensions.validate(instance, report, parent, parentSchema, name); 2809 } else if (JSV.typeOf(extensions) === "array") { 2810 for (x = 0, xl = extensions.length; x < xl; ++x) { 2811 extensions[x].validate(instance, report, parent, parentSchema, name); 2812 } 2813 } 2814 } 2815 } 2816 } 2817 }, 2818 2819 "optional" : true, 2820 "default" : {}, 2821 "fragmentResolution" : "dot-delimited", 2822 2823 "parser" : function (instance, self) { 2824 if (instance.getType() === "object") { 2825 return instance.getEnvironment().createSchema(instance, self); 2826 } 2827 }, 2828 2829 "validator" : function (instance, schema, self, report, parent, parentSchema, name) { 2830 var propNames = schema.getPropertyNames(), 2831 x, xl, 2832 attributeSchemas = self.getAttribute("properties"), 2833 strict = instance.getEnvironment().getOption("strict"), 2834 validator; 2835 2836 for (x in attributeSchemas) { 2837 if (attributeSchemas[x] !== O[x]) { 2838 if (attributeSchemas[x].getValueOfProperty("validationRequired")) { 2839 JSV.pushUnique(propNames, x); 2840 } 2841 if (strict && attributeSchemas[x].getValueOfProperty("deprecated")) { 2842 JSV.popFirst(propNames, x); 2843 } 2844 } 2845 } 2846 2847 for (x = 0, xl = propNames.length; x < xl; ++x) { 2848 if (attributeSchemas[propNames[x]] !== O[propNames[x]]) { 2849 validator = attributeSchemas[propNames[x]].getValueOfProperty("validator"); 2850 if (typeof validator === "function") { 2851 validator(instance, schema, attributeSchemas[propNames[x]], report, parent, parentSchema, name); 2852 } 2853 } 2854 } 2855 } 2856 }; 2857 2858 HYPERSCHEMA_00_JSON = { 2859 "$schema" : "http://json-schema.org/draft-00/hyper-schema#", 2860 "id" : "http://json-schema.org/draft-00/hyper-schema#", 2861 2862 "properties" : { 2863 "links" : { 2864 "type" : "array", 2865 "items" : {"$ref" : "links#"}, 2866 "optional" : true, 2867 2868 "parser" : function (instance, self, arg) { 2869 var links, 2870 linkSchemaURI = self.getValueOfProperty("items")["$ref"], 2871 linkSchema = self.getEnvironment().findSchema(linkSchemaURI), 2872 linkParser = linkSchema && linkSchema.getValueOfProperty("parser"), 2873 selfReferenceVariable; 2874 arg = JSV.toArray(arg); 2875 2876 if (typeof linkParser === "function") { 2877 links = JSV.mapArray(instance.getProperties(), function (link) { 2878 return linkParser(link, linkSchema); 2879 }); 2880 } else { 2881 links = JSV.toArray(instance.getValue()); 2882 } 2883 2884 if (arg[0]) { 2885 links = JSV.filterArray(links, function (link) { 2886 return link["rel"] === arg[0]; 2887 }); 2888 } 2889 2890 if (arg[1]) { 2891 selfReferenceVariable = self.getValueOfProperty("selfReferenceVariable"); 2892 links = JSV.mapArray(links, function (link) { 2893 var instance = arg[1], 2894 href = link["href"]; 2895 href = href.replace(/\{(.+)\}/g, function (str, p1, offset, s) { 2896 var value; 2897 if (p1 === selfReferenceVariable) { 2898 value = instance.getValue(); 2899 } else { 2900 value = instance.getValueOfProperty(p1); 2901 } 2902 return value !== undefined ? String(value) : ""; 2903 }); 2904 return href ? JSV.formatURI(instance.resolveURI(href)) : href; 2905 }); 2906 } 2907 2908 return links; 2909 }, 2910 2911 "selfReferenceVariable" : "-this" 2912 }, 2913 2914 "fragmentResolution" : { 2915 "type" : "string", 2916 "optional" : true, 2917 "default" : "dot-delimited" 2918 }, 2919 2920 "root" : { 2921 "type" : "boolean", 2922 "optional" : true, 2923 "default" : false 2924 }, 2925 2926 "readonly" : { 2927 "type" : "boolean", 2928 "optional" : true, 2929 "default" : false 2930 }, 2931 2932 "pathStart" : { 2933 "type" : "string", 2934 "optional" : true, 2935 "format" : "uri", 2936 2937 "validator" : function (instance, schema, self, report, parent, parentSchema, name) { 2938 var pathStart; 2939 if (instance.getType() !== "undefined") { 2940 pathStart = schema.getAttribute("pathStart"); 2941 if (typeof pathStart === "string") { 2942 //TODO: Find out what pathStart is relative to 2943 if (instance.getURI().indexOf(pathStart) !== 0) { 2944 report.addError(instance, schema, "pathStart", "Instance's URI does not start with " + pathStart, pathStart); 2945 } 2946 } 2947 } 2948 } 2949 }, 2950 2951 "mediaType" : { 2952 "type" : "string", 2953 "optional" : true, 2954 "format" : "media-type" 2955 }, 2956 2957 "alternate" : { 2958 "type" : "array", 2959 "items" : {"$ref" : "#"}, 2960 "optional" : true 2961 } 2962 }, 2963 2964 "links" : [ 2965 { 2966 "href" : "{$ref}", 2967 "rel" : "full" 2968 }, 2969 2970 { 2971 "href" : "{$schema}", 2972 "rel" : "describedby" 2973 }, 2974 2975 { 2976 "href" : "{id}", 2977 "rel" : "self" 2978 } 2979 ], 2980 2981 "initializer" : function (instance) { 2982 var link, extension, extended; 2983 2984 //if there is a link to a different schema, update instance 2985 link = instance._schema.getLink("describedby", instance); 2986 if (link && instance._schema._uri !== link) { 2987 if (instance._env._schemas[link]) { 2988 instance._schema = instance._env._schemas[link]; 2989 initializer = instance._schema.getValueOfProperty("initializer"); 2990 if (typeof initializer === "function") { 2991 return initializer(instance); //this function will finish initialization 2992 } else { 2993 return instance; //no further initialization 2994 } 2995 } else if (instance._env._options["validateReferences"]) { 2996 throw new InitializationError(instance, instance._schema, "{link:describedby}", "Unknown schema reference", link); 2997 } 2998 } 2999 3000 //if there is a link to the full representation, replace instance 3001 link = instance._schema.getLink("full", instance); 3002 if (link && instance._uri !== link) { 3003 if (instance._env._schemas[link]) { 3004 instance = instance._env._schemas[link]; 3005 return instance; //retrieved schemas are guaranteed to be initialized 3006 } else if (instance._env._options["validateReferences"]) { 3007 throw new InitializationError(instance, instance._schema, "{link:full}", "Unknown schema reference", link); 3008 } 3009 } 3010 3011 //extend schema 3012 extension = instance.getAttribute("extends"); 3013 if (JSV.isJSONSchema(extension)) { 3014 extended = JSV.inherits(extension, instance, true); 3015 instance = instance._env.createSchema(extended, instance._schema, instance._uri); 3016 } 3017 3018 //if instance has a URI link to itself, update it's own URI 3019 link = instance._schema.getLink("self", instance);
3020 if (JSV.typeOf(link) === "string") { 3021 instance._uri = JSV.formatURI(link); 3022 } 3023 3024 return instance; 3025 } 3026 3027 //not needed as JSV.inherits does the job for us 3028 //"extends" : {"$ref" : "http://json-schema.org/schema#"} 3029 }; 3030 3031 LINKS_00_JSON = { 3032 "$schema" : "http://json-schema.org/draft-00/hyper-schema#", 3033 "id" : "http://json-schema.org/draft-00/links#", 3034 "type" : "object", 3035 3036 "properties" : { 3037 "href" : { 3038 "type" : "string" 3039 }, 3040 3041 "rel" : { 3042 "type" : "string" 3043 }, 3044 3045 "method" : { 3046 "type" : "string", 3047 "default" : "GET", 3048 "optional" : true 3049 }, 3050 3051 "enctype" : { 3052 "type" : "string", 3053 "requires" : "method", 3054 "optional" : true 3055 }, 3056 3057 "properties" : { 3058 "type" : "object", 3059 "additionalProperties" : {"$ref" : "hyper-schema#"}, 3060 "optional" : true, 3061 3062 "parser" : function (instance, self, arg) { 3063 var env = instance.getEnvironment(), 3064 selfEnv = self.getEnvironment(), 3065 additionalPropertiesSchemaURI = self.getValueOfProperty("additionalProperties")["$ref"]; 3066 if (instance.getType() === "object") { 3067 if (arg) { 3068 return env.createSchema(instance.getProperty(arg), selfEnv.findSchema(self.resolveURI(additionalPropertiesSchemaURI))); 3069 } else { 3070 return JSV.mapObject(instance.getProperties(), function (instance) { 3071 return env.createSchema(instance, selfEnv.findSchema(self.resolveURI(additionalPropertiesSchemaURI))); 3072 }); 3073 } 3074 } 3075 } 3076 } 3077 }, 3078 3079 "parser" : function (instance, self) { 3080 var selfProperties = self.getProperty("properties"); 3081 if (instance.getType() === "object") { 3082 return JSV.mapObject(instance.getProperties(), function (property, key) { 3083 var propertySchema = selfProperties.getProperty(key), 3084 parser = propertySchema && propertySchema.getValueOfProperty("parser"); 3085 if (typeof parser === "function") { 3086 return parser(property, propertySchema); 3087 } 3088 //else 3089 return property.getValue(); 3090 }); 3091 } 3092 return instance.getValue(); 3093 } 3094 }; 3095 3096 ENVIRONMENT.setOption("defaultFragmentDelimiter", "."); 3097 ENVIRONMENT.setOption("defaultSchemaURI", "http://json-schema.org/draft-00/schema#"); //updated later 3098 3099 SCHEMA_00 = ENVIRONMENT.createSchema(SCHEMA_00_JSON, true, "http://json-schema.org/draft-00/schema#"); 3100 HYPERSCHEMA_00 = ENVIRONMENT.createSchema(JSV.inherits(SCHEMA_00, ENVIRONMENT.createSchema(HYPERSCHEMA_00_JSON, true, "http://json-schema.org/draft-00/hyper-schema#"), true), true, "http://json-schema.org/draft-00/hyper-schema#"); 3101 3102 ENVIRONMENT.setOption("defaultSchemaURI", "http://json-schema.org/draft-00/hyper-schema#"); 3103 3104 LINKS_00 = ENVIRONMENT.createSchema(LINKS_00_JSON, HYPERSCHEMA_00, "http://json-schema.org/draft-00/links#"); 3105 3106 //We need to reinitialize these 3 schemas as they all reference each other 3107 SCHEMA_00 = ENVIRONMENT.createSchema(SCHEMA_00.getValue(), HYPERSCHEMA_00, "http://json-schema.org/draft-00/schema#"); 3108 HYPERSCHEMA_00 = ENVIRONMENT.createSchema(HYPERSCHEMA_00.getValue(), HYPERSCHEMA_00, "http://json-schema.org/draft-00/hyper-schema#"); 3109 LINKS_00 = ENVIRONMENT.createSchema(LINKS_00.getValue(), HYPERSCHEMA_00, "http://json-schema.org/draft-00/links#"); 3110 3111 // 3112 // draft-01 3113 // 3114 3115 SCHEMA_01_JSON = JSV.inherits(SCHEMA_00_JSON, { 3116 "$schema" : "http://json-schema.org/draft-01/hyper-schema#", 3117 "id" : "http://json-schema.org/draft-01/schema#" 3118 }); 3119 3120 HYPERSCHEMA_01_JSON = JSV.inherits(HYPERSCHEMA_00_JSON, { 3121 "$schema" : "http://json-schema.org/draft-01/hyper-schema#", 3122 "id" : "http://json-schema.org/draft-01/hyper-schema#" 3123 }); 3124 3125 LINKS_01_JSON = JSV.inherits(LINKS_00_JSON, { 3126 "$schema" : "http://json-schema.org/draft-01/hyper-schema#", 3127 "id" : "http://json-schema.org/draft-01/links#" 3128 }); 3129 3130 ENVIRONMENT.setOption("defaultSchemaURI", "http://json-schema.org/draft-01/schema#"); //update later 3131 3132 SCHEMA_01 = ENVIRONMENT.createSchema(SCHEMA_01_JSON, true, "http://json-schema.org/draft-01/schema#"); 3133 HYPERSCHEMA_01 = ENVIRONMENT.createSchema(JSV.inherits(SCHEMA_01, ENVIRONMENT.createSchema(HYPERSCHEMA_01_JSON, true, "http://json-schema.org/draft-01/hyper-schema#"), true), true, "http://json-schema.org/draft-01/hyper-schema#"); 3134 3135 ENVIRONMENT.setOption("defaultSchemaURI", "http://json-schema.org/draft-01/hyper-schema#"); 3136 3137 LINKS_01 = ENVIRONMENT.createSchema(LINKS_01_JSON, HYPERSCHEMA_01, "http://json-schema.org/draft-01/links#"); 3138 3139 //We need to reinitialize these 3 schemas as they all reference each other 3140 SCHEMA_01 = ENVIRONMENT.createSchema(SCHEMA_01.getValue(), HYPERSCHEMA_01, "http://json-schema.org/draft-01/schema#"); 3141 HYPERSCHEMA_01 = ENVIRONMENT.createSchema(HYPERSCHEMA_01.getValue(), HYPERSCHEMA_01, "http://json-schema.org/draft-01/hyper-schema#"); 3142 LINKS_01 = ENVIRONMENT.createSchema(LINKS_01.getValue(), HYPERSCHEMA_01, "http://json-schema.org/draft-01/links#"); 3143 3144 // 3145 // draft-02 3146 // 3147 3148 SCHEMA_02_JSON = JSV.inherits(SCHEMA_01_JSON, { 3149 "$schema" : "http://json-schema.org/draft-02/hyper-schema#", 3150 "id" : "http://json-schema.org/draft-02/schema#", 3151 3152 "properties" : { 3153 "uniqueItems" : { 3154 "type" : "boolean", 3155 "optional" : true, 3156 "default" : false, 3157 3158 "parser" : function (instance, self) { 3159 return !!instance.getValue(); 3160 }, 3161 3162 "validator" : function (instance, schema, self, report, parent, parentSchema, name) { 3163 var value, x, xl, y, yl; 3164 if (instance.getType() === "array" && schema.getAttribute("uniqueItems")) { 3165 value = instance.getProperties(); 3166 for (x = 0, xl = value.length - 1; x < xl; ++x) { 3167 for (y = x + 1, yl = value.length; y < yl; ++y) { 3168 if (value[x].equals(value[y])) { 3169 report.addError(instance, schema, "uniqueItems", "Array can only contain unique items", { x : x, y : y }); 3170 } 3171 } 3172 } 3173 } 3174 } 3175 }, 3176 3177 "maxDecimal" : { 3178 "deprecated" : true 3179 }, 3180 3181 "divisibleBy" : { 3182 "type" : "number", 3183 "minimum" : 0, 3184 "minimumCanEqual" : false, 3185 "optional" : true, 3186 3187 "parser" : function (instance, self) { 3188 if (instance.getType() === "number") { 3189 return instance.getValue(); 3190 } 3191 }, 3192 3193 "validator" : function (instance, schema, self, report, parent, parentSchema, name) { 3194 var divisor; 3195 if (instance.getType() === "number") { 3196 divisor = schema.getAttribute("divisibleBy"); 3197 if (divisor === 0) { 3198 report.addError(instance, schema, "divisibleBy", "Nothing is divisible by 0", divisor); 3199 } else if (divisor !== 1 && ((instance.getValue() / divisor) % 1) !== 0) { 3200 report.addError(instance, schema, "divisibleBy", "Number is not divisible by " + divisor, divisor); 3201 } 3202 } 3203 } 3204 } 3205 }, 3206 3207 "fragmentResolution" : "slash-delimited" 3208 }); 3209 3210 HYPERSCHEMA_02_JSON = JSV.inherits(HYPERSCHEMA_01_JSON, { 3211 "id" : "http://json-schema.org/draft-02/hyper-schema#", 3212 3213 "properties" : { 3214 "fragmentResolution" : { 3215 "default" : "slash-delimited" 3216 } 3217 } 3218 }); 3219 3220 LINKS_02_JSON = JSV.inherits(LINKS_01_JSON, { 3221 "$schema" : "http://json-schema.org/draft-02/hyper-schema#", 3222 "id" : "http://json-schema.org/draft-02/links#", 3223 3224 "properties" : { 3225 "targetSchema" : { 3226 "$ref" : "hyper-schema#", 3227 3228 //need this here because parsers are run before links are resolved 3229 "parser" : HYPERSCHEMA_01.getAttribute("parser") 3230 } 3231 } 3232 }); 3233 3234 ENVIRONMENT.setOption("defaultFragmentDelimiter", "/"); 3235 ENVIRONMENT.setOption("defaultSchemaURI", "http://json-schema.org/draft-02/schema#"); //update later 3236 3237 SCHEMA_02 = ENVIRONMENT.createSchema(SCHEMA_02_JSON, true, "http://json-schema.org/draft-02/schema#"); 3238 HYPERSCHEMA_02 = ENVIRONMENT.createSchema(JSV.inherits(SCHEMA_02, ENVIRONMENT.createSchema(HYPERSCHEMA_02_JSON, true, "http://json-schema.org/draft-02/hyper-schema#"), true), true, "http://json-schema.org/draft-02/hyper-schema#"); 3239 3240 ENVIRONMENT.setOption("defaultSchemaURI", "http://json-schema.org/draft-02/hyper-schema#"); 3241 3242 LINKS_02 = ENVIRONMENT.createSchema(LINKS_02_JSON, HYPERSCHEMA_02, "http://json-schema.org/draft-02/links#"); 3243 3244 //We need to reinitialize these 3 schemas as they all reference each other 3245 SCHEMA_02 = ENVIRONMENT.createSchema(SCHEMA_02.getValue(), HYPERSCHEMA_02, "http://json-schema.org/draft-02/schema#"); 3246 HYPERSCHEMA_02 = ENVIRONMENT.createSchema(HYPERSCHEMA_02.getValue(), HYPERSCHEMA_02, "http://json-schema.org/draft-02/hyper-schema#"); 3247 LINKS_02 = ENVIRONMENT.createSchema(LINKS_02.getValue(), HYPERSCHEMA_02, "http://json-schema.org/draft-02/links#"); 3248 3249 // 3250 // draft-03 3251 // 3252 3253 function getMatchedPatternProperties(instance, schema, report, self) { 3254 var matchedProperties = {}, patternProperties, pattern, regexp, properties, key; 3255 3256 if (instance.getType() === "object") { 3257 patternProperties = schema.getAttribute("patternProperties"); 3258 properties = instance.getProperties(); 3259 for (pattern in patternProperties) { 3260 if (patternProperties[pattern] !== O[pattern]) { 3261 regexp = null; 3262 try { 3263 regexp = new RegExp(pattern); 3264 } catch (e) { 3265 if (report) { 3266 report.addError(schema, self, "patternProperties", "Invalid pattern", pattern); 3267 } 3268 } 3269 3270 if (regexp) { 3271 for (key in properties) { 3272 if (properties[key] !== O[key] && regexp.test(key)) { 3273 matchedProperties[key] = matchedProperties[key] ? JSV.pushUnique(matchedProperties[key], patternProperties[pattern]) : [ patternProperties[pattern] ]; 3274 } 3275 } 3276 } 3277 } 3278 } 3279 } 3280 3281 return matchedProperties; 3282 } 3283 3284 SCHEMA_03_JSON = JSV.inherits(SCHEMA_02_JSON, { 3285 "$schema" : "http://json-schema.org/draft-03/schema#", 3286 "id" : "http://json-schema.org/draft-03/schema#", 3287 3288 "properties" : { 3289 "patternProperties" : { 3290 "type" : "object", 3291 "additionalProperties" : {"$ref" : "#"}, 3292 "default" : {}, 3293 3294 "parser" : SCHEMA_02.getValueOfProperty("properties")["properties"]["parser"], 3295 3296 "validator" : function (instance, schema, self, report, parent, parentSchema, name) { 3297 var matchedProperties, key, x; 3298 if (instance.getType() === "object") { 3299 matchedProperties = getMatchedPatternProperties(instance, schema, report, self); 3300 for (key in matchedProperties) { 3301 if (matchedProperties[key] !== O[key]) { 3302 x = matchedProperties[key].length; 3303 while (x--) { 3304 matchedProperties[key][x].validate(instance.getProperty(key), report, instance, schema, key); 3305 } 3306 } 3307 } 3308 } 3309 } 3310 }, 3311 3312 "additionalProperties" : { 3313 "validator" : function (instance, schema, self, report, parent, parentSchema, name) { 3314 var additionalProperties, propertySchemas, properties, matchedProperties, key; 3315 if (instance.getType() === "object") { 3316 additionalProperties = schema.getAttribute("additionalProperties"); 3317 propertySchemas = schema.getAttribute("properties") || {}; 3318 properties = instance.getProperties(); 3319 matchedProperties = getMatchedPatternProperties(instance, schema); 3320 for (key in properties) { 3321 if (properties[key] !== O[key] && properties[key] && propertySchemas[key] === O[key] && matchedProperties[key] === O[key]) { 3322 if (JSV.isJSONSchema(additionalProperties)) { 3323 additionalProperties.validate(properties[key], report, instance, schema, key); 3324 } else if (additionalProperties === false) { 3325 report.addError(instance, schema, "additionalProperties", "Additional properties are not allowed", additionalProperties); 3326 } 3327 } 3328 } 3329 } 3330 } 3331 }, 3332 3333 "items" : { 3334 "validator" : function (instance, schema, self, report, parent, parentSchema, name) { 3335 var properties, items, x, xl, itemSchema, additionalItems; 3336 3337 if (instance.getType() === "array") { 3338 properties = instance.getProperties(); 3339 items = schema.getAttribute("items"); 3340 additionalItems = schema.getAttribute("additionalItems"); 3341
3342 if (JSV.typeOf(items) === "array") { 3343 for (x = 0, xl = properties.length; x < xl; ++x) { 3344 itemSchema = items[x] || additionalItems; 3345 if (itemSchema !== false) { 3346 itemSchema.validate(properties[x], report, instance, schema, x); 3347 } else { 3348 report.addError(instance, schema, "additionalItems", "Additional items are not allowed", itemSchema); 3349 } 3350 } 3351 } else { 3352 itemSchema = items || additionalItems; 3353 for (x = 0, xl = properties.length; x < xl; ++x) { 3354 itemSchema.validate(properties[x], report, instance, schema, x); 3355 } 3356 } 3357 } 3358 } 3359 }, 3360 3361 "additionalItems" : { 3362 "type" : [{"$ref" : "#"}, "boolean"], 3363 "default" : {}, 3364 3365 "parser" : SCHEMA_02.getValueOfProperty("properties")["additionalProperties"]["parser"], 3366 3367 "validator" : function (instance, schema, self, report, parent, parentSchema, name) { 3368 var additionalItems, properties, x, xl; 3369 //only validate if the "items" attribute is undefined 3370 if (instance.getType() === "array" && schema.getProperty("items").getType() === "undefined") { 3371 additionalItems = schema.getAttribute("additionalItems"); 3372 properties = instance.getProperties(); 3373 3374 if (additionalItems !== false) { 3375 for (x = 0, xl = properties.length; x < xl; ++x) { 3376 additionalItems.validate(properties[x], report, instance, schema, x); 3377 } 3378 } else if (properties.length) { 3379 report.addError(instance, schema, "additionalItems", "Additional items are not allowed", additionalItems); 3380 } 3381 } 3382 } 3383 }, 3384 3385 "optional" : { 3386 "validationRequired" : false, 3387 "deprecated" : true 3388 }, 3389 3390 "required" : { 3391 "type" : "boolean", 3392 "default" : false, 3393 3394 "parser" : function (instance, self) { 3395 return !!instance.getValue(); 3396 }, 3397 3398 "validator" : function (instance, schema, self, report, parent, parentSchema, name) { 3399 if (instance.getType() === "undefined" && schema.getAttribute("required")) { 3400 report.addError(instance, schema, "required", "Property is required", true); 3401 } 3402 } 3403 }, 3404 3405 "requires" : { 3406 "deprecated" : true 3407 }, 3408 3409 "dependencies" : { 3410 "type" : "object", 3411 "additionalProperties" : { 3412 "type" : ["string", "array", {"$ref" : "#"}], 3413 "items" : { 3414 "type" : "string" 3415 } 3416 }, 3417 "default" : {}, 3418 3419 "parser" : function (instance, self, arg) { 3420 function parseProperty(property) { 3421 var type = property.getType(); 3422 if (type === "string" || type === "array") { 3423 return property.getValue(); 3424 } else if (type === "object") { 3425 return property.getEnvironment().createSchema(property, self.getEnvironment().findSchema(self.resolveURI("#"))); 3426 } 3427 } 3428 3429 if (instance.getType() === "object") { 3430 if (arg) { 3431 return parseProperty(instance.getProperty(arg)); 3432 } else { 3433 return JSV.mapObject(instance.getProperties(), parseProperty); 3434 } 3435 } 3436 //else 3437 return {}; 3438 }, 3439 3440 "validator" : function (instance, schema, self, report, parent, parentSchema, name) { 3441 var dependencies, key, dependency, type, x, xl; 3442 if (instance.getType() === "object") { 3443 dependencies = schema.getAttribute("dependencies"); 3444 for (key in dependencies) { 3445 if (dependencies[key] !== O[key] && instance.getProperty(key).getType() !== "undefined") { 3446 dependency = dependencies[key]; 3447 type = JSV.typeOf(dependency); 3448 if (type === "string") { 3449 if (instance.getProperty(dependency).getType() === "undefined") { 3450 report.addError(instance, schema, "dependencies", 'Property "' + key + '" requires sibling property "' + dependency + '"', dependencies); 3451 } 3452 } else if (type === "array") { 3453 for (x = 0, xl = dependency.length; x < xl; ++x) { 3454 if (instance.getProperty(dependency[x]).getType() === "undefined") { 3455 report.addError(instance, schema, "dependencies", 'Property "' + key + '" requires sibling property "' + dependency[x] + '"', dependencies); 3456 } 3457 } 3458 } else if (JSV.isJSONSchema(dependency)) { 3459 dependency.validate(instance, report); 3460 } 3461 } 3462 } 3463 } 3464 } 3465 }, 3466 3467 "minimumCanEqual" : { 3468 "deprecated" : true 3469 }, 3470 3471 "maximumCanEqual" : { 3472 "deprecated" : true 3473 }, 3474 3475 "exclusiveMinimum" : { 3476 "type" : "boolean", 3477 "default" : false, 3478 3479 "parser" : function (instance, self) { 3480 return !!instance.getValue(); 3481 } 3482 }, 3483 3484 "exclusiveMaximum" : { 3485 "type" : "boolean", 3486 "default" : false, 3487 3488 "parser" : function (instance, self) { 3489 return !!instance.getValue(); 3490 } 3491 }, 3492 3493 "minimum" : { 3494 "validator" : function (instance, schema, self, report, parent, parentSchema, name) { 3495 var minimum, exclusiveMinimum; 3496 if (instance.getType() === "number") { 3497 minimum = schema.getAttribute("minimum"); 3498 exclusiveMinimum = schema.getAttribute("exclusiveMinimum") || (!instance.getEnvironment().getOption("strict") && !schema.getAttribute("minimumCanEqual")); 3499 if (typeof minimum === "number" && (instance.getValue() < minimum || (exclusiveMinimum === true && instance.getValue() === minimum))) { 3500 report.addError(instance, schema, "minimum", "Number is less then the required minimum value", minimum); 3501 } 3502 } 3503 } 3504 }, 3505 3506 "maximum" : { 3507 "validator" : function (instance, schema, self, report, parent, parentSchema, name) { 3508 var maximum, exclusiveMaximum; 3509 if (instance.getType() === "number") { 3510 maximum = schema.getAttribute("maximum"); 3511 exclusiveMaximum = schema.getAttribute("exclusiveMaximum") || (!instance.getEnvironment().getOption("strict") && !schema.getAttribute("maximumCanEqual")); 3512 if (typeof maximum === "number" && (instance.getValue() > maximum || (exclusiveMaximum === true && instance.getValue() === maximum))) { 3513 report.addError(instance, schema, "maximum", "Number is greater then the required maximum value", maximum); 3514 } 3515 } 3516 } 3517 }, 3518 3519 "contentEncoding" : { 3520 "deprecated" : true 3521 }, 3522 3523 "divisibleBy" : { 3524 "exclusiveMinimum" : true 3525 }, 3526 3527 "disallow" : { 3528 "items" : { 3529 "type" : ["string", {"$ref" : "#"}] 3530 }, 3531 3532 "parser" : SCHEMA_02_JSON["properties"]["type"]["parser"] 3533 }, 3534 3535 "id" : { 3536 "type" : "string", 3537 "format" : "uri" 3538 }, 3539 3540 "$ref" : { 3541 "type" : "string", 3542 "format" : "uri" 3543 }, 3544 3545 "$schema" : { 3546 "type" : "string", 3547 "format" : "uri" 3548 } 3549 }, 3550
3551 "dependencies" : { 3552 "exclusiveMinimum" : "minimum", 3553 "exclusiveMaximum" : "maximum" 3554 }, 3555 3556 "initializer" : function (instance) { 3557 var link, extension, extended, 3558 schemaLink = instance.getValueOfProperty("$schema"), 3559 refLink = instance.getValueOfProperty("$ref"), 3560 idLink = instance.getValueOfProperty("id"); 3561 3562 //if there is a link to a different schema, update instance 3563 if (schemaLink) { 3564 link = instance.resolveURI(schemaLink); 3565 if (link && instance._schema._uri !== link) { 3566 if (instance._env._schemas[link]) { 3567 instance._schema = instance._env._schemas[link]; 3568 initializer = instance._schema.getValueOfProperty("initializer"); 3569 if (typeof initializer === "function") { 3570 return initializer(instance); //this function will finish initialization 3571 } else { 3572 return instance; //no further initialization 3573 } 3574 } else if (instance._env._options["validateReferences"]) { 3575 throw new InitializationError(instance, instance._schema, "$schema", "Unknown schema reference", link); 3576 } 3577 } 3578 } 3579 3580 //if there is a link to the full representation, replace instance 3581 if (refLink) { 3582 link = instance.resolveURI(refLink); 3583 if (link && instance._uri !== link) { 3584 if (instance._env._schemas[link]) { 3585 instance = instance._env._schemas[link]; 3586 return instance; //retrieved schemas are guaranteed to be initialized 3587 } else if (instance._env._options["validateReferences"]) { 3588 throw new InitializationError(instance, instance._schema, "$ref", "Unknown schema reference", link); 3589 } 3590 } 3591 } 3592 3593 //extend schema 3594 extension = instance.getAttribute("extends"); 3595 if (JSV.isJSONSchema(extension)) { 3596 extended = JSV.inherits(extension, instance, true); 3597 instance = instance._env.createSchema(extended, instance._schema, instance._uri); 3598 } 3599 3600 //if instance has a URI link to itself, update it's own URI 3601 if (idLink) { 3602 link = instance.resolveURI(idLink); 3603 if (JSV.typeOf(link) === "string") { 3604 instance._uri = JSV.formatURI(link); 3605 } 3606 } 3607 3608 return instance; 3609 } 3610 }); 3611 3612 HYPERSCHEMA_03_JSON = JSV.inherits(HYPERSCHEMA_02_JSON, { 3613 "$schema" : "http://json-schema.org/draft-03/hyper-schema#", 3614 "id" : "http://json-schema.org/draft-03/hyper-schema#", 3615 3616 "properties" : { 3617 "links" : { 3618 "selfReferenceVariable" : "@" 3619 }, 3620 3621 "root" : { 3622 "deprecated" : true 3623 }, 3624 3625 "contentEncoding" : { 3626 "deprecated" : false //moved from core to hyper 3627 }, 3628 3629 "alternate" : { 3630 "deprecated" : true 3631 } 3632 } 3633 }); 3634 3635 LINKS_03_JSON = JSV.inherits(LINKS_02_JSON, { 3636 "$schema" : "http://json-schema.org/draft-03/hyper-schema#", 3637 "id" : "http://json-schema.org/draft-03/links#", 3638 3639 "properties" : { 3640 "href" : { 3641 "required" : true, 3642 "format" : "link-description-object-template" 3643 }, 3644 3645 "rel" : { 3646 "required" : true 3647 }, 3648 3649 "properties" : { 3650 "deprecated" : true 3651 }, 3652 3653 "schema" : {"$ref" : "http://json-schema.org/draft-03/hyper-schema#"} 3654 } 3655 }); 3656 3657 ENVIRONMENT.setOption("validateReferences", true); 3658 ENVIRONMENT.setOption("defaultSchemaURI", "http://json-schema.org/draft-03/schema#"); //update later 3659 3660 //prevent reference errors 3661 ENVIRONMENT.createSchema({}, true, "http://json-schema.org/draft-03/schema#"); 3662 ENVIRONMENT.createSchema({}, true, "http://json-schema.org/draft-03/hyper-schema#"); 3663 ENVIRONMENT.createSchema({}, true, "http://json-schema.org/draft-03/links#"); 3664 3665 SCHEMA_03 = ENVIRONMENT.createSchema(SCHEMA_03_JSON, true, "http://json-schema.org/draft-03/schema#"); 3666 HYPERSCHEMA_03 = ENVIRONMENT.createSchema(JSV.inherits(SCHEMA_03, ENVIRONMENT.createSchema(HYPERSCHEMA_03_JSON, true, "http://json-schema.org/draft-03/hyper-schema#"), true), true, "http://json-schema.org/draft-03/hyper-schema#"); 3667 LINKS_03 = ENVIRONMENT.createSchema(LINKS_03_JSON, true, "http://json-schema.org/draft-03/links#"); 3668 3669 ENVIRONMENT.setOption("defaultSchemaURI", "http://json-schema.org/draft-03/hyper-schema#"); 3670 3671 //We need to reinitialize these schemas as they reference each other 3672 HYPERSCHEMA_03 = ENVIRONMENT.createSchema(HYPERSCHEMA_03.getValue(), HYPERSCHEMA_03, "http://json-schema.org/draft-03/hyper-schema#"); 3673 3674 ENVIRONMENT.setOption("latestJSONSchemaSchemaURI", "http://json-schema.org/draft-03/schema#"); 3675 ENVIRONMENT.setOption("latestJSONSchemaHyperSchemaURI", "http://json-schema.org/draft-03/hyper-schema#"); 3676 ENVIRONMENT.setOption("latestJSONSchemaLinksURI", "http://json-schema.org/draft-03/links#"); 3677 3678 // 3679 //Latest JSON Schema 3680 // 3681 3682 //Hack, but WAY faster then instantiating a new schema 3683 ENVIRONMENT._schemas["http://json-schema.org/schema#"] = SCHEMA_03; 3684 ENVIRONMENT._schemas["http://json-schema.org/hyper-schema#"] = HYPERSCHEMA_03; 3685 ENVIRONMENT._schemas["http://json-schema.org/links#"] = LINKS_03; 3686 3687 // 3688 //register environment 3689 // 3690 3691 JSV.registerEnvironment("json-schema-draft-03", ENVIRONMENT); 3692 if (!JSV.getDefaultEnvironmentID() || JSV.getDefaultEnvironmentID() === "
3692json-schema-draft-01" || JSV.getDefaultEnvironmentID() === "json-schema-draft-02") { 3693 JSV.setDefaultEnvironmentID("json-schema-draft-03"); 3694 } 3695 3696}()); 3697 3698 3699global.JSONFormValidator = exports.JSV; 3700})(this, false);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.