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

Compare commits

...

21 Commits
1.2.0 ... 1.4.2

Author SHA1 Message Date
f08d017bcb resolve #196 2015-01-15 02:45:45 +02:00
e61a6114b0 resolve #175 2015-01-15 02:37:20 +02:00
9ed72ccd09 resolve #126 2015-01-15 02:24:39 +02:00
09e1184d9f resolve #265 2015-01-15 00:56:12 +02:00
2de60a9a8b improve readme 2015-01-13 15:28:18 +02:00
73a75299f5 improve readme 2015-01-13 15:18:35 +02:00
0d28808392 void markup blocks be marked 2015-01-12 23:24:13 +02:00
78960cf792 improve formatting 2015-01-12 18:53:24 +02:00
8f2e9c7cf6 definitions are blocks
in the old implementation it wasn’t possible to have multiline
definitions
2015-01-12 18:52:17 +02:00
3eb6d349f0 "src" and "alt" attributes should come first 2015-01-12 02:58:08 +02:00
859b1b10c1 update tests 2015-01-12 02:57:20 +02:00
08b01a1a29 blocks should be able to return markup 2015-01-12 02:55:00 +02:00
1686b2fbff we no longer call inline elements spans 2015-01-12 02:55:00 +02:00
15a32fcd0e no need to know the structure of markup blocks 2015-01-12 02:55:00 +02:00
4aca208f96 update readme 2015-01-11 16:23:43 +02:00
cedf96a64e update readme 2015-01-11 16:04:19 +02:00
9f58363e4b Merge pull request #260 from rhukster/master
Fix for Parsedown stripping classes on images supported by ParsedownExtra
2015-01-11 14:50:08 +02:00
6b4a459f97 Merge pull request #261 from naNuke/master
breaksEnabled fix
2015-01-11 14:08:17 +02:00
05bf198d26 breaksEnabled fix 2015-01-11 06:12:01 +01:00
30234a58fa No longer needed in this solution 2015-01-10 19:44:30 -07:00
03ff22c7df Attempted fix for stripped classes on images with ParsedownExtra - re: https://github.com/erusev/parsedown-extra/issues/32 2015-01-10 19:40:39 -07:00
7 changed files with 153 additions and 186 deletions

View File

