PK rAV^Ю phpunit.xml.distnu W+A
' . $this->renderAbsy($block['content']) . "
\n"; } // inline parsing /** * @var array the set of inline markers to use in different contexts. */ private $_inlineMarkers = []; /** * Returns a map of inline markers to the corresponding parser methods. * * This array defines handler methods for inline markdown markers. * When a marker is found in the text, the handler method is called with the text * starting at the position of the marker. * * Note that markers starting with whitespace may slow down the parser, * you may want to use [[renderText]] to deal with them. * * You may override this method to define a set of markers and parsing methods. * The default implementation looks for protected methods starting with `parse` that * also have an `@marker` annotation in PHPDoc. * * @return array a map of markers to parser methods */ protected function inlineMarkers() { $markers = []; // detect "parse" functions $reflection = new \ReflectionClass($this); foreach($reflection->getMethods(ReflectionMethod::IS_PROTECTED) as $method) { $methodName = $method->getName(); if (strncmp($methodName, 'parse', 5) === 0) { preg_match_all('/@marker ([^\s]+)/', $method->getDocComment(), $matches); foreach($matches[1] as $match) { $markers[$match] = $methodName; } } } return $markers; } /** * Prepare markers that are used in the text to parse * * Add all markers that are present in markdown. * Check is done to avoid iterations in parseInline(), good for huge markdown files * @param string $text */ private function prepareMarkers($text) { $this->_inlineMarkers = []; foreach ($this->inlineMarkers() as $marker => $method) { if (strpos($text, $marker) !== false) { $m = $marker[0]; // put the longest marker first if (isset($this->_inlineMarkers[$m])) { reset($this->_inlineMarkers[$m]); if (strlen($marker) > strlen(key($this->_inlineMarkers[$m]))) { $this->_inlineMarkers[$m] = array_merge([$marker => $method], $this->_inlineMarkers[$m]); continue; } } $this->_inlineMarkers[$m][$marker] = $method; } } } /** * Parses inline elements of the language. * * @param string $text the inline text to parse. * @return array */ protected function parseInline($text) { if ($this->_depth >= $this->maximumNestingLevel) { // maximum depth is reached, do not parse input return [['text', $text]]; } $this->_depth++; $markers = implode('', array_keys($this->_inlineMarkers)); $paragraph = []; while (!empty($markers) && ($found = strpbrk($text, $markers)) !== false) { $pos = strpos($text, $found); // add the text up to next marker to the paragraph if ($pos !== 0) { $paragraph[] = ['text', substr($text, 0, $pos)]; } $text = $found; $parsed = false; foreach ($this->_inlineMarkers[$text[0]] as $marker => $method) { if (strncmp($text, $marker, strlen($marker)) === 0) { // parse the marker array_unshift($this->context, $method); list($output, $offset) = $this->$method($text); array_shift($this->context); $paragraph[] = $output; $text = substr($text, $offset); $parsed = true; break; } } if (!$parsed) { $paragraph[] = ['text', substr($text, 0, 1)]; $text = substr($text, 1); } } $paragraph[] = ['text', $text]; $this->_depth--; return $paragraph; } /** * Parses escaped special characters. * @marker \ */ protected function parseEscape($text) { if (isset($text[1]) && in_array($text[1], $this->escapeCharacters)) { return [['text', $text[1]], 2]; } return [['text', $text[0]], 1]; } /** * This function renders plain text sections in the markdown text. * It can be used to work on normal text sections for example to highlight keywords or * do special escaping. */ protected function renderText($block) { return $block[1]; } } PK rAVmU MarkdownExtra.phpnu W+A * @license https://github.com/cebe/markdown/blob/master/LICENSE * @link https://github.com/cebe/markdown#readme */ class MarkdownExtra extends Markdown { // include block element parsing using traits use block\TableTrait; use block\FencedCodeTrait; // include inline element parsing using traits // TODO /** * @var bool whether special attributes on code blocks should be applied on the `` element.
* The default behavior is to put them on the `` element.
*/
public $codeAttributesOnPre = false;
/**
* @inheritDoc
*/
protected $escapeCharacters = [
// from Markdown
'\\', // backslash
'`', // backtick
'*', // asterisk
'_', // underscore
'{', '}', // curly braces
'[', ']', // square brackets
'(', ')', // parentheses
'#', // hash mark
'+', // plus sign
'-', // minus sign (hyphen)
'.', // dot
'!', // exclamation mark
'<', '>',
// added by MarkdownExtra
':', // colon
'|', // pipe
];
private $_specialAttributesRegex = '\{(([#\.][A-z0-9-_]+\s*)+)\}';
// TODO allow HTML intended 3 spaces
// TODO add markdown inside HTML blocks
// TODO implement definition lists
// TODO implement footnotes
// TODO implement Abbreviations
// block parsing
protected function identifyReference($line)
{
return ($line[0] === ' ' || $line[0] === '[') && preg_match('/^ {0,3}\[[^\[](.*?)\]:\s*([^\s]+?)(?:\s+[\'"](.+?)[\'"])?\s*('.$this->_specialAttributesRegex.')?\s*$/', $line);
}
/**
* Consume link references
*/
protected function consumeReference($lines, $current)
{
while (isset($lines[$current]) && preg_match('/^ {0,3}\[(.+?)\]:\s*(.+?)(?:\s+[\(\'"](.+?)[\)\'"])?\s*('.$this->_specialAttributesRegex.')?\s*$/', $lines[$current], $matches)) {
$label = strtolower($matches[1]);
$this->references[$label] = [
'url' => $matches[2],
];
if (isset($matches[3])) {
$this->references[$label]['title'] = $matches[3];
} else {
// title may be on the next line
if (isset($lines[$current + 1]) && preg_match('/^\s+[\(\'"](.+?)[\)\'"]\s*$/', $lines[$current + 1], $matches)) {
$this->references[$label]['title'] = $matches[1];
$current++;
}
}
if (isset($matches[5])) {
$this->references[$label]['attributes'] = $matches[5];
}
$current++;
}
return [false, --$current];
}
/**
* Consume lines for a fenced code block
*/
protected function consumeFencedCode($lines, $current)
{
// consume until ```
$block = [
'code',
];
$line = rtrim($lines[$current]);
if (($pos = strrpos($line, '`')) === false) {
$pos = strrpos($line, '~');
}
$fence = substr($line, 0, $pos + 1);
$block['attributes'] = substr($line, $pos);
$content = [];
for($i = $current + 1, $count = count($lines); $i < $count; $i++) {
if (rtrim($line = $lines[$i]) !== $fence) {
$content[] = $line;
} else {
break;
}
}
$block['content'] = implode("\n", $content);
return [$block, $i];
}
protected function renderCode($block)
{
$attributes = $this->renderAttributes($block);
return ($this->codeAttributesOnPre ? "" : "")
. htmlspecialchars($block['content'] . "\n", ENT_NOQUOTES | ENT_SUBSTITUTE, 'UTF-8')
. "
\n";
}
/**
* Renders a headline
*/
protected function renderHeadline($block)
{
foreach($block['content'] as $i => $element) {
if ($element[0] === 'specialAttributes') {
unset($block['content'][$i]);
$block['attributes'] = $element[1];
}
}
$tag = 'h' . $block['level'];
$attributes = $this->renderAttributes($block);
return "<$tag$attributes>" . rtrim($this->renderAbsy($block['content']), "# \t") . "$tag>\n";
}
protected function renderAttributes($block)
{
$html = [];
if (isset($block['attributes'])) {
$attributes = preg_split('/\s+/', $block['attributes'], -1, PREG_SPLIT_NO_EMPTY);
foreach($attributes as $attribute) {
if ($attribute[0] === '#') {
$html['id'] = substr($attribute, 1);
} else {
$html['class'][] = substr($attribute, 1);
}
}
}
$result = '';
foreach($html as $attr => $value) {
if (is_array($value)) {
$value = trim(implode(' ', $value));
}
if (!empty($value)) {
$result .= " $attr=\"$value\"";
}
}
return $result;
}
// inline parsing
/**
* @marker {
*/
protected function parseSpecialAttributes($text)
{
if (preg_match("~$this->_specialAttributesRegex~", $text, $matches)) {
return [['specialAttributes', $matches[1]], strlen($matches[0])];
}
return [['text', '{'], 1];
}
protected function renderSpecialAttributes($block)
{
return '{' . $block[1] . '}';
}
protected function parseInline($text)
{
$elements = parent::parseInline($text);
// merge special attribute elements to links and images as they are not part of the final absy later
$relatedElement = null;
foreach($elements as $i => $element) {
if ($element[0] === 'link' || $element[0] === 'image') {
$relatedElement = $i;
} elseif ($element[0] === 'specialAttributes') {
if ($relatedElement !== null) {
$elements[$relatedElement]['attributes'] = $element[1];
unset($elements[$i]);
}
$relatedElement = null;
} else {
$relatedElement = null;
}
}
return $elements;
}
protected function renderLink($block)
{
if (isset($block['refkey'])) {
if (($ref = $this->lookupReference($block['refkey'])) !== false) {
$block = array_merge($block, $ref);
} else {
return $block['orig'];
}
}
$attributes = $this->renderAttributes($block);
return '' . $this->renderAbsy($block['text']) . '';
}
protected function renderImage($block)
{
if (isset($block['refkey'])) {
if (($ref = $this->lookupReference($block['refkey'])) !== false) {
$block = array_merge($block, $ref);
} else {
return $block['orig'];
}
}
$attributes = $this->renderAttributes($block);
return 'html5 ? '>' : ' />');
}
}PK rAV78 8 LICENSEnu W+A The MIT License (MIT)
Copyright (c) 2014 Carsten Brandt
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.PK rAV25 inline/StrikeoutTrait.phpnu W+A parseInline($matches[1])
],
strlen($matches[0])
];
}
return [['text', $markdown[0] . $markdown[1]], 2];
}
protected function renderStrike($block)
{
return '' . $this->renderAbsy($block[1]) . '';
}
}
PK rAV inline/EmphStrongTrait.phpnu W+A parseInline($matches[1]),
],
strlen($matches[0])
];
}
} else { // emph
if ($marker == '*' && preg_match('/^[*]((?:[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s', $text, $matches) ||
$marker == '_' && preg_match('/^_((?:[^_]|__[^_]*__)+?)_(?!_)\b/us', $text, $matches)) {
return [
[
'emph',
$this->parseInline($matches[1]),
],
strlen($matches[0])
];
}
}
return [['text', $text[0]], 1];
}
protected function renderStrong($block)
{
return '' . $this->renderAbsy($block[1]) . '';
}
protected function renderEmph($block)
{
return '' . $this->renderAbsy($block[1]) . '';
}
}
PK rAVi, , inline/LinkTrait.phpnu W+A references = [];
* }
* ```
*/
trait LinkTrait
{
/**
* @var array a list of defined references in this document.
*/
protected $references = [];
/**
* Parses a link indicated by `[`.
* @marker [
*/
protected function parseLink($markdown)
{
if (!in_array('parseLink', array_slice($this->context, 1)) && ($parts = $this->parseLinkOrImage($markdown)) !== false) {
list($text, $url, $title, $offset, $key) = $parts;
return [
[
'link',
'text' => $this->parseInline($text),
'url' => $url,
'title' => $title,
'refkey' => $key,
'orig' => substr($markdown, 0, $offset),
],
$offset
];
} else {
// remove all starting [ markers to avoid next one to be parsed as link
$result = '[';
$i = 1;
while (isset($markdown[$i]) && $markdown[$i] == '[') {
$result .= '[';
$i++;
}
return [['text', $result], $i];
}
}
/**
* Parses an image indicated by `![`.
* @marker ![
*/
protected function parseImage($markdown)
{
if (($parts = $this->parseLinkOrImage(substr($markdown, 1))) !== false) {
list($text, $url, $title, $offset, $key) = $parts;
return [
[
'image',
'text' => $text,
'url' => $url,
'title' => $title,
'refkey' => $key,
'orig' => substr($markdown, 0, $offset + 1),
],
$offset + 1
];
} else {
// remove all starting [ markers to avoid next one to be parsed as link
$result = '!';
$i = 1;
while (isset($markdown[$i]) && $markdown[$i] == '[') {
$result .= '[';
$i++;
}
return [['text', $result], $i];
}
}
protected function parseLinkOrImage($markdown)
{
if (strpos($markdown, ']') !== false && preg_match('/\[((?>[^\]\[]+|(?R))*)\]/', $markdown, $textMatches)) { // TODO improve bracket regex
$text = $textMatches[1];
$offset = strlen($textMatches[0]);
$markdown = substr($markdown, $offset);
$pattern = <<[^\s()]+)|(?R))*\)
| # else match a link with title
^\((((?>[^\s()]+)|(?R))*)(\s+"(.*?)")?\)
)/x
REGEXP;
if (preg_match($pattern, $markdown, $refMatches)) {
// inline link
return [
$text,
isset($refMatches[2]) ? $refMatches[2] : '', // url
empty($refMatches[5]) ? null: $refMatches[5], // title
$offset + strlen($refMatches[0]), // offset
null, // reference key
];
} elseif (preg_match('/^([ \n]?\[(.*?)\])?/s', $markdown, $refMatches)) {
// reference style link
if (empty($refMatches[2])) {
$key = strtolower($text);
} else {
$key = strtolower($refMatches[2]);
}
return [
$text,
null, // url
null, // title
$offset + strlen($refMatches[0]), // offset
$key,
];
}
}
return false;
}
/**
* Parses inline HTML.
* @marker <
*/
protected function parseLt($text)
{
if (strpos($text, '>') !== false) {
if (!in_array('parseLink', $this->context)) { // do not allow links in links
if (preg_match('/^<([^\s]*?@[^\s]*?\.\w+?)>/', $text, $matches)) {
// email address
return [
['email', $matches[1]],
strlen($matches[0])
];
} elseif (preg_match('/^<([a-z]{3,}:\/\/[^\s]+?)>/', $text, $matches)) {
// URL
return [
['url', $matches[1]],
strlen($matches[0])
];
}
}
// try inline HTML if it was neither a URL nor email if HtmlTrait is included.
if (method_exists($this, 'parseInlineHtml')) {
return $this->parseInlineHtml($text);
}
}
return [['text', '<'], 1];
}
protected function renderEmail($block)
{
$email = htmlspecialchars($block[1], ENT_NOQUOTES | ENT_SUBSTITUTE, 'UTF-8');
return "$email";
}
protected function renderUrl($block)
{
$url = htmlspecialchars($block[1], ENT_COMPAT | ENT_HTML401, 'UTF-8');
$text = htmlspecialchars(urldecode($block[1]), ENT_NOQUOTES | ENT_SUBSTITUTE, 'UTF-8');
return "$text";
}
protected function lookupReference($key)
{
$normalizedKey = preg_replace('/\s+/', ' ', $key);
if (isset($this->references[$key]) || isset($this->references[$key = $normalizedKey])) {
return $this->references[$key];
}
return false;
}
protected function renderLink($block)
{
if (isset($block['refkey'])) {
if (($ref = $this->lookupReference($block['refkey'])) !== false) {
$block = array_merge($block, $ref);
} else {
return $block['orig'];
}
}
return '' . $this->renderAbsy($block['text']) . '';
}
protected function renderImage($block)
{
if (isset($block['refkey'])) {
if (($ref = $this->lookupReference($block['refkey'])) !== false) {
$block = array_merge($block, $ref);
} else {
return $block['orig'];
}
}
return 'html5 ? '>' : ' />');
}
// references
protected function identifyReference($line)
{
return isset($line[0]) && ($line[0] === ' ' || $line[0] === '[') && preg_match('/^ {0,3}\[[^\[](.*?)\]:\s*([^\s]+?)(?:\s+[\'"](.+?)[\'"])?\s*$/', $line);
}
/**
* Consume link references
*/
protected function consumeReference($lines, $current)
{
while (isset($lines[$current]) && preg_match('/^ {0,3}\[(.+?)\]:\s*(.+?)(?:\s+[\(\'"](.+?)[\)\'"])?\s*$/', $lines[$current], $matches)) {
$label = strtolower($matches[1]);
$this->references[$label] = [
'url' => $matches[2],
];
if (isset($matches[3])) {
$this->references[$label]['title'] = $matches[3];
} else {
// title may be on the next line
if (isset($lines[$current + 1]) && preg_match('/^\s+[\(\'"](.+?)[\)\'"]\s*$/', $lines[$current + 1], $matches)) {
$this->references[$label]['title'] = $matches[1];
$current++;
}
}
$current++;
}
return [false, --$current];
}
}
PK rAVHOE inline/UrlLinkTrait.phpnu W+A [^\s()]+)|(?R))*\)
| # else match a link with title
^(https?|ftp):\/\/(([^\s()]+)|(?R))+(?context) && preg_match($pattern, $markdown, $matches)) {
return [
['autoUrl', $matches[0]],
strlen($matches[0])
];
}
return [['text', substr($markdown, 0, 4)], 4];
}
protected function renderAutoUrl($block)
{
$href = htmlspecialchars($block[1], ENT_COMPAT | ENT_HTML401, 'UTF-8');
$text = htmlspecialchars(urldecode($block[1]), ENT_NOQUOTES | ENT_SUBSTITUTE, 'UTF-8');
return "$text";
}
}
PK rAV