1<?php 2/** 3 * @copyright Copyright (c) 2014 Carsten Brandt 4 * @license https://github.com/cebe/markdown/blob/master/LICENSE 5 * @link https://github.com/cebe/markdown#readme 6 */ 7 8namespace cebe\markdown\block; 9 10/** 11 * Adds the table blocks 12 */ 13trait TableTrait 14{ 15 /** 16 * identify a line as the beginning of a table block. 17 */ 18 protected function identifyTable($line, $lines, $current) 19 { 20 return strpos($line, '|') !== false && isset($lines[$current + 1]) 21 && preg_match('~^\\s*\\|?(\\s*:?-[\\-\\s]*:?\\s*\\|?)*\\s*$~', $lines[$current + 1]) 22 && strpos($lines[$current + 1], '|') !== false 23 && isset($lines[$current + 2]) && trim($lines[$current + 1]) !== ''; 24 } 25 26 /** 27 * Consume lines for a table 28 */ 29 protected function consumeTable($lines, $current) 30 { 31 // consume until newline 32 33 $block = [ 34 'table', 35 'cols' => [], 36 'rows' => [], 37 ]; 38 for ($i = $current, $count = count($lines); $i < $count; $i++) { 39 $line = trim($lines[$i]); 40 41 // extract alignment from second line 42 if ($i == $current+1) { 43 $cols = explode('|', trim($line, ' |')); 44 foreach($cols as $col) { 45 $col = trim($col); 46 if (empty($col)) { 47 $block['cols'][] = ''; 48 continue; 49 } 50 $l = ($col[0] === ':'); 51 $r = (substr($col, -1, 1) === ':'); 52 if ($l && $r) { 53 $block['cols'][] = 'center'; 54 } elseif ($l) { 55 $block['cols'][] = 'left'; 56 } elseif ($r) { 57 $block['cols'][] = 'right'; 58 } else { 59 $block['cols'][] = ''; 60 } 61 } 62 63 continue; 64 } 65 if ($line === '' || substr($lines[$i], 0, 4) === ' ') { 66 break; 67 } 68 if ($line[0] === '|') { 69 $line = substr($line, 1); 70 } 71 if (substr($line, -1, 1) === '|' && (substr($line, -2, 2) !== '\\|' || substr($line, -3, 3) === '\\\\|')) { 72 $line = substr($line, 0, -1); 73 } 74 75 array_unshift($this->context, 'table'); 76 $row = $this->parseInline($line); 77 array_shift($this->context); 78 79 $r = count($block['rows']); 80 $c = 0; 81 $block['rows'][] = []; 82 foreach ($row as $absy) { 83 if (!isset($block['rows'][$r][$c])) { 84 $block['rows'][$r][] = []; 85 } 86 if ($absy[0] === 'tableBoundary') { 87 $c++; 88 } else { 89 $block['rows'][$r][$c][] = $absy; 90 } 91 } 92 } 93 94 return [$block, --$i]; 95 } 96 97 /** 98 * render a table block 99 */ 100 protected function renderTable($block) 101 { 102 $head = ''; 103 $body = ''; 104 $cols = $block['cols']; 105 $first = true; 106 foreach($block['rows'] as $row) { 107 $cellTag = $first ? 'th' : 'td'; 108 $tds = ''; 109 foreach ($row as $c => $cell) { 110 $align = empty($cols[$c]) ? '' : ' align="' . $cols[$c] . '"'; 111 $tds .= "<$cellTag$align>" . trim($this->renderAbsy($cell)) . "</$cellTag>"; 112 } 113 if ($first) { 114 $head .= "<tr>$tds</tr>\n"; 115 } else { 116 $body .= "<tr>$tds</tr>\n"; 117 } 118 $first = false; 119 } 120 return $this->composeTable($head, $body); 121 } 122 123 /** 124 * This method composes a table from parsed body and head HTML. 125 * 126 * You may override this method to customize the table rendering, for example by 127 * adding a `class` to the table tag: 128 * 129 * ```php 130 * return "<table class="table table-striped">\n<thead>\n$head</thead>\n<tbody>\n$body</tbody>\n</table>\n" 131 * ``` 132 * 133 * @param string $head table head HTML. 134 * @param string $body table body HTML. 135 * @return string the complete table HTML. 136 * @since 1.2.0 137 */ 138 protected function composeTable($head, $body) 139 { 140 return "<table>\n<thead>\n$head</thead>\n<tbody>\n$body</tbody>\n</table>\n"; 141 } 142 143 /** 144 * @marker | 145 */ 146 protected function parseTd($markdown) 147 { 148 if (isset($this->context[1]) && $this->context[1] === 'table') { 149 return [['tableBoundary'], isset($markdown[1]) && $markdown[1] === ' ' ? 2 : 1]; 150 } 151 return [['text', $markdown[0]], 1]; 152 } 153 154 abstract protected function parseInline($text); 155 abstract protected function renderAbsy($absy); 156} 157