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

first commit

This commit is contained in:
Emanuil 2013-07-10 23:22:16 +03:00
commit cbc76e5c31
54 changed files with 1474 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.DS_Store
nbproject

21
LICENSE.txt Normal file
View File

@ -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.

626
Parsedown.php Executable file
View File

@ -0,0 +1,626 @@
<?php
#
#
# Parsedown
# http://parsedown.org
#
# (c) Emanuil Rusev
# http://erusev.com
#
# For the full license information, please view the LICENSE file that was
# distributed with this source code.
#
#
class Parsedown
{
#
# Multiton (http://en.wikipedia.org/wiki/Multiton_pattern)
#
static function instance($name = 'default')
{
if (isset(self::$instances[$name]))
return self::$instances[$name];
$instance = new Parsedown();
self::$instances[$name] = $instance;
return $instance;
}
private static $instances = array();
#
# Fields
#
private $reference_map = array();
private $escape_sequence_map = array();
#
# Public Methods
#
function parse($text)
{
# Removes UTF-8 BOM and marker characters.
$text = preg_replace('{^\xEF\xBB\xBF|\x1A}', '', $text);
# Removes \r characters.
$text = str_replace("\r", '', $text);
# Replaces tabs with spaces.
$text = str_replace("\t", ' ', $text);
# Encodes escape sequences.
if (strpos($text, '\\') !== FALSE)
{
$escape_sequences = array('\\\\', '\`', '\*', '\_', '\{', '\}', '\[', '\]', '\(', '\)', '\>', '\#', '\+', '\-', '\.', '\!');
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 .= '<li>';
if (strpos($list_item, "\n\n")) # sparse
{
$list_item = trim($list_item, "\n");
if (strpos($list_item, "\n\n"))
{
$list_item = preg_replace('/^[ ]{0,4}/m', '', $list_item);
$list_item = $this->parse_blocks($list_item);
}
else
{
$list_item = $this->parse_lines($list_item);
}
$markup .= "\n".$list_item;
}
else # dense
{
$list_item = trim($list_item, "\n");
$list_item = strpos($list_item, "\n")
? $this->parse_lines($list_item)
: $this->parse_inline_elements($list_item);
$markup .= $list_item;
}
$markup .= '</li>'."\n";
}
$markup .= '</'.$list_type.'>'."\n";
unset($list);
}
# Code Block
if (isset($block) and strlen($block) > 4 and $block[0] === ' ' and $block[1] === ' ' and $block[2] === ' ' and $block[3] === ' ')
{
if (isset($code_block))
{
$code_block .= "\n\n".$block;
}
else
{
$code_block = $block;
}
unset($block);
}
elseif (isset($code_block))
{
$code_block_text = preg_replace('/^[ ]{4}/m', '', $code_block);
$code_block_text = htmlentities($code_block_text, ENT_NOQUOTES);
# Decodes encoded escape sequences if present.
strpos($code_block_text, "\x1A\\") !== FALSE and $code_block_text = strtr($code_block_text, $this->escape_sequence_map);
$markup .= '<pre><code>'.$code_block_text.'</code></pre>'."\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 .= '<h'.$level.'>'.$heading.'</h'.$level.'>'."\n";
continue;
}
# Quote Block
if (isset($block) and preg_match('/^[ ]{0,3}>/', $block))
{
$block = preg_replace('/^[ ]{0,3}>[ ]?/m', '', $block);
$block = $this->parse_blocks($block);
$markup .= '<blockquote>'."\n".$block.'</blockquote>'."\n";
continue;
}
# Horizontal Line
if (isset($block) and preg_match('/^[ ]{0,3}([-*_])([ ]{0,2}\1){2,}$/', $block))
{
$markup .= '<hr />'."\n";
continue;
}
# ~
if (isset($quick_block))
{
$block = $quick_block;
unset ($quick_block);
}
#
# Paragraph
if (isset($block))
{
if (strpos($block, "\n"))
{
$markup .= $this->parse_lines($block);
}
else
{
$element_text = $this->parse_inline_elements($block);
$element = '<p>'.$element_text.'</p>'."\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 .= '<li>'.$list_item_text.'</li>'."\n";
}
$markup .= '</'.$list_type.'>'."\n";
unset($list);
}
}
# Quote Block
if (isset($line) and preg_match('/^[ ]*>[ ]?(.*)/', $line, $matches))
{
if (isset($quote))
{
$quote .= "\n".$matches[1];
}
else
{
$quote = $matches[1];
}
unset($line);
}
else
{
if (isset($quote))
{
$quote = $this->parse_blocks($quote);
$markup .= '<blockquote>'."\n".$quote.'</blockquote>'."\n";
unset($quote);
}
}
# Atx Heading
if (isset($atx_heading))
{
$markup .= '<h'.$atx_heading_level.'>'.$atx_heading.'</h'.$atx_heading_level.'>'."\n";
unset($atx_heading);
}
if (isset($line) and $line[0] === '#' and preg_match('/^(#{1,6})[ ]*(.+?)[ ]*#*$/', $line, $matches))
{
$atx_heading_level = strlen($matches[1]);
$atx_heading = $this->parse_inline_elements($matches[2]);
unset($line);
}
# Setext Heading
if (isset($line) and isset($paragraph))
{
$setext_characters = array('=', '-');
foreach ($setext_characters as $index => $setext_character)
{
if ($line[0] === $setext_character and preg_match('/^['.$setext_character.']+[ ]*$/', $line))
{
$atx_heading_level = $index + 1;
$markup .= '<h'.$atx_heading_level.'>'.$paragraph.'</h'.$atx_heading_level.'>'."\n";
unset($paragraph);
unset($line);
continue 2;
}
}
}
# Paragraph
if (isset($quick_line))
{
$line = $quick_line;
unset($quick_line);
}
if (isset($line))
{
substr($line, -2) === ' '
and $line = substr($line, 0, -2)
and $line .= '<br/>';
if (isset($paragraph))
{
$paragraph .= "\n".$line;
}
else
{
$paragraph = $line;
}
}
else
{
if (isset($paragraph))
{
$element_text = $this->parse_inline_elements($paragraph);
$markup .= '<p>'.$element_text.'</p>'."\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 = '<code>'.$element_text.'</code>';
# 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 = '<img alt="'.$matches[2].'" src="'.$url.'">';
}
else # anchor
{
$element_text = $this->parse_inline_elements($matches[2]);
$element = '<a href="'.$url.'">'.$element_text.'</a>';
}
# ~
$code = "\x1A".'$'.$index;
$text = str_replace($matches[0], $code, $text);
$map[$code] = $element;
$index ++;
}
}
}
# Inline Link / Image
if (strpos($text, ']') !== FALSE and preg_match_all('/(!?)\[(.*?)\][ ]?\((.*?)\)/', $text, $matches, PREG_SET_ORDER)) # inline
{
foreach ($matches as $matches)
{
if ($matches[1]) # image
{
$element = '<img alt="'.$matches[2].'" src="'.$matches[3].'">';
}
else
{
$element_text = $this->parse_inline_elements($matches[2]);
$element = '<a href="'.$matches[3].'">'.$element_text.'</a>';
}
$element_text = $this->parse_inline_elements($matches[1]);
# ~
$code = "\x1A".'$'.$index;
$text = str_replace($matches[0], $code, $text);
$map[$code] = $element;
$index ++;
}
}
if (strpos($text, '<') !== FALSE and preg_match_all('/<((https?|ftp|dict):[^\^\s]+?)>/i', $text, $matches, PREG_SET_ORDER))
{
foreach ($matches as $matches)
{
$element = '<a href=":href">:text</a>';
$element = str_replace(':text', $matches[1], $element);
$element = str_replace(':href', $matches[1], $element);
# ~
$code = "\x1A".'$'.$index;
$text = str_replace($matches[0], $code, $text);
$map[$code] = $element;
$index ++;
}
}
if (strpos($text, '*') !== FALSE)
{
$text = preg_replace('/\*{2}(.*?)\*{2}/', '<strong>$1</strong>', $text);
$text = preg_replace('/\*(.*?)\*/', '<em>$1</em>', $text);
}
if (strpos($text, '_') !== FALSE)
{
$text = preg_replace('/_{2}(\S.*?\S)_{2}/', '<strong>$1</strong>', $text);
$text = preg_replace('/_(\S.*?\S)_/', '<em>$1</em>', $text);
}
$text = strtr($text, $map);
return $text;
}
}

