| 'use strict'; | 
|   | 
| var resolve = require('./resolve') | 
|   , util = require('./util') | 
|   , errorClasses = require('./error_classes') | 
|   , stableStringify = require('fast-json-stable-stringify'); | 
|   | 
| var validateGenerator = require('../dotjs/validate'); | 
|   | 
| /** | 
|  * Functions below are used inside compiled validations function | 
|  */ | 
|   | 
| var ucs2length = util.ucs2length; | 
| var equal = require('fast-deep-equal'); | 
|   | 
| // this error is thrown by async schemas to return validation errors via exception | 
| var ValidationError = errorClasses.Validation; | 
|   | 
| module.exports = compile; | 
|   | 
|   | 
| /** | 
|  * Compiles schema to validation function | 
|  * @this   Ajv | 
|  * @param  {Object} schema schema object | 
|  * @param  {Object} root object with information about the root schema for this schema | 
|  * @param  {Object} localRefs the hash of local references inside the schema (created by resolve.id), used for inline resolution | 
|  * @param  {String} baseId base ID for IDs in the schema | 
|  * @return {Function} validation function | 
|  */ | 
| function compile(schema, root, localRefs, baseId) { | 
|   /* jshint validthis: true, evil: true */ | 
|   /* eslint no-shadow: 0 */ | 
|   var self = this | 
|     , opts = this._opts | 
|     , refVal = [ undefined ] | 
|     , refs = {} | 
|     , patterns = [] | 
|     , patternsHash = {} | 
|     , defaults = [] | 
|     , defaultsHash = {} | 
|     , customRules = []; | 
|   | 
|   root = root || { schema: schema, refVal: refVal, refs: refs }; | 
|   | 
|   var c = checkCompiling.call(this, schema, root, baseId); | 
|   var compilation = this._compilations[c.index]; | 
|   if (c.compiling) return (compilation.callValidate = callValidate); | 
|   | 
|   var formats = this._formats; | 
|   var RULES = this.RULES; | 
|   | 
|   try { | 
|     var v = localCompile(schema, root, localRefs, baseId); | 
|     compilation.validate = v; | 
|     var cv = compilation.callValidate; | 
|     if (cv) { | 
|       cv.schema = v.schema; | 
|       cv.errors = null; | 
|       cv.refs = v.refs; | 
|       cv.refVal = v.refVal; | 
|       cv.root = v.root; | 
|       cv.$async = v.$async; | 
|       if (opts.sourceCode) cv.source = v.source; | 
|     } | 
|     return v; | 
|   } finally { | 
|     endCompiling.call(this, schema, root, baseId); | 
|   } | 
|   | 
|   /* @this   {*} - custom context, see passContext option */ | 
|   function callValidate() { | 
|     /* jshint validthis: true */ | 
|     var validate = compilation.validate; | 
|     var result = validate.apply(this, arguments); | 
|     callValidate.errors = validate.errors; | 
|     return result; | 
|   } | 
|   | 
|   function localCompile(_schema, _root, localRefs, baseId) { | 
|     var isRoot = !_root || (_root && _root.schema == _schema); | 
|     if (_root.schema != root.schema) | 
|       return compile.call(self, _schema, _root, localRefs, baseId); | 
|   | 
|     var $async = _schema.$async === true; | 
|   | 
|     var sourceCode = validateGenerator({ | 
|       isTop: true, | 
|       schema: _schema, | 
|       isRoot: isRoot, | 
|       baseId: baseId, | 
|       root: _root, | 
|       schemaPath: '', | 
|       errSchemaPath: '#', | 
|       errorPath: '""', | 
|       MissingRefError: errorClasses.MissingRef, | 
|       RULES: RULES, | 
|       validate: validateGenerator, | 
|       util: util, | 
|       resolve: resolve, | 
|       resolveRef: resolveRef, | 
|       usePattern: usePattern, | 
|       useDefault: useDefault, | 
|       useCustomRule: useCustomRule, | 
|       opts: opts, | 
|       formats: formats, | 
|       logger: self.logger, | 
|       self: self | 
|     }); | 
|   | 
|     sourceCode = vars(refVal, refValCode) + vars(patterns, patternCode) | 
|                    + vars(defaults, defaultCode) + vars(customRules, customRuleCode) | 
|                    + sourceCode; | 
|   | 
|     if (opts.processCode) sourceCode = opts.processCode(sourceCode, _schema); | 
|     // console.log('\n\n\n *** \n', JSON.stringify(sourceCode)); | 
|     var validate; | 
|     try { | 
|       var makeValidate = new Function( | 
|         'self', | 
|         'RULES', | 
|         'formats', | 
|         'root', | 
|         'refVal', | 
|         'defaults', | 
|         'customRules', | 
|         'equal', | 
|         'ucs2length', | 
|         'ValidationError', | 
|         sourceCode | 
|       ); | 
|   | 
|       validate = makeValidate( | 
|         self, | 
|         RULES, | 
|         formats, | 
|         root, | 
|         refVal, | 
|         defaults, | 
|         customRules, | 
|         equal, | 
|         ucs2length, | 
|         ValidationError | 
|       ); | 
|   | 
|       refVal[0] = validate; | 
|     } catch(e) { | 
|       self.logger.error('Error compiling schema, function code:', sourceCode); | 
|       throw e; | 
|     } | 
|   | 
|     validate.schema = _schema; | 
|     validate.errors = null; | 
|     validate.refs = refs; | 
|     validate.refVal = refVal; | 
|     validate.root = isRoot ? validate : _root; | 
|     if ($async) validate.$async = true; | 
|     if (opts.sourceCode === true) { | 
|       validate.source = { | 
|         code: sourceCode, | 
|         patterns: patterns, | 
|         defaults: defaults | 
|       }; | 
|     } | 
|   | 
|     return validate; | 
|   } | 
|   | 
|   function resolveRef(baseId, ref, isRoot) { | 
|     ref = resolve.url(baseId, ref); | 
|     var refIndex = refs[ref]; | 
|     var _refVal, refCode; | 
|     if (refIndex !== undefined) { | 
|       _refVal = refVal[refIndex]; | 
|       refCode = 'refVal[' + refIndex + ']'; | 
|       return resolvedRef(_refVal, refCode); | 
|     } | 
|     if (!isRoot && root.refs) { | 
|       var rootRefId = root.refs[ref]; | 
|       if (rootRefId !== undefined) { | 
|         _refVal = root.refVal[rootRefId]; | 
|         refCode = addLocalRef(ref, _refVal); | 
|         return resolvedRef(_refVal, refCode); | 
|       } | 
|     } | 
|   | 
|     refCode = addLocalRef(ref); | 
|     var v = resolve.call(self, localCompile, root, ref); | 
|     if (v === undefined) { | 
|       var localSchema = localRefs && localRefs[ref]; | 
|       if (localSchema) { | 
|         v = resolve.inlineRef(localSchema, opts.inlineRefs) | 
|             ? localSchema | 
|             : compile.call(self, localSchema, root, localRefs, baseId); | 
|       } | 
|     } | 
|   | 
|     if (v === undefined) { | 
|       removeLocalRef(ref); | 
|     } else { | 
|       replaceLocalRef(ref, v); | 
|       return resolvedRef(v, refCode); | 
|     } | 
|   } | 
|   | 
|   function addLocalRef(ref, v) { | 
|     var refId = refVal.length; | 
|     refVal[refId] = v; | 
|     refs[ref] = refId; | 
|     return 'refVal' + refId; | 
|   } | 
|   | 
|   function removeLocalRef(ref) { | 
|     delete refs[ref]; | 
|   } | 
|   | 
|   function replaceLocalRef(ref, v) { | 
|     var refId = refs[ref]; | 
|     refVal[refId] = v; | 
|   } | 
|   | 
|   function resolvedRef(refVal, code) { | 
|     return typeof refVal == 'object' || typeof refVal == 'boolean' | 
|             ? { code: code, schema: refVal, inline: true } | 
|             : { code: code, $async: refVal && !!refVal.$async }; | 
|   } | 
|   | 
|   function usePattern(regexStr) { | 
|     var index = patternsHash[regexStr]; | 
|     if (index === undefined) { | 
|       index = patternsHash[regexStr] = patterns.length; | 
|       patterns[index] = regexStr; | 
|     } | 
|     return 'pattern' + index; | 
|   } | 
|   | 
|   function useDefault(value) { | 
|     switch (typeof value) { | 
|       case 'boolean': | 
|       case 'number': | 
|         return '' + value; | 
|       case 'string': | 
|         return util.toQuotedString(value); | 
|       case 'object': | 
|         if (value === null) return 'null'; | 
|         var valueStr = stableStringify(value); | 
|         var index = defaultsHash[valueStr]; | 
|         if (index === undefined) { | 
|           index = defaultsHash[valueStr] = defaults.length; | 
|           defaults[index] = value; | 
|         } | 
|         return 'default' + index; | 
|     } | 
|   } | 
|   | 
|   function useCustomRule(rule, schema, parentSchema, it) { | 
|     if (self._opts.validateSchema !== false) { | 
|       var deps = rule.definition.dependencies; | 
|       if (deps && !deps.every(function(keyword) { | 
|         return Object.prototype.hasOwnProperty.call(parentSchema, keyword); | 
|       })) | 
|         throw new Error('parent schema must have all required keywords: ' + deps.join(',')); | 
|   | 
|       var validateSchema = rule.definition.validateSchema; | 
|       if (validateSchema) { | 
|         var valid = validateSchema(schema); | 
|         if (!valid) { | 
|           var message = 'keyword schema is invalid: ' + self.errorsText(validateSchema.errors); | 
|           if (self._opts.validateSchema == 'log') self.logger.error(message); | 
|           else throw new Error(message); | 
|         } | 
|       } | 
|     } | 
|   | 
|     var compile = rule.definition.compile | 
|       , inline = rule.definition.inline | 
|       , macro = rule.definition.macro; | 
|   | 
|     var validate; | 
|     if (compile) { | 
|       validate = compile.call(self, schema, parentSchema, it); | 
|     } else if (macro) { | 
|       validate = macro.call(self, schema, parentSchema, it); | 
|       if (opts.validateSchema !== false) self.validateSchema(validate, true); | 
|     } else if (inline) { | 
|       validate = inline.call(self, it, rule.keyword, schema, parentSchema); | 
|     } else { | 
|       validate = rule.definition.validate; | 
|       if (!validate) return; | 
|     } | 
|   | 
|     if (validate === undefined) | 
|       throw new Error('custom keyword "' + rule.keyword + '"failed to compile'); | 
|   | 
|     var index = customRules.length; | 
|     customRules[index] = validate; | 
|   | 
|     return { | 
|       code: 'customRule' + index, | 
|       validate: validate | 
|     }; | 
|   } | 
| } | 
|   | 
|   | 
| /** | 
|  * Checks if the schema is currently compiled | 
|  * @this   Ajv | 
|  * @param  {Object} schema schema to compile | 
|  * @param  {Object} root root object | 
|  * @param  {String} baseId base schema ID | 
|  * @return {Object} object with properties "index" (compilation index) and "compiling" (boolean) | 
|  */ | 
| function checkCompiling(schema, root, baseId) { | 
|   /* jshint validthis: true */ | 
|   var index = compIndex.call(this, schema, root, baseId); | 
|   if (index >= 0) return { index: index, compiling: true }; | 
|   index = this._compilations.length; | 
|   this._compilations[index] = { | 
|     schema: schema, | 
|     root: root, | 
|     baseId: baseId | 
|   }; | 
|   return { index: index, compiling: false }; | 
| } | 
|   | 
|   | 
| /** | 
|  * Removes the schema from the currently compiled list | 
|  * @this   Ajv | 
|  * @param  {Object} schema schema to compile | 
|  * @param  {Object} root root object | 
|  * @param  {String} baseId base schema ID | 
|  */ | 
| function endCompiling(schema, root, baseId) { | 
|   /* jshint validthis: true */ | 
|   var i = compIndex.call(this, schema, root, baseId); | 
|   if (i >= 0) this._compilations.splice(i, 1); | 
| } | 
|   | 
|   | 
| /** | 
|  * Index of schema compilation in the currently compiled list | 
|  * @this   Ajv | 
|  * @param  {Object} schema schema to compile | 
|  * @param  {Object} root root object | 
|  * @param  {String} baseId base schema ID | 
|  * @return {Integer} compilation index | 
|  */ | 
| function compIndex(schema, root, baseId) { | 
|   /* jshint validthis: true */ | 
|   for (var i=0; i<this._compilations.length; i++) { | 
|     var c = this._compilations[i]; | 
|     if (c.schema == schema && c.root == root && c.baseId == baseId) return i; | 
|   } | 
|   return -1; | 
| } | 
|   | 
|   | 
| function patternCode(i, patterns) { | 
|   return 'var pattern' + i + ' = new RegExp(' + util.toQuotedString(patterns[i]) + ');'; | 
| } | 
|   | 
|   | 
| function defaultCode(i) { | 
|   return 'var default' + i + ' = defaults[' + i + '];'; | 
| } | 
|   | 
|   | 
| function refValCode(i, refVal) { | 
|   return refVal[i] === undefined ? '' : 'var refVal' + i + ' = refVal[' + i + '];'; | 
| } | 
|   | 
|   | 
| function customRuleCode(i) { | 
|   return 'var customRule' + i + ' = customRules[' + i + '];'; | 
| } | 
|   | 
|   | 
| function vars(arr, statement) { | 
|   if (!arr.length) return ''; | 
|   var code = ''; | 
|   for (var i=0; i<arr.length; i++) | 
|     code += statement(i, arr); | 
|   return code; | 
| } |