'use strict'; var whitespace = require('is-whitespace-character'); module.exports = table; var C_BACKSLASH = '\\'; var C_TICK = '`'; var C_DASH = '-'; var C_PIPE = '|'; var C_COLON = ':'; var C_SPACE = ' '; var C_NEWLINE = '\n'; var C_TAB = '\t'; var MIN_TABLE_COLUMNS = 1; var MIN_TABLE_ROWS = 2; var TABLE_ALIGN_LEFT = 'left'; var TABLE_ALIGN_CENTER = 'center'; var TABLE_ALIGN_RIGHT = 'right'; var TABLE_ALIGN_NONE = null; function table(eat, value, silent) { var self = this; var index; var alignments; var alignment; var subvalue; var row; var length; var lines; var queue; var character; var hasDash; var align; var cell; var preamble; var count; var opening; var now; var position; var lineCount; var line; var rows; var table; var lineIndex; var pipeIndex; var first; /* Exit when not in gfm-mode. */ if (!self.options.gfm) { return; } /* Get the rows. * Detecting tables soon is hard, so there are some * checks for performance here, such as the minimum * number of rows, and allowed characters in the * alignment row. */ index = 0; lineCount = 0; length = value.length + 1; lines = []; while (index < length) { lineIndex = value.indexOf(C_NEWLINE, index); pipeIndex = value.indexOf(C_PIPE, index + 1); if (lineIndex === -1) { lineIndex = value.length; } if (pipeIndex === -1 || pipeIndex > lineIndex) { if (lineCount < MIN_TABLE_ROWS) { return; } break; } lines.push(value.slice(index, lineIndex)); lineCount++; index = lineIndex + 1; } /* Parse the alignment row. */ subvalue = lines.join(C_NEWLINE); alignments = lines.splice(1, 1)[0] || []; index = 0; length = alignments.length; lineCount--; alignment = false; align = []; while (index < length) { character = alignments.charAt(index); if (character === C_PIPE) { hasDash = null; if (alignment === false) { if (first === false) { return; } } else { align.push(alignment); alignment = false; } first = false; } else if (character === C_DASH) { hasDash = true; alignment = alignment || TABLE_ALIGN_NONE; } else if (character === C_COLON) { if (alignment === TABLE_ALIGN_LEFT) { alignment = TABLE_ALIGN_CENTER; } else if (hasDash && alignment === TABLE_ALIGN_NONE) { alignment = TABLE_ALIGN_RIGHT; } else { alignment = TABLE_ALIGN_LEFT; } } else if (!whitespace(character)) { return; } index++; } if (alignment !== false) { align.push(alignment); } /* Exit when without enough columns. */ if (align.length < MIN_TABLE_COLUMNS) { return; } /* istanbul ignore if - never used (yet) */ if (silent) { return true; } /* Parse the rows. */ position = -1; rows = []; table = eat(subvalue).reset({ type: 'table', align: align, children: rows }); while (++position < lineCount) { line = lines[position]; row = {type: 'tableRow', children: []}; /* Eat a newline character when this is not the * first row. */ if (position) { eat(C_NEWLINE); } /* Eat the row. */ eat(line).reset(row, table); length = line.length + 1; index = 0; queue = ''; cell = ''; preamble = true; count = null; opening = null; while (index < length) { character = line.charAt(index); if (character === C_TAB || character === C_SPACE) { if (cell) { queue += character; } else { eat(character); } index++; continue; } if (character === '' || character === C_PIPE) { if (preamble) { eat(character); } else { if (character && opening) { queue += character; index++; continue; } if ((cell || character) && !preamble) { subvalue = cell; if (queue.length > 1) { if (character) { subvalue += queue.slice(0, queue.length - 1); queue = queue.charAt(queue.length - 1); } else { subvalue += queue; queue = ''; } } now = eat.now(); eat(subvalue)({ type: 'tableCell', children: self.tokenizeInline(cell, now) }, row); } eat(queue + character); queue = ''; cell = ''; } } else { if (queue) { cell += queue; queue = ''; } cell += character; if (character === C_BACKSLASH && index !== length - 2) { cell += line.charAt(index + 1); index++; } if (character === C_TICK) { count = 1; while (line.charAt(index + 1) === character) { cell += character; index++; count++; } if (!opening) { opening = count; } else if (count >= opening) { opening = 0; } } } preamble = false; index++; } /* Eat the alignment row. */ if (!position) { eat(C_NEWLINE + alignments); } } return table; }