| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 | // GFM table, https://github.github.com/gfm/#tables-extension-'use strict';var isSpace = require('../common/utils').isSpace;function getLine(state, line) {  var pos = state.bMarks[line] + state.tShift[line],      max = state.eMarks[line];  return state.src.slice(pos, max);}function escapedSplit(str) {  var result = [],      pos = 0,      max = str.length,      ch,      isEscaped = false,      lastPos = 0,      current = '';  ch  = str.charCodeAt(pos);  while (pos < max) {    if (ch === 0x7c/* | */) {      if (!isEscaped) {        // pipe separating cells, '|'        result.push(current + str.substring(lastPos, pos));        current = '';        lastPos = pos + 1;      } else {        // escaped pipe, '\|'        current += str.substring(lastPos, pos - 1);        lastPos = pos;      }    }    isEscaped = (ch === 0x5c/* \ */);    pos++;    ch = str.charCodeAt(pos);  }  result.push(current + str.substring(lastPos));  return result;}module.exports = function table(state, startLine, endLine, silent) {  var ch, lineText, pos, i, l, nextLine, columns, columnCount, token,      aligns, t, tableLines, tbodyLines, oldParentType, terminate,      terminatorRules, firstCh, secondCh;  // should have at least two lines  if (startLine + 2 > endLine) { return false; }  nextLine = startLine + 1;  if (state.sCount[nextLine] < state.blkIndent) { return false; }  // if it's indented more than 3 spaces, it should be a code block  if (state.sCount[nextLine] - state.blkIndent >= 4) { return false; }  // first character of the second line should be '|', '-', ':',  // and no other characters are allowed but spaces;  // basically, this is the equivalent of /^[-:|][-:|\s]*$/ regexp  pos = state.bMarks[nextLine] + state.tShift[nextLine];  if (pos >= state.eMarks[nextLine]) { return false; }  firstCh = state.src.charCodeAt(pos++);  if (firstCh !== 0x7C/* | */ && firstCh !== 0x2D/* - */ && firstCh !== 0x3A/* : */) { return false; }  if (pos >= state.eMarks[nextLine]) { return false; }  secondCh = state.src.charCodeAt(pos++);  if (secondCh !== 0x7C/* | */ && secondCh !== 0x2D/* - */ && secondCh !== 0x3A/* : */ && !isSpace(secondCh)) {    return false;  }  // if first character is '-', then second character must not be a space  // (due to parsing ambiguity with list)  if (firstCh === 0x2D/* - */ && isSpace(secondCh)) { return false; }  while (pos < state.eMarks[nextLine]) {    ch = state.src.charCodeAt(pos);    if (ch !== 0x7C/* | */ && ch !== 0x2D/* - */ && ch !== 0x3A/* : */ && !isSpace(ch)) { return false; }    pos++;  }  lineText = getLine(state, startLine + 1);  columns = lineText.split('|');  aligns = [];  for (i = 0; i < columns.length; i++) {    t = columns[i].trim();    if (!t) {      // allow empty columns before and after table, but not in between columns;      // e.g. allow ` |---| `, disallow ` ---||--- `      if (i === 0 || i === columns.length - 1) {        continue;      } else {        return false;      }    }    if (!/^:?-+:?$/.test(t)) { return false; }    if (t.charCodeAt(t.length - 1) === 0x3A/* : */) {      aligns.push(t.charCodeAt(0) === 0x3A/* : */ ? 'center' : 'right');    } else if (t.charCodeAt(0) === 0x3A/* : */) {      aligns.push('left');    } else {      aligns.push('');    }  }  lineText = getLine(state, startLine).trim();  if (lineText.indexOf('|') === -1) { return false; }  if (state.sCount[startLine] - state.blkIndent >= 4) { return false; }  columns = escapedSplit(lineText);  if (columns.length && columns[0] === '') columns.shift();  if (columns.length && columns[columns.length - 1] === '') columns.pop();  // header row will define an amount of columns in the entire table,  // and align row should be exactly the same (the rest of the rows can differ)  columnCount = columns.length;  if (columnCount === 0 || columnCount !== aligns.length) { return false; }  if (silent) { return true; }  oldParentType = state.parentType;  state.parentType = 'table';  // use 'blockquote' lists for termination because it's  // the most similar to tables  terminatorRules = state.md.block.ruler.getRules('blockquote');  token     = state.push('table_open', 'table', 1);  token.map = tableLines = [ startLine, 0 ];  token     = state.push('thead_open', 'thead', 1);  token.map = [ startLine, startLine + 1 ];  token     = state.push('tr_open', 'tr', 1);  token.map = [ startLine, startLine + 1 ];  for (i = 0; i < columns.length; i++) {    token          = state.push('th_open', 'th', 1);    if (aligns[i]) {      token.attrs  = [ [ 'style', 'text-align:' + aligns[i] ] ];    }    token          = state.push('inline', '', 0);    token.content  = columns[i].trim();    token.children = [];    token          = state.push('th_close', 'th', -1);  }  token     = state.push('tr_close', 'tr', -1);  token     = state.push('thead_close', 'thead', -1);  for (nextLine = startLine + 2; nextLine < endLine; nextLine++) {    if (state.sCount[nextLine] < state.blkIndent) { break; }    terminate = false;    for (i = 0, l = terminatorRules.length; i < l; i++) {      if (terminatorRules[i](state, nextLine, endLine, true)) {        terminate = true;        break;      }    }    if (terminate) { break; }    lineText = getLine(state, nextLine).trim();    if (!lineText) { break; }    if (state.sCount[nextLine] - state.blkIndent >= 4) { break; }    columns = escapedSplit(lineText);    if (columns.length && columns[0] === '') columns.shift();    if (columns.length && columns[columns.length - 1] === '') columns.pop();    if (nextLine === startLine + 2) {      token     = state.push('tbody_open', 'tbody', 1);      token.map = tbodyLines = [ startLine + 2, 0 ];    }    token     = state.push('tr_open', 'tr', 1);    token.map = [ nextLine, nextLine + 1 ];    for (i = 0; i < columnCount; i++) {      token          = state.push('td_open', 'td', 1);      if (aligns[i]) {        token.attrs  = [ [ 'style', 'text-align:' + aligns[i] ] ];      }      token          = state.push('inline', '', 0);      token.content  = columns[i] ? columns[i].trim() : '';      token.children = [];      token          = state.push('td_close', 'td', -1);    }    token = state.push('tr_close', 'tr', -1);  }  if (tbodyLines) {    token = state.push('tbody_close', 'tbody', -1);    tbodyLines[1] = nextLine;  }  token = state.push('table_close', 'table', -1);  tableLines[1] = nextLine;  state.parentType = oldParentType;  state.line = nextLine;  return true;};
 |