6
README.md Normal file
View File

@ -0,0 +1,6 @@
# Parsedown PHP
Parsedown is a parser for Markdown. It parses Markdown text the way people do. First, it divides texts into blocks. Then it looks at how these blocks start and how they relate to each other. Finally, it looks for special characters to identify inline elements. As a result, Parsedown is (super) fast, predictable and its (open) source code - easy to read.
[Explorer (demo)](http://parsedown.org/explorer/)
[Tests](http://parsedown.org/tests/)

18
composer.json Normal file
View File

@ -0,0 +1,18 @@
{
"name": "erusev/parsedown",
"description": "Parser for Markdown.",
"keywords": ["markdown", "parser"],
"homepage": "http://parsedown.org",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Emanuil Rusev",
"email": "hello@erusev.com",
"homepage": "http://erusev.com"
}
],
"autoload": {
"psr-0": {"Parsedown": ""}
},
}

7
tests/.htaccess Normal file
View File

@ -0,0 +1,7 @@
RewriteEngine on
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ tests/index.php?$1 [L]

51
tests/index.css Normal file
View File

@ -0,0 +1,51 @@
.page {
margin: 0 auto;
width: 640px;
}
.header {
background: #555;
color: #fff;
}
.odd {
background: #fff;
}
.even {
background: #eee;
}
div.fail {
background: #f55;
}
div.pass {
background: #595;
}
span.fail {
color: #d55;
}
span.pass {
color: #595;
}
/* ~ */
p {
margin: 10px 0;
}
th {
font-weight: normal;
text-align: left;
}
th, td {
border-bottom: 1px solid #ddd;
padding: 5px 10px;
}

