'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;
							 | 
						|
								}
							 |