diff --git a/Parsedown.php b/Parsedown.php index e9a8cbd..0258075 100755 --- a/Parsedown.php +++ b/Parsedown.php @@ -66,6 +66,15 @@ class Parsedown return $this; } + private $markupEscaped; + + function setMarkupEscaped($markupEscaped) + { + $this->markupEscaped = $markupEscaped; + + return $this; + } + # # Lines # @@ -350,6 +359,11 @@ class Parsedown protected function identifyComment($Line) { + if ($this->markupEscaped) + { + return; + } + if (isset($Line['text'][3]) and $Line['text'][3] === '-' and $Line['text'][2] === '-' and $Line['text'][1] === '!') { $Block = array( @@ -619,6 +633,11 @@ class Parsedown protected function identifyMarkup($Line) { + if ($this->markupEscaped) + { + return; + } + if (preg_match('/^<(\w[\w\d]*)(?:[ ][^>]*)?(\/?)[ ]*>/', $Line['text'], $matches)) { if (in_array($matches[1], $this->textLevelElements)) @@ -630,7 +649,7 @@ class Parsedown 'element' => $Line['body'], ); - if ($matches[2] or $matches[1] === 'hr' or preg_match('/<\/'.$matches[1].'>[ ]*$/', $Line['text'])) + if ($matches[2] or in_array($matches[1], $this->voidElements) or preg_match('/<\/'.$matches[1].'>[ ]*$/', $Line['text'])) { $Block['closed'] = true; } @@ -651,7 +670,7 @@ class Parsedown return; } - if (preg_match('/<'.$Block['name'].'([ ][^\/]+)?>/', $Line['text'])) # opening tag + if (preg_match('/<'.$Block['name'].'([ ].*[\'"])?[ ]*>/', $Line['text'])) # opening tag { $Block['depth'] ++; } @@ -668,6 +687,13 @@ class Parsedown } } + if (isset($Block['interrupted'])) + { + $Block['element'] .= "\n"; + + unset($Block['interrupted']); + } + $Block['element'] .= "\n".$Line['body']; return $Block; @@ -1144,6 +1170,11 @@ class Parsedown protected function identifyTag($Excerpt) { + if ($this->markupEscaped) + { + return; + } + if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<\/?\w.*?>/', $Excerpt['text'], $matches)) { return array( @@ -1379,13 +1410,17 @@ class Parsedown ); protected $StrongRegex = array( - '*' => '/^[*]{2}((?:[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s', - '_' => '/^__((?:[^_]|_[^_]*_)+?)__(?!_)/us', + '*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s', + '_' => '/^__((?:\\\\_|[^_]|_[^_]*_)+?)__(?!_)/us', ); protected $EmRegex = array( - '*' => '/^[*]((?:[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s', - '_' => '/^_((?:[^_]|__[^_]*__)+?)_(?!_)\b/us', + '*' => '/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s', + '_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us', + ); + + protected $voidElements = array( + 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', ); protected $textLevelElements = array( diff --git a/README.md b/README.md index f1676f4..1984e06 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,15 @@ ## Parsedown -Better [Markdown](http://en.wikipedia.org/wiki/Markdown) parser for PHP. +Better Markdown Parser in PHP -* [Demo](http://parsedown.org/demo) -* [Test Suite](http://parsedown.org/tests/) +[[ 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](https://travis-ci.org/erusev/parsedown) in PHP 5.2, 5.3, 5.4, 5.5, 5.6 and [hhvm](http://www.hhvm.com/) +* [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 * [Markdown Extra extension](https://github.com/erusev/parsedown-extra) new * [JavaScript port](https://github.com/hkdobrev/parsedown.js) under development new @@ -27,7 +26,7 @@ $Parsedown = new Parsedown(); echo $Parsedown->text('Hello _Parsedown_!'); # prints:

Hello Parsedown!

