1
0
mirror of https://github.com/erusev/parsedown.git synced 2023-08-10 21:13:06 +03:00

Compare commits

..

23 Commits

Author SHA1 Message Date
d24439ada0 improve test suite 2014-05-21 23:20:46 +03:00
1ae100beab improve comment 2014-05-17 17:37:17 +03:00
82a5a78a36 improve readme 2014-05-17 17:13:00 +03:00
4ede4340ab improve readme 2014-05-16 03:34:43 +03:00
170a6bf770 improve readme 2014-05-16 01:27:54 +03:00
21db821324 improve readme 2014-05-16 01:15:21 +03:00
b384839d15 update readme 2014-05-14 20:07:52 +03:00
2da10d277b resolve #105 2014-05-14 13:14:49 +03:00
532b5ede35 resolve #129 2014-05-14 01:11:05 +03:00
2bd2f81f4f methods should not have more than one optional parameters 2014-05-12 16:18:00 +03:00
e318e66de5 improve consistency 2014-05-12 00:41:00 +03:00
0820d0a607 paragraph doesn't have to use a type 2014-05-12 00:34:47 +03:00
b8d1cfe91a improve extensibility 2014-05-11 22:31:02 +03:00
d85a233611 Merge pull request #171 from scarwu/master
identifyEscapeSequence() needs Array check
2014-05-11 20:57:05 +03:00
973d4a866d add array check 2014-05-11 23:36:01 +08:00
d19c2b6942 improve names 2014-05-10 16:28:00 +03:00
4dde57451d fix consecutive reference links 2014-05-06 17:05:49 +03:00
44686c4f1e improve extensibility 2014-05-06 01:12:27 +03:00
db02ecf259 "reference" is a definition 2014-05-05 14:43:31 +03:00
aa004d4595 improve code organisation 2014-05-05 14:39:40 +03:00
1bb65457ed remove unnecessary comments 2014-05-05 13:46:26 +03:00
0c9a4af8ab improve naming consistency 2014-05-03 18:02:06 +03:00
cc94c1b584 resolve #167 2014-05-02 18:21:10 +03:00
16 changed files with 319 additions and 275 deletions

View File

