| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341 | /** * class Renderer * * Generates HTML from parsed token stream. Each instance has independent * copy of rules. Those can be rewritten with ease. Also, you can add new * rules if you create plugin and adds new token types. **/'use strict';var assign          = require('./common/utils').assign;var unescapeAll     = require('./common/utils').unescapeAll;var escapeHtml      = require('./common/utils').escapeHtml;////////////////////////////////////////////////////////////////////////////////var default_rules = {};default_rules.code_inline = function (tokens, idx, options, env, slf) {  var token = tokens[idx];  return  '<code' + slf.renderAttrs(token) + '>' +          escapeHtml(tokens[idx].content) +          '</code>';};default_rules.code_block = function (tokens, idx, options, env, slf) {  var token = tokens[idx];  return  '<pre' + slf.renderAttrs(token) + '><code>' +          escapeHtml(tokens[idx].content) +          '</code></pre>\n';};default_rules.fence = function (tokens, idx, options, env, slf) {  var token = tokens[idx],      info = token.info ? unescapeAll(token.info).trim() : '',      langName = '',      langAttrs = '',      highlighted, i, arr, tmpAttrs, tmpToken;  if (info) {    arr = info.split(/(\s+)/g);    langName = arr[0];    langAttrs = arr.slice(2).join('');  }  if (options.highlight) {    highlighted = options.highlight(token.content, langName, langAttrs) || escapeHtml(token.content);  } else {    highlighted = escapeHtml(token.content);  }  if (highlighted.indexOf('<pre') === 0) {    return highlighted + '\n';  }  // If language exists, inject class gently, without modifying original token.  // May be, one day we will add .deepClone() for token and simplify this part, but  // now we prefer to keep things local.  if (info) {    i        = token.attrIndex('class');    tmpAttrs = token.attrs ? token.attrs.slice() : [];    if (i < 0) {      tmpAttrs.push([ 'class', options.langPrefix + langName ]);    } else {      tmpAttrs[i] = tmpAttrs[i].slice();      tmpAttrs[i][1] += ' ' + options.langPrefix + langName;    }    // Fake token just to render attributes    tmpToken = {      attrs: tmpAttrs    };    return  '<pre><code' + slf.renderAttrs(tmpToken) + '>'          + highlighted          + '</code></pre>\n';  }  return  '<pre><code' + slf.renderAttrs(token) + '>'        + highlighted        + '</code></pre>\n';};default_rules.image = function (tokens, idx, options, env, slf) {  var token = tokens[idx];  // "alt" attr MUST be set, even if empty. Because it's mandatory and  // should be placed on proper position for tests.  //  // Replace content with actual value  token.attrs[token.attrIndex('alt')][1] =    slf.renderInlineAsText(token.children, options, env);  return slf.renderToken(tokens, idx, options);};default_rules.hardbreak = function (tokens, idx, options /*, env */) {  return options.xhtmlOut ? '<br />\n' : '<br>\n';};default_rules.softbreak = function (tokens, idx, options /*, env */) {  return options.breaks ? (options.xhtmlOut ? '<br />\n' : '<br>\n') : '\n';};default_rules.text = function (tokens, idx /*, options, env */) {  return escapeHtml(tokens[idx].content);};default_rules.html_block = function (tokens, idx /*, options, env */) {  return tokens[idx].content;};default_rules.html_inline = function (tokens, idx /*, options, env */) {  return tokens[idx].content;};/** * new Renderer() * * Creates new [[Renderer]] instance and fill [[Renderer#rules]] with defaults. **/function Renderer() {  /**   * Renderer#rules -> Object   *   * Contains render rules for tokens. Can be updated and extended.   *   * ##### Example   *   * ```javascript   * var md = require('markdown-it')();   *   * md.renderer.rules.strong_open  = function () { return '<b>'; };   * md.renderer.rules.strong_close = function () { return '</b>'; };   *   * var result = md.renderInline(...);   * ```   *   * Each rule is called as independent static function with fixed signature:   *   * ```javascript   * function my_token_render(tokens, idx, options, env, renderer) {   *   // ...   *   return renderedHTML;   * }   * ```   *   * See [source code](https://github.com/markdown-it/markdown-it/blob/master/lib/renderer.js)   * for more details and examples.   **/  this.rules = assign({}, default_rules);}/** * Renderer.renderAttrs(token) -> String * * Render token attributes to string. **/Renderer.prototype.renderAttrs = function renderAttrs(token) {  var i, l, result;  if (!token.attrs) { return ''; }  result = '';  for (i = 0, l = token.attrs.length; i < l; i++) {    result += ' ' + escapeHtml(token.attrs[i][0]) + '="' + escapeHtml(token.attrs[i][1]) + '"';  }  return result;};/** * Renderer.renderToken(tokens, idx, options) -> String * - tokens (Array): list of tokens * - idx (Numbed): token index to render * - options (Object): params of parser instance * * Default token renderer. Can be overriden by custom function * in [[Renderer#rules]]. **/Renderer.prototype.renderToken = function renderToken(tokens, idx, options) {  var nextToken,      result = '',      needLf = false,      token = tokens[idx];  // Tight list paragraphs  if (token.hidden) {    return '';  }  // Insert a newline between hidden paragraph and subsequent opening  // block-level tag.  //  // For example, here we should insert a newline before blockquote:  //  - a  //    >  //  if (token.block && token.nesting !== -1 && idx && tokens[idx - 1].hidden) {    result += '\n';  }  // Add token name, e.g. `<img`  result += (token.nesting === -1 ? '</' : '<') + token.tag;  // Encode attributes, e.g. `<img src="foo"`  result += this.renderAttrs(token);  // Add a slash for self-closing tags, e.g. `<img src="foo" /`  if (token.nesting === 0 && options.xhtmlOut) {    result += ' /';  }  // Check if we need to add a newline after this tag  if (token.block) {    needLf = true;    if (token.nesting === 1) {      if (idx + 1 < tokens.length) {        nextToken = tokens[idx + 1];        if (nextToken.type === 'inline' || nextToken.hidden) {          // Block-level tag containing an inline tag.          //          needLf = false;        } else if (nextToken.nesting === -1 && nextToken.tag === token.tag) {          // Opening tag + closing tag of the same type. E.g. `<li></li>`.          //          needLf = false;        }      }    }  }  result += needLf ? '>\n' : '>';  return result;};/** * Renderer.renderInline(tokens, options, env) -> String * - tokens (Array): list on block tokens to render * - options (Object): params of parser instance * - env (Object): additional data from parsed input (references, for example) * * The same as [[Renderer.render]], but for single token of `inline` type. **/Renderer.prototype.renderInline = function (tokens, options, env) {  var type,      result = '',      rules = this.rules;  for (var i = 0, len = tokens.length; i < len; i++) {    type = tokens[i].type;    if (typeof rules[type] !== 'undefined') {      result += rules[type](tokens, i, options, env, this);    } else {      result += this.renderToken(tokens, i, options);    }  }  return result;};/** internal * Renderer.renderInlineAsText(tokens, options, env) -> String * - tokens (Array): list on block tokens to render * - options (Object): params of parser instance * - env (Object): additional data from parsed input (references, for example) * * Special kludge for image `alt` attributes to conform CommonMark spec. * Don't try to use it! Spec requires to show `alt` content with stripped markup, * instead of simple escaping. **/Renderer.prototype.renderInlineAsText = function (tokens, options, env) {  var result = '';  for (var i = 0, len = tokens.length; i < len; i++) {    if (tokens[i].type === 'text') {      result += tokens[i].content;    } else if (tokens[i].type === 'image') {      result += this.renderInlineAsText(tokens[i].children, options, env);    } else if (tokens[i].type === 'softbreak') {      result += '\n';    }  }  return result;};/** * Renderer.render(tokens, options, env) -> String * - tokens (Array): list on block tokens to render * - options (Object): params of parser instance * - env (Object): additional data from parsed input (references, for example) * * Takes token stream and generates HTML. Probably, you will never need to call * this method directly. **/Renderer.prototype.render = function (tokens, options, env) {  var i, len, type,      result = '',      rules = this.rules;  for (i = 0, len = tokens.length; i < len; i++) {    type = tokens[i].type;    if (type === 'inline') {      result += this.renderInline(tokens[i].children, options, env);    } else if (typeof rules[type] !== 'undefined') {      result += rules[tokens[i].type](tokens, i, options, env, this);    } else {      result += this.renderToken(tokens, i, options, env);    }  }  return result;};module.exports = Renderer;
 |