/** * User handler for code block * * @param TexyHandlerInvocation handler invocation * @param string block type * @param string text to highlight * @param string language * @param TexyModifier modifier * @return TexyHtml */ function blockHandler($invocation, $blocktype, $content, $lang, $modifier) { if ($blocktype !== 'block/code') { return $invocation->proceed(); } $lang = strtoupper($lang); if ($lang == 'JAVASCRIPT') { $lang = 'JS'; } $fshl = new fshlParser('HTML_UTF8', P_TAB_INDENT); if (!$fshl->isLanguage($lang)) { return $invocation->proceed(); } $texy = $invocation->getTexy(); $content = Texy::outdent($content); $content = $fshl->highlightString($lang, $content); $content = $texy->protect($content, Texy::CONTENT_BLOCK); $elPre = TexyHtml::el('pre'); if ($modifier) { $modifier->decorate($texy, $elPre); } $elPre->attrs['class'] = strtolower($lang); $elCode = $elPre->create('code', $content); return $elPre; }
/** * Finish invocation. * * @param TexyHandlerInvocation handler invocation * @param string * @param TexyModifier * * @return TexyHtml */ public function solve($invocation, $type, $modifier) { $el = TexyHtml::el('hr'); $modifier->decorate($invocation->texy, $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 * * @param TexyBlockParser * @param array regexp matches * @param string pattern name * @return TexyHtml|string|FALSE */ public function pattern($parser, $matches) { list(, $mMod, $mPrefix, $mContent) = $matches; // [1] => .(title)[class]{style}<> // [2] => spaces | // [3] => ... / LINK $tx = $this->texy; $el = TexyHtml::el('blockquote'); $mod = new TexyModifier($mMod); $mod->decorate($tx, $el); $content = ''; $spaces = ''; do { if ($mPrefix === ':') { $mod->cite = $tx->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 = $tx->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($tx, $content, $parser->isIndented()); // no content? if (!$el->count()) return FALSE; // event listener $tx->invokeHandlers('afterBlockquote', array($parser, $el, $mod)); return $el; }
/** * Pattern handler for PHP & JavaScript block syntaxes * * @param TexyBlockParser * @param array regexp matches * @param string pattern name * @return TexyHtml|string|FALSE */ function codeBlockHandler($parser, $matches, $name) { list($content) = $matches; $lang = $name === 'phpBlockSyntax' ? 'PHP' : 'HTML'; $fshl = new fshlParser('HTML_UTF8', P_TAB_INDENT); $texy = $parser->getTexy(); $content = $fshl->highlightString($lang, $content); $content = $texy->protect($content, Texy::CONTENT_BLOCK); $elPre = TexyHtml::el('pre'); $elPre->attrs['class'] = strtolower($lang); $elCode = $elPre->create('code', $content); return $elPre; }
/** * Finish invocation. * * @param TexyHandlerInvocation handler invocation * @param string * @param TexyModifier|NULL * @return TexyHtml|FALSE */ public function solve($invocation, $content, $mod) { $tx = $this->texy; // find hard linebreaks if ($tx->mergeLines) { // .... // ... => \r means break line $content = TexyRegexp::replace($content, '#\\n +(?=\\S)#', "\r"); } else { $content = TexyRegexp::replace($content, '#\\n#', "\r"); } $el = TexyHtml::el('p'); $el->parseLine($tx, $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' . TexyPatterns::MARK . ']#u', $content)) { // leave element p // block contains only replaced element } elseif (strpos($content, Texy::CONTENT_REPLACED) !== FALSE) { $el->setName($tx->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($tx, $el); } // add <br /> if (strpos($content, "\r") !== FALSE) { $key = $tx->protect('<br />', Texy::CONTENT_REPLACED); $content = str_replace("\r", $key, $content); } } $content = strtr($content, "\r\n", ' '); $el->setText($content); return $el; }
/** * Pattern handler for block syntaxes * * @param TexyBlockParser * @param array regexp matches * @param string pattern name (myBlockSyntax1) * @return TexyHtml|string|FALSE */ function userBlockHandler($parser, $matches, $name) { list(, $mTag, $mText) = $matches; $texy = $parser->getTexy(); // create element if ($mTag === 'perex') { $el = TexyHtml::el('div'); $el->attrs['class'][] = 'perex'; } else { $el = TexyHtml::el($mTag); } // create content $el->parseLine($texy, $mText); return $el; }
/** * @param TexyHandlerInvocation handler invocation * @param TexyImage * @param TexyLink * @param string * @param TexyModifier * @return TexyHtml|string|FALSE */ function figureHandler($invocation, $image, $link, $content, $modifier) { // finish invocation by default way $el = $invocation->proceed(); // change div -> dl $el->setName('dl'); // change p -> dd $el[1]->setName('dd'); // wrap img into dt $img = $el[0]; unset($el[0]); $dt = TexyHtml::el('dt'); $dt->add($img); $el->insert(0, $dt); return $el; }
function wiki_texy_InlineHandler($parser, $matches, $name) { list(, $mContent, $mMod) = $matches; $texy = $parser->getTexy(); $tag = 'a'; $el = TexyHtml::el($tag); $mod = new TexyModifier($mMod); $mod->decorate($texy, $el); if ($name == 'wikilink') { $el->attrs['href'] = '?page=' . urlencode($mContent); } else { $el->attrs['href'] = $mContent; } $el->attrs['class'] = $name; $el->setText($mContent); $parser->again = TRUE; return $el; }
/** * User handler for unknown reference * * @param TexyHandlerInvocation handler invocation * @param string [refName] * @return TexyHtml|string */ function newReferenceHandler($parser, $refName) { $names = array('Me', 'Punkrats', 'Servats', 'Bonifats'); if (!isset($names[$refName])) { return FALSE; } // it's not my job $name = $names[$refName]; $el = TexyHtml::el('a'); $el->attrs['href'] = '#comm-' . $refName; // set link destination $el->attrs['class'][] = 'comment'; // set class name $el->attrs['rel'] = 'nofollow'; // enable rel="nofollow" $el->setText("[{$refName}] {$name}:"); // set link label (with Texy formatting) return $el; }
/** * Finish invocation. * * @param TexyHandlerInvocation handler invocation * @param string * @param string * @return TexyHtml|FALSE */ public function solve($invocation, $emoticon, $raw) { $tx = $this->texy; $file = $this->icons[$emoticon]; $el = TexyHtml::el('img'); $el->attrs['src'] = Texy::prependRoot($file, $this->root === NULL ? $tx->imageModule->root : $this->root); $el->attrs['alt'] = $raw; $el->attrs['class'][] = $this->class; // file path $file = rtrim($this->fileRoot === NULL ? $tx->imageModule->fileRoot : $this->fileRoot, '/\\') . '/' . $file; if (@is_file($file)) { // intentionally @ $size = @getImageSize($file); // intentionally @ if (is_array($size)) { $el->attrs['width'] = $size[0]; $el->attrs['height'] = $size[1]; } } $tx->summary['images'][] = $el->attrs['src']; return $el; }
/** * User handler for code block. * * @param TexyHandlerInvocation handler invocation * @param string block type * @param string text to highlight * @param string language * @param TexyModifier modifier * @return TexyHtml */ public static function blockHandler($invocation, $blocktype, $content, $lang, $modifier) { if (preg_match('#^block/(php|neon|javascript|js|css|html|htmlcb|latte)$#', $blocktype)) { list(, $lang) = explode('/', $blocktype); } elseif ($blocktype !== 'block/code') { return $invocation->proceed(); } $lang = strtolower($lang); if ($lang === 'htmlcb' || $lang === 'latte') { $lang = 'html'; } elseif ($lang === 'javascript') { $lang = 'js'; } if ($lang === 'html') { $langClass = 'FSHL\\Lexer\\LatteHtml'; } elseif ($lang === 'js') { $langClass = 'FSHL\\Lexer\\LatteJavascript'; } else { $langClass = 'FSHL\\Lexer\\' . ucfirst($lang); } $texy = $invocation->getTexy(); $content = Texy::outdent($content); if (class_exists($langClass)) { $fshl = new FSHL\Highlighter(new FSHL\Output\Html(), FSHL\Highlighter::OPTION_TAB_INDENT); $content = $fshl->highlight($content, new $langClass()); } else { $content = htmlSpecialChars($content); } $content = $texy->protect($content, Texy::CONTENT_BLOCK); $elPre = TexyHtml::el('pre'); if ($modifier) { $modifier->decorate($texy, $elPre); } $elPre->attrs['class'] = 'src-' . strtolower($lang); $elCode = $elPre->create('code', $content); return $elPre; }
/** * User handler for code block * * @param TexyHandlerInvocation handler invocation * @param string block type * @param string text to highlight * @param string language * @param TexyModifier modifier * @return TexyHtml */ function blockHandler($invocation, $blocktype, $content, $lang, $modifier) { if ($blocktype !== 'block/code') { return $invocation->proceed(); } $texy = $invocation->getTexy(); global $geshiPath; if ($lang == 'html') { $lang = 'html4strict'; } $content = Texy::outdent($content); $geshi = new GeSHi($content, $lang, $geshiPath . 'geshi/'); // GeSHi could not find the language if ($geshi->error) { return $invocation->proceed(); } // do syntax-highlighting $geshi->set_encoding('UTF-8'); $geshi->set_header_type(GESHI_HEADER_PRE); $geshi->enable_classes(); $geshi->set_overall_style('color: #000066; border: 1px solid #d0d0d0; background-color: #f0f0f0;', true); $geshi->set_line_style('font: normal normal 95% \'Courier New\', Courier, monospace; color: #003030;', 'font-weight: bold; color: #006060;', true); $geshi->set_code_style('color: #000020;', 'color: #000020;'); $geshi->set_link_styles(GESHI_LINK, 'color: #000060;'); $geshi->set_link_styles(GESHI_HOVER, 'background-color: #f0f000;'); // save generated stylesheet $texy->styleSheet .= $geshi->get_stylesheet(); $content = $geshi->parse_code(); // check buggy GESHI, it sometimes produce not UTF-8 valid code :-(( $content = iconv('UTF-8', 'UTF-8//IGNORE', $content); // protect output is in HTML $content = $texy->protect($content, Texy::CONTENT_BLOCK); $el = TexyHtml::el(); $el->setText($content); return $el; }
/** * Finish invocation. * * @param TexyHandlerInvocation handler invocation * @param TexyLink * @param TexyHtml|string * @return TexyHtml|string */ public function solve($invocation, $link, $content = NULL) { if ($link->URL == NULL) { return $content; } $tx = $this->texy; $el = TexyHtml::el('a'); if (empty($link->modifier)) { $nofollow = $popup = FALSE; } else { $nofollow = isset($link->modifier->classes['nofollow']); $popup = isset($link->modifier->classes['popup']); unset($link->modifier->classes['nofollow'], $link->modifier->classes['popup']); $el->attrs['href'] = NULL; // trick - move to front $link->modifier->decorate($tx, $el); } if ($link->type === TexyLink::IMAGE) { // image $el->attrs['href'] = Texy::prependRoot($link->URL, $tx->imageModule->linkedRoot); if ($this->imageClass) { $el->attrs['class'][] = $this->imageClass; } else { $el->attrs['onclick'] = $this->imageOnClick; } } else { $el->attrs['href'] = Texy::prependRoot($link->URL, $this->root); // rel="nofollow" if ($nofollow || $this->forceNoFollow && strpos($el->attrs['href'], '//') !== FALSE) { $el->attrs['rel'] = 'nofollow'; } } // popup on click if ($popup) { $el->attrs['onclick'] = $this->popupOnClick; } if ($content !== NULL) { $el->add($content); } $tx->summary['links'][] = $el->attrs['href']; return $el; }
/** * Callback for single list item. * * @param TexyBlockParser * @param string bullet type * @param string left space * @param string html tag * @return TexyHtml|FALSE */ public function patternItem($parser, $bullet, $indented, $tag) { $tx = $this->texy; $spacesBase = $indented ? '\\ {1,}' : ''; $patternItem = "#^\n?({$spacesBase}){$bullet}\\ *(\\S.*)?" . TEXY_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 = TexyHtml::el($tag); $mod = new TexyModifier($mMod); $mod->decorate($tx, $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($tx, $content, TRUE); if (isset($elItem[0]) && $elItem[0] instanceof TexyHtml) { $elItem[0]->setName(NULL); } return $elItem; }
static function texyBlockHandler($invocation, $blocktype, $content, $lang, $modifier) { if ($blocktype !== 'block/code') { return $invocation->proceed(); } $texy = $invocation->getTexy(); if ($lang == 'html') { $lang = 'html4strict'; } elseif ($lang == 'yaml') { $lang = 'python'; } $content = Texy::outdent($content); $geshi = new GeSHi($content, $lang); // GeSHi could not find the language if ($geshi->error) { return $invocation->proceed(); } // do syntax-highlighting $geshi->set_encoding('UTF-8'); $geshi->set_header_type(GESHI_HEADER_PRE); $geshi->enable_classes(); $geshi->enable_keyword_links(false); $geshi->set_overall_style(''); $geshi->set_overall_class('code'); // save generated stylesheet $content = $geshi->parse_code(); // check buggy GESHI, it sometimes produce not UTF-8 valid code :-(( $content = iconv('UTF-8', 'UTF-8//IGNORE', $content); // protect output is in HTML $content = $texy->protect($content, Texy::CONTENT_BLOCK); $el = TexyHtml::el(); $el->setText($content); return $el; }
/** * User handler for code block * * @param TexyHandlerInvocation handler invocation * @param string block type * @param string text to highlight * @param string language * @param TexyModifier modifier * @return TexyHtml */ public function blockHandler($invocation, $blocktype, $content, $lang, $modifier) { /** @var \Texy $texy */ $texy = $invocation->getTexy(); $content = \Texy::outdent($content); $lexerData = $this->resolveLexer($blocktype); $lexer = $this->getLexerInstance($lexerData['name']); $highlighter = $this->getHighlighter($lexerData['countLines']); if ($lexer !== false) { $content = $highlighter->highlight($content, $lexer); } else { $content = htmlspecialchars($content); } $content = $texy->protect($content, \Texy::CONTENT_BLOCK); $elPre = \TexyHtml::el('pre'); if ($modifier) { $modifier->decorate($texy, $elPre); } $elPre->attrs['class'] = mb_strtolower($this->getLanguage($blocktype)); $elPre->create('code', $content); return $elPre; }
/** * Finish invocation. * * @param TexyHandlerInvocation handler invocation * @param int 0..5 * @param string * @param TexyModifier * @param bool * @return TexyHtml */ public function solve($invocation, $level, $content, $mod, $isSurrounded) { // as fixed balancing, for block/texysource & correct decorating $el = TexyHtml::el('h' . min(6, max(1, $level + $this->top))); $mod->decorate($this->texy, $el); $el->parseLine($this->texy, trim($content)); $this->TOC[] = array('el' => $el, 'level' => $level, 'type' => $isSurrounded ? 'surrounded' : 'underlined'); return $el; }
/** * Finish invocation. * * @param TexyHandlerInvocation handler invocation * @param TexyImage * @param TexyLink * @param string * @param TexyModifier * @return TexyHtml|FALSE */ public function solve($invocation, TexyImage $image, $link, $content, $mod) { $tx = $this->texy; $hAlign = $image->modifier->hAlign; $image->modifier->hAlign = NULL; $elImg = $tx->imageModule->solve(NULL, $image, $link); // returns TexyHtml or false! if (!$elImg) { return FALSE; } $el = TexyHtml::el('div'); if (!empty($image->width) && $this->widthDelta !== FALSE) { $el->attrs['style']['width'] = $image->width + $this->widthDelta . 'px'; } $mod->decorate($tx, $el); $el[0] = $elImg; $el[1] = TexyHtml::el('p'); $el[1]->parseLine($tx, ltrim($content)); $class = $this->class; if ($hAlign) { $var = $hAlign . 'Class'; // leftClass, rightClass if (!empty($this->{$var})) { $class = $this->{$var}; } elseif (empty($tx->alignClasses[$hAlign])) { $el->attrs['style']['float'] = $hAlign; } else { $class .= '-' . $tx->alignClasses[$hAlign]; } } $el->attrs['class'][] = $class; return $el; }
/** * Creates template. * * @param \ApiGen\Generator $generator */ public function __construct(Generator $generator) { $this->generator = $generator; $this->config = $generator->getConfig(); $that = $this; // Output in HTML5 Nette\Utils\Html::$xhtml = false; // FSHL $fshl = new FSHL\Highlighter(new FSHL\Output\Html()); $fshl->setLexer(new FSHL\Lexer\Php()); // Texy $this->texy = new \Texy(); $this->texy->allowedTags = array_flip($this->config->allowedHtml); $this->texy->allowed['list/definition'] = false; $this->texy->allowed['phrase/em-alt'] = false; $this->texy->allowed['longwords'] = false; $this->texy->allowed['typography'] = false; $this->texy->linkModule->shorten = false; // Highlighting <code>, <pre> $this->texy->addHandler('beforeParse', function ($texy, &$text, $singleLine) { $text = preg_replace('~<code>(.+?)</code>~', '#code#\\1#/code#', $text); }); $this->texy->registerLinePattern(function ($parser, $matches, $name) use($fshl) { return \TexyHtml::el('code', $fshl->highlight($matches[1])); }, '~#code#(.+?)#/code#~', 'codeInlineSyntax'); $this->texy->registerBlockPattern(function ($parser, $matches, $name) use($fshl) { if ('code' === $matches[1]) { $lines = array_filter(explode("\n", $matches[2])); if (!empty($lines)) { $firstLine = array_shift($lines); $indent = ''; $li = 0; while (isset($firstLine[$li]) && preg_match('~\\s~', $firstLine[$li])) { foreach ($lines as $line) { if (!isset($line[$li]) || $firstLine[$li] !== $line[$li]) { break 2; } } $indent .= $firstLine[$li++]; } if (!empty($indent)) { $matches[2] = str_replace("\n" . $indent, "\n", 0 === strpos($matches[2], $indent) ? substr($matches[2], $li) : $matches[2]); } } $content = $fshl->highlight($matches[2]); } else { $content = htmlspecialchars($matches[2]); } $content = $parser->getTexy()->protect($content, \Texy::CONTENT_BLOCK); return \TexyHtml::el('pre', $content); }, '~<(code|pre)>(.+?)</\\1>~s', 'codeBlockSyntax'); // Common operations $this->registerHelperLoader('Nette\\Templating\\Helpers::loader'); // PHP source highlight $this->registerHelper('highlightPHP', function ($source, $context) use($that, $fshl) { return $that->resolveLink($that->getTypeName($source), $context) ?: $fshl->highlight((string) $source); }); $this->registerHelper('highlightValue', function ($definition, $context) use($that) { return $that->highlightPHP(preg_replace('~^(?:[ ]{4}|\\t)~m', '', $definition), $context); }); // Urls $this->registerHelper('packageUrl', new Nette\Callback($this, 'getPackageUrl')); $this->registerHelper('namespaceUrl', new Nette\Callback($this, 'getNamespaceUrl')); $this->registerHelper('groupUrl', new Nette\Callback($this, 'getGroupUrl')); $this->registerHelper('classUrl', new Nette\Callback($this, 'getClassUrl')); $this->registerHelper('methodUrl', new Nette\Callback($this, 'getMethodUrl')); $this->registerHelper('propertyUrl', new Nette\Callback($this, 'getPropertyUrl')); $this->registerHelper('constantUrl', new Nette\Callback($this, 'getConstantUrl')); $this->registerHelper('functionUrl', new Nette\Callback($this, 'getFunctionUrl')); $this->registerHelper('elementUrl', new Nette\Callback($this, 'getElementUrl')); $this->registerHelper('sourceUrl', new Nette\Callback($this, 'getSourceUrl')); $this->registerHelper('manualUrl', new Nette\Callback($this, 'getManualUrl')); // Packages & namespaces $this->registerHelper('packageLinks', new Nette\Callback($this, 'getPackageLinks')); $this->registerHelper('namespaceLinks', new Nette\Callback($this, 'getNamespaceLinks')); $this->registerHelper('subgroupName', function ($groupName) { if ($pos = strrpos($groupName, '\\')) { return substr($groupName, $pos + 1); } return $groupName; }); // Types $this->registerHelper('typeLinks', new Nette\Callback($this, 'getTypeLinks')); // Docblock descriptions $this->registerHelper('description', function ($annotation, $context) use($that) { $description = trim(strpbrk($annotation, "\n\r\t \$")); if ($context instanceof ReflectionParameter) { $description = preg_replace('~^(\\$' . $context->getName() . '(?:,\\.{3})?)(\\s+|$)~i', '\\2', $description, 1); $context = $context->getDeclaringFunction(); } return $that->doc($description, $context); }); $this->registerHelper('shortDescription', function ($element, $block = false) use($that) { return $that->doc($element->getShortDescription(), $element, $block); }); $this->registerHelper('longDescription', function ($element) use($that) { $long = $element->getLongDescription(); // Merge lines $long = preg_replace_callback('~(?:<(code|pre)>.+?</\\1>)|([^<]*)~s', function ($matches) { return !empty($matches[2]) ? preg_replace('~\\n(?:\\t|[ ])+~', ' ', $matches[2]) : $matches[0]; }, $long); return $that->doc($long, $element, true); }); // Individual annotations processing $this->registerHelper('annotation', function ($value, $name, $context) use($that, $generator) { switch ($name) { case 'param': case 'return': case 'throws': $description = $that->description($value, $context); return sprintf('<code>%s</code>%s', $that->getTypeLinks($value, $context), $description ? '<br>' . $description : ''); case 'license': list($url, $description) = $that->split($value); return $that->link($url, $description ?: $url); case 'link': list($url, $description) = $that->split($value); if (Nette\Utils\Validators::isUrl($url)) { return $that->link($url, $description ?: $url); } break; case 'see': $doc = array(); foreach (preg_split('~\\s*,\\s*~', $value) as $link) { if (null !== $generator->resolveElement($link, $context)) { $doc[] = sprintf('<code>%s</code>', $that->getTypeLinks($link, $context)); } else { $doc[] = $that->doc($link, $context); } } return implode(', ', $doc); case 'uses': case 'usedby': list($link, $description) = $that->split($value); $separator = $context instanceof ReflectionClass || !$description ? ' ' : '<br>'; if (null !== $generator->resolveElement($link, $context)) { return sprintf('<code>%s</code>%s%s', $that->getTypeLinks($link, $context), $separator, $description); } break; default: break; } // Default return $that->doc($value, $context); }); $todo = $this->config->todo; $internal = $this->config->internal; $this->registerHelper('annotationFilter', function (array $annotations, array $filter = array()) use($todo, $internal) { // Filtered, unsupported or deprecated annotations static $filtered = array('package', 'subpackage', 'property', 'property-read', 'property-write', 'method', 'abstract', 'access', 'final', 'filesource', 'global', 'name', 'static', 'staticvar'); foreach ($filtered as $annotation) { unset($annotations[$annotation]); } // Custom filter foreach ($filter as $annotation) { unset($annotations[$annotation]); } // Show/hide internal if (!$internal) { unset($annotations['internal']); } // Show/hide tasks if (!$todo) { unset($annotations['todo']); } return $annotations; }); $this->registerHelper('annotationSort', function (array $annotations) { uksort($annotations, function ($one, $two) { static $order = array('deprecated' => 0, 'category' => 1, 'copyright' => 2, 'license' => 3, 'author' => 4, 'version' => 5, 'since' => 6, 'see' => 7, 'uses' => 8, 'usedby' => 9, 'link' => 10, 'internal' => 11, 'example' => 12, 'tutorial' => 13, 'todo' => 14); if (isset($order[$one], $order[$two])) { return $order[$one] - $order[$two]; } elseif (isset($order[$one])) { return -1; } elseif (isset($order[$two])) { return 1; } else { return strcasecmp($one, $two); } }); return $annotations; }); $this->registerHelper('annotationBeautify', function ($annotation) { static $names = array('usedby' => 'Used by'); if (isset($names[$annotation])) { return $names[$annotation]; } return Nette\Utils\Strings::firstUpper($annotation); }); // Static files versioning $destination = $this->config->destination; $this->registerHelper('staticFile', function ($name) use($destination) { static $versions = array(); $filename = $destination . DIRECTORY_SEPARATOR . $name; if (!isset($versions[$filename]) && is_file($filename)) { $versions[$filename] = sprintf('%u', crc32(file_get_contents($filename))); } if (isset($versions[$filename])) { $name .= '?' . $versions[$filename]; } return $name; }); // Source anchors $this->registerHelper('sourceAnchors', function ($source) { // Classes, interfaces, traits and exceptions $source = preg_replace_callback('~(<span\\s+class="php-keyword1">(?:class|interface|trait)</span>\\s+)(\\w+)~i', function ($matches) { $link = sprintf('<a id="%1$s" href="#%1$s">%1$s</a>', $matches[2]); return $matches[1] . $link; }, $source); // Methods and functions $source = preg_replace_callback('~(<span\\s+class="php-keyword1">function</span>\\s+)(\\w+)~i', function ($matches) { $link = sprintf('<a id="_%1$s" href="#_%1$s">%1$s</a>', $matches[2]); return $matches[1] . $link; }, $source); // Constants $source = preg_replace_callback('~(<span class="php-keyword1">const</span>)(.*?)(;)~is', function ($matches) { $links = preg_replace_callback('~(\\s|,)([A-Z_]+)(\\s+=)~', function ($matches) { return $matches[1] . sprintf('<a id="%1$s" href="#%1$s">%1$s</a>', $matches[2]) . $matches[3]; }, $matches[2]); return $matches[1] . $links . $matches[3]; }, $source); // Properties $source = preg_replace_callback('~(<span\\s+class="php-keyword1">(?:private|protected|public|var|static)</span>\\s+)(<span\\s+class="php-var">.*?)(;)~is', function ($matches) { $links = preg_replace_callback('~(<span\\s+class="php-var">)(\\$\\w+)~i', function ($matches) { return $matches[1] . sprintf('<a id="%1$s" href="#%1$s">%1$s</a>', $matches[2]); }, $matches[2]); return $matches[1] . $links . $matches[3]; }, $source); return $source; }); $this->registerHelper('urlize', array($this, 'urlize')); $this->registerHelper('relativePath', array($generator, 'getRelativePath')); $this->registerHelper('resolveElement', array($generator, 'resolveElement')); $this->registerHelper('getClass', array($generator, 'getClass')); }
/** * Finish invocation. * * @param TexyHandlerInvocation handler invocation * @param string * @param string * @param TexyModifier * @param TexyLink * @return TexyHtml */ public function solve($invocation, $phrase, $content, $mod, $link) { $tx = $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 = $tx->protect(Texy::escapeHtml($content), Texy::CONTENT_TEXTUAL); } if ($phrase === 'phrase/strong+em') { $el = TexyHtml::el($this->tags['phrase/strong']); $el->create($this->tags['phrase/em'], $content); $mod->decorate($tx, $el); } elseif ($tag) { $el = TexyHtml::el($tag)->setText($content); $mod->decorate($tx, $el); if ($tag === 'q') { $el->attrs['cite'] = $mod->cite; } } else { $el = $content; // trick } if ($link && $this->linksAllowed) { return $tx->linkModule->solve(NULL, $link, $el); } return $el; }
function patternTable($parser, $matches) { list(, $mMod) = $matches; $tx = $this->texy; $el = TexyHtml::el('table'); $mod = new TexyModifier($mMod); $mod->decorate($tx, $el); $parser->moveBackward(); if ($parser->next('#^\\|(\\#|\\=){2,}(?!\\1)(.*)\\1*\\|? *' . TEXY_MODIFIER_H . '?()$#Um', $matches)) { list(, , $mContent, $mMod) = $matches; $caption = $el->create('caption'); $mod = new TexyModifier($mMod); $mod->decorate($tx, $caption); $caption->parseLine($tx, $mContent); } $isHead = FALSE; $colModifier = array(); $prevRow = array(); $rowCounter = 0; $colCounter = 0; $elPart = NULL; while (TRUE) { if ($parser->next('#^\\|[+-]{3,}$#Um', $matches)) { $isHead = !$isHead; $prevRow = array(); continue; } if ($parser->next('#^\\|(.*)(?:|\\|\\ *' . TEXY_MODIFIER_HV . '?)()$#U', $matches)) { if ($rowCounter === 0 && !$isHead && $parser->next('#^\\|[+-]{3,}$#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'); } list(, $mContent, $mMod) = $matches; $elRow = TexyHtml::el('tr'); $mod = new TexyModifier($mMod); $mod->decorate($tx, $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; $mContent = str_replace('\\|', '|', $mContent); foreach (explode('|', $mContent) as $cell) { if ($cell === '' && $elCell) { $elCell->colSpan++; unset($prevRow[$col]); $col++; continue; } if (isset($prevRow[$col]) && preg_match('#\\^\\ *$|\\*??(.*)\\ +\\^$#AU', $cell, $matches)) { $prevRow[$col]->rowSpan++; $matches[] = ''; $prevRow[$col]->text .= "\n" . $matches[1]; $col += $prevRow[$col]->colSpan; $elCell = NULL; continue; } if (!preg_match('#(\\*??)\\ *' . TEXY_MODIFIER_HV . '??(.*)' . TEXY_MODIFIER_HV . '?\\ *()$#AU', $cell, $matches)) { continue; } list(, $mHead, $mModCol, $mContent, $mMod) = $matches; if ($mModCol) { $colModifier[$col] = new TexyModifier($mModCol); } if (isset($colModifier[$col])) { $mod = clone $colModifier[$col]; } else { $mod = new TexyModifier(); } $mod->setProperties($mMod); $elCell = new TexyTableCellElement(); $elCell->setName($isHead || $mHead === '*' ? 'th' : 'td'); $mod->decorate($tx, $elCell); $elCell->text = $mContent; $elRow->add($elCell); $prevRow[$col] = $elCell; $col++; } while ($col < $colCounter) { $elCell = new TexyTableCellElement(); $elCell->setName($isHead ? 'th' : 'td'); if (isset($colModifier[$col])) { $colModifier[$col]->decorate($tx, $elCell); } $elRow->add($elCell); $prevRow[$col] = $elCell; $col++; } $colCounter = $col; if ($elRow->count()) { $elPart->add($elRow); $rowCounter++; } else { foreach ($prevRow as $elCell) { $elCell->rowSpan--; } } continue; } break; } if ($elPart === NULL) { return FALSE; } if ($elPart->getName() === 'thead') { $elPart->setName('tbody'); } $this->finishPart($elPart); $tx->invokeHandlers('afterTable', array($parser, $el, $mod)); return $el; }
/** * Callback for:. * * Term: .(title)[class]{style}> * - description 1 * - description 2 * - description 3 * * @param TexyBlockParser * @param array regexp matches * @param string pattern name * * @return TexyHtml */ public function patternDefList($parser, $matches) { list(, $mMod, , , , $mBullet) = $matches; // [1] => .(title)[class]{style}<> // [2] => ... // [3] => .(title)[class]{style}<> // [4] => space // [5] => - * + $tx = $this->texy; $bullet = null; foreach ($this->bullets as $desc) { if (preg_match('#' . $desc[0] . '#Au', $mBullet)) { $bullet = isset($desc[3]) ? $desc[3] : $desc[0]; break; } } $el = TexyHtml::el('dl'); $mod = new TexyModifier($mMod); $mod->decorate($tx, $el); $parser->moveBackward(2); $patternTerm = '#^\\n?(\\S.*)\\:\\ *' . TEXY_MODIFIER_H . '?()$#mUA'; while (true) { if ($elItem = $this->patternItem($parser, $bullet, true, 'dd')) { $el->add($elItem); continue; } if ($parser->next($patternTerm, $matches)) { list(, $mContent, $mMod) = $matches; // [1] => ... // [2] => .(title)[class]{style}<> $elItem = TexyHtml::el('dt'); $modItem = new TexyModifier($mMod); $modItem->decorate($tx, $elItem); $elItem->parseLine($tx, $mContent); $el->add($elItem); continue; } break; } // event listener $tx->invokeHandlers('afterDefinitionList', array($parser, $el, $mod)); return $el; }
/** * Finish invocation. * * @param TexyHandlerInvocation handler invocation * @param TexyHtml element * @param bool is start tag? * @param bool is empty? * @return TexyHtml|string|FALSE */ public function solveTag($invocation, TexyHtml $el, $isStart, $forceEmpty = NULL) { $tx = $this->texy; // tag & attibutes $allowedTags = $tx->allowedTags; // speed-up if (!$allowedTags) { return FALSE; } // all tags are disabled // convert case $name = $el->getName(); $lower = strtolower($name); if (isset($tx->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::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 = array(); } 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 = $tx->_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 = $tx->_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; } } if ($name === 'img') { if (!isset($elAttrs['src'])) { return FALSE; } if (!$tx->checkURL($elAttrs['src'], Texy::FILTER_IMAGE)) { return FALSE; } $tx->summary['images'][] = $elAttrs['src']; } elseif ($name === 'a') { if (!isset($elAttrs['href']) && !isset($elAttrs['name']) && !isset($elAttrs['id'])) { return FALSE; } if (isset($elAttrs['href'])) { if ($tx->linkModule->forceNoFollow && strpos($elAttrs['href'], '//') !== FALSE) { if (isset($elAttrs['rel'])) { $elAttrs['rel'] = (array) $elAttrs['rel']; } $elAttrs['rel'][] = 'nofollow'; } if (!$tx->checkURL($elAttrs['href'], Texy::FILTER_ANCHOR)) { return FALSE; } $tx->summary['links'][] = $elAttrs['href']; } } elseif (preg_match('#^h[1-6]#i', $name)) { $tx->headingModule->TOC[] = array('el' => $el, 'level' => (int) substr($name, 1), 'type' => 'html'); } $el->validateAttrs($tx->dtd); return $el; }
/** * Callback for:. * * .(title)[class]{style}> * |------------------ * | xxx | xxx | xxx | .(..){..}[..] * |------------------ * | aa | bb | cc | * * @param TexyBlockParser * @param array regexp matches * @param string pattern name * @return TexyHtml|string|FALSE */ public function patternTable($parser, $matches) { list(, $mMod) = $matches; // [1] => .(title)[class]{style}<>_ $tx = $this->texy; $el = TexyHtml::el('table'); $mod = new TexyModifier($mMod); $mod->decorate($tx, $el); $parser->moveBackward(); if ($parser->next('#^\\|(\\#|\\=){2,}(?![|\\#=+])(.+)\\1*\\|? *' . TEXY_MODIFIER_H . '?()$#Um', $matches)) { list(, , $mContent, $mMod) = $matches; // [1] => # / = // [2] => .... // [3] => .(title)[class]{style}<> $caption = $el->create('caption'); $mod = new TexyModifier($mMod); $mod->decorate($tx, $caption); $caption->parseLine($tx, $mContent); } $isHead = FALSE; $colModifier = array(); $prevRow = array(); // 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 = array(); continue; } if ($parser->next('#^\\|(.*)(?:|\\|\\ *' . TEXY_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 = TexyHtml::el('tr'); $mod = new TexyModifier($mMod); $mod->decorate($tx, $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); foreach (explode('|', $mContent) as $cell) { // rowSpan if (isset($prevRow[$col]) && ($lineMode || preg_match('#\\^\\ *$|\\*??(.*)\\ +\\^$#AU', $cell, $matches))) { $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 if (!preg_match('#(\\*??)\\ *' . TEXY_MODIFIER_HV . '??(.*)' . TEXY_MODIFIER_HV . '?\\ *()$#AU', $cell, $matches)) { continue; } list(, $mHead, $mModCol, $mContent, $mMod) = $matches; // [1] => * ^ // [2] => .(title)[class]{style}<>_ // [3] => .... // [4] => .(title)[class]{style}<>_ if ($mModCol) { $colModifier[$col] = new TexyModifier($mModCol); } if (isset($colModifier[$col])) { $mod = clone $colModifier[$col]; } else { $mod = new TexyModifier(); } $mod->setProperties($mMod); $elCell = new TexyTableCellElement(); $elCell->setName($isHead || $mHead === '*' ? 'th' : 'td'); $mod->decorate($tx, $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 TexyTableCellElement(); $elCell->setName($isHead ? 'th' : 'td'); if (isset($colModifier[$col])) { $colModifier[$col]->decorate($tx, $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 $tx->invokeHandlers('afterTable', array($parser, $el, $mod)); return $el; }
/** * Converts document in Texy! to (X)HTML code. * * @param string input text * @param bool is single line? * @return string output HTML code */ public function process($text, $singleLine = FALSE) { if ($this->processing) { throw new RuntimeException('Processing is in progress yet.'); } // initialization $this->marks = array(); $this->processing = TRUE; // speed-up if (is_array($this->allowedClasses)) { $this->_classes = array_flip($this->allowedClasses); } else { $this->_classes = $this->allowedClasses; } if (is_array($this->allowedStyles)) { $this->_styles = array_flip($this->allowedStyles); } else { $this->_styles = $this->allowedStyles; } // convert to UTF-8 (and check source encoding) $text = TexyUtf::toUtf($text, $this->encoding); if ($this->removeSoftHyphens) { $text = str_replace("", '', $text); } // standardize line endings and spaces $text = self::normalize($text); // replace tabs with spaces $this->tabWidth = max(1, (int) $this->tabWidth); while (strpos($text, "\t") !== FALSE) { $text = TexyRegexp::replace($text, '#^([^\\t\\n]*+)\\t#mU', array($this, 'tabCb')); } // user before handler $this->invokeHandlers('beforeParse', array($this, &$text, $singleLine)); // select patterns $this->_linePatterns = $this->linePatterns; $this->_blockPatterns = $this->blockPatterns; foreach ($this->_linePatterns as $name => $foo) { if (empty($this->allowed[$name])) { unset($this->_linePatterns[$name]); } } foreach ($this->_blockPatterns as $name => $foo) { if (empty($this->allowed[$name])) { unset($this->_blockPatterns[$name]); } } // parse Texy! document into internal DOM structure $this->DOM = TexyHtml::el(); if ($singleLine) { $this->DOM->parseLine($this, $text); } else { $this->DOM->parseBlock($this, $text); } // user after handler $this->invokeHandlers('afterParse', array($this, $this->DOM, $singleLine)); // converts internal DOM structure to final HTML code $html = $this->DOM->toHtml($this); // created by TexyParagraphModule and then protected $html = str_replace("\r", "\n", $html); $this->processing = FALSE; return TexyUtf::utf2html($html, $this->encoding); }
/** * Finish invocation. * * @param TexyHandlerInvocation handler invocation * @param string blocktype * @param string content * @param string additional parameter * @param TexyModifier * @return TexyHtml|string|FALSE */ public function solve($invocation, $blocktype, $s, $param, $mod) { $tx = $this->texy; $parser = $invocation->parser; if ($blocktype === 'block/texy') { $el = TexyHtml::el(); $el->parseBlock($tx, $s, $parser->isIndented()); return $el; } if (empty($tx->allowed[$blocktype])) { return FALSE; } if ($blocktype === 'block/texysource') { $s = Texy::outdent($s); if ($s === '') { return "\n"; } $el = TexyHtml::el(); if ($param === 'line') { $el->parseLine($tx, $s); } else { $el->parseBlock($tx, $s); } $s = $el->toHtml($tx); $blocktype = 'block/code'; $param = 'html'; // to be continue (as block/code) } if ($blocktype === 'block/code') { $s = Texy::outdent($s); if ($s === '') { return "\n"; } $s = Texy::escapeHtml($s); $s = $tx->protect($s, Texy::CONTENT_BLOCK); $el = TexyHtml::el('pre'); $mod->decorate($tx, $el); $el->attrs['class'][] = $param; // lang $el->create('code', $s); return $el; } if ($blocktype === 'block/default') { $s = Texy::outdent($s); if ($s === '') { return "\n"; } $el = TexyHtml::el('pre'); $mod->decorate($tx, $el); $el->attrs['class'][] = $param; // lang $s = Texy::escapeHtml($s); $s = $tx->protect($s, Texy::CONTENT_BLOCK); $el->setText($s); return $el; } if ($blocktype === 'block/pre') { $s = Texy::outdent($s); if ($s === '') { return "\n"; } $el = TexyHtml::el('pre'); $mod->decorate($tx, $el); $lineParser = new TexyLineParser($tx, $el); // special mode - parse only html tags $tmp = $lineParser->patterns; $lineParser->patterns = array(); 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 = Texy::unescapeHtml($s); $s = Texy::escapeHtml($s); $s = $tx->unprotect($s); $s = $tx->protect($s, Texy::CONTENT_BLOCK); $el->setText($s); return $el; } if ($blocktype === 'block/html') { $s = trim($s, "\n"); if ($s === '') { return "\n"; } $el = TexyHtml::el(); $lineParser = new TexyLineParser($tx, $el); // special mode - parse only html tags $tmp = $lineParser->patterns; $lineParser->patterns = array(); 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 = Texy::unescapeHtml($s); $s = Texy::escapeHtml($s); $s = $tx->unprotect($s); return $tx->protect($s, Texy::CONTENT_BLOCK) . "\n"; } if ($blocktype === 'block/text') { $s = trim($s, "\n"); if ($s === '') { return "\n"; } $s = Texy::escapeHtml($s); $s = str_replace("\n", TexyHtml::el('br')->startTag(), $s); // nl2br return $tx->protect($s, Texy::CONTENT_BLOCK) . "\n"; } if ($blocktype === 'block/comment') { return "\n"; } if ($blocktype === 'block/div') { $s = Texy::outdent($s); if ($s === '') { return "\n"; } $el = TexyHtml::el('div'); $mod->decorate($tx, $el); $el->parseBlock($tx, $s, $parser->isIndented()); // TODO: INDENT or NORMAL ? return $el; } if ($blocktype === 'block/section') { $s = Texy::outdent($s); if ($s === '') { return "\n"; } $el = TexyHtml::el('section'); $mod->decorate($tx, $el); $el->parseBlock($tx, $s, $parser->isIndented()); // TODO: INDENT or NORMAL ? return $el; } if ($blocktype === 'block/article') { $s = Texy::outdent($s); if ($s === '') { return "\n"; } $el = TexyHtml::el('article'); $mod->decorate($tx, $el); $el->parseBlock($tx, $s, $parser->isIndented()); // TODO: INDENT or NORMAL ? return $el; } if ($blocktype === 'block/aside') { $s = Texy::outdent($s); if ($s === '') { return "\n"; } $el = TexyHtml::el('aside'); $mod->decorate($tx, $el); $el->parseBlock($tx, $s, $parser->isIndented()); // TODO: INDENT or NORMAL ? return $el; } if ($blocktype === 'block/header') { $s = Texy::outdent($s); if ($s === '') { return "\n"; } $el = TexyHtml::el('header'); $mod->decorate($tx, $el); $el->parseBlock($tx, $s, $parser->isIndented()); // TODO: INDENT or NORMAL ? return $el; } if ($blocktype === 'block/footer') { $s = Texy::outdent($s); if ($s === '') { return "\n"; } $el = TexyHtml::el('footer'); $mod->decorate($tx, $el); $el->parseBlock($tx, $s, $parser->isIndented()); // TODO: INDENT or NORMAL ? return $el; } if ($blocktype === 'block/nav') { $s = Texy::outdent($s); if ($s === '') { return "\n"; } $el = TexyHtml::el('nav'); $mod->decorate($tx, $el); $el->parseBlock($tx, $s, $parser->isIndented()); // TODO: INDENT or NORMAL ? return $el; } return FALSE; }
/** * Converts document in Texy! to (X)HTML code. * * @param string input text * @param bool is single line? * @return string output HTML code */ public function process($text, $singleLine = FALSE) { if ($this->processing) { throw new InvalidStateException('Processing is in progress yet.'); } // initialization $this->marks = array(); $this->processing = TRUE; // speed-up if (is_array($this->allowedClasses)) { $this->_classes = array_flip($this->allowedClasses); } else { $this->_classes = $this->allowedClasses; } if (is_array($this->allowedStyles)) { $this->_styles = array_flip($this->allowedStyles); } else { $this->_styles = $this->allowedStyles; } // convert to UTF-8 (and check source encoding) $text = TexyUtf::toUtf($text, $this->encoding); if ($this->removeSoftHyphens) { $text = str_replace("", '', $text); } // standardize line endings and spaces $text = self::normalize($text); // replace tabs with spaces $this->tabWidth = max(1, (int) $this->tabWidth); while (strpos($text, "\t") !== FALSE) { $text = preg_replace_callback('#^(.*)\\t#mU', array($this, 'tabCb'), $text); } // user before handler $this->invokeHandlers('beforeParse', array($this, &$text, $singleLine)); // select patterns $this->_linePatterns = $this->linePatterns; $this->_blockPatterns = $this->blockPatterns; foreach ($this->_linePatterns as $name => $foo) { if (empty($this->allowed[$name])) { unset($this->_linePatterns[$name]); } } foreach ($this->_blockPatterns as $name => $foo) { if (empty($this->allowed[$name])) { unset($this->_blockPatterns[$name]); } } // parse Texy! document into internal DOM structure $this->DOM = TexyHtml::el(); if ($singleLine) { $this->DOM->parseLine($this, $text); } else { $this->DOM->parseBlock($this, $text); } // user after handler $this->invokeHandlers('afterParse', array($this, $this->DOM, $singleLine)); // converts internal DOM structure to final HTML code $html = $this->DOM->toHtml($this); // this notice should remain if (self::$advertisingNotice) { $html .= "\n<!-- by Texy2! -->"; if (self::$advertisingNotice === 'once') { self::$advertisingNotice = FALSE; } } $this->processing = FALSE; return TexyUtf::utf2html($html, $this->encoding); }
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\HtmlManual(), \FSHL\Highlighter::OPTION_TAB_INDENT); $texy = $invocation->getTexy(); $content = \Texy::outdent($content); //Set correct lexer: 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 \FSHL\Lexer\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::CONTENT_BLOCK); $elPre = \TexyHtml::el('pre'); if ($modifier) { $modifier->decorate($texy, $elPre); } $elPre->attrs['class'] = strtolower($lang); $elPre->create('code', $content); return $elPre; }
public function process($text, $singleLine = FALSE) { if ($this->processing) { throw new InvalidStateException('Processing is in progress yet.'); } $this->marks = array(); $this->processing = TRUE; if (is_array($this->allowedClasses)) { $this->_classes = array_flip($this->allowedClasses); } else { $this->_classes = $this->allowedClasses; } if (is_array($this->allowedStyles)) { $this->_styles = array_flip($this->allowedStyles); } else { $this->_styles = $this->allowedStyles; } $text = TexyUtf::toUtf($text, $this->encoding); if ($this->removeSoftHyphens) { $text = str_replace("Β", '', $text); } $text = self::normalize($text); $this->tabWidth = max(1, (int) $this->tabWidth); while (strpos($text, "\t") !== FALSE) { $text = preg_replace_callback('#^(.*)\\t#mU', array($this, 'tabCb'), $text); } $this->invokeHandlers('beforeParse', array($this, &$text, $singleLine)); $this->_linePatterns = $this->linePatterns; $this->_blockPatterns = $this->blockPatterns; foreach ($this->_linePatterns as $name => $foo) { if (empty($this->allowed[$name])) { unset($this->_linePatterns[$name]); } } foreach ($this->_blockPatterns as $name => $foo) { if (empty($this->allowed[$name])) { unset($this->_blockPatterns[$name]); } } $this->DOM = TexyHtml::el(); if ($singleLine) { $this->DOM->parseLine($this, $text); } else { $this->DOM->parseBlock($this, $text); } $this->invokeHandlers('afterParse', array($this, $this->DOM, $singleLine)); $html = $this->DOM->toHtml($this); if (self::$advertisingNotice) { $html .= "\n<!-- by Texy2! -->"; if (self::$advertisingNotice === 'once') { self::$advertisingNotice = FALSE; } } $this->processing = FALSE; return TexyUtf::utf2html($html, $this->encoding); }
/** * Finish invocation. * * @param TexyHandlerInvocation handler invocation * @param TexyImage * @param TexyLink * @return TexyHtml|FALSE */ public function solve($invocation, TexyImage $image, $link) { if ($image->URL == NULL) { return FALSE; } $tx = $this->texy; $mod = $image->modifier; $alt = $mod->title; $mod->title = NULL; $hAlign = $mod->hAlign; $mod->hAlign = NULL; $el = TexyHtml::el('img'); $el->attrs['src'] = NULL; // trick - move to front $mod->decorate($tx, $el); $el->attrs['src'] = Texy::prependRoot($image->URL, $this->root); if (!isset($el->attrs['alt'])) { if ($alt !== NULL) { $el->attrs['alt'] = $tx->typographyModule->postLine($alt); } else { $el->attrs['alt'] = $this->defaultAlt; } } if ($hAlign) { $var = $hAlign . 'Class'; // leftClass, rightClass if (!empty($this->{$var})) { $el->attrs['class'][] = $this->{$var}; } elseif (empty($tx->alignClasses[$hAlign])) { $el->attrs['style']['float'] = $hAlign; } else { $el->attrs['class'][] = $tx->alignClasses[$hAlign]; } } if (!is_int($image->width) || !is_int($image->height) || $image->asMax) { // autodetect fileRoot if ($this->fileRoot === NULL && isset($_SERVER['SCRIPT_FILENAME'])) { $this->fileRoot = dirname($_SERVER['SCRIPT_FILENAME']) . '/' . $this->root; } // detect dimensions // absolute URL & security check for double dot if (Texy::isRelative($image->URL) && strpos($image->URL, '..') === FALSE) { $file = rtrim($this->fileRoot, '/\\') . '/' . $image->URL; if (@is_file($file)) { $size = getImageSize($file); if (is_array($size)) { if ($image->asMax) { $ratio = 1; if (is_int($image->width)) { $ratio = min($ratio, $image->width / $size[0]); } if (is_int($image->height)) { $ratio = min($ratio, $image->height / $size[1]); } $image->width = round($ratio * $size[0]); $image->height = round($ratio * $size[1]); } elseif (is_int($image->width)) { $ratio = round($size[1] / $size[0] * $image->width); $image->height = round($size[1] / $size[0] * $image->width); } elseif (is_int($image->height)) { $image->width = round($size[0] / $size[1] * $image->height); } else { $image->width = $size[0]; $image->height = $size[1]; } } } } } $el->attrs['width'] = $image->width; $el->attrs['height'] = $image->height; // onmouseover actions generate if ($image->overURL !== NULL) { $overSrc = Texy::prependRoot($image->overURL, $this->root); $el->attrs['onmouseover'] = 'this.src=\'' . addSlashes($overSrc) . '\''; $el->attrs['onmouseout'] = 'this.src=\'' . addSlashes($el->attrs['src']) . '\''; $el->attrs['onload'] = str_replace('%i', addSlashes($overSrc), $this->onLoad); $tx->summary['preload'][] = $overSrc; } $tx->summary['images'][] = $el->attrs['src']; if ($link) { return $tx->linkModule->solve(NULL, $link, $el); } return $el; }