diff --git a/lib/marked.js b/lib/marked.js index 24490b77..d12a77ba 100644 --- a/lib/marked.js +++ b/lib/marked.js @@ -13,6 +13,8 @@ var block = { newline: /^\n+/, code: /^( {4}[^\n]+\n*)+/, fences: noop, + table: noop, + nptable: noop, hr: /^( *[-*_]){3,} *(?:\n+|$)/, heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/, lheading: /^([^\n]+)\n *(=|-){3,} *\n*/, @@ -58,16 +60,24 @@ block.paragraph = replace(block.paragraph) block.normal = { fences: block.fences, - paragraph: block.paragraph + paragraph: block.paragraph, + table: block.table, + nptable: block.nptable }; block.gfm = { fences: /^ *(`{3,}|~{3,}) *(\w+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/, - paragraph: /^/ + paragraph: /^/, + table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*\n)*)\n*/, + nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*\n)*)\n*/ }; block.gfm.paragraph = replace(block.paragraph) - ('(?!', '(?!' + block.gfm.fences.source.replace('\\1', '\\2') + '|') + ('(?!', '(?!' + + block.gfm.fences.source.replace('\\1', '\\2') + + '|' + block.gfm.table.source + + '|' + block.gfm.nptable.source + + '|') (); /** @@ -86,6 +96,14 @@ function Lexer(options) { block.fences = block.normal.fences; block.paragraph = block.normal.paragraph; } + + if (this.options.gfm && this.options.tables) { + block.table = block.gfm.table; + block.nptable = block.gfm.nptable; + } else { + block.table = block.normal.table; + block.nptable = block.normal.nptable; + } } Lexer.rules = block; @@ -159,6 +177,38 @@ Lexer.prototype.token = function(src, top) { continue; } + // table no leading pipe (gfm) + if (top && (cap = block.nptable.exec(src))) { + src = src.substring(cap[0].length); + + item = { + type: 'table', + header: cap[1].replace(/(^ *| *\| *$)/g, '').split(/ *\| */), + align: cap[2].replace(/(^ *|\| *$)/g, '').split(/ *\| */), + cells: cap[3].replace(/\n$/, '').split('\n') + }; + + for (i = 0; i < item.align.length; i++) { + if (/^ *-+: *$/.test(item.align[i])) { + item.align[i] = 'right'; + } else if (/^ *:-+: *$/.test(item.align[i])) { + item.align[i] = 'center'; + } else if (/^ *:-+ *$/.test(item.align[i])) { + item.align[i] = 'left'; + } else { + item.align[i] = null; + } + } + + for (i = 0; i < item.cells.length; i++) { + item.cells[i] = item.cells[i].split(/ *\| */); + } + + this.tokens.push(item); + + continue; + } + // lheading if (cap = block.lheading.exec(src)) { src = src.substring(cap[0].length); @@ -287,6 +337,40 @@ Lexer.prototype.token = function(src, top) { continue; } + // table (gfm) + if (top && (cap = block.table.exec(src))) { + src = src.substring(cap[0].length); + + item = { + type: 'table', + header: cap[1].replace(/(^ *| *\| *$)/g, '').split(/ *\| */), + align: cap[2].replace(/(^ *|\| *$)/g, '').split(/ *\| */), + cells: cap[3].replace(/( *\| *)?\n$/, '').split('\n') + }; + + for (i = 0; i < item.align.length; i++) { + if (/^ *-+: *$/.test(item.align[i])) { + item.align[i] = 'right'; + } else if (/^ *:-+: *$/.test(item.align[i])) { + item.align[i] = 'center'; + } else if (/^ *:-+ *$/.test(item.align[i])) { + item.align[i] = 'left'; + } else { + item.align[i] = null; + } + } + + for (i = 0; i < item.cells.length; i++) { + item.cells[i] = item.cells[i] + .replace(/(^ *\| *| *\| *$)/g, '') + .split(/ *\| */); + } + + this.tokens.push(item); + + continue; + } + // top-level paragraph if (top && (cap = block.paragraph.exec(src))) { src = src.substring(cap[0].length); @@ -322,7 +406,7 @@ Lexer.prototype.token = function(src, top) { */ var inline = { - escape: /^\\([\\`*{}\[\]()#+\-.!_>])/, + escape: /^\\([\\`*{}\[\]()#+\-.!_>|])/, autolink: /^<([^ >]+(@|:\/)[^ >]+)>/, url: noop, tag: /^|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/, @@ -660,6 +744,45 @@ Parser.prototype.tok = function() { + this.token.text + '\n'; } + case 'table': { + var thead + , heading + , i + , tbody + , row + , cell + , j; + + // header + thead = '\n\n'; + for (i = 0; i < this.token.header.length; i++) { + heading = this.inline.output(this.token.header[i]); + thead += this.token.align[i] + ? '' + heading + '\n' + : '' + heading + '\n'; + } + thead += '\n\n'; + + // body + tbody = '\n' + for (i = 0; i < this.token.cells.length; i++) { + row = this.token.cells[i]; + tbody += '\n'; + for (j = 0; j < row.length; j++) { + cell = this.inline.output(row[j]); + tbody += this.token.align[j] + ? '' + cell + '\n' + : '' + cell + '\n'; + } + tbody += '\n'; + } + tbody += '\n'; + + return '\n' + + thead + + tbody + + '
\n' + } case 'blockquote_start': { var body = ''; @@ -785,6 +908,7 @@ marked.setOptions = function(opt) { marked.defaults = { gfm: true, + tables: true, pedantic: false, sanitize: false, silent: false, diff --git a/test/new/gfm_tables.html b/test/new/gfm_tables.html new file mode 100644 index 00000000..315e1548 --- /dev/null +++ b/test/new/gfm_tables.html @@ -0,0 +1,38 @@ + + + + + + + + +
Heading 1Heading 2
Cell 1Cell 2
Cell 3Cell 4
+ + + + + + + + +
Header 1Header 2Header 3Header 4
Cell 1Cell 2Cell 3Cell 4
Cell 5Cell 6Cell 7Cell 8
+
Test code
+ + + + + + + + +
Header 1Header 2
Cell 1Cell 2
Cell 3Cell 4
+ + + + + + + + +
Header 1Header 2Header 3Header 4
Cell 1Cell 2Cell 3Cell 4
Cell 5Cell 6Cell 7Cell 8
+ diff --git a/test/new/gfm_tables.text b/test/new/gfm_tables.text new file mode 100644 index 00000000..5fd6321c --- /dev/null +++ b/test/new/gfm_tables.text @@ -0,0 +1,21 @@ +| Heading 1 | Heading 2 +| --------- | --------- +| Cell 1 | Cell 2 +| Cell 3 | Cell 4 + +| Header 1 | Header 2 | Header 3 | Header 4 | +| :------: | -------: | :------- | -------- | +| Cell 1 | Cell 2 | Cell 3 | Cell 4 | +| Cell 5 | Cell 6 | Cell 7 | Cell 8 | + + Test code + +Header 1 | Header 2 +-------- | -------- +Cell 1 | Cell 2 +Cell 3 | Cell 4 + +Header 1|Header 2|Header 3|Header 4 +:-------|:------:|-------:|-------- +Cell 1 |Cell 2 |Cell 3 |Cell 4 +*Cell 5*|Cell 6 |Cell 7 |Cell 8 diff --git a/test/tests/gfm_tables.html b/test/tests/gfm_tables.html new file mode 100644 index 00000000..315e1548 --- /dev/null +++ b/test/tests/gfm_tables.html @@ -0,0 +1,38 @@ + + + + + + + + +
Heading 1Heading 2
Cell 1Cell 2
Cell 3Cell 4
+ + + + + + + + +
Header 1Header 2Header 3Header 4
Cell 1Cell 2Cell 3Cell 4
Cell 5Cell 6Cell 7Cell 8
+
Test code
+ + + + + + + + +
Header 1Header 2
Cell 1Cell 2
Cell 3Cell 4
+ + + + + + + + +
Header 1Header 2Header 3Header 4
Cell 1Cell 2Cell 3Cell 4
Cell 5Cell 6Cell 7Cell 8
+ diff --git a/test/tests/gfm_tables.text b/test/tests/gfm_tables.text new file mode 100644 index 00000000..5fd6321c --- /dev/null +++ b/test/tests/gfm_tables.text @@ -0,0 +1,21 @@ +| Heading 1 | Heading 2 +| --------- | --------- +| Cell 1 | Cell 2 +| Cell 3 | Cell 4 + +| Header 1 | Header 2 | Header 3 | Header 4 | +| :------: | -------: | :------- | -------- | +| Cell 1 | Cell 2 | Cell 3 | Cell 4 | +| Cell 5 | Cell 6 | Cell 7 | Cell 8 | + + Test code + +Header 1 | Header 2 +-------- | -------- +Cell 1 | Cell 2 +Cell 3 | Cell 4 + +Header 1|Header 2|Header 3|Header 4 +:-------|:------:|-------:|-------- +Cell 1 |Cell 2 |Cell 3 |Cell 4 +*Cell 5*|Cell 6 |Cell 7 |Cell 8