12
tests/index.php Normal file
View File

@ -0,0 +1,12 @@
<?php
include '../Parsedown.php';
$page = $_SERVER['QUERY_STRING']
? 'test'
: 'index';
$dir = 'tests/';
include $page.'_controller.php';
include $page.'_view.php';

View File

@ -0,0 +1,46 @@
<?php
$DirectoryIterator = new DirectoryIterator($dir);
$failed_test_count = 0;
foreach ($DirectoryIterator as $Item)
{
if ($Item->isFile() and $Item->getBasename() != '.DS_Store')
{
if ($Item->getExtension() === 'md')
{
$basename = $Item->getBasename('.md');
$markdown = file_get_contents($dir.$basename.'.md');
$expected_markup = file_get_contents($dir.$basename.'.html');
if ( ! $markdown)
continue;
$Parsedown = Parsedown::instance();
$start = microtime(true);
$actual_markup = $Parsedown->parse($markdown);
$time = microtime(true) - $start;
$time = $time * 1000; # ms?
$time = round($time, 2);
$result = $expected_markup === $actual_markup
? 'pass'
: 'fail';
$result === 'fail' and $failed_test_count ++;
$Tests []= array(
'basename' => $basename,
'name' => str_replace('_', ' ', $basename),
'result' => $result,
'time' => $time,
);
}
}
}

54
tests/index_view.php Normal file
View File

@ -0,0 +1,54 @@
<!DOCTYPE html>
<!-- (c) 2009 - 2013 Emanuil Rusev, All rights reserved. -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
<link href="reset.css" rel="stylesheet" type="text/css" />
<link href="index.css" rel="stylesheet" type="text/css" />
<title>Parsedown Test</title>
</head>
<body>
<div style="padding: 50px; width: 500px;">
<h1 style="margin: 0;"><a href="/">Parsedown PHP</a> » Tests</h1>
<br/>
<table>
<tr class="header">
<th style="width: 480px;">Test</th>
<th style="text-align: right; width: 120px">Time</th>
</tr>
<?php foreach ($Tests as $index => $Test): ?>
<tr class="<?= $index % 2 ? 'even' : 'odd' ?>">
<td><a href="/tests/<?= $Test['basename'] ?>"><?= $Test['name'] ?></a> - <span class="<?= $Test['result'] ?>"><?= $Test['result'] ?></span></td>
<td style="text-align: right;"><?= $Test['time'] ?> ms</td>
</tr>
<?php endforeach ?>
</table>
<div class="<?= $failed_test_count ? 'fail' : 'pass' ?>" style="border-top: 1px solid #555; color: #fff; margin-top: 1px; padding:5px 10px;">
<?php if ($failed_test_count): ?>
<?= $failed_test_count ?> tests failed.
<?php else: ?>
All <?= count($Tests) ?> tests passed.
<?php endif ?>
</div>
</div>
</body>
</html>

108
tests/reset.css Normal file
View File

