/** * Finish invocation. * @return Texy\HtmlElement|FALSE */ public function solve(Texy\HandlerInvocation $invocation, $emoticon, $raw) { $texy = $this->texy; $file = $this->icons[$emoticon]; $el = new Texy\HtmlElement('img'); $el->attrs['src'] = Texy\Helpers::prependRoot($file, $this->root === NULL ? $texy->imageModule->root : $this->root); $el->attrs['alt'] = $raw; $el->attrs['class'][] = $this->class; // file path $file = rtrim($this->fileRoot === NULL ? $texy->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]; } } $texy->summary['images'][] = $el->attrs['src']; return $el; }
/** * Converts cite source to URL. * @param string * @return string|NULL */ public function citeLink($link) { $texy = $this->texy; if ($link == NULL) { return NULL; } if ($link[0] === '[') { // [ref] $link = substr($link, 1, -1); $ref = $texy->linkModule->getReference($link); if ($ref) { return Texy\Helpers::prependRoot($ref->URL, $texy->linkModule->root); } } // special supported case if (strncasecmp($link, 'www.', 4) === 0) { return 'http://' . $link; } return Texy\Helpers::prependRoot($link, $texy->linkModule->root); }
/** * Callback function: <tag> | </tag> | .... * @return string * @internal */ public function cb($matches) { // html tag list(, $mText, $mComment, $mEnd, $mTag, $mAttr, $mEmpty) = $matches; // [1] => text // [1] => !-- comment -- // [2] => / // [3] => TAG // [4] => ... (attributes) // [5] => / (empty) $s = ''; // phase #1 - stuff between tags if ($mText !== '') { $item = reset($this->tagStack); if ($item && !isset($item['dtdContent']['%DATA'])) { // text not allowed? } elseif (array_intersect(array_keys($this->tagUsed, TRUE), $this->preserveSpaces)) { // inside pre & textarea preserve spaces $s = Texy\Helpers::freezeSpaces($mText); } else { $s = Regexp::replace($mText, '#[ \\n]+#', ' '); // otherwise shrink multiple spaces } } // phase #2 - HTML comment if ($mComment) { return $s . '<' . Texy\Helpers::freezeSpaces($mComment) . '>'; } // phase #3 - HTML tag $mEmpty = $mEmpty || isset(HtmlElement::$emptyElements[$mTag]); if ($mEmpty && $mEnd) { return $s; // bad tag; /end/ } if ($mEnd) { // end tag // has start tag? if (empty($this->tagUsed[$mTag])) { return $s; } // autoclose tags $tmp = []; $back = TRUE; foreach ($this->tagStack as $i => $item) { $tag = $item['tag']; $s .= $item['close']; $this->space -= $item['indent']; $this->tagUsed[$tag]--; $back = $back && isset(HtmlElement::$inlineElements[$tag]); unset($this->tagStack[$i]); if ($tag === $mTag) { break; } array_unshift($tmp, $item); } if (!$back || !$tmp) { return $s; } // allowed-check (nejspis neni ani potreba) $item = reset($this->tagStack); $dtdContent = $item ? $item['dtdContent'] : $this->baseDTD; if (!isset($dtdContent[$tmp[0]['tag']])) { return $s; } // autoopen tags foreach ($tmp as $item) { $s .= $item['open']; $this->space += $item['indent']; $this->tagUsed[$item['tag']]++; array_unshift($this->tagStack, $item); } } else { // start tag $dtdContent = $this->baseDTD; if (!isset($this->texy->dtd[$mTag])) { // unknown (non-html) tag $allowed = TRUE; $item = reset($this->tagStack); if ($item) { $dtdContent = $item['dtdContent']; } } else { // optional end tag closing foreach ($this->tagStack as $i => $item) { // is tag allowed here? $dtdContent = $item['dtdContent']; if (isset($dtdContent[$mTag])) { break; } $tag = $item['tag']; // auto-close hidden, optional and inline tags if ($item['close'] && (!isset(HtmlElement::$optionalEnds[$tag]) && !isset(HtmlElement::$inlineElements[$tag]))) { break; } // close it $s .= $item['close']; $this->space -= $item['indent']; $this->tagUsed[$tag]--; unset($this->tagStack[$i]); $dtdContent = $this->baseDTD; } // is tag allowed in this content? $allowed = isset($dtdContent[$mTag]); // check deep element prohibitions if ($allowed && isset(HtmlElement::$prohibits[$mTag])) { foreach (HtmlElement::$prohibits[$mTag] as $pTag) { if (!empty($this->tagUsed[$pTag])) { $allowed = FALSE; break; } } } } // empty elements se neukladaji do zasobniku if ($mEmpty) { if (!$allowed) { return $s; } if ($this->xml) { $mAttr .= ' /'; } $indent = $this->indent && !array_intersect(array_keys($this->tagUsed, TRUE), $this->preserveSpaces); if ($indent && $mTag === 'br') { // formatting exception return rtrim($s) . '<' . $mTag . $mAttr . ">\n" . str_repeat("\t", max(0, $this->space - 1)) . ""; } elseif ($indent && !isset(HtmlElement::$inlineElements[$mTag])) { $space = "\r" . str_repeat("\t", $this->space); return $s . $space . '<' . $mTag . $mAttr . '>' . $space; } else { return $s . '<' . $mTag . $mAttr . '>'; } } $open = NULL; $close = NULL; $indent = 0; /* if (!isset(Texy\HtmlElement::$inlineElements[$mTag])) { // block tags always decorate with \n $s .= "\n"; $close = "\n"; } */ if ($allowed) { $open = '<' . $mTag . $mAttr . '>'; // receive new content (ins & del are special cases) if (!empty($this->texy->dtd[$mTag][1])) { $dtdContent = $this->texy->dtd[$mTag][1]; } // format output if ($this->indent && !isset(HtmlElement::$inlineElements[$mTag])) { $close = "" . '</' . $mTag . '>' . "\n" . str_repeat("\t", $this->space); $s .= "\n" . str_repeat("\t", $this->space++) . $open . ""; $indent = 1; } else { $close = '</' . $mTag . '>'; $s .= $open; } // TODO: problematic formatting of select / options, object / params } // open tag, put to stack, increase counter $item = ['tag' => $mTag, 'open' => $open, 'close' => $close, 'dtdContent' => $dtdContent, 'indent' => $indent]; array_unshift($this->tagStack, $item); $tmp =& $this->tagUsed[$mTag]; $tmp++; } return $s; }
/** * Finish invocation. * * @param Texy\HtmlElement|string $content * @return Texy\HtmlElement|string */ public function solve(HandlerInvocation $invocation = NULL, Link $link, $content = NULL) { if ($link->URL == NULL) { return $content; } $texy = $this->texy; $el = new Texy\HtmlElement('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($texy, $el); } if ($link->type === Link::IMAGE) { // image $el->attrs['href'] = Texy\Helpers::prependRoot($link->URL, $texy->imageModule->linkedRoot); if ($this->imageClass) { $el->attrs['class'][] = $this->imageClass; } else { $el->attrs['onclick'] = $this->imageOnClick; } } else { $el->attrs['href'] = Texy\Helpers::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); } $texy->summary['links'][] = $el->attrs['href']; return $el; }
/** * Finish invocation. * @return Texy\HtmlElement|FALSE */ public function solve(Texy\HandlerInvocation $invocation = NULL, Image $image, Texy\Link $link = NULL) { if ($image->URL == NULL) { return FALSE; } $texy = $this->texy; $mod = $image->modifier; $alt = $mod->title; $mod->title = NULL; $hAlign = $mod->hAlign; $mod->hAlign = NULL; $el = new Texy\HtmlElement('img'); $el->attrs['src'] = NULL; // trick - move to front $mod->decorate($texy, $el); $el->attrs['src'] = Helpers::prependRoot($image->URL, $this->root); if (!isset($el->attrs['alt'])) { $el->attrs['alt'] = $alt === NULL ? $this->defaultAlt : $texy->typographyModule->postLine($alt); } if ($hAlign) { $var = $hAlign . 'Class'; // leftClass, rightClass if (!empty($this->{$var})) { $el->attrs['class'][] = $this->{$var}; } elseif (empty($texy->alignClasses[$hAlign])) { $el->attrs['style']['float'] = $hAlign; } else { $el->attrs['class'][] = $texy->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 (Helpers::isRelative($image->URL) && strpos($image->URL, '..') === FALSE) { $file = rtrim($this->fileRoot, '/\\') . '/' . $image->URL; if (@is_file($file)) { // intentionally @ $size = @getImageSize($file); // intentionally @ 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)) { $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 (!empty($texy->allowed['image/hover']) && $image->overURL !== NULL) { $overSrc = Helpers::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); $texy->summary['preload'][] = $overSrc; } $texy->summary['images'][] = $el->attrs['src']; if ($link) { 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; }
/** * 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(" "); // } } } }
/** * @return void */ public function afterParse(Texy\Texy $texy, Texy\HtmlElement $DOM, $isSingleLine) { if ($isSingleLine) { return; } if ($this->balancing === self::DYNAMIC) { $top = $this->top; $map = []; $min = 100; foreach ($this->TOC as $item) { $level = $item['level']; if ($item['type'] === 'surrounded') { $min = min($level, $min); $top = $this->top - $min; } elseif ($item['type'] === 'underlined') { $map[$level] = $level; } } asort($map); $map = array_flip(array_values($map)); } foreach ($this->TOC as $key => $item) { if ($this->balancing === self::DYNAMIC) { if ($item['type'] === 'surrounded') { $level = $item['level'] + $top; } elseif ($item['type'] === 'underlined') { $level = $map[$item['level']] + $this->top; } else { $level = $item['level']; } $item['el']->setName('h' . min(6, max(1, $level))); $this->TOC[$key]['level'] = $level; } if ($this->generateID) { if (!empty($item['el']->style['toc']) && is_array($item['el']->style)) { $title = $item['el']->style['toc']; unset($item['el']->style['toc']); } else { $title = trim($item['el']->toText($this->texy)); } $this->TOC[$key]['title'] = $title; if (empty($item['el']->attrs['id'])) { $id = $this->idPrefix . Texy\Helpers::webalize($title); $counter = ''; if (isset($this->usedID[$id . $counter])) { $counter = 2; while (isset($this->usedID[$id . '-' . $counter])) { $counter++; } $id .= '-' . $counter; } $this->usedID[$id] = TRUE; $item['el']->attrs['id'] = $id; } } } // document title if ($this->title === NULL && count($this->TOC)) { $item = reset($this->TOC); $this->title = isset($item['title']) ? $item['title'] : trim($item['el']->toText($this->texy)); } }