@ -29,11 +29,10 @@ class Parsedown
function text($text)
{
# make sure no definitions are set
$this->Definitions = array();
$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");
@ -104,6 +103,7 @@ class Parsedown
'<' => array('Comment', 'Markup'),
'=' => array('SetextHeader'),
'>' => array('Quote'),
'[' => array('Reference'),
'_' => array('Rule'),
'`' => array('FencedCode'),
'|' => array('Table'),
@ -199,21 +199,6 @@ class Parsedown
$marker = $text[0];
if (isset($this->DefinitionTypes[$marker]))
{
foreach ($this->DefinitionTypes[$marker] as $definitionType)
{
$Definition = $this->{'definition'.$definitionType}($Line, $CurrentBlock);
if (isset($Definition))
{
$this->Definitions[$definitionType][$Definition['id']] = $Definition['data'];
continue 2;
}
}
}
# ~
$blockTypes = $this->unmarkedBlockTypes;
@ -239,7 +224,7 @@ class Parsedown
if ( ! isset($Block['identified']))
{
$Elements []= $CurrentBlock['element'];
$Blocks []= $CurrentBlock;
$Block['identified'] = true;
}
@ -263,7 +248,7 @@ class Parsedown
}
else
{
$Elements []= $CurrentBlock['element'];
$Blocks []= $CurrentBlock;
$CurrentBlock = $this->paragraph($Line);
@ -280,13 +265,26 @@ class Parsedown
# ~
$Elements []= $CurrentBlock['element'];
$Blocks []= $CurrentBlock;
unset($Elements[0]);
unset($Blocks[0]);
# ~
$markup = $this->elements($Elements);
$markup = '';
foreach ($Blocks as $Block)
{
if (isset($Block['hidden']))
{
continue;
}
$markup .= "\n";
$markup .= isset($Block['markup']) ? $Block['markup'] : $this->element($Block['element']);
}
$markup .= "\n";
# ~
@ -367,9 +365,7 @@ class Parsedown
if (isset($Line['text'][3]) and $Line['text'][3] === '-' and $Line['text'][2] === '-' and $Line['text'][1] === '!')
{
$Block = array(
'element' => array(
'text' => $Line['body'],
),
'markup' => $Line['body'],
);
if (preg_match('/-->$/', $Line['text']))
@ -388,7 +384,7 @@ class Parsedown
return;
}
$Block['element']['text'] .= "\n" . $Line['body'];
$Block['markup'] .= "\n" . $Line['body'];
if (preg_match('/-->$/', $Line['text']))
{
@ -485,7 +481,7 @@ class Parsedown
$level ++;
}
if ($level > 6 or $Line['text'][$level] !== ' ')
if ($level > 6)
{
return;
}
@ -665,7 +661,7 @@ class Parsedown
#
# Markup
protected function blockMarkup($Line)
{
if ($this->markupEscaped)
@ -673,77 +669,47 @@ class Parsedown
return;
}
$attrName = '[a-zA-Z_:][\w:.-]*';
$attrValue = '(?:[^"\'=<>`\s]+|".*?"|\'.*?\')';
preg_match('/^<(\w[\d\w]*)((?:\s'.$attrName.'(?:\s*=\s*'.$attrValue.')?)*)\s*(\/?)>/', $Line['text'], $matches);
if ( ! $matches or in_array($matches[1], $this->textLevelElements))
if (preg_match('/^<(\w*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches))
{
return;
}
$Block = array(
'depth' => 0,
'element' => array(
'name' => $matches[1],
'text' => null,
),
);
$remainder = substr($Line['text'], strlen($matches[0]));
if (trim($remainder) === '')
{
if ($matches[3] or in_array($matches[1], $this->voidElements))
{
$Block['closed'] = true;
}
}
else
{
if ($matches[3] or in_array($matches[1], $this->voidElements))
if (in_array($matches[1], $this->textLevelElements))
{
return;
}
preg_match('/(.*)<\/'.$matches[1].'>\s*$/i', $remainder, $nestedMatches);
$Block = array(
'name' => $matches[1],
'depth' => 0,
'markup' => $Line['text'],
);
if ($nestedMatches)
$length = strlen($matches[0]);
$remainder = substr($Line['text'], $length);
if (trim($remainder) === '')
{
$Block['closed'] = true;
$Block['element']['text'] = $nestedMatches[1];
if (isset($matches[2]) or in_array($matches[1], $this->voidElements))
{
$Block['closed'] = true;
$Block['void'] = true;
}
}
else
{
$Block['element']['text'] = $remainder;
}
}
if (isset($matches[2]) or in_array($matches[1], $this->voidElements))
{
return;
}
if (preg_match('/<\/'.$matches[1].'>[ ]*$/i', $remainder))
{
$Block['closed'] = true;
}
}
if ( ! $matches[2])
{
return $Block;
}
preg_match_all('/\s('.$attrName.')(?:\s*=\s*('.$attrValue.'))?/', $matches[2], $nestedMatches, PREG_SET_ORDER);
foreach ($nestedMatches as $nestedMatch)
{
if ( ! isset($nestedMatch[2]))
{
$Block['element']['attributes'][$nestedMatch[1]] = '';
}
elseif ($nestedMatch[2][0] === '"' or $nestedMatch[2][0] === '\'')
{
$Block['element']['attributes'][$nestedMatch[1]] = substr($nestedMatch[2], 1, - 1);
}
else
{
$Block['element']['attributes'][$nestedMatch[1]] = $nestedMatch[2];
}
}
return $Block;
}
protected function blockMarkupContinue($Line, array $Block)
@ -753,12 +719,12 @@ class Parsedown
return;
}
if (preg_match('/^<'.$Block['element']['name'].'(?:\s.*[\'"])?\s*>/i', $Line['text'])) # open
if (preg_match('/^<'.$Block['name'].'(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*>/i', $Line['text'])) # open
{
$Block['depth'] ++;
}
if (preg_match('/(.*?)<\/'.$Block['element']['name'].'>\s*$/i', $Line['text'], $matches)) # close
if (preg_match('/(.*?)<\/'.$Block['name'].'>[ ]*$/i', $Line['text'], $matches)) # close
{
if ($Block['depth'] > 0)
{
@ -766,29 +732,53 @@ class Parsedown
}
else
{
$Block['element']['text'] .= "\n";
$Block['closed'] = true;
}
$Block['element']['text'] .= $matches[1];
$Block['markup'] .= $matches[1];
}
if (isset($Block['interrupted']))
{
$Block['element']['text'] .= "\n";
$Block['markup'] .= "\n";
unset($Block['interrupted']);
}
if ( ! isset($Block['closed']))
{
$Block['element']['text'] .= "\n".$Line['body'];
}
$Block['markup'] .= "\n".$Line['body'];
return $Block;
}
#
# Reference
protected function blockReference($Line)
{
if (preg_match('/^\[(.+?)\]:[ ]*<?(\S+?)>?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches))
{
$id = strtolower($matches[1]);
$Data = array(
'url' => $matches[2],
'title' => null,
);
if (isset($matches[3]))
{
$Data['title'] = $matches[3];
}
$this->DefinitionData['Reference'][$id] = $Data;
$Block = array(
'hidden' => true,
);
return $Block;
}
}
#
# Table
@ -901,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();
@ -944,31 +939,6 @@ class Parsedown
}
}
#
# Definitions
#
protected function definitionReference($Line)
{
if (preg_match('/^\[(.+?)\]:[ ]*<?(\S+?)>?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches))
{
$Definition = array(
'id' => strtolower($matches[1]),
'data' => array(
'url' => $matches[2],
'title' => null,
),
);
if (isset($matches[3]))
{
$Definition['data']['title'] = $matches[3];
}
return $Definition;
}
}
#
# ~
#
@ -992,39 +962,25 @@ class Parsedown
protected function element(array $Element)
{
$markup = '';
$markup = '<'.$Element['name'];
if (isset($Element['name']))
if (isset($Element['attributes']))
{
$markup .= '<'.$Element['name'];
if (isset($Element['attributes']))
foreach ($Element['attributes'] as $name => $value)
{
foreach ($Element['attributes'] as $name => $value)
if ($value === null)
{
if ($value === null)
{
continue;
}
$markup .= ' '.$name.'="'.$value.'"';
continue;
}
}
if (isset($Element['text']))
{
$markup .= '>';
}
else
{
$markup .= ' />';
return $markup;
$markup .= ' '.$name.'="'.$value.'"';
}
}
if (isset($Element['text']))
{
$markup .= '>';
if (isset($Element['handler']))
{
$markup .= $this->$Element['handler']($Element['text']);
@ -1033,12 +989,13 @@ class Parsedown
{
$markup .= $Element['text'];
}
}
if (isset($Element['name']))
{
$markup .= '</'.$Element['name'].'>';
}
else
{
$markup .= ' />';
}
return $markup;
}
@ -1049,11 +1006,6 @@ class Parsedown
foreach ($Elements as $Element)
{
if ($Element === null)
{
continue;
}
$markup .= "\n" . $this->element($Element);
}
@ -1063,7 +1015,7 @@ class Parsedown
}
#
# Spans
# Inline Elements
#
protected $InlineTypes = array(
@ -1296,24 +1248,28 @@ class Parsedown
$excerpt = substr($excerpt, 1);
$Inline = $this->inlineLink($excerpt);
$InlineLink = $this->inlineLink($excerpt);
if ($Inline === null)
if ($InlineLink === null)
{
return;
}
$Inline['extent'] ++;
$Inline['element'] = array(
'name' => 'img',
'attributes' => array(
'src' => $Inline['element']['attributes']['href'],
'alt' => $Inline['element']['text'],
'title' => $Inline['element']['attributes']['title'],
$Inline = array(
'extent' => $InlineLink['extent'] + 1,
'element' => array(
'name' => 'img',
'attributes' => array(
'src' => $InlineLink['element']['attributes']['href'],
'alt' => $InlineLink['element']['text'],
),
),
);
$Inline['element']['attributes'] += $InlineLink['element']['attributes'];
unset($Inline['element']['attributes']['href']);
return $Inline;
}
@ -1346,7 +1302,7 @@ class Parsedown
return;
}
if (preg_match('/^\([ ]*([^ ]+?)(?:[ ]+(".+?"|\'.+?\'))?[ ]*\)/', $remainder, $matches))
if (preg_match('/^[(]((?:[^ (]|[(][^ )]+[)])+)(?:[ ]+("[^"]+"|\'[^\']+\'))?[)]/', $remainder, $matches))
{
$Element['attributes']['href'] = $matches[1];
@ -1371,12 +1327,12 @@ class Parsedown
$definition = strtolower($Element['text']);
}
if ( ! isset($this->Definitions['Reference'][$definition]))
if ( ! isset($this->DefinitionData['Reference'][$definition]))
{
return;
}
$Definition = $this->Definitions['Reference'][$definition];
$Definition = $this->DefinitionData['Reference'][$definition];
$Element['attributes']['href'] = $Definition['url'];
$Element['attributes']['title'] = $Definition['title'];
@ -1425,10 +1381,7 @@ class Parsedown
#
# ~
protected $unmarkedInlineTypes = array(
" \n" => 'Break',
'://' => 'Url',
);
protected $unmarkedInlineTypes = array("\n" => 'Break', '://' => 'Url');
# ~
@ -1552,7 +1505,7 @@ class Parsedown
# Fields
#
protected $Definitions;
protected $DefinitionData;
#
# Read-only
@ -1571,6 +1524,8 @@ class Parsedown
'_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us',
);
protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*(?:\s*=\s*(?:[^"\'=<>`\s]+|"[^"]*"|\'[^\']*\'))?';
protected $voidElements = array(
'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source',
);

View File

@ -30,17 +30,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. Its 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 doesnt 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?**
**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.
**Who uses Parsedown?**<br/>
[phpDocumentor](http://www.phpdoc.org/), [October CMS](http://octobercms.com/), [Bolt CMS](http://bolt.cm/), [Kirby CMS](http://getkirby.com/), [RaspberryPi.org](http://www.raspberrypi.org/) and [more](https://www.versioneye.com/php/erusev:parsedown/references).
**Who uses Parsedown?**
[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?**
**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).

View File

@ -1,4 +1,4 @@
<p><a href="http://example.com">link</a> and <a href="/tests/">another link</a></p>
<p><a href="http://example.com">link</a> and <a href="/url-with-(parentheses)">another link</a></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>

View File

@ -1,4 +1,4 @@
[link](http://example.com) and [another link](/tests/)
[link](http://example.com) and [another link](/url-with-(parentheses))
[`link`](http://example.com)

View File

@ -1 +1,4 @@
<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="2 Words">space</a></p>
<p><a href="http://example.com/url-(parentheses)" title="Title">parentheses</a></p>

View File

@ -1 +1,7 @@
[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")
[space](http://example.com "2 Words")
[parentheses](http://example.com/url-(parentheses) "Title")

View File

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