/** * Finish invocation. * @return Texy\HtmlElement|FALSE */ public function solve(Texy\HandlerInvocation $invocation, Texy\Image $image, Texy\Link $link = NULL, $content, Texy\Modifier $mod) { $texy = $this->texy; $hAlign = $image->modifier->hAlign; $image->modifier->hAlign = NULL; $elImg = $texy->imageModule->solve(NULL, $image, $link); // returns Texy\HtmlElement or false! if (!$elImg) { return FALSE; } $el = new Texy\HtmlElement('div'); if (!empty($image->width) && $this->widthDelta !== FALSE) { $el->attrs['style']['width'] = $image->width + $this->widthDelta . 'px'; } $mod->decorate($texy, $el); $el[0] = $elImg; $el[1] = new Texy\HtmlElement('p'); $el[1]->parseLine($texy, ltrim($content)); $class = $this->class; if ($hAlign) { $var = $hAlign . 'Class'; // leftClass, rightClass if (!empty($this->{$var})) { $class = $this->{$var}; } elseif (empty($texy->alignClasses[$hAlign])) { $el->attrs['style']['float'] = $hAlign; } else { $class .= '-' . $texy->alignClasses[$hAlign]; } } $el->attrs['class'][] = $class; return $el; }
/** * Finish invocation. * @return Texy\HtmlElement */ public function solve(Texy\HandlerInvocation $invocation, $type, Texy\Modifier $modifier) { $el = new Texy\HtmlElement('hr'); $modifier->decorate($invocation->getTexy(), $el); $class = $this->classes[$type[0]]; if ($class && !isset($modifier->classes[$class])) { $el->attrs['class'][] = $class; } return $el; }
/** * Callback for:. * * > They went in single file, running like hounds on a strong scent, * and an eager light was in their eyes. Nearly due west the broad * swath of the marching Orcs tramped its ugly slot; the sweet grass * of Rohan had been bruised and blackened as they passed. * >:http://www.mycom.com/tolkien/twotowers.html * * @return Texy\HtmlElement|string|FALSE */ public function pattern(Texy\BlockParser $parser, array $matches) { list(, $mMod, $mPrefix, $mContent) = $matches; // [1] => .(title)[class]{style}<> // [2] => spaces | // [3] => ... / LINK $texy = $this->texy; $el = new Texy\HtmlElement('blockquote'); $mod = new Texy\Modifier($mMod); $mod->decorate($texy, $el); $content = ''; $spaces = ''; do { if ($mPrefix === ':') { $mod->cite = $texy->blockQuoteModule->citeLink($mContent); $content .= "\n"; } else { if ($spaces === '') { $spaces = max(1, strlen($mPrefix)); } $content .= $mContent . "\n"; } if (!$parser->next("#^>(?:|(\\ {1,{$spaces}}|:)(.*))()\$#mA", $matches)) { break; } /* if ($mPrefix === '>') { $content .= $mPrefix . $mContent . "\n"; } elseif ($mPrefix === ':') { $mod->cite = $texy->blockQuoteModule->citeLink($mContent); $content .= "\n"; } else { if ($spaces === '') $spaces = max(1, strlen($mPrefix)); $content .= $mContent . "\n"; } if (!$parser->next("#^\\>(?:(\\>|\\ {1,$spaces}|:)(.*))?()$#mA", $matches)) break; */ list(, $mPrefix, $mContent) = $matches; } while (TRUE); $el->attrs['cite'] = $mod->cite; $el->parseBlock($texy, $content, $parser->isIndented()); // no content? if (!$el->count()) { return FALSE; } // event listener $texy->invokeHandlers('afterBlockquote', [$parser, $el, $mod]); return $el; }
/** * Finish invocation. * @return Texy\HtmlElement|FALSE */ public function solve(Texy\HandlerInvocation $invocation, $content, Texy\Modifier $mod = NULL) { $texy = $this->texy; // find hard linebreaks if ($texy->mergeLines) { // .... // ... => \r means break line $content = Regexp::replace($content, '#\\n +(?=\\S)#', "\r"); } else { $content = Regexp::replace($content, '#\\n#', "\r"); } $el = new Texy\HtmlElement('p'); $el->parseLine($texy, $content); $content = $el->getText(); // string // check content type // block contains block tag if (strpos($content, $texy::CONTENT_BLOCK) !== FALSE) { $el->setName(NULL); // ignores modifier! // block contains text (protected) } elseif (strpos($content, $texy::CONTENT_TEXTUAL) !== FALSE) { // leave element p // block contains text } elseif (preg_match('#[^\\s' . Texy\Patterns::MARK . ']#u', $content)) { // leave element p // block contains only replaced element } elseif (strpos($content, $texy::CONTENT_REPLACED) !== FALSE) { $el->setName($texy->nontextParagraph); // block contains only markup tags or spaces or nothing } else { // if {ignoreEmptyStuff} return FALSE; if (!$mod) { $el->setName(NULL); } } if ($el->getName()) { // apply modifier if ($mod) { $mod->decorate($texy, $el); } // add <br /> if (strpos($content, "\r") !== FALSE) { $key = $texy->protect('<br />', $texy::CONTENT_REPLACED); $content = str_replace("\r", $key, $content); } } $content = strtr($content, "\r\n", ' '); $el->setText($content); return $el; }
/** * Finish invocation. * @return Texy\HtmlElement */ public function solve(Texy\HandlerInvocation $invocation, $phrase, $content, Modifier $mod, Texy\Link $link = NULL) { $texy = $this->texy; $tag = isset($this->tags[$phrase]) ? $this->tags[$phrase] : NULL; if ($tag === 'a') { $tag = $link && $this->linksAllowed ? NULL : 'span'; } if ($phrase === 'phrase/code') { $content = $texy->protect(htmlspecialchars($content, ENT_NOQUOTES, 'UTF-8'), $texy::CONTENT_TEXTUAL); } if ($phrase === 'phrase/strong+em') { $el = new Texy\HtmlElement($this->tags['phrase/strong']); $el->create($this->tags['phrase/em'], $content); $mod->decorate($texy, $el); } elseif ($tag) { $el = new Texy\HtmlElement($tag, $content); $mod->decorate($texy, $el); if ($tag === 'q') { $el->attrs['cite'] = $mod->cite; } } else { $el = $content; // trick } if ($link && $this->linksAllowed) { return $texy->linkModule->solve(NULL, $link, $el); } return $el; }
/** * Finish invocation. * @return HtmlElement|string|FALSE */ public function solve(Texy\HandlerInvocation $invocation, $blocktype, $s, $param, Texy\Modifier $mod) { $texy = $this->texy; $parser = $invocation->getParser(); if ($blocktype === 'block/texy') { $el = new HtmlElement(); $el->parseBlock($texy, $s, $parser->isIndented()); return $el; } if (empty($texy->allowed[$blocktype])) { return FALSE; } if ($blocktype === 'block/texysource') { $s = Helpers::outdent($s); if ($s === '') { return "\n"; } $el = new HtmlElement(); if ($param === 'line') { $el->parseLine($texy, $s); } else { $el->parseBlock($texy, $s); } $s = $el->toHtml($texy); $blocktype = 'block/code'; $param = 'html'; // to be continue (as block/code) } if ($blocktype === 'block/code') { $s = Helpers::outdent($s); if ($s === '') { return "\n"; } $s = htmlspecialchars($s, ENT_NOQUOTES, 'UTF-8'); $s = $texy->protect($s, $texy::CONTENT_BLOCK); $el = new HtmlElement('pre'); $mod->decorate($texy, $el); $el->attrs['class'][] = $param; // lang $el->create('code', $s); return $el; } if ($blocktype === 'block/default') { $s = Helpers::outdent($s); if ($s === '') { return "\n"; } $el = new HtmlElement('pre'); $mod->decorate($texy, $el); $el->attrs['class'][] = $param; // lang $s = htmlspecialchars($s, ENT_NOQUOTES, 'UTF-8'); $s = $texy->protect($s, $texy::CONTENT_BLOCK); $el->setText($s); return $el; } if ($blocktype === 'block/pre') { $s = Helpers::outdent($s); if ($s === '') { return "\n"; } $el = new HtmlElement('pre'); $mod->decorate($texy, $el); $lineParser = new Texy\LineParser($texy, $el); // special mode - parse only html tags $tmp = $lineParser->patterns; $lineParser->patterns = []; if (isset($tmp['html/tag'])) { $lineParser->patterns['html/tag'] = $tmp['html/tag']; } if (isset($tmp['html/comment'])) { $lineParser->patterns['html/comment'] = $tmp['html/comment']; } unset($tmp); $lineParser->parse($s); $s = $el->getText(); $s = html_entity_decode($s, ENT_QUOTES, 'UTF-8'); $s = htmlspecialchars($s, ENT_NOQUOTES, 'UTF-8'); $s = $texy->unprotect($s); $s = $texy->protect($s, $texy::CONTENT_BLOCK); $el->setText($s); return $el; } if ($blocktype === 'block/html') { $s = trim($s, "\n"); if ($s === '') { return "\n"; } $el = new HtmlElement(); $lineParser = new Texy\LineParser($texy, $el); // special mode - parse only html tags $tmp = $lineParser->patterns; $lineParser->patterns = []; if (isset($tmp['html/tag'])) { $lineParser->patterns['html/tag'] = $tmp['html/tag']; } if (isset($tmp['html/comment'])) { $lineParser->patterns['html/comment'] = $tmp['html/comment']; } unset($tmp); $lineParser->parse($s); $s = $el->getText(); $s = html_entity_decode($s, ENT_QUOTES, 'UTF-8'); $s = htmlspecialchars($s, ENT_NOQUOTES, 'UTF-8'); $s = $texy->unprotect($s); return $texy->protect($s, $texy::CONTENT_BLOCK) . "\n"; } if ($blocktype === 'block/text') { $s = trim($s, "\n"); if ($s === '') { return "\n"; } $s = htmlspecialchars($s, ENT_NOQUOTES, 'UTF-8'); $s = str_replace("\n", (new HtmlElement('br'))->startTag(), $s); // nl2br return $texy->protect($s, $texy::CONTENT_BLOCK) . "\n"; } if ($blocktype === 'block/comment') { return "\n"; } if ($blocktype === 'block/div') { $s = Helpers::outdent($s, TRUE); if ($s === '') { return "\n"; } $el = new HtmlElement('div'); $mod->decorate($texy, $el); $el->parseBlock($texy, $s, $parser->isIndented()); // TODO: INDENT or NORMAL ? return $el; } return FALSE; }
/** * 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; }
/** * Finish invocation. * @return Texy\HtmlElement */ public function solve(Texy\HandlerInvocation $invocation, $level, $content, Modifier $mod, $isSurrounded) { // as fixed balancing, for block/texysource & correct decorating $el = new Texy\HtmlElement('h' . min(6, max(1, $level + $this->top))); $mod->decorate($this->texy, $el); $el->parseLine($this->texy, trim($content)); $this->TOC[] = ['el' => $el, 'level' => $level, 'type' => $isSurrounded ? 'surrounded' : 'underlined']; return $el; }
/** * Callback for single list item. * @return HtmlElement|FALSE */ public function patternItem(BlockParser $parser, $bullet, $indented, $tag) { $spacesBase = $indented ? '\\ {1,}' : ''; $patternItem = "#^\n?({$spacesBase}){$bullet}\\ *(\\S.*)?" . Patterns::MODIFIER_H . '?()$#mAUu'; // first line with bullet $matches = NULL; if (!$parser->next($patternItem, $matches)) { return FALSE; } list(, $mIndent, $mContent, $mMod) = $matches; // [1] => indent // [2] => ... // [3] => .(title)[class]{style}<> $elItem = new HtmlElement($tag); $mod = new Modifier($mMod); $mod->decorate($this->texy, $elItem); // next lines $spaces = ''; $content = ' ' . $mContent; // trick while ($parser->next('#^(\\n*)' . $mIndent . '(\\ {1,' . $spaces . '})(.*)()$#Am', $matches)) { list(, $mBlank, $mSpaces, $mContent) = $matches; // [1] => blank line? // [2] => spaces // [3] => ... if ($spaces === '') { $spaces = strlen($mSpaces); } $content .= "\n" . $mBlank . $mContent; } // parse content $elItem->parseBlock($this->texy, $content, TRUE); if (isset($elItem[0]) && $elItem[0] instanceof HtmlElement) { $elItem[0]->setName(NULL); } return $elItem; }