isMarkdownable['h1']['id'] = 'optional';
$this->isMarkdownable['h1']['class'] = 'optional';
$this->isMarkdownable['h2']['id'] = 'optional';
$this->isMarkdownable['h2']['class'] = 'optional';
$this->isMarkdownable['h3']['id'] = 'optional';
$this->isMarkdownable['h3']['class'] = 'optional';
$this->isMarkdownable['h4']['id'] = 'optional';
$this->isMarkdownable['h4']['class'] = 'optional';
$this->isMarkdownable['h5']['id'] = 'optional';
$this->isMarkdownable['h5']['class'] = 'optional';
$this->isMarkdownable['h6']['id'] = 'optional';
$this->isMarkdownable['h6']['class'] = 'optional';
// tables
$this->isMarkdownable['table'] = [];
$this->isMarkdownable['th'] = [
'align' => 'optional',
];
$this->isMarkdownable['td'] = [
'align' => 'optional',
];
$this->isMarkdownable['tr'] = [];
array_push($this->ignore, 'thead');
array_push($this->ignore, 'tbody');
array_push($this->ignore, 'tfoot');
// definition lists
$this->isMarkdownable['dl'] = [];
$this->isMarkdownable['dd'] = [];
$this->isMarkdownable['dt'] = [];
// link class
$this->isMarkdownable['a']['id'] = 'optional';
$this->isMarkdownable['a']['class'] = 'optional';
// footnotes
$this->isMarkdownable['fnref'] = [
'target' => 'required',
];
$this->isMarkdownable['footnotes'] = [];
$this->isMarkdownable['fn'] = [
'name' => 'required',
];
$this->parser->blockElements['fnref'] = false;
$this->parser->blockElements['fn'] = true;
$this->parser->blockElements['footnotes'] = true;
// abbr
$this->isMarkdownable['abbr'] = [
'title' => 'required',
];
// build RegEx lookahead to decide wether table can pe parsed or not
$inlineTags = array_keys($this->parser->blockElements, false);
$colContents = '(?:[^<]|<(?:' . implode('|', $inlineTags) . '|[^a-z]))*';
$this->tableLookaheadHeader = '{
^\s*(?:)?\s* # open optional thead
\s*(?: # start required row with headers
# close row with headers
\s*(?:)? # close optional thead
}sxi';
$this->tdSubstitute = '\s*' . $colContents . '\s* # contents
\s*';
$this->tableLookaheadBody = '{
\s*(?: # header with optional align
\s*' . $colContents . '\s* # contents
\s* # close header
)+
| #si', $matches[0], $cols); $regEx = ''; $i = 1; $aligns = []; foreach ($cols[2] as $align) { $align = strtolower($align); array_push($aligns, $align); if (empty($align)) { $align = 'left'; // default value } $td = '\s+align=("|\')' . $align . '\\' . $i; $i++; if ($align == 'left') { // look for empty align or left $td = '(?:' . $td . ')?'; } $td = ' | '; $regEx .= $td . $this->tdSubstitute; } $regEx = sprintf($this->tableLookaheadBody, $regEx); if (preg_match($regEx, $this->parser->html, $matches, null, strlen($matches[0]))) { // this is a markdownable table tag! $this->table = [ 'rows' => [], 'col_widths' => [], 'aligns' => $aligns, ]; $this->row = 0; } else { // non markdownable table $this->handleTagToText(); } } else { // non markdownable table $this->handleTagToText(); } } else { $this->table = [ 'rows' => [], 'col_widths' => [], 'aligns' => [], ]; $this->row = 0; } } else { // finally build the table in Markdown Extra syntax $separator = []; if (!isset($this->table['aligns'])) { $this->table['aligns'] = []; } // seperator with correct align identifiers foreach ($this->table['aligns'] as $col => $align) { if (!$this->keepHTML && !isset($this->table['col_widths'][$col])) { break; } $left = ' '; $right = ' '; switch ($align) { case 'left': $left = ':'; break; case 'center': $right = ':'; $left = ':'; case 'right': $right = ':'; break; } array_push($separator, $left . str_repeat('-', $this->table['col_widths'][$col]) . $right); } $separator = '|' . implode('|', $separator) . '|'; $rows = []; // add padding array_walk_recursive($this->table['rows'], [&$this, 'alignTdContent']); $header = array_shift($this->table['rows']); array_push($rows, '| ' . implode(' | ', $header) . ' |'); array_push($rows, $separator); foreach ($this->table['rows'] as $row) { array_push($rows, '| ' . implode(' | ', $row) . ' |'); } $this->out(implode("\n" . $this->indent, $rows)); $this->table = []; $this->setLineBreaks(2); } } /** * properly pad content so it is aligned as whished * should be used with array_walk_recursive on $this->table['rows'] * * @param string &$content * @param int $col * @return void */ protected function alignTdContent(&$content, $col) { if (!isset($this->table['aligns'][$col])) { $this->table['aligns'][$col] = 'left'; } switch ($this->table['aligns'][$col]) { default: case 'left': $content .= str_repeat(' ', $this->table['col_widths'][$col] - $this->strlen($content)); break; case 'right': $content = str_repeat(' ', $this->table['col_widths'][$col] - $this->strlen($content)) . $content; break; case 'center': $paddingNeeded = $this->table['col_widths'][$col] - $this->strlen($content); $left = floor($paddingNeeded / 2); $right = $paddingNeeded - $left; $content = str_repeat(' ', $left) . $content . str_repeat(' ', $right); break; } } /** * handle |
|---|---|
| tags * * @param void * @return void */ protected function handleTag_td() { if ($this->parser->isStartTag) { $this->col++; if (!isset($this->table['col_widths'][$this->col])) { $this->table['col_widths'][$this->col] = 0; } $this->buffer(); } else { $buffer = trim($this->unbuffer()); if (!isset($this->table['col_widths'][$this->col])) { $this->table['col_widths'][$this->col] = 0; } $this->table['col_widths'][$this->col] = max($this->table['col_widths'][$this->col], $this->strlen($buffer)); $this->table['rows'][$this->row][$this->col] = $buffer; } } /** * handle | tags
*
* @param void
* @return void
*/
protected function handleTag_th()
{
if (!$this->keepHTML && !isset($this->table['rows'][1]) && !isset($this->table['aligns'][$this->col + 1])) {
if (isset($this->parser->tagAttributes['align'])) {
$this->table['aligns'][$this->col + 1] = $this->parser->tagAttributes['align'];
} else {
$this->table['aligns'][$this->col + 1] = '';
}
}
$this->handleTag_td();
}
/**
* handle
|