diff --git a/src/Components/Blocks/Table.php b/src/Components/Blocks/Table.php new file mode 100644 index 0000000..9d4977e --- /dev/null +++ b/src/Components/Blocks/Table.php @@ -0,0 +1,221 @@ + */ + private $alignments; + + /** @var array */ + private $headerCells; + + /** @var array> */ + private $rows; + + /** + * @param array $alignments + * @param array $headerCells + * @param array> $rows + * @param bool $acquired + */ + public function __construct($alignments, $headerCells, $rows, $acquired = false) + { + $this->alignments = $alignments; + $this->headerCells = $headerCells; + $this->rows = $rows; + $this->acquired = $acquired; + } + + /** + * @param Context $Context + * @param Block|null $Block + * @param State|null $State + * @return static|null + */ + public static function build( + Context $Context, + Block $Block = null, + State $State = null + ) { + if (! isset($Block) or ! $Block instanceof Paragraph or $Context->previousEmptyLines() > 0) { + return null; + } + + if ( + \strpos($Block->text(), '|') === false + and \strpos($Context->line()->text(), '|') === false + and \strpos($Context->line()->text(), ':') === false + or \strpos($Block->text(), "\n") !== false + ) { + return null; + } + + if (\chop($Context->line()->text(), ' -:|') !== '') { + return null; + } + + + $alignments = self::parseAlignments($Context->line()->text()); + + if (! isset($alignments)) { + return null; + } + + # ~ + + $headerRow = \trim(\trim($Block->text()), '|'); + + $headerCells = \array_map('trim', \explode('|', $headerRow)); + + if (\count($headerCells) !== \count($alignments)) { + return null; + } + + # ~ + + return new self($alignments, $headerCells, [], true); + } + + /** + * @param Context $Context + * @return self|null + */ + public function continue(Context $Context) + { + if ($Context->previousEmptyLines() > 0) { + return null; + } + + if (\count($this->alignments) !== 1 and $Context->line()->text()[0] !== '|' and !\strpos($Context->line()->text(), '|')) { + return null; + } + + $Elements = []; + + $row = \trim(\trim($Context->line()->text()), '|'); + + if ( + ! \preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]++`|`)++/', $row, $matches) + or ! isset($matches[0]) + or ! \is_array($matches[0]) + ) { + return null; + } + + $cells = \array_map('trim', \array_slice($matches[0], 0, \count($this->alignments))); + + return new self( + $this->alignments, + $this->headerCells, + \array_merge($this->rows, [$cells]) + ); + } + + /** + * @param string $dividerRow + * @return array|null + */ + private static function parseAlignments($dividerRow) + { + $dividerRow = \trim($dividerRow); + $dividerRow = \trim($dividerRow, '|'); + + $dividerCells = \explode('|', $dividerRow); + + /** @var array */ + $alignments = []; + + foreach ($dividerCells as $dividerCell) { + $dividerCell = \trim($dividerCell); + + if ($dividerCell === '') { + return null; + } + + /** @var _Alignment|null */ + $alignment = null; + + if ($dividerCell[0] === ':') { + $alignment = 'left'; + } + + if (\substr($dividerCell, - 1) === ':') { + $alignment = $alignment === 'left' ? 'center' : 'right'; + } + + $alignments []= $alignment; + } + + return $alignments; + } + + /** + * @return Handler + */ + public function stateRenderable(Parsedown $Parsedown) + { + return new Handler( + /** @return Element */ + function (State $State) use ($Parsedown) { + return new Element('table', [], [ + new Element('thead', [], [new Element('tr', [], \array_map( + /** + * @param string $cell + * @param _Alignment|null $alignment + * @return Element + */ + function ($cell, $alignment) use ($Parsedown, $State) { + return new Element( + 'th', + isset($alignment) ? ['style' => "text-align: $alignment;"] : [], + $State->applyTo($Parsedown->lineElements($cell)) + ); + }, + $this->headerCells, + $this->alignments + ))]), + new Element('tbody', [], \array_map( + /** + * @param array $cells + * @return Element + */ + function ($cells) use ($Parsedown, $State) { + return new Element('tr', [], \array_map( + /** + * @param string $cell + * @param _Alignment|null $alignment + * @return Element + */ + function ($cell, $alignment) use ($Parsedown, $State) { + return new Element( + 'td', + isset($alignment) ? ['style' => "text-align: $alignment;"] : [], + $State->applyTo($Parsedown->lineElements($cell)) + ); + }, + $cells, + \array_slice($this->alignments, 0, \count($cells)) + )); + }, + $this->rows + )) + ]); + } + ); + } +} diff --git a/src/Parsedown.php b/src/Parsedown.php index 7041ced..09b6077 100644 --- a/src/Parsedown.php +++ b/src/Parsedown.php @@ -288,173 +288,6 @@ class Parsedown return \method_exists($this, 'block' . $Type . 'Complete'); } - # - # Table - - protected function blockTable(Context $Context, array $Block = null) - { - if (! isset($Block) or $Block['type'] !== 'Paragraph' or $Context->previousEmptyLines() > 0) { - return; - } - - if ( - \strpos($Block['element']['handler']['argument'], '|') === false - and \strpos($Context->line()->text(), '|') === false - and \strpos($Context->line()->text(), ':') === false - or \strpos($Block['element']['handler']['argument'], "\n") !== false - ) { - return; - } - - if (\chop($Context->line()->text(), ' -:|') !== '') { - return; - } - - $alignments = []; - - $divider = $Context->line()->text(); - - $divider = \trim($divider); - $divider = \trim($divider, '|'); - - $dividerCells = \explode('|', $divider); - - foreach ($dividerCells as $dividerCell) { - $dividerCell = \trim($dividerCell); - - if ($dividerCell === '') { - return; - } - - $alignment = null; - - if ($dividerCell[0] === ':') { - $alignment = 'left'; - } - - if (\substr($dividerCell, - 1) === ':') { - $alignment = $alignment === 'left' ? 'center' : 'right'; - } - - $alignments []= $alignment; - } - - # ~ - - $HeaderElements = []; - - $header = $Block['element']['handler']['argument']; - - $header = \trim($header); - $header = \trim($header, '|'); - - $headerCells = \explode('|', $header); - - if (\count($headerCells) !== \count($alignments)) { - return; - } - - foreach ($headerCells as $index => $headerCell) { - $headerCell = \trim($headerCell); - - $HeaderElement = [ - 'name' => 'th', - 'handler' => [ - 'function' => 'lineElements', - 'argument' => $headerCell, - 'destination' => 'elements', - ] - ]; - - if (isset($alignments[$index])) { - $alignment = $alignments[$index]; - - $HeaderElement['attributes'] = [ - 'style' => "text-align: $alignment;", - ]; - } - - $HeaderElements []= $HeaderElement; - } - - # ~ - - $Block = [ - 'alignments' => $alignments, - 'identified' => true, - 'element' => [ - 'name' => 'table', - 'elements' => [], - ], - ]; - - $Block['element']['elements'] []= [ - 'name' => 'thead', - ]; - - $Block['element']['elements'] []= [ - 'name' => 'tbody', - 'elements' => [], - ]; - - $Block['element']['elements'][0]['elements'] []= [ - 'name' => 'tr', - 'elements' => $HeaderElements, - ]; - - return $Block; - } - - protected function blockTableContinue(Context $Context, array $Block) - { - if ($Context->previousEmptyLines() > 0) { - return; - } - - if (\count($Block['alignments']) === 1 or $Context->line()->text()[0] === '|' or \strpos($Context->line()->text(), '|')) { - $Elements = []; - - $row = $Context->line()->text(); - - $row = \trim($row); - $row = \trim($row, '|'); - - \preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]++`|`)++/', $row, $matches); - - $cells = \array_slice($matches[0], 0, \count($Block['alignments'])); - - foreach ($cells as $index => $cell) { - $cell = \trim($cell); - - $Element = [ - 'name' => 'td', - 'handler' => [ - 'function' => 'lineElements', - 'argument' => $cell, - 'destination' => 'elements', - ] - ]; - - if (isset($Block['alignments'][$index])) { - $Element['attributes'] = [ - 'style' => 'text-align: ' . $Block['alignments'][$index] . ';', - ]; - } - - $Elements []= $Element; - } - - $Element = [ - 'name' => 'tr', - 'elements' => $Elements, - ]; - - $Block['element']['elements'][1]['elements'] []= $Element; - - return $Block; - } - } - # # ~ #