From cbc76e5c312fa936bba3a4c6018ed351372e6689 Mon Sep 17 00:00:00 2001 From: Emanuil <4thmail@gmail.com> Date: Wed, 10 Jul 2013 23:22:16 +0300 Subject: [PATCH] first commit --- .gitignore | 2 + LICENSE.txt | 21 + Parsedown.php | 626 ++++++++++++++++++++++++++ README.md | 6 + composer.json | 18 + tests/.htaccess | 7 + tests/index.css | 51 +++ tests/index.php | 12 + tests/index_controller.php | 46 ++ tests/index_view.php | 54 +++ tests/reset.css | 108 +++++ tests/test.css | 58 +++ tests/test_controller.php | 27 ++ tests/test_view.php | 62 +++ tests/tests/atx_heading.html | 2 + tests/tests/atx_heading.md | 3 + tests/tests/automatic_link.html | 1 + tests/tests/automatic_link.md | 1 + tests/tests/blockquote.html | 25 + tests/tests/blockquote.md | 24 + tests/tests/code_block.html | 9 + tests/tests/code_block.md | 13 + tests/tests/code_span.html | 1 + tests/tests/code_span.md | 1 + tests/tests/compound_list.html | 13 + tests/tests/compound_list.md | 10 + tests/tests/cyrillic.html | 0 tests/tests/cyrillic.md | 0 tests/tests/email.html | 0 tests/tests/email.md | 0 tests/tests/emphasis.html | 5 + tests/tests/emphasis.md | 9 + tests/tests/escaping.html | 6 + tests/tests/escaping.md | 11 + tests/tests/inline_link.html | 0 tests/tests/inline_link.md | 0 tests/tests/ordered_list.html | 16 + tests/tests/ordered_list.md | 16 + tests/tests/paragraph_blockquote.html | 0 tests/tests/paragraph_blockquote.md | 0 tests/tests/paragraph_list.html | 5 + tests/tests/paragraph_list.md | 4 + tests/tests/quote_block.html | 20 + tests/tests/quote_block.md | 19 + tests/tests/reference_link.html | 8 + tests/tests/reference_link.md | 29 ++ tests/tests/regular_list.html | 17 + tests/tests/regular_list.md | 20 + tests/tests/setext_heading.html | 6 + tests/tests/setext_heading.md | 16 + tests/tests/sparse_list.html | 14 + tests/tests/sparse_list.md | 11 + tests/tests/unordered_list.html | 20 + tests/tests/unordered_list.md | 21 + 54 files changed, 1474 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE.txt create mode 100755 Parsedown.php create mode 100644 README.md create mode 100644 composer.json create mode 100644 tests/.htaccess create mode 100644 tests/index.css create mode 100644 tests/index.php create mode 100644 tests/index_controller.php create mode 100644 tests/index_view.php create mode 100644 tests/reset.css create mode 100644 tests/test.css create mode 100644 tests/test_controller.php create mode 100644 tests/test_view.php create mode 100644 tests/tests/atx_heading.html create mode 100644 tests/tests/atx_heading.md create mode 100644 tests/tests/automatic_link.html create mode 100644 tests/tests/automatic_link.md create mode 100644 tests/tests/blockquote.html create mode 100644 tests/tests/blockquote.md create mode 100644 tests/tests/code_block.html create mode 100644 tests/tests/code_block.md create mode 100644 tests/tests/code_span.html create mode 100644 tests/tests/code_span.md create mode 100644 tests/tests/compound_list.html create mode 100644 tests/tests/compound_list.md create mode 100644 tests/tests/cyrillic.html create mode 100644 tests/tests/cyrillic.md create mode 100644 tests/tests/email.html create mode 100644 tests/tests/email.md create mode 100644 tests/tests/emphasis.html create mode 100644 tests/tests/emphasis.md create mode 100644 tests/tests/escaping.html create mode 100644 tests/tests/escaping.md create mode 100644 tests/tests/inline_link.html create mode 100644 tests/tests/inline_link.md create mode 100644 tests/tests/ordered_list.html create mode 100644 tests/tests/ordered_list.md create mode 100644 tests/tests/paragraph_blockquote.html create mode 100644 tests/tests/paragraph_blockquote.md create mode 100644 tests/tests/paragraph_list.html create mode 100644 tests/tests/paragraph_list.md create mode 100644 tests/tests/quote_block.html create mode 100644 tests/tests/quote_block.md create mode 100644 tests/tests/reference_link.html create mode 100644 tests/tests/reference_link.md create mode 100644 tests/tests/regular_list.html create mode 100644 tests/tests/regular_list.md create mode 100644 tests/tests/setext_heading.html create mode 100644 tests/tests/setext_heading.md create mode 100644 tests/tests/sparse_list.html create mode 100644 tests/tests/sparse_list.md create mode 100644 tests/tests/unordered_list.html create mode 100644 tests/tests/unordered_list.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..77d3395 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +nbproject \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..1d1a329 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +Copyright 2013 Emanuil Rusev +http://erusev.com + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/Parsedown.php b/Parsedown.php new file mode 100755 index 0000000..733a58a --- /dev/null +++ b/Parsedown.php @@ -0,0 +1,626 @@ +', '\#', '\+', '\-', '\.', '\!'); + + foreach ($escape_sequences as $index => $escape_sequence) + { + if (strpos($text, $escape_sequence) !== FALSE) + { + $code = "\x1A".'\\'.$index; + + $text = str_replace($escape_sequence, $code, $text); + + $this->escape_sequence_map[$code] = $escape_sequence; + } + } + } + + # Extracts link references. + + if (preg_match_all('/^[ ]{0,3}\[(.+)\][ ]?:[ ]*\n?[ ]*(.+)$/m', $text, $matches, PREG_SET_ORDER)) + { + foreach ($matches as $matches) + { + $this->reference_map[$matches[1]] = $matches[2]; + + $text = str_replace($matches[0], '', $text); + } + } + + # ~ + + $text = $this->parse_blocks($text); + + # Decodes escape sequences (leaves out backslashes). + + foreach ($this->escape_sequence_map as $code => $escape_sequence) + { + $text = str_replace($code, $escape_sequence[1], $text); + } + + $text = rtrim($text, "\n"); + + return $text; + } + + # + # Private Methods + # + + private function parse_blocks($text) + { + # Divides text into blocks. + $blocks = preg_split('/\n\s*\n/', $text, -1, PREG_SPLIT_NO_EMPTY); + + # Makes sure compound blocks get rendered. + $blocks []= NULL; + + $markup = ''; + + # Parses blocks. + + foreach ($blocks as $block) + { + if (isset($block) and $block[0] > 'A') + { + $quick_block = $block; + + unset($block); + } + + # List + + if (isset($block) and preg_match('/^([ ]{0,3})(\d+[.]|[*+-])[ ]/', $block, $matches)) # list item + { + if (isset($list)) # subsequent + { + $list .= "\n\n".$block; + } + else # first + { + $list = $block; + $list_indentation = strlen($matches[1]); + + list($list_type, $list_marker_pattern) = ($matches[2] === '-' or $matches[2] === '+' or $matches[2] === '*') + ? array('ul', '[*+-]') + : array('ol', '\d+[.]'); + } + + unset($block); + } + elseif (isset($list) and $block[0] === ' ') # list item block + { + $list .= "\n\n".$block; + + unset($block); + } + elseif (isset($list)) + { + $markup .= '<'.$list_type.'>'."\n"; + + # Of the same type and indentation. + $list_items = preg_split('/^([ ]{'.$list_indentation.'})'.$list_marker_pattern.'[ ]/m', $list, -1, PREG_SPLIT_NO_EMPTY); + + foreach ($list_items as $list_item) + { + $markup .= '
'.$code_block_text.'
'."\n";
+
+ unset($code_block);
+ }
+
+ # Atx Heading
+
+ if (isset($block) and $block[0] === '#' and preg_match('/^(#{1,6})[ ]*(.+?)[ ]*#*$/', $block, $matches))
+ {
+ $level = strlen($matches[1]);
+
+ $heading = $this->parse_inline_elements($matches[2]);
+
+ $markup .= ''."\n".$block.''."\n"; + + continue; + } + + # Horizontal Line + + if (isset($block) and preg_match('/^[ ]{0,3}([-*_])([ ]{0,2}\1){2,}$/', $block)) + { + $markup .= '
'.$element_text.'
'."\n"; + + $markup .= $element; + } + } + } + + return $markup; + } + + private function parse_lines($text) + { + $text = trim($text, "\n"); + + $lines = explode("\n", $text); + + $lines []= NULL; + + $markup = ''; + + foreach ($lines as $line) + { + if (isset($line) and $line === '') + { + unset($line); + } + + # Paragraph + + if (isset($line) and $line[0] > 'A') + { + $quick_line = $line; + + unset($line); + } + + # List + + if (isset($line) and preg_match('/^([ ]*)(\d+[.]|[*+-])[ ](.*)/', $line, $matches)) # list item + { + $list_item_indentation = strlen($matches[1]); + $list_item_type = ($matches[2] === '-' or $matches[2] === '+' or $matches[2] === '*') + ? 'ul' + : 'ol'; + + if (isset($list)) # subsequent + { + if ($list_item_indentation === $list_indentation and $list_item_type === $list_type) + { + # Adds last list item to the list. + $list []= $list_item; + + # Creates a separate list item. + $list_item = $matches[3]; + } + else + { + # Adds line to the current list item. + $list_item .= "\n".$line; + } + } + else # first + { + $list = array(); + $list_indentation = $list_item_indentation; + $list_type = $list_item_type; + + $list_item = $matches[3]; + } + + unset($line); + } + else + { + if (isset($list)) + { + $list []= $list_item; + + $markup .= '<'.$list_type.'>'."\n"; + + foreach ($list as $list_item) + { + $list_item_text = strpos($list_item, "\n") + ? $this->parse_lines($list_item) + : $this->parse_inline_elements($list_item); + + $markup .= ''."\n".$quote.''."\n"; + + unset($quote); + } + } + + # Atx Heading + + if (isset($atx_heading)) + { + $markup .= '
'.$element_text.'
'."\n"; + + unset($paragraph); + } + } + } + + return $markup; + } + + private function parse_inline_elements($text) + { + $map = array(); + + $index = 0; + + # Code Span + + if (strpos($text, '`') !== FALSE and preg_match_all('/`(.+?)`/', $text, $matches, PREG_SET_ORDER)) + { + foreach ($matches as $matches) + { + $element_text = $matches[1]; + $element_text = htmlentities($element_text, ENT_NOQUOTES); + + # Decodes escape sequences. + + $this->escape_sequence_map + and strpos($element_text, "\x1A") !== FALSE + and $element_text = strtr($element_text, $this->escape_sequence_map); + + # Composes element. + + $element = ''.$element_text.'
';
+
+ # Encodes element.
+
+ $code = "\x1A".'$'.$index;
+
+ $text = str_replace($matches[0], $code, $text);
+
+ $map[$code] = $element;
+
+ $index ++;
+ }
+ }
+
+ # Reference(d) Link / Image
+
+ if ($this->reference_map and strpos($text, '[') !== FALSE and preg_match_all('/(!?)\[(.+?)\][ ]?\[(.+?)\]/', $text, $matches, PREG_SET_ORDER))
+ {
+ foreach ($matches as $matches)
+ {
+ if (array_key_exists($matches[3], $this->reference_map))
+ {
+ $url = $this->reference_map[$matches[3]];
+
+ if ($matches[1]) # image
+ {
+ $element = 'Test | +Time | +
---|---|
= $Test['name'] ?> - = $Test['result'] ?> | += $Test['time'] ?> ms | +
+ | ||
Parsedown PHP » Tests » = $name ?> | ++ + | +|
+ = $md ?>+ |
+
|
+
|
+
Markdown | +Expected Markup | +Actual Markup | +
Here's a regular blockquote:
+++This is a blockquote.
+
Here's one with no space after the ">":
+++This is a blockquote.
+
Here's one with multiple paragraphs:
+++This is line one.
+This is line two.
+
Here's one with multiple types of blocks:
++\ No newline at end of file diff --git a/tests/tests/blockquote.md b/tests/tests/blockquote.md new file mode 100644 index 0000000..63e4099 --- /dev/null +++ b/tests/tests/blockquote.md @@ -0,0 +1,24 @@ +Here's a regular blockquote: + +> This is a blockquote. + +Here's one with no space after the ">": + +>This is a blockquote. + +Here's one with multiple paragraphs: + +> This is line one. +> +> This is line two. + +Here's one with multiple types of blocks: + +> This is a quoted paragraph. +> +> - This is a list item of a quoted list. +> - This is another list item. +> +> > This is a nested quote block. +> +> This is another paragraph. \ No newline at end of file diff --git a/tests/tests/code_block.html b/tests/tests/code_block.html new file mode 100644 index 0000000..37b7660 --- /dev/null +++ b/tests/tests/code_block.html @@ -0,0 +1,9 @@ +This is a quoted paragraph.
++
+- This is a list item of a quoted list.
+- This is another list item.
+++This is a nested quote block.
+This is another paragraph.
+
Here's a regular code block:
+<?php
+
+echo 'Hello World!';
+
+?>
+Here's one that holds a list:
+- list item
+- another list item
\ No newline at end of file
diff --git a/tests/tests/code_block.md b/tests/tests/code_block.md
new file mode 100644
index 0000000..bfa4904
--- /dev/null
+++ b/tests/tests/code_block.md
@@ -0,0 +1,13 @@
+Here's a regular code block:
+
+
+
+Here's one that holds a list:
+
+ - list item
+ - another list item
+
diff --git a/tests/tests/code_span.html b/tests/tests/code_span.html
new file mode 100644
index 0000000..9c03025
--- /dev/null
+++ b/tests/tests/code_span.html
@@ -0,0 +1 @@
+This is a code span
.
Here's a compound list:
+This is the first paragraph of the list item.
+This is the second one.
+This is another list item.
+++This is a quote block that belongs to it.
+
Here's an emphasis.
+Here's a strong one.
+Here's an emphasis that uses underscores.
+Here's a strong emphasis that uses underscores.
+This is _ not an emphasis _.
\ No newline at end of file diff --git a/tests/tests/emphasis.md b/tests/tests/emphasis.md new file mode 100644 index 0000000..45b57c0 --- /dev/null +++ b/tests/tests/emphasis.md @@ -0,0 +1,9 @@ +Here's *an emphasis*. + +Here's **a strong one**. + +Here's _an emphasis that uses underscores_. + +Here's __a strong emphasis that uses underscores__. + +This is _ not an emphasis _. \ No newline at end of file diff --git a/tests/tests/escaping.html b/tests/tests/escaping.html new file mode 100644 index 0000000..bcb44b4 --- /dev/null +++ b/tests/tests/escaping.html @@ -0,0 +1,6 @@ +Here's an emphasis and here's an escaped *emphasis*. Here are also an escaped `code span`, escaped [inline link](http://example.com).
+Here's an escaped \*emphasis\* inside of a code span
.
Here's one inside of a code block:
+An escaped \*emphasis\*.
+Finally, an escaped reference:
+[1]: http://example.com
\ No newline at end of file diff --git a/tests/tests/escaping.md b/tests/tests/escaping.md new file mode 100644 index 0000000..8bca463 --- /dev/null +++ b/tests/tests/escaping.md @@ -0,0 +1,11 @@ +Here's an *emphasis* and here's an escaped \*emphasis\*. Here are also an escaped \`code span\`, escaped \[inline link](http://example.com). + +Here's `an escaped \*emphasis\* inside of a code span`. + +Here's one inside of a code block: + + An escaped \*emphasis\*. + +Finally, an escaped reference: + +\[1]: http://example.com \ No newline at end of file diff --git a/tests/tests/inline_link.html b/tests/tests/inline_link.html new file mode 100644 index 0000000..e69de29 diff --git a/tests/tests/inline_link.md b/tests/tests/inline_link.md new file mode 100644 index 0000000..e69de29 diff --git a/tests/tests/ordered_list.html b/tests/tests/ordered_list.html new file mode 100644 index 0000000..81652ac --- /dev/null +++ b/tests/tests/ordered_list.html @@ -0,0 +1,16 @@ +Here's a regular ordered list:
+Here's one with repeating numbers:
+Here's one with large numbers:
+Here's a list that's "inside" a paragraph:
+Here's a regular quote block:
+++Some quoted text. +Here goes some more.
+
Here's one with space before lines:
+++Some quoted text. +Here goes some more.
+
Here's one with no space after >:
+++Some quoted text. +Here goes some more.
+
Here's one with no > on the second line:
++\ No newline at end of file diff --git a/tests/tests/quote_block.md b/tests/tests/quote_block.md new file mode 100644 index 0000000..f01f6ec --- /dev/null +++ b/tests/tests/quote_block.md @@ -0,0 +1,19 @@ +Here's a regular quote block: + +> Some quoted text. +> Here goes some more. + +Here's one with space before lines: + + > Some quoted text. + > Here goes some more. + +Here's one with no space after >: + +>Some quoted text. +>Here goes some more. + +Here's one with no > on the second line: + +> Some quoted text. +Here goes some more. \ No newline at end of file diff --git a/tests/tests/reference_link.html b/tests/tests/reference_link.html new file mode 100644 index 0000000..84ed9e5 --- /dev/null +++ b/tests/tests/reference_link.html @@ -0,0 +1,8 @@ +Some quoted text. +Here goes some more.
+
Here's a reference link.
+Here's one with an alternative syntax.
+Here's one on the next line.
+Here's one on 2 lines.
+Here's one with a different URL.
+Here's one with a semantic name.
+Here's [one][404] with no definition.
+Here's an image:
Here's a regular list:
+Here's one with white space around items:
+Here's one with too much space before items:
+- list item
+- another list item
+Here's one with no space after markers:
+-list item +-another list item
\ No newline at end of file diff --git a/tests/tests/regular_list.md b/tests/tests/regular_list.md new file mode 100644 index 0000000..bde80ee --- /dev/null +++ b/tests/tests/regular_list.md @@ -0,0 +1,20 @@ +Here's a regular list: + +- list item +- another list item +- 3rd list item + +Here's one with white space around items: + + - list item + - another list item + +Here's one with too much space before items: + + - list item + - another list item + +Here's one with no space after markers: + +-list item +-another list item \ No newline at end of file diff --git a/tests/tests/setext_heading.html b/tests/tests/setext_heading.html new file mode 100644 index 0000000..7692278 --- /dev/null +++ b/tests/tests/setext_heading.html @@ -0,0 +1,6 @@ +This is the rest of the block.
+Here's a list where items are separated by empty lines:
+list item
+Here's an ordered one:
+item one
+Here's a regular unordered list:
+Here's one with a variety of markers:
+Here's one with white space around items:
+Here's one with no space after markers:
+-list item +-another list item
\ No newline at end of file diff --git a/tests/tests/unordered_list.md b/tests/tests/unordered_list.md new file mode 100644 index 0000000..0e1717d --- /dev/null +++ b/tests/tests/unordered_list.md @@ -0,0 +1,21 @@ +Here's a regular unordered list: + +- list item +- another list item +- 3rd list item + +Here's one with a variety of markers: + +- hyphen ++ plus +* asterisk + +Here's one with white space around items: + + - list item + - another list item + +Here's one with no space after markers: + +-list item +-another list item \ No newline at end of file