| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352 | /** * class Ruler * * Helper class, used by [[MarkdownIt#core]], [[MarkdownIt#block]] and * [[MarkdownIt#inline]] to manage sequences of functions (rules): * * - keep rules in defined order * - assign the name to each rule * - enable/disable rules * - add/replace rules * - allow assign rules to additional named chains (in the same) * - cacheing lists of active rules * * You will not need use this class directly until write plugins. For simple * rules control use [[MarkdownIt.disable]], [[MarkdownIt.enable]] and * [[MarkdownIt.use]]. **/'use strict';/** * new Ruler() **/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 - diginal anchor for fast filtering by charcodes.  //  this.__cache__ = null;}////////////////////////////////////////////////////////////////////////////////// Helper methods, should not be used directly// Find rule index by name//Ruler.prototype.__find__ = function (name) {  for (var i = 0; i < this.__rules__.length; i++) {    if (this.__rules__[i].name === name) {      return i;    }  }  return -1;};// Build rules lookup cache//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.at(name, fn [, options]) * - name (String): rule name to replace. * - fn (Function): new rule function. * - options (Object): new rule options (not mandatory). * * Replace rule by name with new function & options. Throws error if name not * found. * * ##### Options: * * - __alt__ - array with names of "alternate" chains. * * ##### Example * * Replace existing typographer replacement rule with new one: * * ```javascript * var md = require('markdown-it')(); * * md.core.ruler.at('replacements', function replace(state) { *   //... * }); * ``` **/Ruler.prototype.at = function (name, fn, options) {  var index = this.__find__(name);  var opt = options || {};  if (index === -1) { throw new Error('Parser rule not found: ' + name); }  this.__rules__[index].fn = fn;  this.__rules__[index].alt = opt.alt || [];  this.__cache__ = null;};/** * Ruler.before(beforeName, ruleName, fn [, options]) * - beforeName (String): new rule will be added before this one. * - ruleName (String): name of added rule. * - fn (Function): rule function. * - options (Object): rule options (not mandatory). * * Add new rule to chain before one with given name. See also * [[Ruler.after]], [[Ruler.push]]. * * ##### Options: * * - __alt__ - array with names of "alternate" chains. * * ##### Example * * ```javascript * var md = require('markdown-it')(); * * md.block.ruler.before('paragraph', 'my_rule', function replace(state) { *   //... * }); * ``` **/Ruler.prototype.before = function (beforeName, ruleName, fn, options) {  var index = this.__find__(beforeName);  var opt = options || {};  if (index === -1) { throw new Error('Parser rule not found: ' + beforeName); }  this.__rules__.splice(index, 0, {    name: ruleName,    enabled: true,    fn: fn,    alt: opt.alt || []  });  this.__cache__ = null;};/** * Ruler.after(afterName, ruleName, fn [, options]) * - afterName (String): new rule will be added after this one. * - ruleName (String): name of added rule. * - fn (Function): rule function. * - options (Object): rule options (not mandatory). * * Add new rule to chain after one with given name. See also * [[Ruler.before]], [[Ruler.push]]. * * ##### Options: * * - __alt__ - array with names of "alternate" chains. * * ##### Example * * ```javascript * var md = require('markdown-it')(); * * md.inline.ruler.after('text', 'my_rule', function replace(state) { *   //... * }); * ``` **/Ruler.prototype.after = function (afterName, ruleName, fn, options) {  var index = this.__find__(afterName);  var opt = options || {};  if (index === -1) { throw new Error('Parser rule not found: ' + afterName); }  this.__rules__.splice(index + 1, 0, {    name: ruleName,    enabled: true,    fn: fn,    alt: opt.alt || []  });  this.__cache__ = null;};/** * Ruler.push(ruleName, fn [, options]) * - ruleName (String): name of added rule. * - fn (Function): rule function. * - options (Object): rule options (not mandatory). * * Push new rule to the end of chain. See also * [[Ruler.before]], [[Ruler.after]]. * * ##### Options: * * - __alt__ - array with names of "alternate" chains. * * ##### Example * * ```javascript * var md = require('markdown-it')(); * * md.core.ruler.push('my_rule', function replace(state) { *   //... * }); * ``` **/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;};/** * Ruler.enable(list [, ignoreInvalid]) -> Array * - list (String|Array): list of rule names to enable. * - ignoreInvalid (Boolean): set `true` to ignore errors when rule not found. * * Enable rules with given names. If any rule name not found - throw Error. * Errors can be disabled by second param. * * Returns list of found rule names (if no exception happened). * * See also [[Ruler.disable]], [[Ruler.enableOnly]]. **/Ruler.prototype.enable = function (list, ignoreInvalid) {  if (!Array.isArray(list)) { list = [ list ]; }  var result = [];  // Search by name and enable  list.forEach(function (name) {    var idx = this.__find__(name);    if (idx < 0) {      if (ignoreInvalid) { return; }      throw new Error('Rules manager: invalid rule name ' + name);    }    this.__rules__[idx].enabled = true;    result.push(name);  }, this);  this.__cache__ = null;  return result;};/** * Ruler.enableOnly(list [, ignoreInvalid]) * - list (String|Array): list of rule names to enable (whitelist). * - ignoreInvalid (Boolean): set `true` to ignore errors when rule not found. * * Enable rules with given names, and disable everything else. If any rule name * not found - throw Error. Errors can be disabled by second param. * * See also [[Ruler.disable]], [[Ruler.enable]]. **/Ruler.prototype.enableOnly = function (list, ignoreInvalid) {  if (!Array.isArray(list)) { list = [ list ]; }  this.__rules__.forEach(function (rule) { rule.enabled = false; });  this.enable(list, ignoreInvalid);};/** * Ruler.disable(list [, ignoreInvalid]) -> Array * - list (String|Array): list of rule names to disable. * - ignoreInvalid (Boolean): set `true` to ignore errors when rule not found. * * Disable rules with given names. If any rule name not found - throw Error. * Errors can be disabled by second param. * * Returns list of found rule names (if no exception happened). * * See also [[Ruler.enable]], [[Ruler.enableOnly]]. **/Ruler.prototype.disable = function (list, ignoreInvalid) {  if (!Array.isArray(list)) { list = [ list ]; }  var result = [];  // Search by name and disable  list.forEach(function (name) {    var idx = this.__find__(name);    if (idx < 0) {      if (ignoreInvalid) { return; }      throw new Error('Rules manager: invalid rule name ' + name);    }    this.__rules__[idx].enabled = false;    result.push(name);  }, this);  this.__cache__ = null;  return result;};/** * Ruler.getRules(chainName) -> Array * * Return array of active functions (rules) for given chain name. It analyzes * rules configuration, compiles caches if not exists and returns result. * * Default chain name is `''` (empty string). It can't be skipped. That's * done intentionally, to keep signature monomorphic for high speed. **/Ruler.prototype.getRules = function (chainName) {  if (this.__cache__ === null) {    this.__compile__();  }  // Chain can be empty, if rules disabled. But we still have to return Array.  return this.__cache__[chainName] || [];};module.exports = Ruler;
 |