feat(subParsers/table): support for table alignment

Credits to [torcellite (Karthik Balakrishnan)](https://github.com/torcellite)
This commit is contained in:
Estevão Soares dos Santos 2015-07-11 18:21:43 +01:00
parent 3a924e3c7e
commit 1c8c928d0a
22 changed files with 342 additions and 110 deletions

View File

@ -33,7 +33,11 @@ Credits
Bug fixing and late maintainer
* [Hannah Wolfe](https://github.com/ErisDS)<br/>
Bug fixes
* [Alexandre Courtiol](https://github.com/acourtiol)
* [Alexandre Courtiol](https://github.com/acourtiol)<br/>
Bug fixes and build optimization
* [Karthik Balakrishnan](https://github.com/torcellite)<br/>
Support for table alignment
- Original Project
* [John Gruber](http://daringfireball.net/projects/markdown/)<br/>

94
dist/showdown.js vendored
View File

@ -17,7 +17,8 @@ var showdown = {},
simplifiedAutoLink: false,
literalMidWordUnderscores: false,
strikethrough: false,
tables: false
tables: false,
tablesHeaderId: false
},
globalOptions = JSON.parse(JSON.stringify(defaultOptions)); //clone default options out of laziness =P
@ -2012,46 +2013,68 @@ showdown.subParser('tables', function (text, options, globals) {
var table = function () {
var tables = {},
style = 'text-align:left;',
filter;
tables.th = function (header) {
if (header.trim() === '') {
tables.th = function (header, style) {
var id = '';
header = header.trim();
if (header === '') {
return '';
}
var id = header.trim().replace(/ /g, '_').toLowerCase();
return '<th id="' + id + '" style="' + style + '">' + header + '</th>';
if (options.tableHeaderId) {
id = ' id="' + header.replace(/ /g, '_').toLowerCase() + '"';
}
if (style.trim() === '') {
style = '';
} else {
style = ' style="' + style + '"';
}
return '<th' + id + style + '>' + header + '</th>';
};
tables.td = function (cell) {
var subText = showdown.subParser('blockGamut')(cell, options, globals);
return '<td style="' + style + '">' + subText + '</td>';
tables.td = function (cell, style) {
var subText = showdown.subParser('spanGamut')(cell.trim(), options, globals);
if (style.trim() === '') {
style = '';
} else {
style = ' style="' + style + '"';
}
return '<td' + style + '>' + subText + '</td>';
};
tables.ths = function () {
var out = '',
i = 0,
hs = [].slice.apply(arguments);
hs = [].slice.apply(arguments[0]),
style = [].slice.apply(arguments[1]);
for (i; i < hs.length; i += 1) {
out += tables.th(hs[i]) + '\n';
out += tables.th(hs[i], style[i]) + '\n';
}
return out;
};
tables.tds = function () {
var out = '', i = 0, ds = [].slice.apply(arguments);
var out = '',
i = 0,
ds = [].slice.apply(arguments[0]),
style = [].slice.apply(arguments[1]);
for (i; i < ds.length; i += 1) {
out += tables.td(ds[i]) + '\n';
out += tables.td(ds[i], style[i]) + '\n';
}
return out;
};
tables.thead = function () {
var out,
hs = [].slice.apply(arguments);
hs = [].slice.apply(arguments[0]),
style = [].slice.apply(arguments[1]);
out = '<thead>\n';
out += '<tr>\n';
out += tables.ths.apply(this, hs);
out += tables.ths.apply(this, [hs, style]);
out += '</tr>\n';
out += '</thead>\n';
return out;
@ -2059,9 +2082,11 @@ showdown.subParser('tables', function (text, options, globals) {
tables.tr = function () {
var out,
cs = [].slice.apply(arguments);
cs = [].slice.apply(arguments[0]),
style = [].slice.apply(arguments[1]);
out = '<tr>\n';
out += tables.tds.apply(this, cs);
out += tables.tds.apply(this, [cs, style]);
out += '</tr>\n';
return out;
};
@ -2072,15 +2097,44 @@ showdown.subParser('tables', function (text, options, globals) {
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 = [];
var tbl = [],
align = lines[i + 1].trim(),
styles = [],
j = 0;
if (align.match(/^[|][-=|: ]+[|]$/)) {
styles = align.substring(1, align.length - 1).split('|');
for (j = 0; j < styles.length; ++j) {
styles[j] = styles[j].trim();
if (styles[j].match(/^[:][-=| ]+[:]$/)) {
styles[j] = 'text-align:center;';
} else if (styles[j].match(/^[-=| ]+[:]$/)) {
styles[j] = 'text-align:right;';
} else if (styles[j].match(/^[:][-=| ]+$/)) {
styles[j] = 'text-align:left;';
} else {
styles[j] = '';
}
}
}
tbl.push('<table>');
hs = line.substring(1, line.length - 1).split('|');
tbl.push(tables.thead.apply(this, hs));
if (styles.length === 0) {
for (j = 0; j < hs.length; ++j) {
styles.push('text-align:left');
}
}
tbl.push(tables.thead.apply(this, [hs, styles]));
line = lines[++i];
if (!line.trim().match(/^[|][-=|: ]+[|]$/)) {
// not a table rolling back
@ -2090,7 +2144,7 @@ showdown.subParser('tables', function (text, options, globals) {
tbl.push('<tbody>');
while (line.trim().match(/^[|].*[|]$/)) {
line = line.trim();
tbl.push(tables.tr.apply(this, line.substring(1, line.length - 1).split('|')));
tbl.push(tables.tr.apply(this, [line.substring(1, line.length - 1).split('|'), styles]));
line = lines[++i];
}
tbl.push('</tbody>');

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

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

View File

@ -4,46 +4,68 @@ showdown.subParser('tables', function (text, options, globals) {
var table = function () {
var tables = {},
style = 'text-align:left;',
filter;
tables.th = function (header) {
if (header.trim() === '') {
tables.th = function (header, style) {
var id = '';
header = header.trim();
if (header === '') {
return '';
}
var id = header.trim().replace(/ /g, '_').toLowerCase();
return '<th id="' + id + '" style="' + style + '">' + header + '</th>';
if (options.tableHeaderId) {
id = ' id="' + header.replace(/ /g, '_').toLowerCase() + '"';
}
if (style.trim() === '') {
style = '';
} else {
style = ' style="' + style + '"';
}
return '<th' + id + style + '>' + header + '</th>';
};
tables.td = function (cell) {
var subText = showdown.subParser('blockGamut')(cell, options, globals);
return '<td style="' + style + '">' + subText + '</td>';
tables.td = function (cell, style) {
var subText = showdown.subParser('spanGamut')(cell.trim(), options, globals);
if (style.trim() === '') {
style = '';
} else {
style = ' style="' + style + '"';
}
return '<td' + style + '>' + subText + '</td>';
};
tables.ths = function () {
var out = '',
i = 0,
hs = [].slice.apply(arguments);
hs = [].slice.apply(arguments[0]),
style = [].slice.apply(arguments[1]);
for (i; i < hs.length; i += 1) {
out += tables.th(hs[i]) + '\n';
out += tables.th(hs[i], style[i]) + '\n';
}
return out;
};
tables.tds = function () {
var out = '', i = 0, ds = [].slice.apply(arguments);
var out = '',
i = 0,
ds = [].slice.apply(arguments[0]),
style = [].slice.apply(arguments[1]);
for (i; i < ds.length; i += 1) {
out += tables.td(ds[i]) + '\n';
out += tables.td(ds[i], style[i]) + '\n';
}
return out;
};
tables.thead = function () {
var out,
hs = [].slice.apply(arguments);
hs = [].slice.apply(arguments[0]),
style = [].slice.apply(arguments[1]);
out = '<thead>\n';
out += '<tr>\n';
out += tables.ths.apply(this, hs);
out += tables.ths.apply(this, [hs, style]);
out += '</tr>\n';
out += '</thead>\n';
return out;
@ -51,9 +73,11 @@ showdown.subParser('tables', function (text, options, globals) {
tables.tr = function () {
var out,
cs = [].slice.apply(arguments);
cs = [].slice.apply(arguments[0]),
style = [].slice.apply(arguments[1]);
out = '<tr>\n';
out += tables.tds.apply(this, cs);
out += tables.tds.apply(this, [cs, style]);
out += '</tr>\n';
return out;
};
@ -64,15 +88,44 @@ showdown.subParser('tables', function (text, options, globals) {
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 = [];
var tbl = [],
align = lines[i + 1].trim(),
styles = [],
j = 0;
if (align.match(/^[|][-=|: ]+[|]$/)) {
styles = align.substring(1, align.length - 1).split('|');
for (j = 0; j < styles.length; ++j) {
styles[j] = styles[j].trim();
if (styles[j].match(/^[:][-=| ]+[:]$/)) {
styles[j] = 'text-align:center;';
} else if (styles[j].match(/^[-=| ]+[:]$/)) {
styles[j] = 'text-align:right;';
} else if (styles[j].match(/^[:][-=| ]+$/)) {
styles[j] = 'text-align:left;';
} else {
styles[j] = '';
}
}
}
tbl.push('<table>');
hs = line.substring(1, line.length - 1).split('|');
tbl.push(tables.thead.apply(this, hs));
if (styles.length === 0) {
for (j = 0; j < hs.length; ++j) {
styles.push('text-align:left');
}
}
tbl.push(tables.thead.apply(this, [hs, styles]));
line = lines[++i];
if (!line.trim().match(/^[|][-=|: ]+[|]$/)) {
// not a table rolling back
@ -82,7 +135,7 @@ showdown.subParser('tables', function (text, options, globals) {
tbl.push('<tbody>');
while (line.trim().match(/^[|].*[|]$/)) {
line = line.trim();
tbl.push(tables.tr.apply(this, line.substring(1, line.length - 1).split('|')));
tbl.push(tables.tr.apply(this, [line.substring(1, line.length - 1).split('|'), styles]));
line = lines[++i];
}
tbl.push('</tbody>');

View File

@ -1,20 +1,20 @@
<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 style="text-align:left;">First Header</th>
<th 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>
<td style="text-align:left;">Row 1 Cell 1</td>
<td style="text-align:left;">Row 1 Cell 2</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;">Row 2 Cell 1</td>
<td style="text-align:left;">Row 2 Cell 2</td>
</tr>
</tbody>

View File

@ -1,20 +1,20 @@
<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>First Header</th>
<th>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>
<td>Row 1 Cell 1</td>
<td>Row 1 Cell 2</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>Row 2 Cell 1</td>
<td>Row 2 Cell 2</td>
</tr>
</tbody>

View File

@ -0,0 +1,21 @@
<table>
<thead>
<tr>
<th id="first_header">First Header</th>
<th id="second_header">Second Header</th>
</tr>
</thead>
<tbody>
<tr>
<td>Row 1 Cell 1</td>
<td>Row 1 Cell 2</td>
</tr>
<tr>
<td>Row 2 Cell 1</td>
<td>Row 2 Cell 2</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 style="text-align:left;">First Header</th>
<th style="text-align:center;">Second Header</th>
<th style="text-align:right;">Third Header</th>
<th>Fourth Header</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left;">Row 1 Cell 1</td>
<td style="text-align:center;">Row 1 Cell 2</td>
<td style="text-align:right;">Row 1 Cell 3</td>
<td>Row 1 Cell 4</td>
</tr>
<tr>
<td style="text-align:left;">Row 2 Cell 1</td>
<td style="text-align:center;">Row 2 Cell 2</td>
<td style="text-align:right;">Row 2 Cell 3</td>
<td>Row 2 Cell 4</td>
</tr>
<tr>
<td style="text-align:left;">Row 3 Cell 1</td>
<td style="text-align:center;">Row 3 Cell 2</td>
<td style="text-align:right;">Row 3 Cell 3</td>
<td>Row 3 Cell 4</td>
</tr>
<tr>
<td style="text-align:left;">Row 4 Cell 1</td>
<td style="text-align:center;">Row 4 Cell 2</td>
<td style="text-align:right;">Row 4 Cell 3</td>
<td>Row 4 Cell 4</td>
</tr>
<tr>
<td style="text-align:left;">Row 5 Cell 1</td>
<td style="text-align:center;">Row 5 Cell 2</td>
<td style="text-align:right;">Row 5 Cell 3</td>
<td>Row 5 Cell 4</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

@ -1,47 +1,47 @@
<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>
<th>First Header</th>
<th>Second Header</th>
<th>Third Header</th>
<th>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>
<td>Row 1 Cell 1</td>
<td>Row 1 Cell 2</td>
<td>Row 1 Cell 3</td>
<td>Row 1 Cell 4</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>
<td>Row 2 Cell 1</td>
<td>Row 2 Cell 2</td>
<td>Row 2 Cell 3</td>
<td>Row 2 Cell 4</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>
<td>Row 3 Cell 1</td>
<td>Row 3 Cell 2</td>
<td>Row 3 Cell 3</td>
<td>Row 3 Cell 4</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>
<td>Row 4 Cell 1</td>
<td>Row 4 Cell 2</td>
<td>Row 4 Cell 3</td>
<td>Row 4 Cell 4</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>
<td>Row 5 Cell 1</td>
<td>Row 5 Cell 2</td>
<td>Row 5 Cell 3</td>
<td>Row 5 Cell 4</td>
</tr>
</tbody>

View File

@ -0,0 +1,29 @@
<table>
<thead>
<tr>
<th style="text-align:left;">Left-Aligned</th>
<th style="text-align:center;">Center-Aligned</th>
<th style="text-align:right;">Right-Aligned</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left;">col 3 is</td>
<td style="text-align:center;">some wordy paragraph</td>
<td style="text-align:right;">$1600</td>
</tr>
<tr>
<td style="text-align:left;">col 2 is</td>
<td style="text-align:center;">centered</td>
<td style="text-align:right;">$12</td>
</tr>
<tr>
<td style="text-align:left;">zebra stripes</td>
<td style="text-align:center;">are neat</td>
<td style="text-align:right;">$1</td>
</tr>
</tbody>
</table>

View File

@ -0,0 +1,5 @@
| Left-Aligned | Center-Aligned | Right-Aligned |
| :------------ |:--------------------:| -------------:|
| col 3 is | some wordy paragraph | $1600 |
| col 2 is | centered | $12 |
| zebra stripes | are neat | $1 |

View File

@ -5,17 +5,17 @@
<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>
<th>header1</th>
<th>header2</th>
<th>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>
<td>Value1</td>
<td>Value2</td>
<td>Value3</td>
</tr>
</tbody>
@ -26,17 +26,17 @@
<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>
<th>headerA</th>
<th>headerB</th>
<th>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>
<td>ValueA</td>
<td>ValueB</td>
<td>ValueC</td>
</tr>
</tbody>

View File

@ -1,20 +1,20 @@
<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>First Header</th>
<th>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>
<td>Row 1 Cell 1</td>
<td>Row 1 Cell 2</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>Row 2 Cell 1</td>
<td>Row 2 Cell 2</td>
</tr>
</tbody>

View File

@ -6,20 +6,20 @@ vulputate dictum. Vestibulum consequat ultricies nibh, sed tempus nisl mattis a.
<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>First Header</th>
<th>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>
<td>Row 1 Cell 1</td>
<td>Row 1 Cell 2</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>Row 2 Cell 1</td>
<td>Row 2 Cell 2</td>
</tr>
</tbody>

View File

@ -1,8 +1,8 @@
<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>First Header</th>
<th>Second Header</th>
</tr>
</thead>

View File

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

View File

@ -28,8 +28,13 @@ describe('makeHtml() features testsuite', function () {
}
describe('table support', function () {
var converter = new showdown.Converter({tables: true});
var converter;
for (var i = 0; i < tableSuite.length; ++i) {
if (tableSuite[i].name === 'basic_with_header_ids') {
converter = new showdown.Converter({tables: true, tableHeaderId: true});
} else {
converter = new showdown.Converter({tables: true});
}
it(tableSuite[i].name, assertion(tableSuite[i], converter));
}
});