Update Parsedown library

This commit is contained in:
Frédéric Guillot
2018-04-20 16:05:50 -07:00
parent 32a95ce5ea
commit bb406d57b1
123 changed files with 374 additions and 1227 deletions

219
vendor/erusev/parsedown/Parsedown.php vendored Executable file → Normal file
View File

@@ -17,7 +17,7 @@ class Parsedown
{
# ~
const version = '1.6.0';
const version = '1.7.1';
# ~
@@ -75,6 +75,32 @@ class Parsedown
protected $urlsLinked = true;
function setSafeMode($safeMode)
{
$this->safeMode = (bool) $safeMode;
return $this;
}
protected $safeMode;
protected $safeLinksWhitelist = array(
'http://',
'https://',
'ftp://',
'ftps://',
'mailto:',
'data:image/png;base64,',
'data:image/gif;base64,',
'data:image/jpeg;base64,',
'irc:',
'ircs:',
'git:',
'ssh:',
'news:',
'steam:',
);
#
# Lines
#
@@ -115,7 +141,7 @@ class Parsedown
# Blocks
#
private function lines(array $lines)
protected function lines(array $lines)
{
$CurrentBlock = null;
@@ -175,7 +201,7 @@ class Parsedown
}
else
{
if (method_exists($this, 'block'.$CurrentBlock['type'].'Complete'))
if ($this->isBlockCompletable($CurrentBlock['type']))
{
$CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
}
@@ -216,7 +242,7 @@ class Parsedown
$Block['identified'] = true;
}
if (method_exists($this, 'block'.$blockType.'Continue'))
if ($this->isBlockContinuable($blockType))
{
$Block['continuable'] = true;
}
@@ -245,7 +271,7 @@ class Parsedown
# ~
if (isset($CurrentBlock['continuable']) and method_exists($this, 'block'.$CurrentBlock['type'].'Complete'))
if (isset($CurrentBlock['continuable']) and $this->isBlockCompletable($CurrentBlock['type']))
{
$CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
}
@@ -278,6 +304,16 @@ class Parsedown
return $markup;
}
protected function isBlockContinuable($Type)
{
return method_exists($this, 'block'.$Type.'Continue');
}
protected function isBlockCompletable($Type)
{
return method_exists($this, 'block'.$Type.'Complete');
}
#
# Code
@@ -332,8 +368,6 @@ class Parsedown
{
$text = $Block['element']['text']['text'];
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
$Block['element']['text']['text'] = $text;
return $Block;
@@ -344,7 +378,7 @@ class Parsedown
protected function blockComment($Line)
{
if ($this->markupEscaped)
if ($this->markupEscaped or $this->safeMode)
{
return;
}
@@ -386,7 +420,7 @@ class Parsedown
protected function blockFencedCode($Line)
{
if (preg_match('/^['.$Line['text'][0].']{3,}[ ]*([\w-]+)?[ ]*$/', $Line['text'], $matches))
if (preg_match('/^['.$Line['text'][0].']{3,}[ ]*([^`]+)?[ ]*$/', $Line['text'], $matches))
{
$Element = array(
'name' => 'code',
@@ -438,7 +472,7 @@ class Parsedown
return $Block;
}
$Block['element']['text']['text'] .= "\n".$Line['body'];;
$Block['element']['text']['text'] .= "\n".$Line['body'];
return $Block;
}
@@ -447,8 +481,6 @@ class Parsedown
{
$text = $Block['element']['text']['text'];
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
$Block['element']['text']['text'] = $text;
return $Block;
@@ -505,6 +537,16 @@ class Parsedown
),
);
if($name === 'ol')
{
$listStart = stristr($matches[0], '.', true);
if($listStart !== '1')
{
$Block['element']['attributes'] = array('start' => $listStart);
}
}
$Block['li'] = array(
'name' => 'li',
'handler' => 'li',
@@ -527,6 +569,8 @@ class Parsedown
{
$Block['li']['text'] []= '';
$Block['loose'] = true;
unset($Block['interrupted']);
}
@@ -575,6 +619,22 @@ class Parsedown
}
}
protected function blockListComplete(array $Block)
{
if (isset($Block['loose']))
{
foreach ($Block['element']['text'] as &$li)
{
if (end($li['text']) !== '')
{
$li['text'] []= '';
}
}
}
return $Block;
}
#
# Quote
@@ -658,12 +718,12 @@ class Parsedown
protected function blockMarkup($Line)
{
if ($this->markupEscaped)
if ($this->markupEscaped or $this->safeMode)
{
return;
}
if (preg_match('/^<(\w*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches))
if (preg_match('/^<(\w[\w-]*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches))
{
$element = strtolower($matches[1]);
@@ -977,7 +1037,7 @@ class Parsedown
# ~
#
public function line($text)
public function line($text, $nonNestables=array())
{
$markup = '';
@@ -993,6 +1053,13 @@ class Parsedown
foreach ($this->InlineTypes[$marker] as $inlineType)
{
# check to see if the current inline type is nestable in the current context
if ( ! empty($nonNestables) and in_array($inlineType, $nonNestables))
{
continue;
}
$Inline = $this->{'inline'.$inlineType}($Excerpt);
if ( ! isset($Inline))
@@ -1014,6 +1081,13 @@ class Parsedown
$Inline['position'] = $markerPosition;
}
# cause the new element to 'inherit' our non nestables
foreach ($nonNestables as $non_nestable)
{
$Inline['element']['nonNestables'][] = $non_nestable;
}
# the text that comes before the inline
$unmarkedText = substr($text, 0, $Inline['position']);
@@ -1054,7 +1128,6 @@ class Parsedown
if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(?<!'.$marker.')\1(?!'.$marker.')/s', $Excerpt['text'], $matches))
{
$text = $matches[2];
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
$text = preg_replace("/[ ]*\n/", ' ', $text);
return array(
@@ -1173,6 +1246,7 @@ class Parsedown
$Element = array(
'name' => 'a',
'handler' => 'line',
'nonNestables' => array('Url', 'Link'),
'text' => null,
'attributes' => array(
'href' => null,
@@ -1184,7 +1258,7 @@ class Parsedown
$remainder = $Excerpt['text'];
if (preg_match('/\[((?:[^][]|(?R))*)\]/', $remainder, $matches))
if (preg_match('/\[((?:[^][]++|(?R))*+)\]/', $remainder, $matches))
{
$Element['text'] = $matches[1];
@@ -1197,7 +1271,7 @@ class Parsedown
return;
}
if (preg_match('/^[(]((?:[^ ()]|[(][^ )]+[)])+)(?:[ ]+("[^"]*"|\'[^\']*\'))?[)]/', $remainder, $matches))
if (preg_match('/^[(]\s*+((?:[^ ()]++|[(][^ )]+[)])++)(?:[ ]+("[^"]*"|\'[^\']*\'))?\s*[)]/', $remainder, $matches))
{
$Element['attributes']['href'] = $matches[1];
@@ -1233,8 +1307,6 @@ class Parsedown
$Element['attributes']['title'] = $Definition['title'];
}
$Element['attributes']['href'] = str_replace(array('&', '<'), array('&amp;', '&lt;'), $Element['attributes']['href']);
return array(
'extent' => $extent,
'element' => $Element,
@@ -1243,12 +1315,12 @@ class Parsedown
protected function inlineMarkup($Excerpt)
{
if ($this->markupEscaped or strpos($Excerpt['text'], '>') === false)
if ($this->markupEscaped or $this->safeMode or strpos($Excerpt['text'], '>') === false)
{
return;
}
if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w*[ ]*>/s', $Excerpt['text'], $matches))
if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w[\w-]*[ ]*>/s', $Excerpt['text'], $matches))
{
return array(
'markup' => $matches[0],
@@ -1264,7 +1336,7 @@ class Parsedown
);
}
if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w*(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s', $Excerpt['text'], $matches))
if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w[\w-]*(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s', $Excerpt['text'], $matches))
{
return array(
'markup' => $matches[0],
@@ -1323,14 +1395,16 @@ class Parsedown
if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE))
{
$url = $matches[0][0];
$Inline = array(
'extent' => strlen($matches[0][0]),
'position' => $matches[0][1],
'element' => array(
'name' => 'a',
'text' => $matches[0][0],
'text' => $url,
'attributes' => array(
'href' => $matches[0][0],
'href' => $url,
),
),
);
@@ -1343,7 +1417,7 @@ class Parsedown
{
if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches))
{
$url = str_replace(array('&', '<'), array('&amp;', '&lt;'), $matches[1]);
$url = $matches[1];
return array(
'extent' => strlen($matches[0]),
@@ -1381,6 +1455,11 @@ class Parsedown
protected function element(array $Element)
{
if ($this->safeMode)
{
$Element = $this->sanitiseElement($Element);
}
$markup = '<'.$Element['name'];
if (isset($Element['attributes']))
@@ -1392,7 +1471,7 @@ class Parsedown
continue;
}
$markup .= ' '.$name.'="'.$value.'"';
$markup .= ' '.$name.'="'.self::escape($value).'"';
}
}
@@ -1400,13 +1479,18 @@ class Parsedown
{
$markup .= '>';
if (!isset($Element['nonNestables']))
{
$Element['nonNestables'] = array();
}
if (isset($Element['handler']))
{
$markup .= $this->{$Element['handler']}($Element['text']);
$markup .= $this->{$Element['handler']}($Element['text'], $Element['nonNestables']);
}
else
{
$markup .= $Element['text'];
$markup .= self::escape($Element['text'], true);
}
$markup .= '</'.$Element['name'].'>';
@@ -1465,10 +1549,77 @@ class Parsedown
return $markup;
}
protected function sanitiseElement(array $Element)
{
static $goodAttribute = '/^[a-zA-Z0-9][a-zA-Z0-9-_]*+$/';
static $safeUrlNameToAtt = array(
'a' => 'href',
'img' => 'src',
);
if (isset($safeUrlNameToAtt[$Element['name']]))
{
$Element = $this->filterUnsafeUrlInAttribute($Element, $safeUrlNameToAtt[$Element['name']]);
}
if ( ! empty($Element['attributes']))
{
foreach ($Element['attributes'] as $att => $val)
{
# filter out badly parsed attribute
if ( ! preg_match($goodAttribute, $att))
{
unset($Element['attributes'][$att]);
}
# dump onevent attribute
elseif (self::striAtStart($att, 'on'))
{
unset($Element['attributes'][$att]);
}
}
}
return $Element;
}
protected function filterUnsafeUrlInAttribute(array $Element, $attribute)
{
foreach ($this->safeLinksWhitelist as $scheme)
{
if (self::striAtStart($Element['attributes'][$attribute], $scheme))
{
return $Element;
}
}
$Element['attributes'][$attribute] = str_replace(':', '%3A', $Element['attributes'][$attribute]);
return $Element;
}
#
# Static Methods
#
protected static function escape($text, $allowQuotes = false)
{
return htmlspecialchars($text, $allowQuotes ? ENT_NOQUOTES : ENT_QUOTES, 'UTF-8');
}
protected static function striAtStart($string, $needle)
{
$len = strlen($needle);
if ($len > strlen($string))
{
return false;
}
else
{
return strtolower(substr($string, 0, $len)) === strtolower($needle);
}
}
static function instance($name = 'default')
{
if (isset(self::$instances[$name]))
@@ -1519,10 +1670,10 @@ class Parsedown
'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing',
'i', 'rp', 'del', 'code', 'strike', 'marquee',
'q', 'rt', 'ins', 'font', 'strong',
's', 'tt', 'sub', 'mark',
'u', 'xm', 'sup', 'nobr',
'var', 'ruby',
'wbr', 'span',
'time',
's', 'tt', 'kbd', 'mark',
'u', 'xm', 'sub', 'nobr',
'sup', 'ruby',
'var', 'span',
'wbr', 'time',
);
}