``` -More examples in [the wiki](https://github.com/erusev/parsedown/wiki/Usage). +More examples in [the wiki](https://github.com/erusev/parsedown/wiki/Usage) and in [this video tutorial](http://youtu.be/wYZBY8DEikI). ### Questions @@ -35,7 +34,10 @@ More examples in [the wiki](https://github.com/erusev/parsedown/wiki/Usage). 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 doesn’t Parsedown use namespaces?**
-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. +Using namespaces would mean dropping support for PHP 5.2. We believe that since Parsedown is a single class with an uncommon name, making this trade wouldn't be worth it. + +**Is Parsedown compliant with CommonMark?**
+We are [working on it](https://github.com/erusev/parsedown/tree/commonmark). **Who uses Parsedown?**
-[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). +[phpDocumentor](http://www.phpdoc.org/), [October CMS](http://octobercms.com/), [Bolt CMS](http://bolt.cm/), [RaspberryPi.org](http://www.raspberrypi.org/) and [more](https://www.versioneye.com/php/erusev:parsedown/references). diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 289a215..4b6eb9a 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -2,7 +2,7 @@ - test/Test.php + test/ParsedownTest.php test/commonmark/CommonMarkTest.php diff --git a/test/ParsedownTest.php b/test/ParsedownTest.php new file mode 100644 index 0000000..d094f2d --- /dev/null +++ b/test/ParsedownTest.php @@ -0,0 +1,139 @@ +dirs = $this->initDirs(); + $this->Parsedown = $this->initParsedown(); + + parent::__construct($name, $data, $dataName); + } + + private $dirs, $Parsedown; + + /** + * @return array + */ + protected function initDirs() + { + $dirs []= dirname(__FILE__).'/data/'; + + return $dirs; + } + + /** + * @return Parsedown + */ + protected function initParsedown() + { + $Parsedown = new Parsedown(); + + return $Parsedown; + } + + /** + * @dataProvider data + * @param $test + * @param $dir + */ + function test_($test, $dir) + { + $markdown = file_get_contents($dir . $test . '.md'); + + $expectedMarkup = file_get_contents($dir . $test . '.html'); + + $expectedMarkup = str_replace("\r\n", "\n", $expectedMarkup); + $expectedMarkup = str_replace("\r", "\n", $expectedMarkup); + + $actualMarkup = $this->Parsedown->text($markdown); + + $this->assertEquals($expectedMarkup, $actualMarkup); + } + + function data() + { + $data = array(); + + foreach ($this->dirs as $dir) + { + $Folder = new DirectoryIterator($dir); + + foreach ($Folder as $File) + { + /** @var $File DirectoryIterator */ + + if ( ! $File->isFile()) + { + continue; + } + + $filename = $File->getFilename(); + + $extension = pathinfo($filename, PATHINFO_EXTENSION); + + if ($extension !== 'md') + { + continue; + } + + $basename = $File->getBasename('.md'); + + if (file_exists($dir . $basename . '.html')) + { + $data []= array($basename, $dir); + } + } + } + + return $data; + } + + public function test_no_markup() + { + $markdownWithHtml = <<_content_ + +sparse: + +
+
+_content_ +
+
+ +paragraph + + + +comment + + +MARKDOWN_WITH_MARKUP; + + $expectedHtml = <<<div>content</div>

+

sparse:

+

<div> +<div class="inner"> +content +</div> +</div>

+

paragraph

+

<style type="text/css">

+
p {
+    color: red;
+}
+

</style>

+

comment

+

<!-- html comment -->

+EXPECTED_HTML; + $parsedownWithNoMarkup = new Parsedown(); + $parsedownWithNoMarkup->setMarkupEscaped(true); + $this->assertEquals($expectedHtml, $parsedownWithNoMarkup->text($markdownWithHtml)); + } +} diff --git a/test/Test.php b/test/Test.php deleted file mode 100644 index 5171d84..0000000 --- a/test/Test.php +++ /dev/null @@ -1,65 +0,0 @@ -dataDir = dirname(__FILE__).'/data/'; - - parent::__construct($name, $data, $dataName); - } - - private $dataDir; - - /** - * @dataProvider data - */ - function test_($filename) - { - $markdown = file_get_contents($this->dataDir . $filename . '.md'); - - $expectedMarkup = file_get_contents($this->dataDir . $filename . '.html'); - - $expectedMarkup = str_replace("\r\n", "\n", $expectedMarkup); - $expectedMarkup = str_replace("\r", "\n", $expectedMarkup); - - $actualMarkup = Parsedown::instance()->text($markdown); - - $this->assertEquals($expectedMarkup, $actualMarkup); - } - - function data() - { - $data = array(); - - $Folder = new DirectoryIterator($this->dataDir); - - foreach ($Folder as $File) - { - /** @var $File DirectoryIterator */ - - if ( ! $File->isFile()) - { - continue; - } - - $filename = $File->getFilename(); - - $extension = pathinfo($filename, PATHINFO_EXTENSION); - - if ($extension !== 'md') - { - continue; - } - - $basename = $File->getBasename('.md'); - - if (file_exists($this->dataDir . $basename . '.html')) - { - $data []= array($basename); - } - } - - return $data; - } -} diff --git a/test/data/escaping.html b/test/data/escaping.html index 64676cb..ab1c41f 100644 --- a/test/data/escaping.html +++ b/test/data/escaping.html @@ -1,4 +1,6 @@

escaped *emphasis*.

escaped \*emphasis\* in a code span

escaped \*emphasis\* in a code block
-

\ ` * _ { } [ ] ( ) > # + - . !

\ No newline at end of file +

\ ` * _ { } [ ] ( ) > # + - . !

+

one_two one_two

+

one*two one*two

\ No newline at end of file diff --git a/test/data/escaping.md b/test/data/escaping.md index 164039f..9f174e9 100644 --- a/test/data/escaping.md +++ b/test/data/escaping.md @@ -4,4 +4,8 @@ escaped \*emphasis\*. escaped \*emphasis\* in a code block -\\ \` \* \_ \{ \} \[ \] \( \) \> \# \+ \- \. \! \ No newline at end of file +\\ \` \* \_ \{ \} \[ \] \( \) \> \# \+ \- \. \! + +_one\_two_ __one\_two__ + +*one\*two* **one\*two** \ No newline at end of file diff --git a/test/data/sparse_html.html b/test/data/sparse_html.html new file mode 100644 index 0000000..9e89627 --- /dev/null +++ b/test/data/sparse_html.html @@ -0,0 +1,8 @@ +
+line 1 + +line 2 +line 3 + +line 4 +
\ No newline at end of file diff --git a/test/data/sparse_html.md b/test/data/sparse_html.md new file mode 100644 index 0000000..9e89627 --- /dev/null +++ b/test/data/sparse_html.md @@ -0,0 +1,8 @@ +
+line 1 + +line 2 +line 3 + +line 4 +
\ No newline at end of file