@ -0,0 +1,108 @@
/*
*
*
*
*/
a {
color: #159;
outline: none;
text-decoration: none;
}
a img {
border: none;
}
abbr {
border-bottom: 1px solid #ddd;
cursor: help;
padding: 2px 3px;
}
body {
background: #ddd;
color: #333;
font-family: Verdana, Sans-serif;
font-size: 14px;
height: 100%;
line-height: 20px;
margin: 0;
padding: 0;
}
blockquote {
background: #eee;
margin: 0 0 10px 0;
padding: 10px 10px 1px 10px;
}
form {
margin: 0;
padding: 0;
}
h1, h2, h3, h4, h5, h6 {
font-family: Georgia, "Times New Roman", Times, serif;
font-weight: normal;
letter-spacing: 1px;
margin: 20px 0;
}
h1 {
line-height: 30px;
}
html {
height: 100%;
margin: 0;
padding: 0;
overflow-y: scroll;
}
img {
outline: none;
}
input {
font-family: Verdana, Sans-serif;
font-size: 14px;
line-height: 20px;
margin: 0;
}
object {
outline: none;
}
p {
margin-top: 0;
margin-bottom: 10px;
}
select {
font-family: Verdana, Sans-serif;
font-size: 14px;
/* Makes for the same height as <input>. */
height: 40px;
margin: 0;
}
table {
border-spacing: 0;
}
textarea {
background: #fff;
font-family: Verdana, Sans-serif;
font-size: 14px;
line-height: 20px;
margin: 0;
padding: 9px;
width: 280px;
}
ul {
list-style-type: square;
}

58
tests/test.css Normal file
View File

@ -0,0 +1,58 @@
/*
*
* ...
*
*/
tr.header td {
background: #333;
color: #fff;
padding: 20px;
}
tr.header a {
color: #fff;
text-decoration: underline;
}
tr.body td {
background: #fff;
padding: 20px;
width: 35%;
}
tr.footer td {
background: #fff;
border-top: 1px solid #999;
padding: 10px 20px;
}
/* ~ */
tr.fail td {
background: #f55;
}
tr.pass td {
background: #5d5;
}
/* ~ */
code {
font-family: Source Code Pro, Monaco, monospace;
}
pre {
margin: 0;
white-space: -moz-pre-wrap; /* Mozilla, supported since 1999 */
white-space: -pre-wrap; /* Opera */
white-space: -o-pre-wrap; /* Opera */
white-space: pre-wrap; /* CSS3 - Text module (Candidate Recommendation) http://www.w3.org/TR/css3-text/#white-space */
word-wrap: break-word; /* IE 5.5+ */
}
span.tag {
color: #b19;
}

27
tests/test_controller.php Normal file
View File

@ -0,0 +1,27 @@
<?php
$test = $_SERVER['QUERY_STRING'];
preg_match('/^\w+$/', $test) or die('illegal test name');
$md_file = $dir.$test.'.md';
$mu_file = $dir.$test.'.html';
file_exists($md_file) or die("$md_file not found");
file_exists($mu_file) or die("$mu_file not found");
$md = file_get_contents($md_file);
$expected_mu = file_get_contents($mu_file);
$actual_mu = Parsedown::instance()->parse($md);
$result = $expected_mu === $actual_mu
? 'pass'
: 'fail';
$md = htmlentities($md, ENT_NOQUOTES);
$expected_mu = htmlentities($expected_mu, ENT_NOQUOTES);
$actual_mu = htmlentities($actual_mu, ENT_NOQUOTES);
$name = str_replace('_', ' ', $test);
$name = ucwords($name);

62
tests/test_view.php Normal file
View File

@ -0,0 +1,62 @@
<!DOCTYPE html>
<!-- (c) 2009 - 2013 Emanuil Rusev, All rights reserved. -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
<link href="reset.css" rel="stylesheet" type="text/css" />
<link href="test.css" rel="stylesheet" type="text/css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.js" type="text/javascript"></script>
<script src="http://code.jquery.com/jquery-2.0.0.min.js" type="text/javascript"></script>
<title><?= $name ?> &laquo; Parsedown Test</title>
</head>
<body onload="prettyPrint();">
<table style="width: 100%; height: 100%;">
<tr class="<?= $result ?>">
<td colspan="3"></td>
</tr>
<tr class="header">
<td colspan="2"><a href="/">Parsedown PHP</a> » <a href=".">Tests</a> » <?= $name ?></td>
<td style="text-align: right;">
<form action="http://parsedown.org<?= $_SERVER['SERVER_NAME'] === 'parsedown.org.local' ? '.local' : '' ?>/explorer/" method="post">
<input type="hidden" name="text" />
<a id="explorer" href="">Open in Explorer</a>
</form>
</td>
</tr>
<tr class="body" style="height: 100%; vertical-align: top;">
<td style="background: #eee; width: 30%;">
<pre id="md" style="word-wrap: break-word;"><?= $md ?></pre>
</td>
<td><pre class="prettyprint"><code><?= $expected_mu ?></code></pre></td>
<td><pre class="prettyprint"><code><?= $actual_mu ?></code></pre></td>
</tr>
<tr class="footer">
<td style="background: #eee;">Markdown</td>
<td>Expected Markup</td>
<td>Actual Markup</td>
</tr>
</table>
<script type="text/javascript">
$('#explorer').click(function(e) {
$('input[name=text]').val($('#md').text());
$('form').submit();
return false;
});
</script>
</body>
</html>

