mirror of
https://github.com/erusev/parsedown.git
synced 2023-08-10 21:13:06 +03:00
Compare commits
55 Commits
Author | SHA1 | Date | |
---|---|---|---|
468d1e3da8 | |||
7aa1d97bba | |||
f768f9c63f | |||
aa83968534 | |||
85eadccc05 | |||
c94fa12d67 | |||
11e02d45fa | |||
ecd53f9add | |||
844b2f49ea | |||
65116c3cb0 | |||
147003107a | |||
618b26056c | |||
b828fe7c8d | |||
6c9df528aa | |||
cb8cc57742 | |||
9da19c1108 | |||
ffd9d3b407 | |||
e94ecf4adc | |||
4d3079b908 | |||
70e7a17380 | |||
9518c8e384 | |||
c581284231 | |||
cb1940255a | |||
93d0ec9397 | |||
9c6e7e880a | |||
2d62e29625 | |||
595f33871e | |||
97e1e0efaa | |||
648419467a | |||
6ddb6b2b33 | |||
0008e69a83 | |||
c664785485 | |||
bdf0ef024e | |||
21a3e8790a | |||
e5e8d02934 | |||
7ff0f97811 | |||
596350d1f5 | |||
2cbd3010e4 | |||
3b4aa6bff7 | |||
05a8f16e95 | |||
79d924040a | |||
b4a8eb3315 | |||
4383cce85b | |||
ada39109e4 | |||
a06cdfb814 | |||
6bee326c92 | |||
3fe867d294 | |||
f08d017bcb | |||
e61a6114b0 | |||
9ed72ccd09 | |||
09e1184d9f | |||
2de60a9a8b | |||
73a75299f5 | |||
4b7d7cdef2 | |||
97e667ab30 |
@ -5,6 +5,4 @@ php:
|
||||
- 5.5
|
||||
- 5.4
|
||||
- 5.3
|
||||
- 5.2
|
||||
- hhvm
|
||||
|
564
Parsedown.php
564
Parsedown.php
@ -15,15 +15,10 @@
|
||||
|
||||
class Parsedown
|
||||
{
|
||||
#
|
||||
# Philosophy
|
||||
# ~
|
||||
|
||||
# Parsedown recognises that the Markdown syntax is optimised for humans so
|
||||
# it tries to read like one. It goes through text line by line. It looks at
|
||||
# how lines start to identify blocks. It looks for special characters to
|
||||
# identify inline elements.
|
||||
const version = '1.5.1';
|
||||
|
||||
#
|
||||
# ~
|
||||
|
||||
function text($text)
|
||||
@ -32,8 +27,7 @@ class Parsedown
|
||||
$this->DefinitionData = array();
|
||||
|
||||
# standardize line breaks
|
||||
$text = str_replace("\r\n", "\n", $text);
|
||||
$text = str_replace("\r", "\n", $text);
|
||||
$text = str_replace(array("\r\n", "\r"), "\n", $text);
|
||||
|
||||
# remove surrounding line breaks
|
||||
$text = trim($text, "\n");
|
||||
@ -54,8 +48,6 @@ class Parsedown
|
||||
# Setters
|
||||
#
|
||||
|
||||
private $breaksEnabled;
|
||||
|
||||
function setBreaksEnabled($breaksEnabled)
|
||||
{
|
||||
$this->breaksEnabled = $breaksEnabled;
|
||||
@ -63,7 +55,7 @@ class Parsedown
|
||||
return $this;
|
||||
}
|
||||
|
||||
private $markupEscaped;
|
||||
protected $breaksEnabled;
|
||||
|
||||
function setMarkupEscaped($markupEscaped)
|
||||
{
|
||||
@ -72,7 +64,7 @@ class Parsedown
|
||||
return $this;
|
||||
}
|
||||
|
||||
private $urlsLinked = true;
|
||||
protected $markupEscaped;
|
||||
|
||||
function setUrlsLinked($urlsLinked)
|
||||
{
|
||||
@ -81,6 +73,8 @@ class Parsedown
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected $urlsLinked = true;
|
||||
|
||||
#
|
||||
# Lines
|
||||
#
|
||||
@ -482,7 +476,7 @@ class Parsedown
|
||||
$level ++;
|
||||
}
|
||||
|
||||
if ($level > 6 or $Line['text'][$level] !== ' ')
|
||||
if ($level > 6)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -535,7 +529,7 @@ class Parsedown
|
||||
|
||||
protected function blockListContinue($Line, array $Block)
|
||||
{
|
||||
if ($Block['indent'] === $Line['indent'] and preg_match('/^'.$Block['pattern'].'[ ]+(.*)/', $Line['text'], $matches))
|
||||
if ($Block['indent'] === $Line['indent'] and preg_match('/^'.$Block['pattern'].'(?:[ ]+(.*)|$)/', $Line['text'], $matches))
|
||||
{
|
||||
if (isset($Block['interrupted']))
|
||||
{
|
||||
@ -546,11 +540,13 @@ class Parsedown
|
||||
|
||||
unset($Block['li']);
|
||||
|
||||
$text = isset($matches[1]) ? $matches[1] : '';
|
||||
|
||||
$Block['li'] = array(
|
||||
'name' => 'li',
|
||||
'handler' => 'li',
|
||||
'text' => array(
|
||||
$matches[1],
|
||||
$text,
|
||||
),
|
||||
);
|
||||
|
||||
@ -559,6 +555,11 @@ class Parsedown
|
||||
return $Block;
|
||||
}
|
||||
|
||||
if ($Line['text'][0] === '[' and $this->blockReference($Line))
|
||||
{
|
||||
return $Block;
|
||||
}
|
||||
|
||||
if ( ! isset($Block['interrupted']))
|
||||
{
|
||||
$text = preg_replace('/^[ ]{0,4}/', '', $Line['body']);
|
||||
@ -735,8 +736,6 @@ class Parsedown
|
||||
{
|
||||
$Block['closed'] = true;
|
||||
}
|
||||
|
||||
$Block['markup'] .= $matches[1];
|
||||
}
|
||||
|
||||
if (isset($Block['interrupted']))
|
||||
@ -851,7 +850,7 @@ class Parsedown
|
||||
$alignment = $alignments[$index];
|
||||
|
||||
$HeaderElement['attributes'] = array(
|
||||
'align' => $alignment,
|
||||
'style' => 'text-align: '.$alignment.';',
|
||||
);
|
||||
}
|
||||
|
||||
@ -892,6 +891,11 @@ class Parsedown
|
||||
|
||||
protected function blockTableContinue($Line, array $Block)
|
||||
{
|
||||
if (isset($Block['interrupted']))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ($Line['text'][0] === '|' or strpos($Line['text'], '|'))
|
||||
{
|
||||
$Elements = array();
|
||||
@ -901,9 +905,9 @@ class Parsedown
|
||||
$row = trim($row);
|
||||
$row = trim($row, '|');
|
||||
|
||||
$cells = explode('|', $row);
|
||||
preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]+`|`)+/', $row, $matches);
|
||||
|
||||
foreach ($cells as $index => $cell)
|
||||
foreach ($matches[0] as $index => $cell)
|
||||
{
|
||||
$cell = trim($cell);
|
||||
|
||||
@ -916,7 +920,7 @@ class Parsedown
|
||||
if (isset($Block['alignments'][$index]))
|
||||
{
|
||||
$Element['attributes'] = array(
|
||||
'align' => $Block['alignments'][$index],
|
||||
'style' => 'text-align: '.$Block['alignments'][$index].';',
|
||||
);
|
||||
}
|
||||
|
||||
@ -952,75 +956,18 @@ class Parsedown
|
||||
return $Block;
|
||||
}
|
||||
|
||||
#
|
||||
# ~
|
||||
#
|
||||
|
||||
protected function element(array $Element)
|
||||
{
|
||||
$markup = '<'.$Element['name'];
|
||||
|
||||
if (isset($Element['attributes']))
|
||||
{
|
||||
foreach ($Element['attributes'] as $name => $value)
|
||||
{
|
||||
if ($value === null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$markup .= ' '.$name.'="'.$value.'"';
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($Element['text']))
|
||||
{
|
||||
$markup .= '>';
|
||||
|
||||
if (isset($Element['handler']))
|
||||
{
|
||||
$markup .= $this->$Element['handler']($Element['text']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$markup .= $Element['text'];
|
||||
}
|
||||
|
||||
$markup .= '</'.$Element['name'].'>';
|
||||
}
|
||||
else
|
||||
{
|
||||
$markup .= ' />';
|
||||
}
|
||||
|
||||
return $markup;
|
||||
}
|
||||
|
||||
protected function elements(array $Elements)
|
||||
{
|
||||
$markup = '';
|
||||
|
||||
foreach ($Elements as $Element)
|
||||
{
|
||||
$markup .= "\n" . $this->element($Element);
|
||||
}
|
||||
|
||||
$markup .= "\n";
|
||||
|
||||
return $markup;
|
||||
}
|
||||
|
||||
#
|
||||
# Inline Elements
|
||||
#
|
||||
|
||||
protected $InlineTypes = array(
|
||||
'"' => array('QuotationMark'),
|
||||
'"' => array('SpecialCharacter'),
|
||||
'!' => array('Image'),
|
||||
'&' => array('Ampersand'),
|
||||
'&' => array('SpecialCharacter'),
|
||||
'*' => array('Emphasis'),
|
||||
'<' => array('UrlTag', 'EmailTag', 'Tag', 'LessThan'),
|
||||
'>' => array('GreaterThan'),
|
||||
':' => array('Url'),
|
||||
'<' => array('UrlTag', 'EmailTag', 'Markup', 'SpecialCharacter'),
|
||||
'>' => array('SpecialCharacter'),
|
||||
'[' => array('Link'),
|
||||
'_' => array('Emphasis'),
|
||||
'`' => array('Code'),
|
||||
@ -1030,7 +977,7 @@ class Parsedown
|
||||
|
||||
# ~
|
||||
|
||||
protected $inlineMarkerList = '!"*_&[<>`~\\';
|
||||
protected $inlineMarkerList = '!"*_&[:<>`~\\';
|
||||
|
||||
#
|
||||
# ~
|
||||
@ -1040,43 +987,53 @@ class Parsedown
|
||||
{
|
||||
$markup = '';
|
||||
|
||||
$remainder = $text;
|
||||
$unexaminedText = $text;
|
||||
|
||||
$markerPosition = 0;
|
||||
|
||||
while ($excerpt = strpbrk($remainder, $this->inlineMarkerList))
|
||||
while ($excerpt = strpbrk($unexaminedText, $this->inlineMarkerList))
|
||||
{
|
||||
$marker = $excerpt[0];
|
||||
|
||||
$markerPosition += strpos($remainder, $marker);
|
||||
$markerPosition += strpos($unexaminedText, $marker);
|
||||
|
||||
$Excerpt = array('text' => $excerpt, 'context' => $text);
|
||||
|
||||
foreach ($this->InlineTypes[$marker] as $inlineType)
|
||||
{
|
||||
$handler = 'inline'.$inlineType;
|
||||
|
||||
$Inline = $this->$handler($excerpt);
|
||||
$Inline = $this->{'inline'.$inlineType}($Excerpt);
|
||||
|
||||
if ( ! isset($Inline))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$plainText = substr($text, 0, $markerPosition);
|
||||
if (isset($Inline['position']) and $Inline['position'] > $markerPosition) # position is ahead of marker
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$markup .= $this->unmarkedText($plainText);
|
||||
if ( ! isset($Inline['position']))
|
||||
{
|
||||
$Inline['position'] = $markerPosition;
|
||||
}
|
||||
|
||||
$unmarkedText = substr($text, 0, $Inline['position']);
|
||||
|
||||
$markup .= $this->unmarkedText($unmarkedText);
|
||||
|
||||
$markup .= isset($Inline['markup']) ? $Inline['markup'] : $this->element($Inline['element']);
|
||||
|
||||
$text = substr($text, $markerPosition + $Inline['extent']);
|
||||
$text = substr($text, $Inline['position'] + $Inline['extent']);
|
||||
|
||||
$remainder = $text;
|
||||
$unexaminedText = $text;
|
||||
|
||||
$markerPosition = 0;
|
||||
|
||||
continue 2;
|
||||
}
|
||||
|
||||
$remainder = substr($excerpt, 1);
|
||||
$unexaminedText = substr($excerpt, 1);
|
||||
|
||||
$markerPosition ++;
|
||||
}
|
||||
@ -1090,94 +1047,29 @@ class Parsedown
|
||||
# ~
|
||||
#
|
||||
|
||||
protected function inlineAmpersand($excerpt)
|
||||
protected function inlineCode($Excerpt)
|
||||
{
|
||||
if ( ! preg_match('/^&#?\w+;/', $excerpt))
|
||||
{
|
||||
return array(
|
||||
'markup' => '&',
|
||||
'extent' => 1,
|
||||
);
|
||||
}
|
||||
}
|
||||
$marker = $Excerpt['text'][0];
|
||||
|
||||
protected function inlineStrikethrough($excerpt)
|
||||
{
|
||||
if ( ! isset($excerpt[1]))
|
||||
if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(?<!'.$marker.')\1(?!'.$marker.')/s', $Excerpt['text'], $matches))
|
||||
{
|
||||
return;
|
||||
}
|
||||
$text = $matches[2];
|
||||
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
|
||||
$text = preg_replace("/[ ]*\n/", ' ', $text);
|
||||
|
||||
if ($excerpt[1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $excerpt, $matches))
|
||||
{
|
||||
return array(
|
||||
'extent' => strlen($matches[0]),
|
||||
'element' => array(
|
||||
'name' => 'del',
|
||||
'text' => $matches[1],
|
||||
'handler' => 'line',
|
||||
'name' => 'code',
|
||||
'text' => $text,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function inlineEscapeSequence($excerpt)
|
||||
protected function inlineEmailTag($Excerpt)
|
||||
{
|
||||
if (isset($excerpt[1]) and in_array($excerpt[1], $this->specialCharacters))
|
||||
{
|
||||
return array(
|
||||
'markup' => $excerpt[1],
|
||||
'extent' => 2,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function inlineLessThan()
|
||||
{
|
||||
return array(
|
||||
'markup' => '<',
|
||||
'extent' => 1,
|
||||
);
|
||||
}
|
||||
|
||||
protected function inlineGreaterThan()
|
||||
{
|
||||
return array(
|
||||
'markup' => '>',
|
||||
'extent' => 1,
|
||||
);
|
||||
}
|
||||
|
||||
protected function inlineQuotationMark()
|
||||
{
|
||||
return array(
|
||||
'markup' => '"',
|
||||
'extent' => 1,
|
||||
);
|
||||
}
|
||||
|
||||
protected function inlineUrlTag($excerpt)
|
||||
{
|
||||
if (strpos($excerpt, '>') !== false and preg_match('/^<(https?:[\/]{2}[^\s]+?)>/i', $excerpt, $matches))
|
||||
{
|
||||
$url = str_replace(array('&', '<'), array('&', '<'), $matches[1]);
|
||||
|
||||
return array(
|
||||
'extent' => strlen($matches[0]),
|
||||
'element' => array(
|
||||
'name' => 'a',
|
||||
'text' => $url,
|
||||
'attributes' => array(
|
||||
'href' => $url,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function inlineEmailTag($excerpt)
|
||||
{
|
||||
if (strpos($excerpt, '>') !== false and preg_match('/^<((mailto:)?\S+?@\S+?)>/i', $excerpt, $matches))
|
||||
if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<((mailto:)?\S+?@\S+?)>/i', $Excerpt['text'], $matches))
|
||||
{
|
||||
$url = $matches[1];
|
||||
|
||||
@ -1199,77 +1091,84 @@ class Parsedown
|
||||
}
|
||||
}
|
||||
|
||||
protected function inlineTag($excerpt)
|
||||
protected function inlineEmphasis($Excerpt)
|
||||
{
|
||||
if ($this->markupEscaped)
|
||||
if ( ! isset($Excerpt['text'][1]))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (strpos($excerpt, '>') !== false and preg_match('/^<\/?\w.*?>/s', $excerpt, $matches))
|
||||
$marker = $Excerpt['text'][0];
|
||||
|
||||
if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches))
|
||||
{
|
||||
return array(
|
||||
'markup' => $matches[0],
|
||||
'extent' => strlen($matches[0]),
|
||||
);
|
||||
$emphasis = 'strong';
|
||||
}
|
||||
}
|
||||
|
||||
protected function inlineCode($excerpt)
|
||||
{
|
||||
$marker = $excerpt[0];
|
||||
|
||||
if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(?<!'.$marker.')\1(?!'.$marker.')/s', $excerpt, $matches))
|
||||
elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches))
|
||||
{
|
||||
$text = $matches[2];
|
||||
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
|
||||
$text = preg_replace("/[ ]*\n/", ' ', $text);
|
||||
|
||||
return array(
|
||||
'extent' => strlen($matches[0]),
|
||||
'element' => array(
|
||||
'name' => 'code',
|
||||
'text' => $text,
|
||||
),
|
||||
);
|
||||
$emphasis = 'em';
|
||||
}
|
||||
}
|
||||
|
||||
protected function inlineImage($excerpt)
|
||||
{
|
||||
if ( ! isset($excerpt[1]) or $excerpt[1] !== '[')
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$excerpt = substr($excerpt, 1);
|
||||
return array(
|
||||
'extent' => strlen($matches[0]),
|
||||
'element' => array(
|
||||
'name' => $emphasis,
|
||||
'handler' => 'line',
|
||||
'text' => $matches[1],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
$InlineLink = $this->inlineLink($excerpt);
|
||||
protected function inlineEscapeSequence($Excerpt)
|
||||
{
|
||||
if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters))
|
||||
{
|
||||
return array(
|
||||
'markup' => $Excerpt['text'][1],
|
||||
'extent' => 2,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($InlineLink === null)
|
||||
protected function inlineImage($Excerpt)
|
||||
{
|
||||
if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$Excerpt['text']= substr($Excerpt['text'], 1);
|
||||
|
||||
$Link = $this->inlineLink($Excerpt);
|
||||
|
||||
if ($Link === null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$Inline = array(
|
||||
'extent' => $InlineLink['extent'] + 1,
|
||||
'extent' => $Link['extent'] + 1,
|
||||
'element' => array(
|
||||
'name' => 'img',
|
||||
'attributes' => array(
|
||||
'src' => $InlineLink['element']['attributes']['href'],
|
||||
'alt' => $InlineLink['element']['text'],
|
||||
'src' => $Link['element']['attributes']['href'],
|
||||
'alt' => $Link['element']['text'],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$Inline['element']['attributes'] += $InlineLink['element']['attributes'];
|
||||
$Inline['element']['attributes'] += $Link['element']['attributes'];
|
||||
|
||||
unset($Inline['element']['attributes']['href']);
|
||||
|
||||
return $Inline;
|
||||
}
|
||||
|
||||
protected function inlineLink($excerpt)
|
||||
protected function inlineLink($Excerpt)
|
||||
{
|
||||
$Element = array(
|
||||
'name' => 'a',
|
||||
@ -1283,7 +1182,7 @@ class Parsedown
|
||||
|
||||
$extent = 0;
|
||||
|
||||
$remainder = $excerpt;
|
||||
$remainder = $Excerpt['text'];
|
||||
|
||||
if (preg_match('/\[((?:[^][]|(?R))*)\]/', $remainder, $matches))
|
||||
{
|
||||
@ -1298,7 +1197,7 @@ class Parsedown
|
||||
return;
|
||||
}
|
||||
|
||||
if (preg_match('/^\([ ]*([^ ]+?)(?:[ ]+(".+?"|\'.+?\'))?[ ]*\)/', $remainder, $matches))
|
||||
if (preg_match('/^[(]((?:[^ ()]|[(][^ )]+[)])+)(?:[ ]+("[^"]*"|\'[^\']*\'))?[)]/', $remainder, $matches))
|
||||
{
|
||||
$Element['attributes']['href'] = $matches[1];
|
||||
|
||||
@ -1342,59 +1241,126 @@ class Parsedown
|
||||
);
|
||||
}
|
||||
|
||||
protected function inlineEmphasis($excerpt)
|
||||
protected function inlineMarkup($Excerpt)
|
||||
{
|
||||
if ( ! isset($excerpt[1]))
|
||||
if ($this->markupEscaped or strpos($Excerpt['text'], '>') === false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$marker = $excerpt[0];
|
||||
|
||||
if ($excerpt[1] === $marker and preg_match($this->StrongRegex[$marker], $excerpt, $matches))
|
||||
if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w*[ ]*>/s', $Excerpt['text'], $matches))
|
||||
{
|
||||
$emphasis = 'strong';
|
||||
}
|
||||
elseif (preg_match($this->EmRegex[$marker], $excerpt, $matches))
|
||||
{
|
||||
$emphasis = 'em';
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
return array(
|
||||
'markup' => $matches[0],
|
||||
'extent' => strlen($matches[0]),
|
||||
);
|
||||
}
|
||||
|
||||
return array(
|
||||
'extent' => strlen($matches[0]),
|
||||
'element' => array(
|
||||
'name' => $emphasis,
|
||||
'handler' => 'line',
|
||||
'text' => $matches[1],
|
||||
),
|
||||
);
|
||||
if ($Excerpt['text'][1] === '!' and preg_match('/^<!---?[^>-](?:-?[^-])*-->/s', $Excerpt['text'], $matches))
|
||||
{
|
||||
return array(
|
||||
'markup' => $matches[0],
|
||||
'extent' => strlen($matches[0]),
|
||||
);
|
||||
}
|
||||
|
||||
if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w*(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s', $Excerpt['text'], $matches))
|
||||
{
|
||||
return array(
|
||||
'markup' => $matches[0],
|
||||
'extent' => strlen($matches[0]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# ~
|
||||
protected function inlineSpecialCharacter($Excerpt)
|
||||
{
|
||||
if ($Excerpt['text'][0] === '&' and ! preg_match('/^&#?\w+;/', $Excerpt['text']))
|
||||
{
|
||||
return array(
|
||||
'markup' => '&',
|
||||
'extent' => 1,
|
||||
);
|
||||
}
|
||||
|
||||
protected $unmarkedInlineTypes = array("\n" => 'Break', '://' => 'Url');
|
||||
$SpecialCharacter = array('>' => 'gt', '<' => 'lt', '"' => 'quot');
|
||||
|
||||
if (isset($SpecialCharacter[$Excerpt['text'][0]]))
|
||||
{
|
||||
return array(
|
||||
'markup' => '&'.$SpecialCharacter[$Excerpt['text'][0]].';',
|
||||
'extent' => 1,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function inlineStrikethrough($Excerpt)
|
||||
{
|
||||
if ( ! isset($Excerpt['text'][1]))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ($Excerpt['text'][1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches))
|
||||
{
|
||||
return array(
|
||||
'extent' => strlen($matches[0]),
|
||||
'element' => array(
|
||||
'name' => 'del',
|
||||
'text' => $matches[1],
|
||||
'handler' => 'line',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function inlineUrl($Excerpt)
|
||||
{
|
||||
if ($this->urlsLinked !== true or ! isset($Excerpt['text'][2]) or $Excerpt['text'][2] !== '/')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE))
|
||||
{
|
||||
$Inline = array(
|
||||
'extent' => strlen($matches[0][0]),
|
||||
'position' => $matches[0][1],
|
||||
'element' => array(
|
||||
'name' => 'a',
|
||||
'text' => $matches[0][0],
|
||||
'attributes' => array(
|
||||
'href' => $matches[0][0],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $Inline;
|
||||
}
|
||||
}
|
||||
|
||||
protected function inlineUrlTag($Excerpt)
|
||||
{
|
||||
if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches))
|
||||
{
|
||||
$url = str_replace(array('&', '<'), array('&', '<'), $matches[1]);
|
||||
|
||||
return array(
|
||||
'extent' => strlen($matches[0]),
|
||||
'element' => array(
|
||||
'name' => 'a',
|
||||
'text' => $url,
|
||||
'attributes' => array(
|
||||
'href' => $url,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
# ~
|
||||
|
||||
protected function unmarkedText($text)
|
||||
{
|
||||
foreach ($this->unmarkedInlineTypes as $snippet => $inlineType)
|
||||
{
|
||||
if (strpos($text, $snippet) !== false)
|
||||
{
|
||||
$text = $this->{'unmarkedInline'.$inlineType}($text);
|
||||
}
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
protected function unmarkedInlineBreak($text)
|
||||
{
|
||||
if ($this->breaksEnabled)
|
||||
{
|
||||
@ -1409,38 +1375,65 @@ class Parsedown
|
||||
return $text;
|
||||
}
|
||||
|
||||
protected function unmarkedInlineUrl($text)
|
||||
#
|
||||
# Handlers
|
||||
#
|
||||
|
||||
protected function element(array $Element)
|
||||
{
|
||||
if ($this->urlsLinked !== true)
|
||||
$markup = '<'.$Element['name'];
|
||||
|
||||
if (isset($Element['attributes']))
|
||||
{
|
||||
return $text;
|
||||
foreach ($Element['attributes'] as $name => $value)
|
||||
{
|
||||
if ($value === null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$markup .= ' '.$name.'="'.$value.'"';
|
||||
}
|
||||
}
|
||||
|
||||
$re = '/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui';
|
||||
|
||||
$offset = 0;
|
||||
|
||||
while (strpos($text, '://', $offset) and preg_match($re, $text, $matches, PREG_OFFSET_CAPTURE, $offset))
|
||||
if (isset($Element['text']))
|
||||
{
|
||||
$url = $matches[0][0];
|
||||
$markup .= '>';
|
||||
|
||||
$urlLength = strlen($url);
|
||||
$urlPosition = $matches[0][1];
|
||||
if (isset($Element['handler']))
|
||||
{
|
||||
$markup .= $this->{$Element['handler']}($Element['text']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$markup .= $Element['text'];
|
||||
}
|
||||
|
||||
$markup = '<a href="'.$url.'">'.$url.'</a>';
|
||||
$markupLength = strlen($markup);
|
||||
|
||||
$text = substr_replace($text, $markup, $urlPosition, $urlLength);
|
||||
|
||||
$offset = $urlPosition + $markupLength;
|
||||
$markup .= '</'.$Element['name'].'>';
|
||||
}
|
||||
else
|
||||
{
|
||||
$markup .= ' />';
|
||||
}
|
||||
|
||||
return $text;
|
||||
return $markup;
|
||||
}
|
||||
|
||||
protected function elements(array $Elements)
|
||||
{
|
||||
$markup = '';
|
||||
|
||||
foreach ($Elements as $Element)
|
||||
{
|
||||
$markup .= "\n" . $this->element($Element);
|
||||
}
|
||||
|
||||
$markup .= "\n";
|
||||
|
||||
return $markup;
|
||||
}
|
||||
|
||||
#
|
||||
# ~
|
||||
#
|
||||
|
||||
protected function li($lines)
|
||||
{
|
||||
@ -1462,7 +1455,18 @@ class Parsedown
|
||||
}
|
||||
|
||||
#
|
||||
# Multiton
|
||||
# Deprecated Methods
|
||||
#
|
||||
|
||||
function parse($text)
|
||||
{
|
||||
$markup = $this->text($text);
|
||||
|
||||
return $markup;
|
||||
}
|
||||
|
||||
#
|
||||
# Static Methods
|
||||
#
|
||||
|
||||
static function instance($name = 'default')
|
||||
@ -1481,22 +1485,6 @@ class Parsedown
|
||||
|
||||
private static $instances = array();
|
||||
|
||||
#
|
||||
# Deprecated Methods
|
||||
#
|
||||
|
||||
/**
|
||||
* @deprecated in favor of "text"
|
||||
* @param $text
|
||||
* @return string
|
||||
*/
|
||||
function parse($text)
|
||||
{
|
||||
$markup = $this->text($text);
|
||||
|
||||
return $markup;
|
||||
}
|
||||
|
||||
#
|
||||
# Fields
|
||||
#
|
||||
@ -1504,10 +1492,10 @@ class Parsedown
|
||||
protected $DefinitionData;
|
||||
|
||||
#
|
||||
# Read-only
|
||||
# Read-Only
|
||||
|
||||
protected $specialCharacters = array(
|
||||
'\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!',
|
||||
'\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|',
|
||||
);
|
||||
|
||||
protected $StrongRegex = array(
|
||||
|
29
README.md
29
README.md
@ -1,18 +1,20 @@
|
||||
## Parsedown
|
||||
|
||||
[](https://travis-ci.org/erusev/parsedown)
|
||||
<!--[](https://packagist.org/packages/erusev/parsedown)-->
|
||||
|
||||
Better Markdown Parser in PHP
|
||||
|
||||
[[ demo ]](http://parsedown.org/demo)
|
||||
[See Demo](http://parsedown.org/demo)
|
||||
|
||||
### Features
|
||||
|
||||
* [Fast](http://parsedown.org/speed)
|
||||
* [Consistent](http://parsedown.org/consistency)
|
||||
* [GitHub flavored](https://help.github.com/articles/github-flavored-markdown)
|
||||
* [Tested](http://parsedown.org/tests/) in PHP 5.2, 5.3, 5.4, 5.5, 5.6 and [hhvm](http://www.hhvm.com/)
|
||||
* Extensible
|
||||
* [Tested](http://parsedown.org/tests/) in PHP 5.3, 5.4, 5.5, 5.6 and [HHVM](http://www.hhvm.com/)
|
||||
* [Extensible](https://github.com/erusev/parsedown/wiki/Writing-Extensions)
|
||||
* [Markdown Extra extension](https://github.com/erusev/parsedown-extra)
|
||||
* [JavaScript port](https://github.com/hkdobrev/parsedown.js) under development
|
||||
|
||||
### Installation
|
||||
|
||||
@ -30,17 +32,20 @@ More examples in [the wiki](https://github.com/erusev/parsedown/wiki/Usage) and
|
||||
|
||||
### Questions
|
||||
|
||||
**How does Parsedown work?**<br/>
|
||||
**How does Parsedown work?**
|
||||
|
||||
It tries to read Markdown like a human. First, it looks at the lines. It’s interested in how the lines start. This helps it recognise blocks. It knows, for example, that if a line start with a `-` then it perhaps belong to a list. Once it recognises the blocks, it continues to the content. As it reads, it watches out for special characters. This helps it recognise inline elements (or inlines).
|
||||
|
||||
**Why doesn’t Parsedown use namespaces?**<br/>
|
||||
It'd mean no support for PHP 5.2. Would it be worth it?
|
||||
We call this approach "line based". We believe that Parsedown is the first Markdown parser to use it. Since the release of Parsedown, other developers have used the same approach to develop other Markdown parsers in PHP and in other languages.
|
||||
|
||||
**Is Parsedown compliant with CommonMark?**<br/>
|
||||
The majority of the CommonMark tests pass. Most of the tests that don't pass deal with cases that are quite extreme. Yet, we are working on them. As CommonMark matures, compliance should improve.
|
||||
**Is it compliant with CommonMark?**
|
||||
|
||||
It passes most of the CommonMark tests. Most of the tests that don't pass deal with cases that are quite uncommon. Still, as CommonMark matures, compliance should improve.
|
||||
|
||||
**Who uses it?**
|
||||
|
||||
**Who uses Parsedown?**<br/>
|
||||
[phpDocumentor](http://www.phpdoc.org/), [October CMS](http://octobercms.com/), [Bolt CMS](http://bolt.cm/), [Kirby CMS](http://getkirby.com/), [Grav CMS](http://getgrav.org/), [Statamic CMS](http://www.statamic.com/), [RaspberryPi.org](http://www.raspberrypi.org/) and [more](https://www.versioneye.com/php/erusev:parsedown/references).
|
||||
|
||||
**How can I help?**<br/>
|
||||
Use the project, tell friends about it and if you feel generous, [donate some money](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=528P3NZQMP8N2).
|
||||
**How can I help?**
|
||||
|
||||
Use it, star it, share it and if you feel generous, [donate some money](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=528P3NZQMP8N2).
|
||||
|
@ -1,21 +1,21 @@
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th align="left">header 1</th>
|
||||
<th align="center">header 2</th>
|
||||
<th align="right">header 2</th>
|
||||
<th style="text-align: left;">header 1</th>
|
||||
<th style="text-align: center;">header 2</th>
|
||||
<th style="text-align: right;">header 2</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left">cell 1.1</td>
|
||||
<td align="center">cell 1.2</td>
|
||||
<td align="right">cell 1.3</td>
|
||||
<td style="text-align: left;">cell 1.1</td>
|
||||
<td style="text-align: center;">cell 1.2</td>
|
||||
<td style="text-align: right;">cell 1.3</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left">cell 2.1</td>
|
||||
<td align="center">cell 2.2</td>
|
||||
<td align="right">cell 2.3</td>
|
||||
<td style="text-align: left;">cell 2.1</td>
|
||||
<td style="text-align: center;">cell 2.2</td>
|
||||
<td style="text-align: right;">cell 2.3</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
@ -1,13 +1,12 @@
|
||||
<div>_content_</div>
|
||||
<p>sparse:</p>
|
||||
<div>
|
||||
<div class="inner">
|
||||
_content_
|
||||
</div>
|
||||
</div>
|
||||
<p>paragraph</p>
|
||||
<div>
|
||||
<div class="inner">
|
||||
_content_
|
||||
</div>
|
||||
</div>
|
||||
<style type="text/css">
|
||||
p {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
p {color: #789;}
|
||||
</style>
|
||||
<div>
|
||||
<a href="/">home</a></div>
|
@ -1,17 +1,16 @@
|
||||
<div>_content_</div>
|
||||
|
||||
sparse:
|
||||
|
||||
<div>
|
||||
<div class="inner">
|
||||
_content_
|
||||
</div>
|
||||
</div>
|
||||
|
||||
paragraph
|
||||
|
||||
<div>
|
||||
<div class="inner">
|
||||
_content_
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style type="text/css">
|
||||
p {
|
||||
color: red;
|
||||
}
|
||||
p {color: #789;}
|
||||
</style>
|
||||
|
||||
<div>
|
||||
<a href="/">home</a></div>
|
@ -1 +1,2 @@
|
||||
<p><img src="/md.png" alt="alt" title="title" /></p>
|
||||
<p><img src="/md.png" alt="alt" title="title" /></p>
|
||||
<p><img src="/md.png" alt="blank title" title="" /></p>
|
@ -1 +1,3 @@
|
||||

|
||||

|
||||
|
||||

|
@ -1,4 +1,6 @@
|
||||
<p><a href="http://example.com">link</a> and <a href="/tests/">another link</a></p>
|
||||
<p><a href="http://example.com">link</a></p>
|
||||
<p><a href="/url-(parentheses)">link</a> with parentheses in URL </p>
|
||||
<p>(<a href="/index.php">link</a>) in parentheses</p>
|
||||
<p><a href="http://example.com"><code>link</code></a></p>
|
||||
<p><a href="http://example.com"><img src="http://parsedown.org/md.png" alt="MD Logo" /></a></p>
|
||||
<p><a href="http://example.com"><img src="http://parsedown.org/md.png" alt="MD Logo" /> and text</a></p>
|
@ -1,4 +1,8 @@
|
||||
[link](http://example.com) and [another link](/tests/)
|
||||
[link](http://example.com)
|
||||
|
||||
[link](/url-(parentheses)) with parentheses in URL
|
||||
|
||||
([link](/index.php)) in parentheses
|
||||
|
||||
[`link`](http://example.com)
|
||||
|
||||
|
@ -1 +1,6 @@
|
||||
<p><a href="http://example.com" title="Title">single quotes</a> and <a href="http://example.com" title="Title">double quotes</a></p>
|
||||
<p><a href="http://example.com" title="Title">single quotes</a></p>
|
||||
<p><a href="http://example.com" title="Title">double quotes</a></p>
|
||||
<p><a href="http://example.com" title="">single quotes blank</a></p>
|
||||
<p><a href="http://example.com" title="">double quotes blank</a></p>
|
||||
<p><a href="http://example.com" title="2 Words">space</a></p>
|
||||
<p><a href="http://example.com/url-(parentheses)" title="Title">parentheses</a></p>
|
@ -1 +1,11 @@
|
||||
[single quotes](http://example.com 'Title') and [double quotes](http://example.com "Title")
|
||||
[single quotes](http://example.com 'Title')
|
||||
|
||||
[double quotes](http://example.com "Title")
|
||||
|
||||
[single quotes blank](http://example.com '')
|
||||
|
||||
[double quotes blank](http://example.com "")
|
||||
|
||||
[space](http://example.com "2 Words")
|
||||
|
||||
[parentheses](http://example.com/url-(parentheses) "Title")
|
@ -20,17 +20,17 @@
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th align="left">header 1</th>
|
||||
<th style="text-align: left;">header 1</th>
|
||||
<th>header 2</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left">cell 1.1</td>
|
||||
<td style="text-align: left;">cell 1.1</td>
|
||||
<td>cell 1.2</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left">cell 2.1</td>
|
||||
<td style="text-align: left;">cell 2.1</td>
|
||||
<td>cell 2.2</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
@ -11,8 +11,12 @@
|
||||
<td><del>cell</del> 1.2</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>cell</code> 2.1</td>
|
||||
<td>cell 2.2</td>
|
||||
<td><code>|</code> 2.1</td>
|
||||
<td>| 2.2</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>\|</code> 2.1</td>
|
||||
<td><a href="/">link</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
@ -1,4 +1,5 @@
|
||||
| _header_ 1 | header 2 |
|
||||
| ------------ | ------------ |
|
||||
| _cell_ 1.1 | ~~cell~~ 1.2 |
|
||||
| `cell` 2.1 | cell 2.2 |
|
||||
| `|` 2.1 | \| 2.2 |
|
||||
| `\|` 2.1 | [link](/) |
|
Reference in New Issue
Block a user