@ -18,17 +18,19 @@ class Parsedown
#
# Philosophy
# Markdown is intended to be easy-to-read by humans - those of us who read
# line by line, left to right, top to bottom. In order to take advantage of
# this, Parsedown tries to read in a similar way. It breaks texts into
# lines, it iterates through them and it looks at how they start and relate
# to each other.
# 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.
#
# ~
function text($text)
{
# make sure no definitions are set
$this->Definitions = array();
# standardize line breaks
$text = str_replace("\r\n", "\n", $text);
$text = str_replace("\r", "\n", $text);
@ -48,9 +50,6 @@ class Parsedown
# trim line breaks
$markup = trim($markup, "\n");
# clean up
$this->definitions = array();
return $markup;
}
@ -58,6 +57,8 @@ class Parsedown
# Setters
#
private $breaksEnabled;
function setBreaksEnabled($breaksEnabled)
{
$this->breaksEnabled = $breaksEnabled;
@ -65,13 +66,11 @@ class Parsedown
return $this;
}
private $breaksEnabled;
#
# Blocks
# Lines
#
protected $blockMarkers = array(
protected $BlockTypes = array(
'#' => array('Atx'),
'*' => array('Rule', 'List'),
'+' => array('List'),
@ -87,24 +86,31 @@ class Parsedown
'8' => array('List'),
'9' => array('List'),
':' => array('Table'),
'<' => array('Markup'),
'<' => array('Comment', 'Markup'),
'=' => array('Setext'),
'>' => array('Quote'),
'[' => array('Reference'),
'_' => array('Rule'),
'`' => array('FencedCode'),
'|' => array('Table'),
'~' => array('FencedCode'),
);
protected $definitionMarkers = array(
# ~
protected $DefinitionTypes = array(
'[' => array('Reference'),
);
# ~
protected $unmarkedBlockTypes = array(
'CodeBlock',
);
#
# Blocks
#
private function lines(array $lines)
{
$CurrentBlock = null;
@ -134,7 +140,7 @@ class Parsedown
$Line = array('body' => $line, 'indent' => $indent, 'text' => $text);
# Multiline block types define "addTo" methods.
# ~
if (isset($CurrentBlock['incomplete']))
{
@ -161,17 +167,15 @@ class Parsedown
$marker = $text[0];
# Definitions
if (isset($this->definitionMarkers[$marker]))
if (isset($this->DefinitionTypes[$marker]))
{
foreach ($this->definitionMarkers[$marker] as $definitionType)
foreach ($this->DefinitionTypes[$marker] as $definitionType)
{
$Definition = $this->{'identify'.$definitionType}($Line, $CurrentBlock);
if (isset($Definition))
{
$this->definitions[$definitionType][$Definition['id']] = $Definition['data'];
$this->Definitions[$definitionType][$Definition['id']] = $Definition['data'];
continue 2;
}
@ -182,9 +186,9 @@ class Parsedown
$blockTypes = $this->unmarkedBlockTypes;
if (isset($this->blockMarkers[$marker]))
if (isset($this->BlockTypes[$marker]))
{
foreach ($this->blockMarkers[$marker] as $blockType)
foreach ($this->BlockTypes[$marker] as $blockType)
{
$blockTypes []= $blockType;
}
@ -195,23 +199,19 @@ class Parsedown
foreach ($blockTypes as $blockType)
{
# Block types define "identify" methods.
$Block = $this->{'identify'.$blockType}($Line, $CurrentBlock);
if (isset($Block))
{
$Block['type'] = $blockType;
if ( ! isset($Block['identified'])) # »
if ( ! isset($Block['identified']))
{
$Elements []= $CurrentBlock['element'];
$Block['identified'] = true;
}
# Multiline block types define "addTo" methods.
if (method_exists($this, 'addTo'.$blockType))
{
$Block['incomplete'] = true;
@ -225,7 +225,7 @@ class Parsedown
# ~
if ($CurrentBlock['type'] === 'Paragraph' and ! isset($CurrentBlock['interrupted']))
if (isset($CurrentBlock) and ! isset($CurrentBlock['type']) and ! isset($CurrentBlock['interrupted']))
{
$CurrentBlock['element']['text'] .= "\n".$text;
}
@ -233,15 +233,9 @@ class Parsedown
{
$Elements []= $CurrentBlock['element'];
$CurrentBlock = array(
'type' => 'Paragraph',
'identified' => true,
'element' => array(
'name' => 'p',
'text' => $text,
'handler' => 'line',
),
);
$CurrentBlock = $this->buildParagraph($Line);
$CurrentBlock['identified'] = true;
}
}
@ -296,15 +290,22 @@ class Parsedown
}
#
# Rule
# Code
protected function identifyRule($Line)
protected function identifyCodeBlock($Line)
{
if (preg_match('/^(['.$Line['text'][0].'])([ ]{0,2}\1){2,}[ ]*$/', $Line['text']))
if ($Line['indent'] >= 4)
{
$text = substr($Line['body'], 4);
$Block = array(
'element' => array(
'name' => 'hr'
'name' => 'pre',
'handler' => 'element',
'text' => array(
'name' => 'code',
'text' => $text,
),
),
);
@ -312,104 +313,72 @@ class Parsedown
}
}
#
# Reference
protected function identifyReference($Line)
protected function addToCodeBlock($Line, $Block)
{
if (preg_match('/^\[(.+?)\]:[ ]*<?(\S+?)>?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches))
if ($Line['indent'] >= 4)
{
$Definition = array(
'id' => strtolower($matches[1]),
'data' => array(
'url' => $matches[2],
),
);
if (isset($matches[3]))
if (isset($Block['interrupted']))
{
$Definition['data']['title'] = $matches[3];
$Block['element']['text']['text'] .= "\n";
unset($Block['interrupted']);
}
return $Definition;
}
}
$Block['element']['text']['text'] .= "\n";
#
# Setext
$text = substr($Line['body'], 4);
protected function identifySetext($Line, array $Block = null)
{
if ( ! isset($Block) or $Block['type'] !== 'Paragraph' or isset($Block['interrupted']))
{
return;
}
if (chop($Line['text'], $Line['text'][0]) === '')
{
$Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2';
$Block['element']['text']['text'] .= $text;
return $Block;
}
}
#
# Markup
protected function identifyMarkup($Line)
protected function completeCodeBlock($Block)
{
if (preg_match('/^<(\w[\w\d]*)(?:[ ][^>\/]*)?(\/?)[ ]*>/', $Line['text'], $matches))
{
if (in_array($matches[1], $this->textLevelElements))
{
return;
}
$text = $Block['element']['text']['text'];
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
$Block['element']['text']['text'] = $text;
return $Block;
}
#
# Comment
protected function identifyComment($Line)
{
if (isset($Line['text'][3]) and $Line['text'][3] === '-' and $Line['text'][2] === '-' and $Line['text'][1] === '!')
{
$Block = array(
'element' => $Line['body'],
);
if ($matches[2] or $matches[1] === 'hr' or preg_match('/<\/'.$matches[1].'>[ ]*$/', $Line['text']))
if (preg_match('/-->$/', $Line['text']))
{
$Block['closed'] = true;
}
else
{
$Block['depth'] = 0;
$Block['start'] = '<'.$matches[1].'>';
$Block['end'] = '</'.$matches[1].'>';
}
return $Block;
}
}
protected function addToMarkup($Line, array $Block)
protected function addToComment($Line, array $Block)
{
if (isset($Block['closed']))
{
return;
}
if (stripos($Line['text'], $Block['start']) !== false) # opening tag
{
$Block['depth'] ++;
}
$Block['element'] .= "\n" . $Line['body'];
if (stripos($Line['text'], $Block['end']) !== false) # closing tag
if (preg_match('/-->$/', $Line['text']))
{
if ($Block['depth'] > 0)
{
$Block['depth'] --;
}
else
{
$Block['closed'] = true;
}
$Block['closed'] = true;
}
$Block['element'] .= "\n".$Line['body'];
return $Block;
}
@ -610,12 +579,106 @@ class Parsedown
}
}
#
# Rule
protected function identifyRule($Line)
{
if (preg_match('/^(['.$Line['text'][0].'])([ ]{0,2}\1){2,}[ ]*$/', $Line['text']))
{
$Block = array(
'element' => array(
'name' => 'hr'
),
);
return $Block;
}
}
#
# Setext
protected function identifySetext($Line, array $Block = null)
{
if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
{
return;
}
if (chop($Line['text'], $Line['text'][0]) === '')
{
$Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2';
return $Block;
}
}
#
# Markup
protected function identifyMarkup($Line)
{
if (preg_match('/^<(\w[\w\d]*)(?:[ ][^>\/]*)?(\/?)[ ]*>/', $Line['text'], $matches))
{
if (in_array($matches[1], $this->textLevelElements))
{
return;
}
$Block = array(
'element' => $Line['body'],
);
if ($matches[2] or $matches[1] === 'hr' or preg_match('/<\/'.$matches[1].'>[ ]*$/', $Line['text']))
{
$Block['closed'] = true;
}
else
{
$Block['depth'] = 0;
$Block['name'] = $matches[1];
}
return $Block;
}
}
protected function addToMarkup($Line, array $Block)
{
if (isset($Block['closed']))
{
return;
}
if (preg_match('/<'.$Block['name'].'([ ][^\/]+)?>/', $Line['text'])) # opening tag
{
$Block['depth'] ++;
}
if (stripos($Line['text'], '</'.$Block['name'].'>') !== false) # closing tag
{
if ($Block['depth'] > 0)
{
$Block['depth'] --;
}
else
{
$Block['closed'] = true;
}
}
$Block['element'] .= "\n".$Line['body'];
return $Block;
}
#
# Table
protected function identifyTable($Line, array $Block = null)
{
if ( ! isset($Block) or $Block['type'] !== 'Paragraph' or isset($Block['interrupted']))
if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
{
return;
}
@ -766,57 +829,42 @@ class Parsedown
}
#
# Code
# Definitions
#
protected function identifyCodeBlock($Line)
protected function identifyReference($Line)
{
if ($Line['indent'] >= 4)
if (preg_match('/^\[(.+?)\]:[ ]*<?(\S+?)>?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches))
{
$text = substr($Line['body'], 4);
$Block = array(
'element' => array(
'name' => 'pre',
'handler' => 'element',
'text' => array(
'name' => 'code',
'text' => $text,
),
$Definition = array(
'id' => strtolower($matches[1]),
'data' => array(
'url' => $matches[2],
),
);
return $Block;
}
}
protected function addToCodeBlock($Line, $Block)
{
if ($Line['indent'] >= 4)
{
if (isset($Block['interrupted']))
if (isset($matches[3]))
{
$Block['element']['text']['text'] .= "\n";
unset($Block['interrupted']);
$Definition['data']['title'] = $matches[3];
}
$Block['element']['text']['text'] .= "\n";
$text = substr($Line['body'], 4);
$Block['element']['text']['text'] .= $text;
return $Block;
return $Definition;
}
}
protected function completeCodeBlock($Block)
#
# ~
#
protected function buildParagraph($Line)
{
$text = $Block['element']['text']['text'];
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
$Block['element']['text']['text'] = $text;
$Block = array(
'element' => array(
'name' => 'p',
'text' => $Line['text'],
'handler' => 'line',
),
);
return $Block;
}
@ -825,7 +873,7 @@ class Parsedown
# ~
#
private function element(array $Element)
protected function element(array $Element)
{
$markup = '<'.$Element['name'];
@ -860,7 +908,7 @@ class Parsedown
return $markup;
}
private function elements(array $Elements)
protected function elements(array $Elements)
{
$markup = '';
@ -873,7 +921,7 @@ class Parsedown
$markup .= "\n";
if (is_string($Element)) # because of markup
if (is_string($Element)) # because of Markup
{
$markup .= $Element;
@ -892,7 +940,7 @@ class Parsedown
# Spans
#
protected $spanMarkers = array(
protected $SpanTypes = array(
'!' => array('Link'), # ?
'&' => array('Ampersand'),
'*' => array('Emphasis'),
@ -905,8 +953,14 @@ class Parsedown
'\\' => array('EscapeSequence'),
);
# ~
protected $spanMarkerList = '*_!&[</`~\\';
#
# ~
#
public function line($text)
{
$markup = '';
@ -915,17 +969,19 @@ class Parsedown
$markerPosition = 0;
while ($markedExcerpt = strpbrk($remainder, $this->spanMarkerList))
while ($excerpt = strpbrk($remainder, $this->spanMarkerList))
{
$marker = $markedExcerpt[0];
$marker = $excerpt[0];
$markerPosition += strpos($remainder, $marker);
foreach ($this->spanMarkers[$marker] as $spanType)
$Excerpt = array('text' => $excerpt, 'context' => $text);
foreach ($this->SpanTypes[$marker] as $spanType)
{
$handler = 'identify'.$spanType;
$Span = $this->$handler($markedExcerpt, $text);
$Span = $this->$handler($Excerpt);
if ( ! isset($Span))
{
@ -950,7 +1006,7 @@ class Parsedown
$markup .= $this->readPlainText($plainText);
$markup .= isset($Span['element']) ? $this->element($Span['element']) : $Span['markup'];
$markup .= isset($Span['markup']) ? $Span['markup'] : $this->element($Span['element']);
$text = substr($text, $Span['position'] + $Span['extent']);
@ -961,7 +1017,7 @@ class Parsedown
continue 2;
}
$remainder = substr($markedExcerpt, 1);
$remainder = substr($excerpt, 1);
$markerPosition ++;
}
@ -975,14 +1031,14 @@ class Parsedown
# ~
#
protected function identifyUrl($excerpt, $text)
protected function identifyUrl($Excerpt)
{
if ( ! isset($excerpt[1]) or $excerpt[1] !== '/')
if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '/')
{
return;
}
if (preg_match('/\bhttps?:[\/]{2}[^\s]+\b\/*/ui', $text, $matches, PREG_OFFSET_CAPTURE))
if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE))
{
$url = str_replace(array('&', '<'), array('&amp;', '&lt;'), $matches[0][0]);
@ -1000,9 +1056,9 @@ class Parsedown
}
}
protected function identifyAmpersand($excerpt)
protected function identifyAmpersand($Excerpt)
{
if ( ! preg_match('/^&#?\w+;/', $excerpt))
if ( ! preg_match('/^&#?\w+;/', $Excerpt['text']))
{
return array(
'markup' => '&amp;',
@ -1011,14 +1067,14 @@ class Parsedown
}
}
protected function identifyStrikethrough($excerpt)
protected function identifyStrikethrough($Excerpt)
{
if ( ! isset($excerpt[1]))
if ( ! isset($Excerpt['text'][1]))
{
return;
}
if ($excerpt[1] === $excerpt[0] and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $excerpt, $matches))
if ($Excerpt['text'][1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches))
{
return array(
'extent' => strlen($matches[0]),
@ -1031,12 +1087,12 @@ class Parsedown
}
}
protected function identifyEscapeSequence($excerpt)
protected function identifyEscapeSequence($Excerpt)
{
if (in_array($excerpt[1], $this->specialCharacters))
if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters))
{
return array(
'markup' => $excerpt[1],
'markup' => $Excerpt['text'][1],
'extent' => 2,
);
}
@ -1050,9 +1106,9 @@ class Parsedown
);
}
protected function identifyUrlTag($excerpt)
protected function identifyUrlTag($Excerpt)
{
if (strpos($excerpt, '>') !== false and preg_match('/^<(https?:[\/]{2}[^\s]+?)>/i', $excerpt, $matches))
if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(https?:[\/]{2}[^\s]+?)>/i', $Excerpt['text'], $matches))
{
$url = str_replace(array('&', '<'), array('&amp;', '&lt;'), $matches[1]);
@ -1069,9 +1125,9 @@ class Parsedown
}
}
protected function identifyEmailTag($excerpt)
protected function identifyEmailTag($Excerpt)
{
if (strpos($excerpt, '>') !== false and preg_match('/^<(\S+?@\S+?)>/', $excerpt, $matches))
if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\S+?@\S+?)>/', $Excerpt['text'], $matches))
{
return array(
'extent' => strlen($matches[0]),
@ -1086,9 +1142,9 @@ class Parsedown
}
}
protected function identifyTag($excerpt)
protected function identifyTag($Excerpt)
{
if (strpos($excerpt, '>') !== false and preg_match('/^<\/?\w.*?>/', $excerpt, $matches))
if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<\/?\w.*?>/', $Excerpt['text'], $matches))
{
return array(
'markup' => $matches[0],
@ -1097,11 +1153,11 @@ class Parsedown
}
}
protected function identifyInlineCode($excerpt)
protected function identifyInlineCode($Excerpt)
{
$marker = $excerpt[0];
$marker = $Excerpt['text'][0];
if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(?<!'.$marker.')\1(?!'.$marker.')/', $excerpt, $matches))
if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(?<!'.$marker.')\1(?!'.$marker.')/', $Excerpt['text'], $matches))
{
$text = $matches[2];
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
@ -1116,25 +1172,25 @@ class Parsedown
}
}
protected function identifyLink($excerpt)
protected function identifyLink($Excerpt)
{
$extent = $excerpt[0] === '!' ? 1 : 0;
$extent = $Excerpt['text'][0] === '!' ? 1 : 0;
if (strpos($excerpt, ']') and preg_match('/\[((?:[^][]|(?R))*)\]/', $excerpt, $matches))
if (strpos($Excerpt['text'], ']') and preg_match('/\[((?:[^][]|(?R))*)\]/', $Excerpt['text'], $matches))
{
$Link = array('text' => $matches[1], 'label' => strtolower($matches[1]));
$extent += strlen($matches[0]);
$substring = substr($excerpt, $extent);
$substring = substr($Excerpt['text'], $extent);
if (preg_match('/^\s*\[(.+?)\]/', $substring, $matches))
if (preg_match('/^\s*\[([^][]+)\]/', $substring, $matches))
{
$Link['label'] = strtolower($matches[1]);
if (isset($this->definitions['Reference'][$Link['label']]))
if (isset($this->Definitions['Reference'][$Link['label']]))
{
$Link += $this->definitions['Reference'][$Link['label']];
$Link += $this->Definitions['Reference'][$Link['label']];
$extent += strlen($matches[0]);
}
@ -1143,9 +1199,9 @@ class Parsedown
return;
}
}
elseif (isset($this->definitions['Reference'][$Link['label']]))
elseif (isset($this->Definitions['Reference'][$Link['label']]))
{
$Link += $this->definitions['Reference'][$Link['label']];
$Link += $this->Definitions['Reference'][$Link['label']];
if (preg_match('/^[ ]*\[\]/', $substring, $matches))
{
@ -1175,7 +1231,7 @@ class Parsedown
$url = str_replace(array('&', '<'), array('&amp;', '&lt;'), $Link['url']);
if ($excerpt[0] === '!')
if ($Excerpt['text'][0] === '!')
{
$Element = array(
'name' => 'img',
@ -1208,20 +1264,20 @@ class Parsedown
);
}
protected function identifyEmphasis($excerpt)
protected function identifyEmphasis($Excerpt)
{
if ( ! isset($excerpt[1]))
if ( ! isset($Excerpt['text'][1]))
{
return;
}
$marker = $excerpt[0];
$marker = $Excerpt['text'][0];
if ($excerpt[1] === $marker and preg_match($this->strongRegex[$marker], $excerpt, $matches))
if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches))
{
$emphasis = 'strong';
}
elseif (preg_match($this->emRegex[$marker], $excerpt, $matches))
elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches))
{
$emphasis = 'em';
}
@ -1313,7 +1369,7 @@ class Parsedown
# Fields
#
protected $definitions;
protected $Definitions;
#
# Read-only
@ -1322,12 +1378,12 @@ class Parsedown
'\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!',
);
protected $strongRegex = array(
protected $StrongRegex = array(
'*' => '/^[*]{2}((?:[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s',
'_' => '/^__((?:[^_]|_[^_]*_)+?)__(?!_)/us',
);
protected $emRegex = array(
protected $EmRegex = array(
'*' => '/^[*]((?:[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s',
'_' => '/^_((?:[^_]|__[^_]*__)+?)_(?!_)\b/us',
);

View File

@ -2,8 +2,8 @@
Better [Markdown](http://en.wikipedia.org/wiki/Markdown) parser for PHP.
- [Demo](http://parsedown.org/demo)
- [Tests](http://parsedown.org/tests/)
* [Demo](http://parsedown.org/demo)
* [Test Suite](http://parsedown.org/tests/)
### Features
@ -12,6 +12,7 @@ Better [Markdown](http://en.wikipedia.org/wiki/Markdown) parser for PHP.
* [GitHub Flavored](https://help.github.com/articles/github-flavored-markdown)
* [Tested](https://travis-ci.org/erusev/parsedown) in PHP 5.2, 5.3, 5.4, 5.5, 5.6 and [hhvm](http://www.hhvm.com/)
* Extensible
* [Markdown Extra extension](https://github.com/erusev/parsedown-extra) <sup>new</sup>
### Installation
@ -24,3 +25,16 @@ $Parsedown = new Parsedown();
echo $Parsedown->text('Hello _Parsedown_!'); # prints: <p>Hello <em>Parsedown</em>!</p>
```
More examples in [the wiki](https://github.com/erusev/parsedown/wiki/Usage).
### Questions
**How does Parsedown work?**<br/>
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.
**Why doesnt Parsedown use namespaces?**<br/>
Using namespaces would mean dropping support for PHP 5.2. Since Parsedown is a single class with an uncommon name, making this trade wouldn't make much sense.
**Who uses Parsedown?**<br/>
[phpDocumentor](http://www.phpdoc.org/), [Bolt CMS](http://bolt.cm/), [RaspberryPi.org](http://www.raspberrypi.org/) and [more](https://www.versioneye.com/php/erusev:parsedown/references).

View File

@ -0,0 +1,5 @@
<!-- single line -->
<p>paragraph</p>
<!--
multiline -->
<p>paragraph</p>

View File

@ -0,0 +1,8 @@
<!-- single line -->
paragraph
<!--
multiline -->
paragraph

View File

@ -1,5 +1,8 @@
<div>_content_</div>
<p>sparse:</p>
<div>
<div class="inner">
_content_
</div>
</div>
</div>
<p>paragraph</p>

View File

@ -3,5 +3,9 @@
sparse:
<div>
<div class="inner">
_content_
</div>
</div>
</div>
paragraph

View File

@ -1,28 +0,0 @@
<p>Headings:</p>
<h2 id="overview">Overview</h2>
<p>blah</p>
<H2 id="block">Block Elements</H2>
<p>blah</p>
<h3 id="span">
Span Elements
</h3>
<p>blah</p>
<p>Hr's:</p>
<hr>
<p>blah</p>
<hr/>
<p>blah</p>
<hr />
<p>blah</p>
<hr>
<p>blah</p>
<hr/>
<p>blah</p>
<hr />
<p>blah</p>
<hr class="foo" id="bar" />
<p>blah</p>
<hr class="foo" id="bar"/>
<p>blah</p>
<hr class="foo" id="bar" >
<p>blah</p>

View File

@ -1,39 +0,0 @@
Headings:
<h2 id="overview">Overview</h2>
blah
<H2 id="block">Block Elements</H2>
blah
<h3 id="span">
Span Elements
</h3>
blah
Hr's:
<hr>
blah
<hr/>
blah
<hr />
blah
<hr>
blah
<hr/>
blah
<hr />
blah
<hr class="foo" id="bar" />
blah
<hr class="foo" id="bar"/>
blah
<hr class="foo" id="bar" >
blah

View File

@ -1,3 +1,4 @@
<p>an <a href="http://example.com">implicit</a> reference link</p>
<p>an <a href="http://example.com">implicit</a> reference link with an empty link definition</p>
<p>an <a href="http://example.com">implicit</a> reference link followed by <a href="http://cnn.com">another</a></p>
<p>an <a href="http://example.com" title="Example">explicit</a> reference link with a title</p>

View File

@ -4,6 +4,10 @@ an [implicit] reference link
an [implicit][] reference link with an empty link definition
an [implicit][] reference link followed by [another][]
[another]: http://cnn.com
an [explicit][example] reference link with a title
[example]: http://example.com "Example"

View File

@ -1,4 +0,0 @@
<hr />
<p>attributes:</p>
<hr style="background: #9bd;" />
<p>...</p>

View File

@ -1,7 +0,0 @@
<hr />
attributes:
<hr style="background: #9bd;" />
...

View File

@ -0,0 +1,12 @@
<hr>
<p>paragraph</p>
<hr/>
<p>paragraph</p>
<hr />
<p>paragraph</p>
<hr class="foo" id="bar" />
<p>paragraph</p>
<hr class="foo" id="bar"/>
<p>paragraph</p>
<hr class="foo" id="bar" >
<p>paragraph</p>

View File

@ -0,0 +1,12 @@
<hr>
paragraph
<hr/>
paragraph
<hr />
paragraph
<hr class="foo" id="bar" />
paragraph
<hr class="foo" id="bar"/>
paragraph
<hr class="foo" id="bar" >
paragraph

View File

@ -1,4 +1,5 @@
<p>an <b>important</b> <a href=''>link</a></p>
<p>broken<br/>
line</p>
<p><b>inline tag</b> at the beginning</p>
<p><b>inline tag</b> at the beginning</p>
<p><span><a href="http://example.com">http://example.com</a></span></p>

View File

@ -3,4 +3,6 @@ an <b>important</b> <a href=''>link</a>
broken<br/>
line
<b>inline tag</b> at the beginning
<b>inline tag</b> at the beginning
<span>http://example.com</span>