View File

@ -0,0 +1,2 @@
<h1>This is an h1</h1>
<h2>This is an h2</h2>

View File

@ -0,0 +1,3 @@
# This is an h1
## This is an h2

View File

@ -0,0 +1 @@
<p><a href="http://example.com">http://example.com</a></p>

View File

@ -0,0 +1 @@
<http://example.com>

View File

@ -0,0 +1,25 @@
<p>Here's a regular blockquote:</p>
<blockquote>
<p>This is a blockquote.</p>
</blockquote>
<p>Here's one with no space after the ">":</p>
<blockquote>
<p>This is a blockquote.</p>
</blockquote>
<p>Here's one with multiple paragraphs:</p>
<blockquote>
<p>This is line one.</p>
<p>This is line two.</p>
</blockquote>
<p>Here's one with multiple types of blocks:</p>
<blockquote>
<p>This is a quoted paragraph.</p>
<ul>
<li>This is a list item of a quoted list.</li>
<li>This is another list item.</li>
</ul>
<blockquote>
<p>This is a nested quote block.</p>
</blockquote>
<p>This is another paragraph.</p>
</blockquote>

24
tests/tests/blockquote.md Normal file
View File

@ -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.

View File

@ -0,0 +1,9 @@
<p>Here's a regular code block:</p>
<pre><code>&lt;?php
echo 'Hello World!';
?&gt;</code></pre>
<p>Here's one that holds a list:</p>
<pre><code>- list item
- another list item</code></pre>

13
tests/tests/code_block.md Normal file
View File

@ -0,0 +1,13 @@
Here's a regular code block:
<?php
echo 'Hello World!';
?>
Here's one that holds a list:
- list item
- another list item

View File

@ -0,0 +1 @@
<p>This is a <code>code span</code>.</p>

1
tests/tests/code_span.md Normal file
View File

@ -0,0 +1 @@
This is a `code span`.

View File

@ -0,0 +1,13 @@
<p>Here's a compound list:</p>
<ul>
<li>
<p>This is the first paragraph of the list item.</p>
<p>This is the second one.</p>
</li>
<li>
<p>This is another list item.</p>
<blockquote>
<p>This is a quote block that belongs to it.</p>
</blockquote>
</li>
</ul>

View File

@ -0,0 +1,10 @@
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.

View File

0
tests/tests/cyrillic.md Normal file
View File

0
tests/tests/email.html Normal file
View File

0
tests/tests/email.md Normal file
View File

View File

@ -0,0 +1,5 @@
<p>Here's <em>an emphasis</em>.</p>
<p>Here's <strong>a strong one</strong>. </p>
<p>Here's <em>an emphasis that uses underscores</em>. </p>
<p>Here's <strong>a strong emphasis that uses underscores</strong>.</p>
<p>This is _ not an emphasis _.</p>

9
tests/tests/emphasis.md Normal file
View File

@ -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 _.

View File

