/** * 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; }
/** * Callback for:. * * 1) .... .(title)[class]{style}> * 2) .... * + ... * + ... * 3) .... * * @return HtmlElement|FALSE */ public function patternList(BlockParser $parser, array $matches) { list(, $mMod, $mBullet) = $matches; // [1] => .(title)[class]{style}<> // [2] => bullet * + - 1) a) A) IV) $el = new HtmlElement(); $bullet = $min = NULL; foreach ($this->bullets as $type => $desc) { if (preg_match('#' . $desc[0] . '#Au', $mBullet)) { $bullet = isset($desc[3]) ? $desc[3] : $desc[0]; $min = isset($desc[3]) ? 2 : 1; $el->setName($desc[1] ? 'ol' : 'ul'); $el->attrs['style']['list-style-type'] = $desc[2]; if ($desc[1]) { // ol if ($type[0] === '1' && (int) $mBullet > 1) { $el->attrs['start'] = (int) $mBullet; } elseif ($type[0] === 'a' && $mBullet[0] > 'a') { $el->attrs['start'] = ord($mBullet[0]) - 96; } elseif ($type[0] === 'A' && $mBullet[0] > 'A') { $el->attrs['start'] = ord($mBullet[0]) - 64; } } break; } } $mod = new Modifier($mMod); $mod->decorate($this->texy, $el); $parser->moveBackward(1); while ($elItem = $this->patternItem($parser, $bullet, FALSE, 'li')) { $el->add($elItem); } if ($el->count() < $min) { return FALSE; } // event listener $this->texy->invokeHandlers('afterList', [$parser, $el, $mod]); return $el; }
/** * Callback for: [ref]. * @return Texy\HtmlElement|string|FALSE */ public function patternReference(LineParser $parser, array $matches) { list(, $mRef) = $matches; // [1] => [ref] $texy = $this->texy; $name = substr($mRef, 1, -1); $link = $this->getReference($name); if (!$link) { return $texy->invokeAroundHandlers('newReference', $parser, [$name]); } $link->type = $link::BRACKET; if ($link->label != '') { // NULL or '' // prevent circular references if (isset(self::$livelock[$link->name])) { $content = $link->label; } else { self::$livelock[$link->name] = TRUE; $el = new Texy\HtmlElement(); $lineParser = new LineParser($texy, $el); $lineParser->parse($link->label); $content = $el->toString($texy); unset(self::$livelock[$link->name]); } } else { $content = $this->textualUrl($link); $content = $this->texy->protect($content, $texy::CONTENT_TEXTUAL); } return $texy->invokeAroundHandlers('linkReference', $parser, [$link, $content]); }
/** * 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 solveTag(Texy\HandlerInvocation $invocation, HtmlElement $el, $isStart, $forceEmpty = NULL) { $texy = $this->texy; // tag & attibutes $allowedTags = $texy->allowedTags; // speed-up if (!$allowedTags) { return FALSE; // all tags are disabled } // convert case $name = $el->getName(); $lower = strtolower($name); if (isset($texy->dtd[$lower]) || $name === strtoupper($name)) { // complete UPPER convert to lower $name = $lower; $el->setName($name); } if (is_array($allowedTags)) { if (!isset($allowedTags[$name])) { return FALSE; } $allowedAttrs = $allowedTags[$name]; // allowed attrs } else { // allowedTags === Texy\Texy::ALL if ($forceEmpty) { $el->setName($name, TRUE); } $allowedAttrs = $texy::ALL; // all attrs are allowed } // end tag? we are finished if (!$isStart) { return $el; } $elAttrs =& $el->attrs; // process attributes if (!$allowedAttrs) { $elAttrs = []; } elseif (is_array($allowedAttrs)) { // skip disabled $allowedAttrs = array_flip($allowedAttrs); foreach ($elAttrs as $key => $foo) { if (!isset($allowedAttrs[$key])) { unset($elAttrs[$key]); } } } // apply allowedClasses $tmp = $texy->_classes; // speed-up if (isset($elAttrs['class'])) { if (is_array($tmp)) { $elAttrs['class'] = explode(' ', $elAttrs['class']); foreach ($elAttrs['class'] as $key => $value) { if (!isset($tmp[$value])) { unset($elAttrs['class'][$key]); // id & class are case-sensitive } } } elseif ($tmp !== $texy::ALL) { $elAttrs['class'] = NULL; } } // apply allowedClasses for ID if (isset($elAttrs['id'])) { if (is_array($tmp)) { if (!isset($tmp['#' . $elAttrs['id']])) { $elAttrs['id'] = NULL; } } elseif ($tmp !== $texy::ALL) { $elAttrs['id'] = NULL; } } // apply allowedStyles if (isset($elAttrs['style'])) { $tmp = $texy->_styles; // speed-up if (is_array($tmp)) { $styles = explode(';', $elAttrs['style']); $elAttrs['style'] = NULL; foreach ($styles as $value) { $pair = explode(':', $value, 2); $prop = trim($pair[0]); if (isset($pair[1]) && isset($tmp[strtolower($prop)])) { // CSS is case-insensitive $elAttrs['style'][$prop] = $pair[1]; } } } elseif ($tmp !== $texy::ALL) { $elAttrs['style'] = NULL; } } foreach (['src', 'href', 'name', 'id'] as $attr) { if (isset($elAttrs[$attr])) { $elAttrs[$attr] = is_string($elAttrs[$attr]) ? trim($elAttrs[$attr]) : ''; if ($elAttrs[$attr] === '') { unset($elAttrs[$attr]); } } } if ($name === 'img') { if (!isset($elAttrs['src']) || !$texy->checkURL($elAttrs['src'], $texy::FILTER_IMAGE)) { return FALSE; } $texy->summary['images'][] = $elAttrs['src']; } elseif ($name === 'a') { if (!isset($elAttrs['href']) && !isset($elAttrs['name']) && !isset($elAttrs['id'])) { return FALSE; } if (isset($elAttrs['href'])) { if ($texy->linkModule->forceNoFollow && strpos($elAttrs['href'], '//') !== FALSE) { if (isset($elAttrs['rel'])) { $elAttrs['rel'] = (array) $elAttrs['rel']; } $elAttrs['rel'][] = 'nofollow'; } if (!$texy->checkURL($elAttrs['href'], $texy::FILTER_ANCHOR)) { return FALSE; } $texy->summary['links'][] = $elAttrs['href']; } } elseif (preg_match('#^h[1-6]#i', $name)) { $texy->headingModule->TOC[] = ['el' => $el, 'level' => (int) substr($name, 1), 'type' => 'html']; } $el->validateAttrs($texy->dtd); 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; }
/** * Parse text in all cells. * @return void */ private function finishPart(HtmlElement $elPart) { foreach ($elPart->getChildren() as $elRow) { foreach ($elRow->getChildren() as $elCell) { if ($elCell->colSpan > 1) { $elCell->attrs['colspan'] = $elCell->colSpan; } if ($elCell->rowSpan > 1) { $elCell->attrs['rowspan'] = $elCell->rowSpan; } $text = rtrim($elCell->text); if (strpos($text, "\n") !== FALSE) { // multiline parse as block // HACK: disable tables $this->disableTables = TRUE; $elCell->parseBlock($this->texy, Texy\Helpers::outdent($text)); $this->disableTables = FALSE; } else { $elCell->parseLine($this->texy, ltrim($text)); } if ($elCell->getText() === '') { $elCell->setText(" "); // } } } }
public function blockHandler($invocation, $blocktype, $content, $lang, $modifier) { if ($blocktype !== 'block/code') { return $invocation->proceed(); //vstup se nebude zpracovavat } $highlighter = new \FSHL\Highlighter(new \FSHL\Output\Html(), \FSHL\Highlighter::OPTION_TAB_INDENT); $texy = $invocation->getTexy(); $content = \Texy\Texy::outdent($content); //Set correct lexer: $lang = $lang ?: ''; switch (strtoupper($lang)) { case 'C': case 'CPP': $lexer = new \FSHL\Lexer\Cpp(); break; case 'CSS': $lexer = new \FSHL\Lexer\Css(); break; case 'HTML': $lexer = new \FSHL\Lexer\Html(); break; //HtmlOnly lexer //HtmlOnly lexer case 'JAVA': $lexer = new \FSHL\Lexer\Java(); break; case 'JS': case 'JAVASCRIPT': $lexer = new \FSHL\Lexer\Javascript(); break; case 'NEON': $lexer = new \FSHL\Lexer\Neon(); break; case 'PHP': $lexer = new \FSHL\Lexer\Php(); break; case 'PYTHON': $lexer = new \FSHL\Lexer\Python(); break; case 'SQL': $lexer = new \FSHL\Lexer\Sql(); break; case 'TEX': $lexer = new \App\Texy\Tex(); break; case 'TEXY': $lexer = new \FSHL\Lexer\Texy(); break; default: $lexer = new \FSHL\Lexer\Minimal(); } $content = $highlighter->highlight($content, $lexer); $content = $texy->protect($content, \Texy\Texy::CONTENT_BLOCK); $elPre = \Texy\HtmlElement::el('pre'); if ($modifier) { $modifier->decorate($texy, $elPre); } $elPre->attrs['class'] = strtolower($lang); $elPre->create('code', $content); return $elPre; }