Parcourir la source

fix(tables): fix tables to match github's md spec

Now Leading and trailing pipes (|) are optional in tables

Closes #230
Estevão Soares dos Santos il y a 9 ans
Parent
commit
f58f014bc3

+ 104 - 139
dist/showdown.js

@@ -2357,164 +2357,129 @@ showdown.subParser('stripLinkDefinitions', function (text, options, globals) {
 showdown.subParser('tables', function (text, options, globals) {
   'use strict';
 
-  var table = function () {
+  if (!options.tables) {
+    return text;
+  }
 
-    var tables = {},
-        filter;
+  var tableRgx = /^[ \t]{0,3}\|?.+\|.+\n[ \t]{0,3}\|?[ \t]*:?[ \t]*(?:-|=){3,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:-|=){3,}[^]+?(?:\n\n|~0)/gm;
 
-    tables.th = function (header, style) {
-      var id = '';
-      header = header.trim();
-      if (header === '') {
-        return '';
-      }
-      if (options.tableHeaderId) {
-        id = ' id="' + header.replace(/ /g, '_').toLowerCase() + '"';
-      }
-      header = showdown.subParser('spanGamut')(header, options, globals);
-      if (!style || style.trim() === '') {
-        style = '';
-      } else {
-        style = ' style="' + style + '"';
-      }
-      return '<th' + id + style + '>' + header + '</th>';
-    };
+  function parseStyles(sLine) {
+    if (/^:[ \t]*---*$/.test(sLine)) {
+      return ' style="text-align:left;"';
+    } else if (/^---*[ \t]*:[ \t]*$/.test(sLine)) {
+      return ' style="text-align:right;"';
+    } else if (/^:[ \t]*---*[ \t]*:$/.test(sLine)) {
+      return ' style="text-align:center;"';
+    } else {
+      return '';
+    }
+  }
 
-    tables.td = function (cell, style) {
-      var subText = showdown.subParser('spanGamut')(cell.trim(), options, globals);
-      if (!style || style.trim() === '') {
-        style = '';
-      } else {
-        style = ' style="' + style + '"';
+  function parseHeaders(header, style) {
+    var id = '';
+    header = header.trim();
+    if (options.tableHeaderId) {
+      id = ' id="' + header.replace(/ /g, '_').toLowerCase() + '"';
+    }
+    header = showdown.subParser('spanGamut')(header, options, globals);
+
+    return '<th' + id + style + '>' + header + '</th>';
+  }
+
+  function parseCells(cell, style) {
+    var subText = showdown.subParser('spanGamut')(cell, options, globals);
+    return '<td' + style + '>' + subText + '</td>';
+  }
+
+  function buildTable(headers, cells) {
+    var tb = '<table>\n<thead>\n<tr>\n',
+        tblLgn = headers.length;
+
+    for (var i = 0; i < tblLgn; ++i) {
+      tb += headers[i];
+    }
+    tb += '</tr>\n</thead>\n<tbody>\n';
+
+    for (i = 0; i < cells.length; ++i) {
+      tb += '<tr>';
+      for (var ii = 0; ii < tblLgn; ++ii) {
+        tb += cells[i][ii];
       }
-      return '<td' + style + '>' + subText + '</td>';
-    };
+      tb += '</tr>';
+    }
+    tb += '</tbody></table>';
+    return tb;
+  }
 
-    tables.ths = function () {
-      var out = '',
-          i = 0,
-          hs = [].slice.apply(arguments[0]),
-          style = [].slice.apply(arguments[1]);
+  text = globals.converter._dispatch('tables.before', text, options);
 
-      for (i; i < hs.length; i += 1) {
-        out += tables.th(hs[i], style[i]) + '\n';
+  text = text.replace(tableRgx, function (rawTable) {
+
+    var i, tableLines = rawTable.split('\n');
+
+    // strip wrong first and last column if wrapped tables are used
+    for (i = 0; i < tableLines.length; ++i) {
+      if (/^[ \t]{0,3}\|/.test(tableLines[i])) {
+        tableLines[i] = tableLines[i].replace(/^[ \t]{0,3}\|/, '');
+      }
+      if (/\|[ \t]*$/.test(tableLines[i])) {
+        tableLines[i] = tableLines[i].replace(/\|[ \t]*$/, '');
       }
+    }
 
-      return out;
-    };
+    var rawHeaders = tableLines[0].split('|').map(function (s) { return s.trim();}),
+        rawStyles = tableLines[1].split('|').map(function (s) { return s.trim();}),
+        rawCells = [],
+        headers = [],
+        styles = [],
+        cells = [];
 
-    tables.tds = function () {
-      var out = '',
-          i = 0,
-          ds = [].slice.apply(arguments[0]),
-          style = [].slice.apply(arguments[1]);
+    tableLines.shift();
+    tableLines.shift();
 
-      for (i; i < ds.length; i += 1) {
-        out += tables.td(ds[i], style[i]) + '\n';
+    for (i = 0; i < tableLines.length; ++i) {
+      if (tableLines[i].trim() === '') {
+        continue;
       }
-      return out;
-    };
+      rawCells.push(
+        tableLines[i]
+          .split('|')
+          .map(function (s) {
+            return s.trim();
+          })
+      );
+    }
 
-    tables.thead = function () {
-      var out,
-          hs = [].slice.apply(arguments[0]),
-          style = [].slice.apply(arguments[1]);
-
-      out = '<thead>\n';
-      out += '<tr>\n';
-      out += tables.ths.apply(this, [hs, style]);
-      out += '</tr>\n';
-      out += '</thead>\n';
-      return out;
-    };
+    if (rawHeaders.length < rawStyles.length) {
+      return rawTable;
+    }
 
-    tables.tr = function () {
-      var out,
-        cs = [].slice.apply(arguments[0]),
-        style = [].slice.apply(arguments[1]);
+    for (i = 0; i < rawStyles.length; ++i) {
+      styles.push(parseStyles(rawStyles[i]));
+    }
 
-      out = '<tr>\n';
-      out += tables.tds.apply(this, [cs, style]);
-      out += '</tr>\n';
-      return out;
-    };
+    for (i = 0; i < rawHeaders.length; ++i) {
+      if (showdown.helper.isUndefined(styles[i])) {
+        styles[i] = '';
+      }
+      headers.push(parseHeaders(rawHeaders[i], styles[i]));
+    }
 
-    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 = [],
-              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('|');
+    for (i = 0; i < rawCells.length; ++i) {
+      var row = [];
+      for (var ii = 0; ii < headers.length; ++ii) {
+        if (showdown.helper.isUndefined(rawCells[i][ii])) {
 
-          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
-            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('|'), styles]));
-              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);
+        row.push(parseCells(rawCells[i][ii], styles[ii]));
       }
-      return out.join('\n');
-    };
-    return {parse: filter};
-  };
+      cells.push(row);
+    }
 
-  if (options.tables) {
-    text = globals.converter._dispatch('tables.before', text, options);
-    var tableParser = table();
-    text = tableParser.parse(text);
-    text = globals.converter._dispatch('tables.after', text, options);
-  }
+    return buildTable(headers, cells);
+  });
+
+  text = globals.converter._dispatch('tables.after', text, options);
 
   return text;
 });

Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
dist/showdown.js.map


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
dist/showdown.min.js


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
dist/showdown.min.js.map


+ 111 - 146
src/subParsers/tables.js

@@ -1,164 +1,129 @@
 showdown.subParser('tables', function (text, options, globals) {
   'use strict';
 
-  var table = function () {
+  if (!options.tables) {
+    return text;
+  }
 
-    var tables = {},
-        filter;
+  var tableRgx = /^[ \t]{0,3}\|?.+\|.+\n[ \t]{0,3}\|?[ \t]*:?[ \t]*(?:-|=){3,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:-|=){3,}[^]+?(?:\n\n|~0)/gm;
+
+  function parseStyles(sLine) {
+    if (/^:[ \t]*---*$/.test(sLine)) {
+      return ' style="text-align:left;"';
+    } else if (/^---*[ \t]*:[ \t]*$/.test(sLine)) {
+      return ' style="text-align:right;"';
+    } else if (/^:[ \t]*---*[ \t]*:$/.test(sLine)) {
+      return ' style="text-align:center;"';
+    } else {
+      return '';
+    }
+  }
 
-    tables.th = function (header, style) {
-      var id = '';
-      header = header.trim();
-      if (header === '') {
-        return '';
-      }
-      if (options.tableHeaderId) {
-        id = ' id="' + header.replace(/ /g, '_').toLowerCase() + '"';
-      }
-      header = showdown.subParser('spanGamut')(header, options, globals);
-      if (!style || style.trim() === '') {
-        style = '';
-      } else {
-        style = ' style="' + style + '"';
-      }
-      return '<th' + id + style + '>' + header + '</th>';
-    };
-
-    tables.td = function (cell, style) {
-      var subText = showdown.subParser('spanGamut')(cell.trim(), options, globals);
-      if (!style || style.trim() === '') {
-        style = '';
-      } else {
-        style = ' style="' + style + '"';
+  function parseHeaders(header, style) {
+    var id = '';
+    header = header.trim();
+    if (options.tableHeaderId) {
+      id = ' id="' + header.replace(/ /g, '_').toLowerCase() + '"';
+    }
+    header = showdown.subParser('spanGamut')(header, options, globals);
+
+    return '<th' + id + style + '>' + header + '</th>';
+  }
+
+  function parseCells(cell, style) {
+    var subText = showdown.subParser('spanGamut')(cell, options, globals);
+    return '<td' + style + '>' + subText + '</td>';
+  }
+
+  function buildTable(headers, cells) {
+    var tb = '<table>\n<thead>\n<tr>\n',
+        tblLgn = headers.length;
+
+    for (var i = 0; i < tblLgn; ++i) {
+      tb += headers[i];
+    }
+    tb += '</tr>\n</thead>\n<tbody>\n';
+
+    for (i = 0; i < cells.length; ++i) {
+      tb += '<tr>';
+      for (var ii = 0; ii < tblLgn; ++ii) {
+        tb += cells[i][ii];
       }
-      return '<td' + style + '>' + subText + '</td>';
-    };
+      tb += '</tr>';
+    }
+    tb += '</tbody></table>';
+    return tb;
+  }
+
+  text = globals.converter._dispatch('tables.before', text, options);
 
-    tables.ths = function () {
-      var out = '',
-          i = 0,
-          hs = [].slice.apply(arguments[0]),
-          style = [].slice.apply(arguments[1]);
+  text = text.replace(tableRgx, function (rawTable) {
 
-      for (i; i < hs.length; i += 1) {
-        out += tables.th(hs[i], style[i]) + '\n';
+    var i, tableLines = rawTable.split('\n');
+
+    // strip wrong first and last column if wrapped tables are used
+    for (i = 0; i < tableLines.length; ++i) {
+      if (/^[ \t]{0,3}\|/.test(tableLines[i])) {
+        tableLines[i] = tableLines[i].replace(/^[ \t]{0,3}\|/, '');
+      }
+      if (/\|[ \t]*$/.test(tableLines[i])) {
+        tableLines[i] = tableLines[i].replace(/\|[ \t]*$/, '');
       }
+    }
 
-      return out;
-    };
+    var rawHeaders = tableLines[0].split('|').map(function (s) { return s.trim();}),
+        rawStyles = tableLines[1].split('|').map(function (s) { return s.trim();}),
+        rawCells = [],
+        headers = [],
+        styles = [],
+        cells = [];
 
-    tables.tds = function () {
-      var out = '',
-          i = 0,
-          ds = [].slice.apply(arguments[0]),
-          style = [].slice.apply(arguments[1]);
+    tableLines.shift();
+    tableLines.shift();
 
-      for (i; i < ds.length; i += 1) {
-        out += tables.td(ds[i], style[i]) + '\n';
+    for (i = 0; i < tableLines.length; ++i) {
+      if (tableLines[i].trim() === '') {
+        continue;
+      }
+      rawCells.push(
+        tableLines[i]
+          .split('|')
+          .map(function (s) {
+            return s.trim();
+          })
+      );
+    }
+
+    if (rawHeaders.length < rawStyles.length) {
+      return rawTable;
+    }
+
+    for (i = 0; i < rawStyles.length; ++i) {
+      styles.push(parseStyles(rawStyles[i]));
+    }
+
+    for (i = 0; i < rawHeaders.length; ++i) {
+      if (showdown.helper.isUndefined(styles[i])) {
+        styles[i] = '';
       }
-      return out;
-    };
-
-    tables.thead = function () {
-      var out,
-          hs = [].slice.apply(arguments[0]),
-          style = [].slice.apply(arguments[1]);
-
-      out = '<thead>\n';
-      out += '<tr>\n';
-      out += tables.ths.apply(this, [hs, style]);
-      out += '</tr>\n';
-      out += '</thead>\n';
-      return out;
-    };
-
-    tables.tr = function () {
-      var out,
-        cs = [].slice.apply(arguments[0]),
-        style = [].slice.apply(arguments[1]);
-
-      out = '<tr>\n';
-      out += tables.tds.apply(this, [cs, style]);
-      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 = [],
-              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('|');
-
-          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
-            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('|'), styles]));
-              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;
-          }
+      headers.push(parseHeaders(rawHeaders[i], styles[i]));
+    }
+
+    for (i = 0; i < rawCells.length; ++i) {
+      var row = [];
+      for (var ii = 0; ii < headers.length; ++ii) {
+        if (showdown.helper.isUndefined(rawCells[i][ii])) {
+
         }
-        out.push(line);
+        row.push(parseCells(rawCells[i][ii], styles[ii]));
       }
-      return out.join('\n');
-    };
-    return {parse: filter};
-  };
-
-  if (options.tables) {
-    text = globals.converter._dispatch('tables.before', text, options);
-    var tableParser = table();
-    text = tableParser.parse(text);
-    text = globals.converter._dispatch('tables.after', text, options);
-  }
+      cells.push(row);
+    }
+
+    return buildTable(headers, cells);
+  });
+
+  text = globals.converter._dispatch('tables.after', text, options);
 
   return text;
 });

+ 24 - 0
test/features/tables/gh-style-tables.html

@@ -0,0 +1,24 @@
+<table>
+    <thead>
+    <tr>
+        <th>First Header</th>
+        <th>Second Header</th>
+        <th>Third Header</th>
+    </tr>
+    </thead>
+
+    <tbody>
+    <tr>
+        <td>Content Cell</td>
+        <td>Content Cell</td>
+        <td>C</td>
+    </tr>
+
+    <tr>
+        <td>Content Cell</td>
+        <td>Content Cell</td>
+        <td>C</td>
+    </tr>
+
+    </tbody>
+</table>

+ 4 - 0
test/features/tables/gh-style-tables.md

@@ -0,0 +1,4 @@
+First Header  | Second Header|Third Header
+------------- | -------------|---
+Content Cell  | Content Cell|C
+Content Cell  | Content Cell|C

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff