From 37308209459963de922e0eb4df8eb2369314ccf2 Mon Sep 17 00:00:00 2001 From: Vitaly Puzrin Date: Sat, 7 Mar 2015 21:45:11 +0300 Subject: [PATCH] Added token helpers & reorganized renderer --- lib/renderer.js | 180 +++++++++++++++++++++------------------- lib/rules_block/list.js | 2 - lib/token.js | 33 ++++++++ test/token.js | 23 +++++ 4 files changed, 152 insertions(+), 86 deletions(-) create mode 100644 test/token.js diff --git a/lib/renderer.js b/lib/renderer.js index c84cc0c..201d7a0 100644 --- a/lib/renderer.js +++ b/lib/renderer.js @@ -23,16 +23,14 @@ default_rules.code_block = function (tokens, idx /*, options, env */) { }; -default_rules.fence = function (tokens, idx, options /*, env, self*/) { - var token = tokens[idx]; - var langClass = ''; - var langPrefix = options.langPrefix; - var langName = ''; - var highlighted; +default_rules.fence = function (tokens, idx, options, env, self) { + var token = tokens[idx], + langName = '', + highlighted; if (token.info) { - langName = escapeHtml(unescapeAll(token.info.trim().split(/\s+/g)[0])); - langClass = ' class="' + langPrefix + langName + '"'; + langName = unescapeAll(token.info.trim().split(/\s+/g)[0]); + token.attrPush([ 'class', options.langPrefix + langName ]); } if (options.highlight) { @@ -41,28 +39,24 @@ default_rules.fence = function (tokens, idx, options /*, env, self*/) { highlighted = escapeHtml(token.content); } - - return '
'
+  return  '
'
         + highlighted
         + '
\n'; }; default_rules.image = function (tokens, idx, options, env, self) { - var i, token = tokens[idx], - alt = self.renderInlineAsText(tokens[idx].children, options, env); - - if (!token.attrs) { token.attrs = []; } + var token = tokens[idx]; - // Replace "alt" tag with children tags rendered as text + // "alt" attr MUST be set, even if empty. Because it's mandatory and + // should be placed on proper position for tests. // - for (i = 0; i < token.attrs.length; i++) { - if (token.attrs[i][0] === 'alt') { - token.attrs[i][1] = alt; - } - } + // Replace content with actual value - return self.rules.default_token(tokens, idx, options, env, self); + token.attrs[token.attrIndex('alt')][1] = + self.renderInlineAsText(token.children, options, env); + + return self.renderToken(tokens, idx, options, env, self); }; @@ -87,12 +81,86 @@ default_rules.html_inline = function (tokens, idx /*, options, env */) { }; -default_rules.default_token = function (tokens, idx, options /*, env, self */) { - var i, l, nextToken, +/** + * 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 ''; }; + * md.renderer.rules.strong_close = function () { return ''; }; + * + * var result = md.renderInline(...); + * ``` + * + * Each rule is called as independed 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(attrs) -> String + * - attrs (Array): array of token attributes + * + * Render token attributes to string. + **/ +Renderer.prototype.renderAttrs = function renderAttrs(attrs) { + var i, l, result; + + if (!attrs) { return ''; } + + result = ''; + + for (i = 0, l = attrs.length; i < l; i++) { + result += ' ' + escapeHtml(attrs[i][0]) + '="' + escapeHtml(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. // @@ -100,39 +168,22 @@ default_rules.default_token = function (tokens, idx, options /*, env, self */) { // - a // > // - if (token.block && idx && tokens[idx - 1].hidden && token.nesting !== -1) { + if (token.block && token.nesting !== -1 && idx && tokens[idx - 1].hidden) { result += '\n'; } - // Self-contained token with no tag name, e.g. `inline` or hidden paragraph. - // - // We should return content if it exists and an empty string otherwise. - // - if (!token.tag) { - result += (token.content ? escapeHtml(token.content) : ''); - return result; - } - // Add token name, e.g. `\n' : '>'; + return result; }; -/** - * 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 ''; }; - * md.renderer.rules.strong_close = function () { return ''; }; - * - * var result = md.renderInline(...); - * ``` - * - * Each rule is called as independed 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.renderInline(tokens, options, env) -> String * - tokens (Array): list on block tokens to renter @@ -223,7 +235,7 @@ Renderer.prototype.renderInline = function (tokens, options, env) { if (typeof rules[type] !== 'undefined') { result += rules[type](tokens, i, options, env, this); } else { - result += rules.default_token(tokens, i, options, env); + result += this.renderToken(tokens, i, options, env); } } @@ -279,7 +291,7 @@ Renderer.prototype.render = function (tokens, options, env) { } else if (typeof rules[type] !== 'undefined') { result += rules[tokens[i].type](tokens, i, options, env, this); } else { - result += rules.default_token(tokens, i, options, env); + result += this.renderToken(tokens, i, options, env); } } diff --git a/lib/rules_block/list.js b/lib/rules_block/list.js index ae571b3..bdff935 100644 --- a/lib/rules_block/list.js +++ b/lib/rules_block/list.js @@ -73,8 +73,6 @@ function markTightParagraphs(state, idx) { for (i = idx + 2, l = state.tokens.length - 2; i < l; i++) { if (state.tokens[i].level === level && state.tokens[i].type === 'paragraph_open') { - state.tokens[i + 2].tag = ''; - state.tokens[i].tag = ''; state.tokens[i + 2].hidden = true; state.tokens[i].hidden = true; i += 2; diff --git a/lib/token.js b/lib/token.js index ef446b6..3a8451d 100644 --- a/lib/token.js +++ b/lib/token.js @@ -106,4 +106,37 @@ function Token(type, tag, nesting) { } +/** + * Token.attrIndex(name) -> Number + * + * Search attribute index by name. + **/ +Token.prototype.attrIndex = function attrIndex(name) { + var attrs, i, len; + + if (!this.attrs) { return -1; } + + attrs = this.attrs; + + for (i = 0, len = attrs.length; i < len; i++) { + if (attrs[i][0] === name) { return i; } + } + return -1; +}; + + +/** + * Token.attrPush(attrData) + * + * Add `[ name, value ]` attribute to list. Init attrs if necessary + **/ +Token.prototype.attrPush = function attrPush(attrData) { + if (this.attrs) { + this.attrs.push(attrData); + } else { + this.attrs = [ attrData ]; + } +}; + + module.exports = Token; diff --git a/test/token.js b/test/token.js new file mode 100644 index 0000000..285f052 --- /dev/null +++ b/test/token.js @@ -0,0 +1,23 @@ +'use strict'; + +var assert = require('chai').assert; +var Token = require('../lib/token'); + + +describe('Token', function () { + + it('attr', function () { + var t = new Token('test_token', 'tok', 1); + + assert.strictEqual(t.attrs, null); + assert.equal(t.attrIndex('foo'), -1); + + t.attrPush([ 'foo', 'bar' ]); + t.attrPush([ 'baz', 'bad' ]); + + assert.equal(t.attrIndex('foo'), 0); + assert.equal(t.attrIndex('baz'), 1); + assert.equal(t.attrIndex('none'), -1); + }); + +});