text: // 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.blkIndent,
        max = state.eMarks[line];
  
    return state.src.substr(pos, max - pos);
  }
  
  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;
  
    // 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; }
  
    ch = state.src.charCodeAt(pos++);
    if (ch !== 0x7C/* | */ && ch !== 0x2D/* - */ && ch !== 0x3A/* : */) { 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 !== 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);
      token.map      = [ startLine, startLine + 1 ];
      if (aligns[i]) {
        token.attrs  = [ [ 'style', 'text-align:' + aligns[i] ] ];
      }
  
      token          = state.push('inline', '', 0);
      token.content  = columns[i].trim();
      token.map      = [ startLine, startLine + 1 ];
      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);
      for (i = 0; i < columnCount; i++) {
        token          = state.push('td_open', 'td', 1);
        token.map      = [ nextLine, nextLine + 1 ];
        if (aligns[i]) {
          token.attrs  = [ [ 'style', 'text-align:' + aligns[i] ] ];
        }
  
        token          = state.push('inline', '', 0);
        token.map      = [ nextLine, nextLine + 1 ];
        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;
  };
  