| 'use strict'; | 
|   | 
| var IDENTIFIER = /^[a-z_$][a-z0-9_$-]*$/i; | 
| var customRuleCode = require('./dotjs/custom'); | 
| var definitionSchema = require('./definition_schema'); | 
|   | 
| module.exports = { | 
|   add: addKeyword, | 
|   get: getKeyword, | 
|   remove: removeKeyword, | 
|   validate: validateKeyword | 
| }; | 
|   | 
|   | 
| /** | 
|  * Define custom keyword | 
|  * @this  Ajv | 
|  * @param {String} keyword custom keyword, should be unique (including different from all standard, custom and macro keywords). | 
|  * @param {Object} definition keyword definition object with properties `type` (type(s) which the keyword applies to), `validate` or `compile`. | 
|  * @return {Ajv} this for method chaining | 
|  */ | 
| function addKeyword(keyword, definition) { | 
|   /* jshint validthis: true */ | 
|   /* eslint no-shadow: 0 */ | 
|   var RULES = this.RULES; | 
|   if (RULES.keywords[keyword]) | 
|     throw new Error('Keyword ' + keyword + ' is already defined'); | 
|   | 
|   if (!IDENTIFIER.test(keyword)) | 
|     throw new Error('Keyword ' + keyword + ' is not a valid identifier'); | 
|   | 
|   if (definition) { | 
|     this.validateKeyword(definition, true); | 
|   | 
|     var dataType = definition.type; | 
|     if (Array.isArray(dataType)) { | 
|       for (var i=0; i<dataType.length; i++) | 
|         _addRule(keyword, dataType[i], definition); | 
|     } else { | 
|       _addRule(keyword, dataType, definition); | 
|     } | 
|   | 
|     var metaSchema = definition.metaSchema; | 
|     if (metaSchema) { | 
|       if (definition.$data && this._opts.$data) { | 
|         metaSchema = { | 
|           anyOf: [ | 
|             metaSchema, | 
|             { '$ref': 'https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#' } | 
|           ] | 
|         }; | 
|       } | 
|       definition.validateSchema = this.compile(metaSchema, true); | 
|     } | 
|   } | 
|   | 
|   RULES.keywords[keyword] = RULES.all[keyword] = true; | 
|   | 
|   | 
|   function _addRule(keyword, dataType, definition) { | 
|     var ruleGroup; | 
|     for (var i=0; i<RULES.length; i++) { | 
|       var rg = RULES[i]; | 
|       if (rg.type == dataType) { | 
|         ruleGroup = rg; | 
|         break; | 
|       } | 
|     } | 
|   | 
|     if (!ruleGroup) { | 
|       ruleGroup = { type: dataType, rules: [] }; | 
|       RULES.push(ruleGroup); | 
|     } | 
|   | 
|     var rule = { | 
|       keyword: keyword, | 
|       definition: definition, | 
|       custom: true, | 
|       code: customRuleCode, | 
|       implements: definition.implements | 
|     }; | 
|     ruleGroup.rules.push(rule); | 
|     RULES.custom[keyword] = rule; | 
|   } | 
|   | 
|   return this; | 
| } | 
|   | 
|   | 
| /** | 
|  * Get keyword | 
|  * @this  Ajv | 
|  * @param {String} keyword pre-defined or custom keyword. | 
|  * @return {Object|Boolean} custom keyword definition, `true` if it is a predefined keyword, `false` otherwise. | 
|  */ | 
| function getKeyword(keyword) { | 
|   /* jshint validthis: true */ | 
|   var rule = this.RULES.custom[keyword]; | 
|   return rule ? rule.definition : this.RULES.keywords[keyword] || false; | 
| } | 
|   | 
|   | 
| /** | 
|  * Remove keyword | 
|  * @this  Ajv | 
|  * @param {String} keyword pre-defined or custom keyword. | 
|  * @return {Ajv} this for method chaining | 
|  */ | 
| function removeKeyword(keyword) { | 
|   /* jshint validthis: true */ | 
|   var RULES = this.RULES; | 
|   delete RULES.keywords[keyword]; | 
|   delete RULES.all[keyword]; | 
|   delete RULES.custom[keyword]; | 
|   for (var i=0; i<RULES.length; i++) { | 
|     var rules = RULES[i].rules; | 
|     for (var j=0; j<rules.length; j++) { | 
|       if (rules[j].keyword == keyword) { | 
|         rules.splice(j, 1); | 
|         break; | 
|       } | 
|     } | 
|   } | 
|   return this; | 
| } | 
|   | 
|   | 
| /** | 
|  * Validate keyword definition | 
|  * @this  Ajv | 
|  * @param {Object} definition keyword definition object. | 
|  * @param {Boolean} throwError true to throw exception if definition is invalid | 
|  * @return {boolean} validation result | 
|  */ | 
| function validateKeyword(definition, throwError) { | 
|   validateKeyword.errors = null; | 
|   var v = this._validateKeyword = this._validateKeyword | 
|                                   || this.compile(definitionSchema, true); | 
|   | 
|   if (v(definition)) return true; | 
|   validateKeyword.errors = v.errors; | 
|   if (throwError) | 
|     throw new Error('custom keyword definition is invalid: '  + this.errorsText(v.errors)); | 
|   else | 
|     return false; | 
| } |