UNPKG

tripledoc

Version:

Library to read, create and update documents on a Solid Pod

274 lines (227 loc) 5.39 kB
'use strict'; /** * Ruler is a helper class for building responsibility chains from * parse rules. It allows: * * - easy stack rules chains * - getting main chain and named chains content (as arrays of functions) * * Helper methods, should not be used directly. * @api private */ function Ruler() { // List of added rules. Each element is: // // { name: XXX, // enabled: Boolean, // fn: Function(), // alt: [ name2, name3 ] } // this.__rules__ = []; // Cached rule chains. // // First level - chain name, '' for default. // Second level - digital anchor for fast filtering by charcodes. // this.__cache__ = null; } /** * Find the index of a rule by `name`. * * @param {String} `name` * @return {Number} Index of the given `name` * @api private */ Ruler.prototype.__find__ = function (name) { var len = this.__rules__.length; var i = -1; while (len--) { if (this.__rules__[++i].name === name) { return i; } } return -1; }; /** * Build the rules lookup cache * * @api private */ Ruler.prototype.__compile__ = function () { var self = this; var chains = [ '' ]; // collect unique names self.__rules__.forEach(function (rule) { if (!rule.enabled) { return; } rule.alt.forEach(function (altName) { if (chains.indexOf(altName) < 0) { chains.push(altName); } }); }); self.__cache__ = {}; chains.forEach(function (chain) { self.__cache__[chain] = []; self.__rules__.forEach(function (rule) { if (!rule.enabled) { return; } if (chain && rule.alt.indexOf(chain) < 0) { return; } self.__cache__[chain].push(rule.fn); }); }); }; /** * Ruler public methods * ------------------------------------------------ */ /** * Replace rule function * * @param {String} `name` Rule name * @param {Function `fn` * @param {Object} `options` * @api private */ Ruler.prototype.at = function (name, fn, options) { var idx = this.__find__(name); var opt = options || {}; if (idx === -1) { throw new Error('Parser rule not found: ' + name); } this.__rules__[idx].fn = fn; this.__rules__[idx].alt = opt.alt || []; this.__cache__ = null; }; /** * Add a rule to the chain before given the `ruleName`. * * @param {String} `beforeName` * @param {String} `ruleName` * @param {Function} `fn` * @param {Object} `options` * @api private */ Ruler.prototype.before = function (beforeName, ruleName, fn, options) { var idx = this.__find__(beforeName); var opt = options || {}; if (idx === -1) { throw new Error('Parser rule not found: ' + beforeName); } this.__rules__.splice(idx, 0, { name: ruleName, enabled: true, fn: fn, alt: opt.alt || [] }); this.__cache__ = null; }; /** * Add a rule to the chain after the given `ruleName`. * * @param {String} `afterName` * @param {String} `ruleName` * @param {Function} `fn` * @param {Object} `options` * @api private */ Ruler.prototype.after = function (afterName, ruleName, fn, options) { var idx = this.__find__(afterName); var opt = options || {}; if (idx === -1) { throw new Error('Parser rule not found: ' + afterName); } this.__rules__.splice(idx + 1, 0, { name: ruleName, enabled: true, fn: fn, alt: opt.alt || [] }); this.__cache__ = null; }; /** * Add a rule to the end of chain. * * @param {String} `ruleName` * @param {Function} `fn` * @param {Object} `options` * @return {String} */ Ruler.prototype.push = function (ruleName, fn, options) { var opt = options || {}; this.__rules__.push({ name: ruleName, enabled: true, fn: fn, alt: opt.alt || [] }); this.__cache__ = null; }; /** * Enable a rule or list of rules. * * @param {String|Array} `list` Name or array of rule names to enable * @param {Boolean} `strict` If `true`, all non listed rules will be disabled. * @api private */ Ruler.prototype.enable = function (list, strict) { list = !Array.isArray(list) ? [ list ] : list; // In strict mode disable all existing rules first if (strict) { this.__rules__.forEach(function (rule) { rule.enabled = false; }); } // Search by name and enable list.forEach(function (name) { var idx = this.__find__(name); if (idx < 0) { throw new Error('Rules manager: invalid rule name ' + name); } this.__rules__[idx].enabled = true; }, this); this.__cache__ = null; }; /** * Disable a rule or list of rules. * * @param {String|Array} `list` Name or array of rule names to disable * @api private */ Ruler.prototype.disable = function (list) { list = !Array.isArray(list) ? [ list ] : list; // Search by name and disable list.forEach(function (name) { var idx = this.__find__(name); if (idx < 0) { throw new Error('Rules manager: invalid rule name ' + name); } this.__rules__[idx].enabled = false; }, this); this.__cache__ = null; }; /** * Get a rules list as an array of functions. * * @param {String} `chainName` * @return {Object} * @api private */ Ruler.prototype.getRules = function (chainName) { if (this.__cache__ === null) { this.__compile__(); } return this.__cache__[chainName] || []; }; /** * Expose `Ruler` */ module.exports = Ruler;