/**
* Callback for:.
*
* .(title)[class]{style}>
* |------------------
* | xxx | xxx | xxx | .(..){..}[..]
* |------------------
* | aa | bb | cc |
*
* @return HtmlElement|string|FALSE
*/
public function patternTable(Texy\BlockParser $parser, array $matches)
{
if ($this->disableTables) {
return FALSE;
}
list(, $mMod) = $matches;
// [1] => .(title)[class]{style}<>_
$texy = $this->texy;
$el = new HtmlElement('table');
$mod = new Modifier($mMod);
$mod->decorate($texy, $el);
$parser->moveBackward();
if ($parser->next('#^\\|(\\#|\\=){2,}(?![|\\#=+])(.+)\\1*\\|? *' . Patterns::MODIFIER_H . '?()$#Um', $matches)) {
list(, , $mContent, $mMod) = $matches;
// [1] => # / =
// [2] => ....
// [3] => .(title)[class]{style}<>
$caption = $el->create('caption');
$mod = new Modifier($mMod);
$mod->decorate($texy, $caption);
$caption->parseLine($texy, $mContent);
}
$isHead = FALSE;
$colModifier = [];
$prevRow = [];
// rowSpan building helper
$rowCounter = 0;
$colCounter = 0;
$elPart = NULL;
$lineMode = FALSE;
// rows must be separated by lines
while (TRUE) {
if ($parser->next('#^\\|([=-])[+|=-]{2,}$#Um', $matches)) {
// line
if ($lineMode) {
if ($matches[1] === '=') {
$isHead = !$isHead;
}
} else {
$isHead = !$isHead;
$lineMode = $matches[1] === '=';
}
$prevRow = [];
continue;
}
if ($parser->next('#^\\|(.*)(?:|\\|\\ *' . Patterns::MODIFIER_HV . '?)()$#U', $matches)) {
// smarter head detection
if ($rowCounter === 0 && !$isHead && $parser->next('#^\\|[=-][+|=-]{2,}$#Um', $foo)) {
$isHead = TRUE;
$parser->moveBackward();
}
if ($elPart === NULL) {
$elPart = $el->create($isHead ? 'thead' : 'tbody');
} elseif (!$isHead && $elPart->getName() === 'thead') {
$this->finishPart($elPart);
$elPart = $el->create('tbody');
}
// PARSE ROW
list(, $mContent, $mMod) = $matches;
// [1] => ....
// [2] => .(title)[class]{style}<>_
$elRow = new HtmlElement('tr');
$mod = new Modifier($mMod);
$mod->decorate($texy, $elRow);
$rowClass = $rowCounter % 2 === 0 ? $this->oddClass : $this->evenClass;
if ($rowClass && !isset($mod->classes[$this->oddClass]) && !isset($mod->classes[$this->evenClass])) {
$elRow->attrs['class'][] = $rowClass;
}
$col = 0;
$elCell = NULL;
// special escape sequence \|
$mContent = str_replace('\\|', "", $mContent);
$mContent = Regexp::replace($mContent, '#(\\[[^\\]]*)\\|#', "\$1");
// HACK: support for [..|..]
foreach (explode('|', $mContent) as $cell) {
$cell = strtr($cell, "", '|');
// rowSpan
if (isset($prevRow[$col]) && ($lineMode || ($matches = Regexp::match($cell, '#\\^\\ *$|\\*??(.*)\\ +\\^$#AU')))) {
$prevRow[$col]->rowSpan++;
if (!$lineMode) {
$cell = isset($matches[1]) ? $matches[1] : '';
}
$prevRow[$col]->text .= "\n" . $cell;
$col += $prevRow[$col]->colSpan;
$elCell = NULL;
continue;
}
// colSpan
if ($cell === '' && $elCell) {
$elCell->colSpan++;
unset($prevRow[$col]);
$col++;
continue;
}
// common cell
$matches = Regexp::match($cell, '#(\\*??)\\ *' . Patterns::MODIFIER_HV . '??(.*)' . Patterns::MODIFIER_HV . '?\\ *()$#AU');
if (!$matches) {
continue;
}
list(, $mHead, $mModCol, $mContent, $mMod) = $matches;
// [1] => * ^
// [2] => .(title)[class]{style}<>_
// [3] => ....
// [4] => .(title)[class]{style}<>_
if ($mModCol) {
$colModifier[$col] = new Modifier($mModCol);
}
if (isset($colModifier[$col])) {
$mod = clone $colModifier[$col];
} else {
$mod = new Modifier();
}
$mod->setProperties($mMod);
$elCell = new TableCellElement();
$elCell->setName($isHead || $mHead === '*' ? 'th' : 'td');
$mod->decorate($texy, $elCell);
$elCell->text = $mContent;
$elRow->add($elCell);
$prevRow[$col] = $elCell;
$col++;
}
// even up with empty cells
while ($col < $colCounter) {
if (isset($prevRow[$col]) && $lineMode) {
$prevRow[$col]->rowSpan++;
$prevRow[$col]->text .= "\n";
} else {
$elCell = new TableCellElement();
$elCell->setName($isHead ? 'th' : 'td');
if (isset($colModifier[$col])) {
$colModifier[$col]->decorate($texy, $elCell);
}
$elRow->add($elCell);
$prevRow[$col] = $elCell;
}
$col++;
}
$colCounter = $col;
if ($elRow->count()) {
$elPart->add($elRow);
$rowCounter++;
} else {
// redundant row
foreach ($prevRow as $elCell) {
$elCell->rowSpan--;
}
}
continue;
}
break;
}
if ($elPart === NULL) {
// invalid table
return FALSE;
}
if ($elPart->getName() === 'thead') {
// thead is optional, tbody is required
$elPart->setName('tbody');
}
$this->finishPart($elPart);
// event listener
$texy->invokeHandlers('afterTable', [$parser, $el, $mod]);
return $el;
}