feat(tables): add support for GFM tables

Github Flavored Markdown supports a specific table syntax. Table support was already available as an extension.
With this commit, the feature was moved to core, adding this feature to showdown through an option called "tables".

Related to #164
This commit is contained in:
Estevão Soares dos Santos 2015-07-11 16:44:24 +01:00
parent 43e9448d6e
commit 3a924e3c7e
26 changed files with 378 additions and 4 deletions

BIN
dist/showdown.js vendored

Binary file not shown.

BIN
dist/showdown.js.map vendored

Binary file not shown.

BIN
dist/showdown.min.js vendored

Binary file not shown.

Binary file not shown.

View File

@ -44,6 +44,7 @@ showdown.Converter = function (converterOptions) {
* @type {string[]}
*/
parserOrder = [
'tables',
'githubCodeBlocks',
'hashHTMLBlocks',
'stripLinkDefinitions',

View File

@ -14,7 +14,8 @@ var showdown = {},
parseImgDimensions: false,
simplifiedAutoLink: false,
literalMidWordUnderscores: false,
strikethrough: false
strikethrough: false,
tables: false
},
globalOptions = JSON.parse(JSON.stringify(defaultOptions)); //clone default options out of laziness =P

View File

@ -11,7 +11,7 @@ showdown.subParser('blockGamut', function (text, options, globals) {
var key = showdown.subParser('hashBlock')('<hr />', options, globals);
text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm, key);
text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm, key);
text = text.replace(/^[ ]{0,2}([ ]?\_[ ]?){3,}[ \t]*$/gm, key);
text = text.replace(/^[ ]{0,2}([ ]?_[ ]?){3,}[ \t]*$/gm, key);
text = showdown.subParser('lists')(text, options, globals);
text = showdown.subParser('codeBlocks')(text, options, globals);

108
src/subParsers/tables.js Normal file
View File

@ -0,0 +1,108 @@
showdown.subParser('tables', function (text, options, globals) {
'use strict';
var table = function () {
var tables = {},
style = 'text-align:left;',
filter;
tables.th = function (header) {
if (header.trim() === '') {
return '';
}
var id = header.trim().replace(/ /g, '_').toLowerCase();
return '<th id="' + id + '" style="' + style + '">' + header + '</th>';
};
tables.td = function (cell) {
var subText = showdown.subParser('blockGamut')(cell, options, globals);
return '<td style="' + style + '">' + subText + '</td>';
};
tables.ths = function () {
var out = '',
i = 0,
hs = [].slice.apply(arguments);
for (i; i < hs.length; i += 1) {
out += tables.th(hs[i]) + '\n';
}
return out;
};
tables.tds = function () {
var out = '', i = 0, ds = [].slice.apply(arguments);
for (i; i < ds.length; i += 1) {
out += tables.td(ds[i]) + '\n';
}
return out;
};
tables.thead = function () {
var out,
hs = [].slice.apply(arguments);
out = '<thead>\n';
out += '<tr>\n';
out += tables.ths.apply(this, hs);
out += '</tr>\n';
out += '</thead>\n';
return out;
};
tables.tr = function () {
var out,
cs = [].slice.apply(arguments);
out = '<tr>\n';
out += tables.tds.apply(this, cs);
out += '</tr>\n';
return out;
};
filter = function (text) {
var i = 0,
lines = text.split('\n'),
line,
hs,
out = [];
for (i; i < lines.length; i += 1) {
line = lines[i];
// looks like a table heading
if (line.trim().match(/^[|].*[|]$/)) {
line = line.trim();
var tbl = [];
tbl.push('<table>');
hs = line.substring(1, line.length - 1).split('|');
tbl.push(tables.thead.apply(this, hs));
line = lines[++i];
if (!line.trim().match(/^[|][-=|: ]+[|]$/)) {
// not a table rolling back
line = lines[--i];
} else {
line = lines[++i];
tbl.push('<tbody>');
while (line.trim().match(/^[|].*[|]$/)) {
line = line.trim();
tbl.push(tables.tr.apply(this, line.substring(1, line.length - 1).split('|')));
line = lines[++i];
}
tbl.push('</tbody>');
tbl.push('</table>');
// we are done with this table and we move along
out.push(tbl.join('\n'));
continue;
}
}
out.push(line);
}
return out.join('\n');
};
return {parse: filter};
};
if (options.tables) {
var tableParser = table();
return tableParser.parse(text);
} else {
return text;
}
});

View File

@ -0,0 +1,21 @@
<table>
<thead>
<tr>
<th id="first_header" style="text-align:left;"> First Header </th>
<th id="second_header" style="text-align:left;"> Second Header </th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left;"><p>Row 1 Cell 1 </p></td>
<td style="text-align:left;"><p>Row 1 Cell 2 </p></td>
</tr>
<tr>
<td style="text-align:left;"><p>Row 2 Cell 1 </p></td>
<td style="text-align:left;"><p>Row 2 Cell 2 </p></td>
</tr>
</tbody>
</table>

View File

@ -0,0 +1,4 @@
| First Header | Second Header |
| :------------ | :------------ |
| Row 1 Cell 1 | Row 1 Cell 2 |
| Row 2 Cell 1 | Row 2 Cell 2 |

View File

@ -0,0 +1,21 @@
<table>
<thead>
<tr>
<th id="first_header" style="text-align:left;"> First Header </th>
<th id="second_header" style="text-align:left;"> Second Header </th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left;"><p>Row 1 Cell 1 </p></td>
<td style="text-align:left;"><p>Row 1 Cell 2 </p></td>
</tr>
<tr>
<td style="text-align:left;"><p>Row 2 Cell 1 </p></td>
<td style="text-align:left;"><p>Row 2 Cell 2 </p></td>
</tr>
</tbody>
</table>

View File

@ -0,0 +1,4 @@
| First Header | Second Header |
| ------------- | ------------- |
| Row 1 Cell 1 | Row 1 Cell 2 |
| Row 2 Cell 1 | Row 2 Cell 2 |

View File

@ -0,0 +1,48 @@
<table>
<thead>
<tr>
<th id="first_header" style="text-align:left;"> First Header </th>
<th id="second_header" style="text-align:left;"> Second Header </th>
<th id="third_header" style="text-align:left;"> Third Header </th>
<th id="fourth_header" style="text-align:left;"> Fourth Header </th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left;"><p>Row 1 Cell 1 </p></td>
<td style="text-align:left;"><p>Row 1 Cell 2 </p></td>
<td style="text-align:left;"><p>Row 1 Cell 3 </p></td>
<td style="text-align:left;"><p>Row 1 Cell 4 </p></td>
</tr>
<tr>
<td style="text-align:left;"><p>Row 2 Cell 1 </p></td>
<td style="text-align:left;"><p>Row 2 Cell 2 </p></td>
<td style="text-align:left;"><p>Row 2 Cell 3 </p></td>
<td style="text-align:left;"><p>Row 2 Cell 4 </p></td>
</tr>
<tr>
<td style="text-align:left;"><p>Row 3 Cell 1 </p></td>
<td style="text-align:left;"><p>Row 3 Cell 2 </p></td>
<td style="text-align:left;"><p>Row 3 Cell 3 </p></td>
<td style="text-align:left;"><p>Row 3 Cell 4 </p></td>
</tr>
<tr>
<td style="text-align:left;"><p>Row 4 Cell 1 </p></td>
<td style="text-align:left;"><p>Row 4 Cell 2 </p></td>
<td style="text-align:left;"><p>Row 4 Cell 3 </p></td>
<td style="text-align:left;"><p>Row 4 Cell 4 </p></td>
</tr>
<tr>
<td style="text-align:left;"><p>Row 5 Cell 1 </p></td>
<td style="text-align:left;"><p>Row 5 Cell 2 </p></td>
<td style="text-align:left;"><p>Row 5 Cell 3 </p></td>
<td style="text-align:left;"><p>Row 5 Cell 4 </p></td>
</tr>
</tbody>
</table>

View File

@ -0,0 +1,7 @@
| First Header | Second Header | Third Header | Fourth Header |
| ------------- | ------------- | ------------ | ------------- |
| Row 1 Cell 1 | Row 1 Cell 2 | Row 1 Cell 3 | Row 1 Cell 4 |
| Row 2 Cell 1 | Row 2 Cell 2 | Row 2 Cell 3 | Row 2 Cell 4 |
| Row 3 Cell 1 | Row 3 Cell 2 | Row 3 Cell 3 | Row 3 Cell 4 |
| Row 4 Cell 1 | Row 4 Cell 2 | Row 4 Cell 3 | Row 4 Cell 4 |
| Row 5 Cell 1 | Row 5 Cell 2 | Row 5 Cell 3 | Row 5 Cell 4 |

View File

@ -0,0 +1,43 @@
<h1 id="tabletest">Table Test</h1>
<h2 id="section1">section 1</h2>
<table>
<thead>
<tr>
<th id="header1" style="text-align:left;">header1 </th>
<th id="header2" style="text-align:left;">header2 </th>
<th id="header3" style="text-align:left;">header3</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left;"><p>Value1 </p></td>
<td style="text-align:left;"><p>Value2 </p></td>
<td style="text-align:left;"><p>Value3 </p></td>
</tr>
</tbody>
</table>
<h2 id="section2">section 2</h2>
<table>
<thead>
<tr>
<th id="headera" style="text-align:left;">headerA </th>
<th id="headerb" style="text-align:left;">headerB </th>
<th id="headerc" style="text-align:left;">headerC</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left;"><p>ValueA </p></td>
<td style="text-align:left;"><p>ValueB </p></td>
<td style="text-align:left;"><p>ValueC </p></td>
</tr>
</tbody>
</table>

View File

@ -0,0 +1,17 @@
Table Test
============
section 1
------------
|header1 |header2 |header3|
|-----------|-----------|---------|
|Value1 |Value2 |Value3 |
section 2
-----------
|headerA |headerB |headerC|
|-----------|-----------|---------|
|ValueA |ValueB |ValueC |

View File

@ -0,0 +1,21 @@
<table>
<thead>
<tr>
<th id="first_header" style="text-align:left;"> First Header </th>
<th id="second_header" style="text-align:left;"> Second Header </th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left;"><p>Row 1 Cell 1 </p></td>
<td style="text-align:left;"><p>Row 1 Cell 2 </p></td>
</tr>
<tr>
<td style="text-align:left;"><p>Row 2 Cell 1 </p></td>
<td style="text-align:left;"><p>Row 2 Cell 2 </p></td>
</tr>
</tbody>
</table>

View File

@ -0,0 +1,4 @@
| First Header | Second Header |
| ============= | ============= |
| Row 1 Cell 1 | Row 1 Cell 2 |
| Row 2 Cell 1 | Row 2 Cell 2 |

View File

@ -0,0 +1,33 @@
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent nisi est,
ullamcorper euismod iaculis sed, tristique at neque. Nullam metus risus,
malesuada vitae imperdiet ac, tincidunt eget lacus. Proin ullamcorper
vulputate dictum. Vestibulum consequat ultricies nibh, sed tempus nisl mattis a.</p>
<table>
<thead>
<tr>
<th id="first_header" style="text-align:left;"> First Header </th>
<th id="second_header" style="text-align:left;"> Second Header </th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left;"><p>Row 1 Cell 1 </p></td>
<td style="text-align:left;"><p>Row 1 Cell 2 </p></td>
</tr>
<tr>
<td style="text-align:left;"><p>Row 2 Cell 1 </p></td>
<td style="text-align:left;"><p>Row 2 Cell 2 </p></td>
</tr>
</tbody>
</table>
<p>Phasellus ac porttitor quam. Integer cursus accumsan mauris nec interdum.
Etiam iaculis urna vitae risus facilisis faucibus eu quis risus. Sed aliquet
rutrum dictum. Vivamus pulvinar malesuada ultricies. Pellentesque in commodo
nibh. Maecenas justo erat, sodales vel bibendum a, dignissim in orci. Duis
blandit ornare mi non facilisis. Aliquam rutrum fringilla lacus in semper.
Sed vel pretium lorem.</p>

View File

@ -0,0 +1,16 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent nisi est,
ullamcorper euismod iaculis sed, tristique at neque. Nullam metus risus,
malesuada vitae imperdiet ac, tincidunt eget lacus. Proin ullamcorper
vulputate dictum. Vestibulum consequat ultricies nibh, sed tempus nisl mattis a.
| First Header | Second Header |
| ------------- | ------------- |
| Row 1 Cell 1 | Row 1 Cell 2 |
| Row 2 Cell 1 | Row 2 Cell 2 |
Phasellus ac porttitor quam. Integer cursus accumsan mauris nec interdum.
Etiam iaculis urna vitae risus facilisis faucibus eu quis risus. Sed aliquet
rutrum dictum. Vivamus pulvinar malesuada ultricies. Pellentesque in commodo
nibh. Maecenas justo erat, sodales vel bibendum a, dignissim in orci. Duis
blandit ornare mi non facilisis. Aliquam rutrum fringilla lacus in semper.
Sed vel pretium lorem.

View File

@ -0,0 +1,11 @@
<table>
<thead>
<tr>
<th id="first_header" style="text-align:left;"> First Header </th>
<th id="second_header" style="text-align:left;"> Second Header </th>
</tr>
</thead>
<tbody>
</tbody>
</table>

View File

@ -0,0 +1,2 @@
| First Header | Second Header |
| ------------- | ------------- |

View File

@ -0,0 +1 @@
<p>| First Header | Second Header |</p>

View File

@ -0,0 +1 @@
| First Header | Second Header |

View File

@ -25,7 +25,8 @@ describe('showdown.options', function () {
parseImgDimensions: false,
simplifiedAutoLink: false,
literalMidWordUnderscores: false,
strikethrough: false
strikethrough: false,
tables: false
};
expect(showdown.getDefaultOptions()).to.be.eql(opts);
});

View File

@ -4,7 +4,8 @@
var showdown = require('../../dist/showdown.js'),
bootstrap = require('../bootstrap.js'),
assertion = bootstrap.assertion,
testsuite = bootstrap.getTestSuite('test/features/');
testsuite = bootstrap.getTestSuite('test/features/'),
tableSuite = bootstrap.getTestSuite('test/features/tables/');
describe('makeHtml() features testsuite', function () {
'use strict';
@ -25,4 +26,12 @@ describe('makeHtml() features testsuite', function () {
}
it(testsuite[i].name, assertion(testsuite[i], converter));
}
describe('table support', function () {
var converter = new showdown.Converter({tables: true});
for (var i = 0; i < tableSuite.length; ++i) {
it(tableSuite[i].name, assertion(tableSuite[i], converter));
}
});
});