@ -0,0 +1,6 @@
<p>Here's an <em>emphasis</em> and here's an escaped *emphasis*. Here are also an escaped `code span`, escaped [inline link](http://example.com).</p>
<p>Here's <code>an escaped \*emphasis\* inside of a code span</code>.</p>
<p>Here's one inside of a code block:</p>
<pre><code>An escaped \*emphasis\*.</code></pre>
<p>Finally, an escaped reference:</p>
<p>[1]: http://example.com</p>

11
tests/tests/escaping.md Normal file
View File

@ -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

View File

View File

View File

@ -0,0 +1,16 @@
<p>Here's a regular ordered list:</p>
<ol>
<li>one</li>
<li>two</li>
<li>three</li>
</ol>
<p>Here's one with repeating numbers:</p>
<ol>
<li>one</li>
<li>two</li>
</ol>
<p>Here's one with large numbers:</p>
<ol>
<li>one</li>
<li>two</li>
</ol>

View File

@ -0,0 +1,16 @@
Here's a regular ordered list:
1. one
2. two
3. three
Here's one with repeating numbers:
1. one
1. two
Here's one with large numbers:
123. one
123. two

View File

View File

View File

@ -0,0 +1,5 @@
<p>Here's a list that's "inside" a paragraph:</p>
<ul>
<li>list item</li>
<li>another list item</li>
</ul>

View File

@ -0,0 +1,4 @@
Here's a list that's "inside" a paragraph:
- list item
- another list item

View File

@ -0,0 +1,20 @@
<p>Here's a regular quote block:</p>
<blockquote>
<p>Some quoted text.
Here goes some more.</p>
</blockquote>
<p>Here's one with space before lines:</p>
<blockquote>
<p>Some quoted text.
Here goes some more.</p>
</blockquote>
<p>Here's one with no space after >:</p>
<blockquote>
<p>Some quoted text.
Here goes some more.</p>
</blockquote>
<p>Here's one with no > on the second line:</p>
<blockquote>
<p>Some quoted text.
Here goes some more.</p>
</blockquote>

View File

@ -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.

View File

@ -0,0 +1,8 @@
<p>Here's a <a href="http://parsedown.org">reference link</a>.</p>
<p>Here's <a href="http://parsedown.org">one</a> with an alternative syntax.</p>
<p>Here's <a href="http://parsedown.org">one</a> on the next line.</p>
<p>Here's <a href="http://parsedown.org">one</a> on 2 lines.</p>
<p>Here's <a href="http://parsedown.org/tests/">one</a> with a different URL.</p>
<p>Here's <a href="http://parsedown.org">one</a> with a semantic name.</p>
<p>Here's [one][404] with no definition.</p>
<p>Here's an image: <img alt="Markdown Logo" src="https://raw.github.com/dcurtis/markdown-mark/master/png/32x20-solid.png"></p>

View File

@ -0,0 +1,29 @@
Here's a [reference link][1].
[1]: http://parsedown.org
Here's [one] [2] with an alternative syntax.
[2] :http://parsedown.org
Here's [one][3] on the next line.
[3]: http://parsedown.org
Here's [one][4] on 2 lines.
[4]:
http://parsedown.org
Here's [one][5] with a different URL.
[5]: http://parsedown.org/tests/
Here's [one][the website] with a semantic name.
[the website]: http://parsedown.org
Here's [one][404] with no definition.
Here's an image: ![Markdown Logo][image]
[image]: https://raw.github.com/dcurtis/markdown-mark/master/png/32x20-solid.png

View File

@ -0,0 +1,17 @@
<p>Here's a regular list:</p>
<ul>
<li>list item</li>
<li>another list item</li>
<li>3rd list item</li>
</ul>
<p>Here's one with white space around items:</p>
<ul>
<li>list item </li>
<li>another list item </li>
</ul>
<p>Here's one with too much space before items:</p>
<pre><code>- list item
- another list item</code></pre>
<p>Here's one with no space after markers:</p>
<p>-list item
-another list item</p>

View File

@ -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

View File

@ -0,0 +1,6 @@
<h1>Heading 1</h1>
<h2>Heading 2</h2>
<h2>Block Heading</h2>
<p>This is the rest of the block.</p>
<h1>Single "="</h1>
<h2>Single "-"</h2>

View File

@ -0,0 +1,16 @@
Heading 1
=========
Heading 2
---------
Block Heading
-------------
This is the rest of the block.
Single "="
=
Single "-"
-

View File

@ -0,0 +1,14 @@
<p>Here's a list where items are separated by empty lines:</p>
<ul>
<li>
<p>list item</p>
</li>
<li>another list item</li>
</ul>
<p>Here's an ordered one:</p>
<ol>
<li>
<p>item one</p>
</li>
<li>item two</li>
</ol>

View File

@ -0,0 +1,11 @@
Here's a list where items are separated by empty lines:
- list item
- another list item
Here's an ordered one:
1. item one
2. item two

View File

@ -0,0 +1,20 @@
<p>Here's a regular unordered list:</p>
<ul>
<li>list item</li>
<li>another list item</li>
<li>3rd list item</li>
</ul>
<p>Here's one with a variety of markers:</p>
<ul>
<li>hyphen</li>
<li>plus</li>
<li>asterisk</li>
</ul>
<p>Here's one with white space around items:</p>
<ul>
<li>list item </li>
<li>another list item </li>
</ul>
<p>Here's one with no space after markers:</p>
<p>-list item
-another list item</p>

View File

@ -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