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 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/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 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.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