This commit is contained in:
2020-02-01 16:47:12 +07:00
commit 4c619ad6e6
16739 changed files with 3329179 additions and 0 deletions

6
vendor/cebe/markdown/.gitattributes vendored Normal file
View File

@@ -0,0 +1,6 @@
* text=auto
/.gitattributes
/.gitignore
/.scrutinizer.yml
/.travis.yml

4
vendor/cebe/markdown/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
/.idea/
composer.lock
/vendor
README.html

6
vendor/cebe/markdown/.scrutinizer.yml vendored Normal file
View File

@@ -0,0 +1,6 @@
imports:
- php
tools:
external_code_coverage:
timeout: 600 # Timeout in seconds.

41
vendor/cebe/markdown/.travis.yml vendored Normal file
View File

@@ -0,0 +1,41 @@
language: php
php:
- 5.4
- 5.5
- 5.6
- 7.0
- 7.1
- nightly
- hhvm
# faster builds on new travis setup not using sudo
sudo: false
# travis does not support HHVM on other platforms, choosing trusty
dist: trusty
# cache composer cache
cache:
directories:
- $HOME/.composer/cache
# run build against hhvm but allow them to fail
# http://docs.travis-ci.com/user/build-configuration/#Rows-That-are-Allowed-To-Fail
matrix:
fast_finish: true
allow_failures:
- php: nightly
install:
- composer self-update && composer --version
- composer install --prefer-dist
script:
- vendor/bin/phpunit --verbose --coverage-clover=coverage.clover
# test against standard markdown spec
# - git clone https://github.com/jgm/stmd && cd stmd && perl runtests.pl spec.txt ../bin/markdown
after_script:
- wget https://scrutinizer-ci.com/ocular.phar
- php ocular.phar code-coverage:upload --format=php-clover coverage.clover

84
vendor/cebe/markdown/CHANGELOG.md vendored Normal file
View File

@@ -0,0 +1,84 @@
CHANGELOG
=========
Version 1.1.2 on 16. Jul 2017
-----------------------------
- #126 Fixed crash on empty lines that extend a lazy list
- #128 Fix table renderer which including default alignment (@tanakahisateru)
- #129 Use given encoded URL if decoded URL text looks insecure, e.g. uses broken UTF-8 (@tanakahisateru)
- Added a workaround for a [PHP bug](https://bugs.php.net/bug.php?id=45735) which exists in versions `<` 7.0, where `preg_match()` causes a segfault
on [catastropic backtracking][] in emph/strong parsing.
[catastropic backtracking]: http://www.regular-expressions.info/catastrophic.html
Version 1.1.1 on 14. Sep 2016
-----------------------------
- #112 Fixed parsing for custom self-closing HTML tags
- #113 improve extensibility by making `prepareMarkers()` protected and add `parseBlock()` method
- #114 better handling of continued inline HTML in paragraphs
Version 1.1.0 on 06. Mar. 2015
------------------------------
- improve compatibility with github flavored markdown
- #64 fixed some rendering issue with emph and strong
- #56 trailing and leading spaces in a link are now ignored
- fixed various issues with table rendering
- #98 Fix PHP fatal error when maximumNestingLevel was reached (@tanakahisateru)
- refactored nested and lazy list handling, improved overall list rendering consistency
- Lines containing "0" where skipped or considered empty in some cases (@tanakahisateru)
- #54 escape characters are now also considered inside of urls
Version 1.0.1 on 25. Okt. 2014
------------------------------
- Fixed the `bin/markdown` script to work with composer autoloader (c497bada0e15f61873ba6b2e29f4bb8b3ef2a489)
- #74 fixed a bug that caused a bunch of broken characters when non-ASCII input was given. Parser now handles UTF-8 input correctly. Other encodings are currently untested, UTF-8 is recommended.
Version 1.0.0 on 12. Okt. 2014
------------------------------
This is the first stable release of version 1.0 which is incompatible to the 0.9.x branch regarding the internal API which is used when extending the Markdown parser. The external API has no breaking changes. The rendered Markdown however has changed in some edge cases and some rendering issues have been fixed.
The parser got a bit slower compared to earlier versions but is able to parse Markdown more accurately and uses an abstract syntax tree as the internal representation of the parsed text which allows extensions to work with the parsed Markdown in many ways including rendering as other formats than HTML.
For more details about the changes see the [release message of 1.0.0-rc](https://github.com/cebe/markdown/releases/tag/1.0.0-rc).
You can try it out on the website: <http://markdown.cebe.cc/try>
The parser is now also regsitered on the [Babelmark 2 page](http://johnmacfarlane.net/babelmark2/?normalize=1&text=Hello+**World**!) by [John MacFarlane](http://johnmacfarlane.net/) which you can use to compare Markdown output of different parsers.
Version 1.0.0-rc on 10. Okt. 2014
---------------------------------
- #21 speed up inline parsing using [strpbrk](http://www.php.net/manual/de/function.strpbrk.php) about 20% speedup compared to parsing before.
- #24 CLI script now sends all error output to stderr instead of stdout
- #25 Added partial support for the Markdown Extra flavor
- #10 GithubMarkdown is now fully supported including tables
- #67 All Markdown classes are now composed out of php traits
- #67 The way to extend markdown has changed due to the introduction of an abstract syntax tree. See https://github.com/cebe/markdown/commit/dd2d0faa71b630e982d6651476872469b927db6d for how it changes or read the new README.
- Introduced an abstract syntax tree as an intermediate representation between parsing steps.
This not only fixes some issues with nested block elements but also allows manipulation of the markdown
before rendering.
- This version also fixes serveral rendering issues.
Version 0.9.2 on 18. Feb. 2014
------------------------------
- #27 Fixed some rendering problems with block elements not separated by newlines
Version 0.9.1 on 18. Feb. 2014
------------------------------
Fixed an issue with inline markers that begin with the same character e.g. `[` and `[[`.
Version 0.9.0 on 18. Feb. 2014
------------------------------
The initial release.
- Complete implementation of the original Markdown spec
- GFM without tables
- a command line tool for markdown parsing

36
vendor/cebe/markdown/CONTRIBUTING.md vendored Normal file
View File

@@ -0,0 +1,36 @@
Contributing
============
First of all, **thank you** for contributing, **you are awesome**! :)
If you have an idea or found a bug, please [open an issue](https://github.com/cebe/markdown/issues/new) on github.
If you want to contribute code, there a few rules to follow:
- I am following a code style that is basically [PSR-2](http://www.php-fig.org/psr/2/) but with TABS indentation (yes, I really do that ;) ).
I am not going to nit-pick on all the details about the code style but indentation is a must. The important part is that code is readable.
Methods should be documented using phpdoc style.
- All code must be covered by tests so if you fix a bug or add a feature, please include a test case for it. See below on how that works.
- If you add a feature it should be documented.
- Also, while creating your Pull Request on GitHub, please write a description
which gives the context and/or explains why you are creating it.
Thank you very much!
Running the tests
-----------------
The Markdown parser classes are tested with [PHPUnit](https://phpunit.de/). For each test case there is a set of files in
the subfolders of the `/tests` folder. The result of the parser is tested with an input and an output file respectively
where the input file contains the Markdown and the output file contains the expected HTML.
You can run the tests after initializing the lib with composer(`composer install`) with the following command:
vendor/bin/phpunit
To create a new test case, create a `.md` file a`.html` with the same base name in the subfolders of
the `/tests` directory. See existing files for examples.

114
vendor/cebe/markdown/GithubMarkdown.php vendored Normal file
View File

@@ -0,0 +1,114 @@
<?php
/**
* @copyright Copyright (c) 2014 Carsten Brandt
* @license https://github.com/cebe/markdown/blob/master/LICENSE
* @link https://github.com/cebe/markdown#readme
*/
namespace cebe\markdown;
/**
* Markdown parser for github flavored markdown.
*
* @author Carsten Brandt <mail@cebe.cc>
*/
class GithubMarkdown extends Markdown
{
// include block element parsing using traits
use block\TableTrait;
use block\FencedCodeTrait;
// include inline element parsing using traits
use inline\StrikeoutTrait;
use inline\UrlLinkTrait;
/**
* @var boolean whether to interpret newlines as `<br />`-tags.
* This feature is useful for comments where newlines are often meant to be real new lines.
*/
public $enableNewlines = false;
/**
* @inheritDoc
*/
protected $escapeCharacters = [
// from Markdown
'\\', // backslash
'`', // backtick
'*', // asterisk
'_', // underscore
'{', '}', // curly braces
'[', ']', // square brackets
'(', ')', // parentheses
'#', // hash mark
'+', // plus sign
'-', // minus sign (hyphen)
'.', // dot
'!', // exclamation mark
'<', '>',
// added by GithubMarkdown
':', // colon
'|', // pipe
];
/**
* Consume lines for a paragraph
*
* Allow headlines, lists and code to break paragraphs
*/
protected function consumeParagraph($lines, $current)
{
// consume until newline
$content = [];
for ($i = $current, $count = count($lines); $i < $count; $i++) {
$line = $lines[$i];
if ($line === ''
|| ltrim($line) === ''
|| !ctype_alpha($line[0]) && (
$this->identifyQuote($line, $lines, $i) ||
$this->identifyFencedCode($line, $lines, $i) ||
$this->identifyUl($line, $lines, $i) ||
$this->identifyOl($line, $lines, $i) ||
$this->identifyHr($line, $lines, $i)
)
|| $this->identifyHeadline($line, $lines, $i))
{
break;
} elseif ($this->identifyCode($line, $lines, $i)) {
// possible beginning of a code block
// but check for continued inline HTML
// e.g. <img src="file.jpg"
// alt="some alt aligned with src attribute" title="some text" />
if (preg_match('~<\w+([^>]+)$~s', implode("\n", $content))) {
$content[] = $line;
} else {
break;
}
} else {
$content[] = $line;
}
}
$block = [
'paragraph',
'content' => $this->parseInline(implode("\n", $content)),
];
return [$block, --$i];
}
/**
* @inheritdocs
*
* Parses a newline indicated by two spaces on the end of a markdown line.
*/
protected function renderText($text)
{
if ($this->enableNewlines) {
$br = $this->html5 ? "<br>\n" : "<br />\n";
return strtr($text[1], [" \n" => $br, "\n" => $br]);
} else {
return parent::renderText($text);
}
}
}

21
vendor/cebe/markdown/LICENSE vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Carsten Brandt
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.

128
vendor/cebe/markdown/Markdown.php vendored Normal file
View File

@@ -0,0 +1,128 @@
<?php
/**
* @copyright Copyright (c) 2014 Carsten Brandt
* @license https://github.com/cebe/markdown/blob/master/LICENSE
* @link https://github.com/cebe/markdown#readme
*/
namespace cebe\markdown;
/**
* Markdown parser for the [initial markdown spec](http://daringfireball.net/projects/markdown/syntax).
*
* @author Carsten Brandt <mail@cebe.cc>
*/
class Markdown extends Parser
{
// include block element parsing using traits
use block\CodeTrait;
use block\HeadlineTrait;
use block\HtmlTrait {
parseInlineHtml as private;
}
use block\ListTrait {
// Check Ul List before headline
identifyUl as protected identifyBUl;
consumeUl as protected consumeBUl;
}
use block\QuoteTrait;
use block\RuleTrait {
// Check Hr before checking lists
identifyHr as protected identifyAHr;
consumeHr as protected consumeAHr;
}
// include inline element parsing using traits
use inline\CodeTrait;
use inline\EmphStrongTrait;
use inline\LinkTrait;
/**
* @var boolean whether to format markup according to HTML5 spec.
* Defaults to `false` which means that markup is formatted as HTML4.
*/
public $html5 = false;
/**
* @var array these are "escapeable" characters. When using one of these prefixed with a
* backslash, the character will be outputted without the backslash and is not interpreted
* as markdown.
*/
protected $escapeCharacters = [
'\\', // backslash
'`', // backtick
'*', // asterisk
'_', // underscore
'{', '}', // curly braces
'[', ']', // square brackets
'(', ')', // parentheses
'#', // hash mark
'+', // plus sign
'-', // minus sign (hyphen)
'.', // dot
'!', // exclamation mark
'<', '>',
];
/**
* @inheritDoc
*/
protected function prepare()
{
// reset references
$this->references = [];
}
/**
* Consume lines for a paragraph
*
* Allow headlines and code to break paragraphs
*/
protected function consumeParagraph($lines, $current)
{
// consume until newline
$content = [];
for ($i = $current, $count = count($lines); $i < $count; $i++) {
$line = $lines[$i];
// a list may break a paragraph when it is inside of a list
if (isset($this->context[1]) && $this->context[1] === 'list' && !ctype_alpha($line[0]) && (
$this->identifyUl($line, $lines, $i) || $this->identifyOl($line, $lines, $i))) {
break;
}
if ($line === '' || ltrim($line) === '' || $this->identifyHeadline($line, $lines, $i)) {
break;
} elseif ($line[0] === "\t" || $line[0] === " " && strncmp($line, ' ', 4) === 0) {
// possible beginning of a code block
// but check for continued inline HTML
// e.g. <img src="file.jpg"
// alt="some alt aligned with src attribute" title="some text" />
if (preg_match('~<\w+([^>]+)$~s', implode("\n", $content))) {
$content[] = $line;
} else {
break;
}
} else {
$content[] = $line;
}
}
$block = [
'paragraph',
'content' => $this->parseInline(implode("\n", $content)),
];
return [$block, --$i];
}
/**
* @inheritdocs
*
* Parses a newline indicated by two spaces on the end of a markdown line.
*/
protected function renderText($text)
{
return str_replace(" \n", $this->html5 ? "<br>\n" : "<br />\n", $text[1]);
}
}

250
vendor/cebe/markdown/MarkdownExtra.php vendored Normal file
View File

@@ -0,0 +1,250 @@
<?php
namespace cebe\markdown;
use cebe\markdown\block\TableTrait;
// work around https://github.com/facebook/hhvm/issues/1120
defined('ENT_HTML401') || define('ENT_HTML401', 0);
/**
* Markdown parser for the [markdown extra](http://michelf.ca/projects/php-markdown/extra/) flavor.
*
* @author Carsten Brandt <mail@cebe.cc>
* @license https://github.com/cebe/markdown/blob/master/LICENSE
* @link https://github.com/cebe/markdown#readme
*/
class MarkdownExtra extends Markdown
{
// include block element parsing using traits
use block\TableTrait;
use block\FencedCodeTrait;
// include inline element parsing using traits
// TODO
/**
* @var bool whether special attributes on code blocks should be applied on the `<pre>` element.
* The default behavior is to put them on the `<code>` element.
*/
public $codeAttributesOnPre = false;
/**
* @inheritDoc
*/
protected $escapeCharacters = [
// from Markdown
'\\', // backslash
'`', // backtick
'*', // asterisk
'_', // underscore
'{', '}', // curly braces
'[', ']', // square brackets
'(', ')', // parentheses
'#', // hash mark
'+', // plus sign
'-', // minus sign (hyphen)
'.', // dot
'!', // exclamation mark
'<', '>',
// added by MarkdownExtra
':', // colon
'|', // pipe
];
private $_specialAttributesRegex = '\{(([#\.][A-z0-9-_]+\s*)+)\}';
// TODO allow HTML intended 3 spaces
// TODO add markdown inside HTML blocks
// TODO implement definition lists
// TODO implement footnotes
// TODO implement Abbreviations
// block parsing
protected function identifyReference($line)
{
return ($line[0] === ' ' || $line[0] === '[') && preg_match('/^ {0,3}\[(.+?)\]:\s*([^\s]+?)(?:\s+[\'"](.+?)[\'"])?\s*('.$this->_specialAttributesRegex.')?\s*$/', $line);
}
/**
* Consume link references
*/
protected function consumeReference($lines, $current)
{
while (isset($lines[$current]) && preg_match('/^ {0,3}\[(.+?)\]:\s*(.+?)(?:\s+[\(\'"](.+?)[\)\'"])?\s*('.$this->_specialAttributesRegex.')?\s*$/', $lines[$current], $matches)) {
$label = strtolower($matches[1]);
$this->references[$label] = [
'url' => $this->replaceEscape($matches[2]),
];
if (isset($matches[3])) {
$this->references[$label]['title'] = $matches[3];
} else {
// title may be on the next line
if (isset($lines[$current + 1]) && preg_match('/^\s+[\(\'"](.+?)[\)\'"]\s*$/', $lines[$current + 1], $matches)) {
$this->references[$label]['title'] = $matches[1];
$current++;
}
}
if (isset($matches[5])) {
$this->references[$label]['attributes'] = $matches[5];
}
$current++;
}
return [false, --$current];
}
/**
* Consume lines for a fenced code block
*/
protected function consumeFencedCode($lines, $current)
{
// consume until ```
$block = [
'code',
];
$line = rtrim($lines[$current]);
if (($pos = strrpos($line, '`')) === false) {
$pos = strrpos($line, '~');
}
$fence = substr($line, 0, $pos + 1);
$block['attributes'] = substr($line, $pos);
$content = [];
for($i = $current + 1, $count = count($lines); $i < $count; $i++) {
if (rtrim($line = $lines[$i]) !== $fence) {
$content[] = $line;
} else {
break;
}
}
$block['content'] = implode("\n", $content);
return [$block, $i];
}
protected function renderCode($block)
{
$attributes = $this->renderAttributes($block);
return ($this->codeAttributesOnPre ? "<pre$attributes><code>" : "<pre><code$attributes>")
. htmlspecialchars($block['content'] . "\n", ENT_NOQUOTES | ENT_SUBSTITUTE, 'UTF-8')
. "</code></pre>\n";
}
/**
* Renders a headline
*/
protected function renderHeadline($block)
{
foreach($block['content'] as $i => $element) {
if ($element[0] === 'specialAttributes') {
unset($block['content'][$i]);
$block['attributes'] = $element[1];
}
}
$tag = 'h' . $block['level'];
$attributes = $this->renderAttributes($block);
return "<$tag$attributes>" . rtrim($this->renderAbsy($block['content']), "# \t") . "</$tag>\n";
}
protected function renderAttributes($block)
{
$html = [];
if (isset($block['attributes'])) {
$attributes = preg_split('/\s+/', $block['attributes'], -1, PREG_SPLIT_NO_EMPTY);
foreach($attributes as $attribute) {
if ($attribute[0] === '#') {
$html['id'] = substr($attribute, 1);
} else {
$html['class'][] = substr($attribute, 1);
}
}
}
$result = '';
foreach($html as $attr => $value) {
if (is_array($value)) {
$value = trim(implode(' ', $value));
}
if (!empty($value)) {
$result .= " $attr=\"$value\"";
}
}
return $result;
}
// inline parsing
/**
* @marker {
*/
protected function parseSpecialAttributes($text)
{
if (preg_match("~$this->_specialAttributesRegex~", $text, $matches)) {
return [['specialAttributes', $matches[1]], strlen($matches[0])];
}
return [['text', '{'], 1];
}
protected function renderSpecialAttributes($block)
{
return '{' . $block[1] . '}';
}
protected function parseInline($text)
{
$elements = parent::parseInline($text);
// merge special attribute elements to links and images as they are not part of the final absy later
$relatedElement = null;
foreach($elements as $i => $element) {
if ($element[0] === 'link' || $element[0] === 'image') {
$relatedElement = $i;
} elseif ($element[0] === 'specialAttributes') {
if ($relatedElement !== null) {
$elements[$relatedElement]['attributes'] = $element[1];
unset($elements[$i]);
}
$relatedElement = null;
} else {
$relatedElement = null;
}
}
return $elements;
}
protected function renderLink($block)
{
if (isset($block['refkey'])) {
if (($ref = $this->lookupReference($block['refkey'])) !== false) {
$block = array_merge($block, $ref);
} else {
return $block['orig'];
}
}
$attributes = $this->renderAttributes($block);
return '<a href="' . htmlspecialchars($block['url'], ENT_COMPAT | ENT_HTML401, 'UTF-8') . '"'
. (empty($block['title']) ? '' : ' title="' . htmlspecialchars($block['title'], ENT_COMPAT | ENT_HTML401 | ENT_SUBSTITUTE, 'UTF-8') . '"')
. $attributes . '>' . $this->renderAbsy($block['text']) . '</a>';
}
protected function renderImage($block)
{
if (isset($block['refkey'])) {
if (($ref = $this->lookupReference($block['refkey'])) !== false) {
$block = array_merge($block, $ref);
} else {
return $block['orig'];
}
}
$attributes = $this->renderAttributes($block);
return '<img src="' . htmlspecialchars($block['url'], ENT_COMPAT | ENT_HTML401, 'UTF-8') . '"'
. ' alt="' . htmlspecialchars($block['text'], ENT_COMPAT | ENT_HTML401 | ENT_SUBSTITUTE, 'UTF-8') . '"'
. (empty($block['title']) ? '' : ' title="' . htmlspecialchars($block['title'], ENT_COMPAT | ENT_HTML401 | ENT_SUBSTITUTE, 'UTF-8') . '"')
. $attributes . ($this->html5 ? '>' : ' />');
}
}

389
vendor/cebe/markdown/Parser.php vendored Normal file
View File

@@ -0,0 +1,389 @@
<?php
/**
* @copyright Copyright (c) 2014 Carsten Brandt
* @license https://github.com/cebe/markdown/blob/master/LICENSE
* @link https://github.com/cebe/markdown#readme
*/
namespace cebe\markdown;
use ReflectionMethod;
/**
* A generic parser for markdown-like languages.
*
* @author Carsten Brandt <mail@cebe.cc>
*/
abstract class Parser
{
/**
* @var integer the maximum nesting level for language elements.
*/
public $maximumNestingLevel = 32;
/**
* @var string the current context the parser is in.
* TODO remove in favor of absy
*/
protected $context = [];
/**
* @var array these are "escapeable" characters. When using one of these prefixed with a
* backslash, the character will be outputted without the backslash and is not interpreted
* as markdown.
*/
protected $escapeCharacters = [
'\\', // backslash
];
private $_depth = 0;
/**
* Parses the given text considering the full language.
*
* This includes parsing block elements as well as inline elements.
*
* @param string $text the text to parse
* @return string parsed markup
*/
public function parse($text)
{
$this->prepare();
if (ltrim($text) === '') {
return '';
}
$text = str_replace(["\r\n", "\n\r", "\r"], "\n", $text);
$this->prepareMarkers($text);
$absy = $this->parseBlocks(explode("\n", $text));
$markup = $this->renderAbsy($absy);
$this->cleanup();
return $markup;
}
/**
* Parses a paragraph without block elements (block elements are ignored).
*
* @param string $text the text to parse
* @return string parsed markup
*/
public function parseParagraph($text)
{
$this->prepare();
if (ltrim($text) === '') {
return '';
}
$text = str_replace(["\r\n", "\n\r", "\r"], "\n", $text);
$this->prepareMarkers($text);
$absy = $this->parseInline($text);
$markup = $this->renderAbsy($absy);
$this->cleanup();
return $markup;
}
/**
* This method will be called before `parse()` and `parseParagraph()`.
* You can override it to do some initialization work.
*/
protected function prepare()
{
}
/**
* This method will be called after `parse()` and `parseParagraph()`.
* You can override it to do cleanup.
*/
protected function cleanup()
{
}
// block parsing
private $_blockTypes;
/**
* @return array a list of block element types available.
*/
protected function blockTypes()
{
if ($this->_blockTypes === null) {
// detect block types via "identify" functions
$reflection = new \ReflectionClass($this);
$this->_blockTypes = array_filter(array_map(function($method) {
$name = $method->getName();
return strncmp($name, 'identify', 8) === 0 ? strtolower(substr($name, 8)) : false;
}, $reflection->getMethods(ReflectionMethod::IS_PROTECTED)));
sort($this->_blockTypes);
}
return $this->_blockTypes;
}
/**
* Given a set of lines and an index of a current line it uses the registed block types to
* detect the type of this line.
* @param array $lines
* @param integer $current
* @return string name of the block type in lower case
*/
protected function detectLineType($lines, $current)
{
$line = $lines[$current];
$blockTypes = $this->blockTypes();
foreach($blockTypes as $blockType) {
if ($this->{'identify' . $blockType}($line, $lines, $current)) {
return $blockType;
}
}
// consider the line a normal paragraph if no other block type matches
return 'paragraph';
}
/**
* Parse block elements by calling `detectLineType()` to identify them
* and call consume function afterwards.
*/
protected function parseBlocks($lines)
{
if ($this->_depth >= $this->maximumNestingLevel) {
// maximum depth is reached, do not parse input
return [['text', implode("\n", $lines)]];
}
$this->_depth++;
$blocks = [];
// convert lines to blocks
for ($i = 0, $count = count($lines); $i < $count; $i++) {
$line = $lines[$i];
if ($line !== '' && rtrim($line) !== '') { // skip empty lines
// identify a blocks beginning and parse the content
list($block, $i) = $this->parseBlock($lines, $i);
if ($block !== false) {
$blocks[] = $block;
}
}
}
$this->_depth--;
return $blocks;
}
/**
* Parses the block at current line by identifying the block type and parsing the content
* @param $lines
* @param $current
* @return array Array of two elements, the first element contains the block,
* the second contains the next line index to be parsed.
*/
protected function parseBlock($lines, $current)
{
// identify block type for this line
$blockType = $this->detectLineType($lines, $current);
// call consume method for the detected block type to consume further lines
return $this->{'consume' . $blockType}($lines, $current);
}
protected function renderAbsy($blocks)
{
$output = '';
foreach ($blocks as $block) {
array_unshift($this->context, $block[0]);
$output .= $this->{'render' . $block[0]}($block);
array_shift($this->context);
}
return $output;
}
/**
* Consume lines for a paragraph
*
* @param $lines
* @param $current
* @return array
*/
protected function consumeParagraph($lines, $current)
{
// consume until newline
$content = [];
for ($i = $current, $count = count($lines); $i < $count; $i++) {
if (ltrim($lines[$i]) !== '') {
$content[] = $lines[$i];
} else {
break;
}
}
$block = [
'paragraph',
'content' => $this->parseInline(implode("\n", $content)),
];
return [$block, --$i];
}
/**
* Render a paragraph block
*
* @param $block
* @return string
*/
protected function renderParagraph($block)
{
return '<p>' . $this->renderAbsy($block['content']) . "</p>\n";
}
// inline parsing
/**
* @var array the set of inline markers to use in different contexts.
*/
private $_inlineMarkers = [];
/**
* Returns a map of inline markers to the corresponding parser methods.
*
* This array defines handler methods for inline markdown markers.
* When a marker is found in the text, the handler method is called with the text
* starting at the position of the marker.
*
* Note that markers starting with whitespace may slow down the parser,
* you may want to use [[renderText]] to deal with them.
*
* You may override this method to define a set of markers and parsing methods.
* The default implementation looks for protected methods starting with `parse` that
* also have an `@marker` annotation in PHPDoc.
*
* @return array a map of markers to parser methods
*/
protected function inlineMarkers()
{
$markers = [];
// detect "parse" functions
$reflection = new \ReflectionClass($this);
foreach($reflection->getMethods(ReflectionMethod::IS_PROTECTED) as $method) {
$methodName = $method->getName();
if (strncmp($methodName, 'parse', 5) === 0) {
preg_match_all('/@marker ([^\s]+)/', $method->getDocComment(), $matches);
foreach($matches[1] as $match) {
$markers[$match] = $methodName;
}
}
}
return $markers;
}
/**
* Prepare markers that are used in the text to parse
*
* Add all markers that are present in markdown.
* Check is done to avoid iterations in parseInline(), good for huge markdown files
* @param string $text
*/
protected function prepareMarkers($text)
{
$this->_inlineMarkers = [];
foreach ($this->inlineMarkers() as $marker => $method) {
if (strpos($text, $marker) !== false) {
$m = $marker[0];
// put the longest marker first
if (isset($this->_inlineMarkers[$m])) {
reset($this->_inlineMarkers[$m]);
if (strlen($marker) > strlen(key($this->_inlineMarkers[$m]))) {
$this->_inlineMarkers[$m] = array_merge([$marker => $method], $this->_inlineMarkers[$m]);
continue;
}
}
$this->_inlineMarkers[$m][$marker] = $method;
}
}
}
/**
* Parses inline elements of the language.
*
* @param string $text the inline text to parse.
* @return array
*/
protected function parseInline($text)
{
if ($this->_depth >= $this->maximumNestingLevel) {
// maximum depth is reached, do not parse input
return [['text', $text]];
}
$this->_depth++;
$markers = implode('', array_keys($this->_inlineMarkers));
$paragraph = [];
while (!empty($markers) && ($found = strpbrk($text, $markers)) !== false) {
$pos = strpos($text, $found);
// add the text up to next marker to the paragraph
if ($pos !== 0) {
$paragraph[] = ['text', substr($text, 0, $pos)];
}
$text = $found;
$parsed = false;
foreach ($this->_inlineMarkers[$text[0]] as $marker => $method) {
if (strncmp($text, $marker, strlen($marker)) === 0) {
// parse the marker
array_unshift($this->context, $method);
list($output, $offset) = $this->$method($text);
array_shift($this->context);
$paragraph[] = $output;
$text = substr($text, $offset);
$parsed = true;
break;
}
}
if (!$parsed) {
$paragraph[] = ['text', substr($text, 0, 1)];
$text = substr($text, 1);
}
}
$paragraph[] = ['text', $text];
$this->_depth--;
return $paragraph;
}
/**
* Parses escaped special characters.
* @marker \
*/
protected function parseEscape($text)
{
if (isset($text[1]) && in_array($text[1], $this->escapeCharacters)) {
return [['text', $text[1]], 2];
}
return [['text', $text[0]], 1];
}
/**
* This function renders plain text sections in the markdown text.
* It can be used to work on normal text sections for example to highlight keywords or
* do special escaping.
*/
protected function renderText($block)
{
return $block[1];
}
}

516
vendor/cebe/markdown/README.md vendored Normal file
View File

@@ -0,0 +1,516 @@
A super fast, highly extensible markdown parser for PHP
=======================================================
[![Latest Stable Version](https://poser.pugx.org/cebe/markdown/v/stable.png)](https://packagist.org/packages/cebe/markdown)
[![Total Downloads](https://poser.pugx.org/cebe/markdown/downloads.png)](https://packagist.org/packages/cebe/markdown)
[![Build Status](https://travis-ci.org/cebe/markdown.svg?branch=master)](http://travis-ci.org/cebe/markdown)
[![Tested against HHVM](http://hhvm.h4cc.de/badge/cebe/markdown.png)](http://hhvm.h4cc.de/package/cebe/markdown)
[![Code Coverage](https://scrutinizer-ci.com/g/cebe/markdown/badges/coverage.png?s=db6af342d55bea649307ef311fbd536abb9bab76)](https://scrutinizer-ci.com/g/cebe/markdown/)
[![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/cebe/markdown/badges/quality-score.png?s=17448ca4d140429fd687c58ff747baeb6568d528)](https://scrutinizer-ci.com/g/cebe/markdown/)
What is this? <a name="what"></a>
-------------
A set of [PHP][] classes, each representing a [Markdown][] flavor, and a command line tool
for converting markdown files to HTML files.
The implementation focus is to be **fast** (see [benchmark][]) and **extensible**.
Parsing Markdown to HTML is as simple as calling a single method (see [Usage](#usage)) providing a solid implementation
that gives most expected results even in non-trivial edge cases.
Extending the Markdown language with new elements is as simple as adding a new method to the class that converts the
markdown text to the expected output in HTML. This is possible without dealing with complex and error prone regular expressions.
It is also possible to hook into the markdown structure and add elements or read meta information using the internal representation
of the Markdown text as an abstract syntax tree (see [Extending the language](#extend)).
Currently the following markdown flavors are supported:
- **Traditional Markdown** according to <http://daringfireball.net/projects/markdown/syntax> ([try it!](http://markdown.cebe.cc/try?flavor=default)).
- **Github flavored Markdown** according to <https://help.github.com/articles/github-flavored-markdown> ([try it!](http://markdown.cebe.cc/try?flavor=gfm)).
- **Markdown Extra** according to <http://michelf.ca/projects/php-markdown/extra/> (currently not fully supported WIP see [#25][], [try it!](http://markdown.cebe.cc/try?flavor=extra))
- Any mixed Markdown flavor you like because of its highly extensible structure (See documentation below).
Future plans are to support:
- Smarty Pants <http://daringfireball.net/projects/smartypants/>
- ... (Feel free to [suggest](https://github.com/cebe/markdown/issues/new) further additions!)
[PHP]: http://php.net/ "PHP is a popular general-purpose scripting language that is especially suited to web development."
[Markdown]: http://en.wikipedia.org/wiki/Markdown "Markdown on Wikipedia"
[#25]: https://github.com/cebe/markdown/issues/25 "issue #25"
[benchmark]: https://github.com/kzykhys/Markbench#readme "kzykhys/Markbench on github"
### Who is using it?
- It powers the [API-docs and the definitive guide](http://www.yiiframework.com/doc-2.0/) for the [Yii Framework][] [2.0](https://github.com/yiisoft/yii2).
[Yii Framework]: http://www.yiiframework.com/ "The Yii PHP Framework"
Installation <a name="installation"></a>
------------
[PHP 5.4 or higher](http://www.php.net/downloads.php) is required to use it.
It will also run on facebook's [hhvm](http://hhvm.com/).
Installation is recommended to be done via [composer][] by running:
composer require cebe/markdown "~1.1.1"
Alternatively you can add the following to the `require` section in your `composer.json` manually:
```json
"cebe/markdown": "~1.1.1"
```
Run `composer update` afterwards.
[composer]: https://getcomposer.org/ "The PHP package manager"
> Note: If you have configured PHP with opcache you need to enable the
> [opcache.save_comments](http://php.net/manual/en/opcache.configuration.php#ini.opcache.save-comments) option because inline element parsing relies on PHPdoc annotations to find declared elements.
Usage <a name="usage"></a>
-----
### In your PHP project
To parse your markdown you need only two lines of code. The first one is to choose the markdown flavor as
one of the following:
- Traditional Markdown: `$parser = new \cebe\markdown\Markdown();`
- Github Flavored Markdown: `$parser = new \cebe\markdown\GithubMarkdown();`
- Markdown Extra: `$parser = new \cebe\markdown\MarkdownExtra();`
The next step is to call the `parse()`-method for parsing the text using the full markdown language
or calling the `parseParagraph()`-method to parse only inline elements.
Here are some examples:
```php
// traditional markdown and parse full text
$parser = new \cebe\markdown\Markdown();
echo $parser->parse($markdown);
// use github markdown
$parser = new \cebe\markdown\GithubMarkdown();
echo $parser->parse($markdown);
// use markdown extra
$parser = new \cebe\markdown\MarkdownExtra();
echo $parser->parse($markdown);
// parse only inline elements (useful for one-line descriptions)
$parser = new \cebe\markdown\GithubMarkdown();
echo $parser->parseParagraph($markdown);
```
You may optionally set one of the following options on the parser object:
For all Markdown Flavors:
- `$parser->html5 = true` to enable HTML5 output instead of HTML4.
- `$parser->keepListStartNumber = true` to enable keeping the numbers of ordered lists as specified in the markdown.
The default behavior is to always start from 1 and increment by one regardless of the number in markdown.
For GithubMarkdown:
- `$parser->enableNewlines = true` to convert all newlines to `<br/>`-tags. By default only newlines with two preceding spaces are converted to `<br/>`-tags.
It is recommended to use UTF-8 encoding for the input strings. Other encodings may work, but are currently untested.
### The command line script
You can use it to render this readme:
bin/markdown README.md > README.html
Using github flavored markdown:
bin/markdown --flavor=gfm README.md > README.html
or convert the original markdown description to html using the unix pipe:
curl http://daringfireball.net/projects/markdown/syntax.text | bin/markdown > md.html
Here is the full Help output you will see when running `bin/markdown --help`:
PHP Markdown to HTML converter
------------------------------
by Carsten Brandt <mail@cebe.cc>
Usage:
bin/markdown [--flavor=<flavor>] [--full] [file.md]
--flavor specifies the markdown flavor to use. If omitted the original markdown by John Gruber [1] will be used.
Available flavors:
gfm - Github flavored markdown [2]
extra - Markdown Extra [3]
--full ouput a full HTML page with head and body. If not given, only the parsed markdown will be output.
--help shows this usage information.
If no file is specified input will be read from STDIN.
Examples:
Render a file with original markdown:
bin/markdown README.md > README.html
Render a file using gihtub flavored markdown:
bin/markdown --flavor=gfm README.md > README.html
Convert the original markdown description to html using STDIN:
curl http://daringfireball.net/projects/markdown/syntax.text | bin/markdown > md.html
[1] http://daringfireball.net/projects/markdown/syntax
[2] https://help.github.com/articles/github-flavored-markdown
[3] http://michelf.ca/projects/php-markdown/extra/
Extensions
----------
Here are some extensions to this library:
- [Bogardo/markdown-codepen](https://github.com/Bogardo/markdown-codepen) - shortcode to embed codepens from http://codepen.io/ in markdown.
- [kartik-v/yii2-markdown](https://github.com/kartik-v/yii2-markdown) - Advanced Markdown editing and conversion utilities for Yii Framework 2.0.
- [cebe/markdown-latex](https://github.com/cebe/markdown-latex) - Convert Markdown to LaTeX and PDF
- [softark/creole](https://github.com/softark/creole) - A creole markup parser
- [hyn/frontmatter](https://github.com/hyn/frontmatter) - Frontmatter Metadata Support (JSON, TOML, YAML)
- ... [add yours!](https://github.com/cebe/markdown/edit/master/README.md#L186)
Extending the language <a name="extend"></a>
----------------------
Markdown consists of two types of language elements, I'll call them block and inline elements simlar to what you have in
HTML with `<div>` and `<span>`. Block elements are normally spreads over several lines and are separated by blank lines.
The most basic block element is a paragraph (`<p>`).
Inline elements are elements that are added inside of block elements i.e. inside of text.
This markdown parser allows you to extend the markdown language by changing existing elements behavior and also adding
new block and inline elements. You do this by extending from the parser class and adding/overriding class methods and
properties. For the different element types there are different ways to extend them as you will see in the following sections.
### Adding block elements
The markdown is parsed line by line to identify each non-empty line as one of the block element types.
To identify a line as the beginning of a block element it calls all protected class methods who's name begins with `identify`.
An identify function returns true if it has identified the block element it is responsible for or false if not.
In the following example we will implement support for [fenced code blocks][] which are part of the github flavored markdown.
[fenced code blocks]: https://help.github.com/articles/github-flavored-markdown#fenced-code-blocks
"Fenced code block feature of github flavored markdown"
```php
<?php
class MyMarkdown extends \cebe\markdown\Markdown
{
protected function identifyFencedCode($line, $lines, $current)
{
// if a line starts with at least 3 backticks it is identified as a fenced code block
if (strncmp($line, '```', 3) === 0) {
return true;
}
return false;
}
// ...
}
```
In the above, `$line` is a string containing the content of the current line and is equal to `$lines[$current]`.
You may use `$lines` and `$current` to check other lines than the current line. In most cases you can ignore these parameters.
Parsing of a block element is done in two steps:
1. **Consuming** all the lines belonging to it. In most cases this is iterating over the lines starting from the identified
line until a blank line occurs. This step is implemented by a method named `consume{blockName}()` where `{blockName}`
is the same name as used for the identify function above. The consume method also takes the lines array
and the number of the current line. It will return two arguments: an array representing the block element in the abstract syntax tree
of the markdown document and the line number to parse next. In the abstract syntax array the first element refers to the name of
the element, all other array elements can be freely defined by yourself.
In our example we will implement it like this:
```php
protected function consumeFencedCode($lines, $current)
{
// create block array
$block = [
'fencedCode',
'content' => [],
];
$line = rtrim($lines[$current]);
// detect language and fence length (can be more than 3 backticks)
$fence = substr($line, 0, $pos = strrpos($line, '`') + 1);
$language = substr($line, $pos);
if (!empty($language)) {
$block['language'] = $language;
}
// consume all lines until ```
for($i = $current + 1, $count = count($lines); $i < $count; $i++) {
if (rtrim($line = $lines[$i]) !== $fence) {
$block['content'][] = $line;
} else {
// stop consuming when code block is over
break;
}
}
return [$block, $i];
}
```
2. **Rendering** the element. After all blocks have been consumed, they are being rendered using the
`render{elementName}()`-method where `elementName` refers to the name of the element in the abstract syntax tree:
```php
protected function renderFencedCode($block)
{
$class = isset($block['language']) ? ' class="language-' . $block['language'] . '"' : '';
return "<pre><code$class>" . htmlspecialchars(implode("\n", $block['content']) . "\n", ENT_NOQUOTES, 'UTF-8') . '</code></pre>';
}
```
You may also add code highlighting here. In general it would also be possible to render ouput in a different language than
HTML for example LaTeX.
### Adding inline elements
Adding inline elements is different from block elements as they are parsed using markers in the text.
An inline element is identified by a marker that marks the beginning of an inline element (e.g. `[` will mark a possible
beginning of a link or `` ` `` will mark inline code).
Parsing methods for inline elements are also protected and identified by the prefix `parse`. Additionally a `@marker` annotation
in PHPDoc is needed to register the parse function for one or multiple markers.
The method will then be called when a marker is found in the text. As an argument it takes the text starting at the position of the marker.
The parser method will return an array containing the element of the abstract sytnax tree and an offset of text it has
parsed from the input markdown. All text up to this offset will be removed from the markdown before the next marker will be searched.
As an example, we will add support for the [strikethrough][] feature of github flavored markdown:
[strikethrough]: https://help.github.com/articles/github-flavored-markdown#strikethrough "Strikethrough feature of github flavored markdown"
```php
<?php
class MyMarkdown extends \cebe\markdown\Markdown
{
/**
* @marker ~~
*/
protected function parseStrike($markdown)
{
// check whether the marker really represents a strikethrough (i.e. there is a closing ~~)
if (preg_match('/^~~(.+?)~~/', $markdown, $matches)) {
return [
// return the parsed tag as an element of the abstract syntax tree and call `parseInline()` to allow
// other inline markdown elements inside this tag
['strike', $this->parseInline($matches[1])],
// return the offset of the parsed text
strlen($matches[0])
];
}
// in case we did not find a closing ~~ we just return the marker and skip 2 characters
return [['text', '~~'], 2];
}
// rendering is the same as for block elements, we turn the abstract syntax array into a string.
protected function renderStrike($element)
{
return '<del>' . $this->renderAbsy($element[1]) . '</del>';
}
}
```
### Composing your own Markdown flavor
This markdown library is composed of traits so it is very easy to create your own markdown flavor by adding and/or removing
the single feature traits.
Designing your Markdown flavor consists of four steps:
1. Select a base class
2. Select language feature traits
3. Define escapeable characters
4. Optionally add custom rendering behavior
#### Select a base class
If you want to extend from a flavor and only add features you can use one of the existing classes
(`Markdown`, `GithubMarkdown` or `MarkdownExtra`) as your flavors base class.
If you want to define a subset of the markdown language, i.e. remove some of the features, you have to
extend your class from `Parser`.
#### Select language feature traits
The following shows the trait selection for traditional Markdown.
```php
class MyMarkdown extends Parser
{
// include block element parsing using traits
use block\CodeTrait;
use block\HeadlineTrait;
use block\HtmlTrait {
parseInlineHtml as private;
}
use block\ListTrait {
// Check Ul List before headline
identifyUl as protected identifyBUl;
consumeUl as protected consumeBUl;
}
use block\QuoteTrait;
use block\RuleTrait {
// Check Hr before checking lists
identifyHr as protected identifyAHr;
consumeHr as protected consumeAHr;
}
// include inline element parsing using traits
use inline\CodeTrait;
use inline\EmphStrongTrait;
use inline\LinkTrait;
/**
* @var boolean whether to format markup according to HTML5 spec.
* Defaults to `false` which means that markup is formatted as HTML4.
*/
public $html5 = false;
protected function prepare()
{
// reset references
$this->references = [];
}
// ...
}
```
In general, just adding the trait with `use` is enough, however in some cases some fine tuning is desired
to get most expected parsing results. Elements are detected in alphabetical order of their identification
function. This means that if a line starting with `-` could be a list or a horizontal rule, the preference has to be set
by renaming the identification function. This is what is done with renaming `identifyHr` to `identifyAHr`
and `identifyBUl` to `identifyBUl`. The consume function always has to have the same name as the identification function
so this has to be renamed too.
There is also a conflict for parsing of the `<` character. This could either be a link/email enclosed in `<` and `>`
or an inline HTML tag. In order to resolve this conflict when adding the `LinkTrait`, we need to hide the `parseInlineHtml`
method of the `HtmlTrait`.
If you use any trait that uses the `$html5` property to adjust its output you also need to define this property.
If you use the link trait it may be useful to implement `prepare()` as shown above to reset references before
parsing to ensure you get a reusable object.
#### Define escapeable characters
Depenedend on the language features you have chosen there is a different set of characters that can be escaped
using `\`. The following is the set of escapeable characters for traditional markdown, you can copy it to your class
as is.
```php
/**
* @var array these are "escapeable" characters. When using one of these prefixed with a
* backslash, the character will be outputted without the backslash and is not interpreted
* as markdown.
*/
protected $escapeCharacters = [
'\\', // backslash
'`', // backtick
'*', // asterisk
'_', // underscore
'{', '}', // curly braces
'[', ']', // square brackets
'(', ')', // parentheses
'#', // hash mark
'+', // plus sign
'-', // minus sign (hyphen)
'.', // dot
'!', // exclamation mark
'<', '>',
];
```
#### Add custom rendering behavior
Optionally you may also want to adjust rendering behavior by overriding some methods.
You may refer to the `consumeParagraph()` method of the `Markdown` and `GithubMarkdown` classes for some inspiration
which define different rules for which elements are allowed to interrupt a paragraph.
Acknowledgements <a name="ack"></a>
----------------
I'd like to thank [@erusev][] for creating [Parsedown][] which heavily influenced this work and provided
the idea of the line based parsing approach.
[@erusev]: https://github.com/erusev "Emanuil Rusev"
[Parsedown]: http://parsedown.org/ "The Parsedown PHP Markdown parser"
FAQ <a name="faq"></a>
---
### Why another markdown parser?
While reviewing PHP markdown parsers for choosing one to use bundled with the [Yii framework 2.0][]
I found that most of the implementations use regex to replace patterns instead
of doing real parsing. This way extending them with new language elements is quite hard
as you have to come up with a complex regex, that matches your addition but does not mess
with other elements. Such additions are very common as you see on github which supports referencing
issues, users and commits in the comments.
A [real parser][] should use context aware methods that walk trough the text and
parse the tokens as they find them. The only implentation that I have found that uses
this approach is [Parsedown][] which also shows that this implementation is [much faster][benchmark]
than the regex way. Parsedown however is an implementation that focuses on speed and implements
its own flavor (mainly github flavored markdown) in one class and at the time of this writing was
not easily extensible.
Given the situation above I decided to start my own implementation using the parsing approach
from Parsedown and making it extensible creating a class for each markdown flavor that extend each
other in the way that also the markdown languages extend each other.
This allows you to choose between markdown language flavors and also provides a way to compose your
own flavor picking the best things from all.
I chose this approach as it is easier to implement and also more intuitive approach compared
to using callbacks to inject functionallity into the parser.
[real parser]: http://en.wikipedia.org/wiki/Parsing#Types_of_parser
[Parsedown]: http://parsedown.org/ "The Parsedown PHP Markdown parser"
### Where do I report bugs or rendering issues?
Just [open an issue][] on github, post your markdown code and describe the problem. You may also attach screenshots of the rendered HTML result to describe your problem.
[open an issue]: https://github.com/cebe/markdown/issues/new
### How can I contribute to this library?
Check the [CONTRIBUTING.md](CONTRIBUTING.md) file for more info.
### Am I free to use this?
This library is open source and licensed under the [MIT License][]. This means that you can do whatever you want
with it as long as you mention my name and include the [license file][license]. Check the [license][] for details.
[MIT License]: http://opensource.org/licenses/MIT
[license]: https://github.com/cebe/markdown/blob/master/LICENSE
Contact
-------
Feel free to contact me using [email](mailto:mail@cebe.cc) or [twitter](https://twitter.com/cebe_cc).

170
vendor/cebe/markdown/bin/markdown vendored Normal file
View File

@@ -0,0 +1,170 @@
#!/usr/bin/env php
<?php
/**
* @copyright Copyright (c) 2014 Carsten Brandt
* @license https://github.com/cebe/markdown/blob/master/LICENSE
* @link https://github.com/cebe/markdown#readme
*/
$composerAutoload = [
__DIR__ . '/../vendor/autoload.php', // standalone with "composer install" run
__DIR__ . '/../../../autoload.php', // script is installed as a composer binary
];
foreach ($composerAutoload as $autoload) {
if (file_exists($autoload)) {
require($autoload);
break;
}
}
// Send all errors to stderr
ini_set('display_errors', 'stderr');
$flavor = 'cebe\\markdown\\Markdown';
$flavors = [
'gfm' => ['cebe\\markdown\\GithubMarkdown', __DIR__ . '/../GithubMarkdown.php'],
'extra' => ['cebe\\markdown\\MarkdownExtra', __DIR__ . '/../MarkdownExtra.php'],
];
$full = false;
$src = [];
foreach($argv as $k => $arg) {
if ($k == 0) {
continue;
}
if ($arg[0] == '-') {
$arg = explode('=', $arg);
switch($arg[0]) {
case '--flavor':
if (isset($arg[1])) {
if (isset($flavors[$arg[1]])) {
require($flavors[$arg[1]][1]);
$flavor = $flavors[$arg[1]][0];
} else {
error("Unknown flavor: " . $arg[1], "usage");
}
} else {
error("Incomplete argument --flavor!", "usage");
}
break;
case '--full':
$full = true;
break;
case '-h':
case '--help':
echo "PHP Markdown to HTML converter\n";
echo "------------------------------\n\n";
echo "by Carsten Brandt <mail@cebe.cc>\n\n";
usage();
break;
default:
error("Unknown argument " . $arg[0], "usage");
}
} else {
$src[] = $arg;
}
}
if (empty($src)) {
$markdown = file_get_contents("php://stdin");
} elseif (count($src) == 1) {
$file = reset($src);
if (!file_exists($file)) {
error("File does not exist:" . $file);
}
$markdown = file_get_contents($file);
} else {
error("Converting multiple files is not yet supported.", "usage");
}
/** @var cebe\markdown\Parser $md */
$md = new $flavor();
$markup = $md->parse($markdown);
if ($full) {
echo <<<HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<style>
body { font-family: Arial, sans-serif; }
code { background: #eeeeff; padding: 2px; }
li { margin-bottom: 5px; }
img { max-width: 1200px; }
table, td, th { border: solid 1px #ccc; border-collapse: collapse; }
</style>
</head>
<body>
$markup
</body>
</html>
HTML;
} else {
echo $markup;
}
// functions
/**
* Display usage information
*/
function usage() {
global $argv;
$cmd = $argv[0];
echo <<<EOF
Usage:
$cmd [--flavor=<flavor>] [--full] [file.md]
--flavor specifies the markdown flavor to use. If omitted the original markdown by John Gruber [1] will be used.
Available flavors:
gfm - Github flavored markdown [2]
extra - Markdown Extra [3]
--full ouput a full HTML page with head and body. If not given, only the parsed markdown will be output.
--help shows this usage information.
If no file is specified input will be read from STDIN.
Examples:
Render a file with original markdown:
$cmd README.md > README.html
Render a file using gihtub flavored markdown:
$cmd --flavor=gfm README.md > README.html
Convert the original markdown description to html using STDIN:
curl http://daringfireball.net/projects/markdown/syntax.text | $cmd > md.html
[1] http://daringfireball.net/projects/markdown/syntax
[2] https://help.github.com/articles/github-flavored-markdown
[3] http://michelf.ca/projects/php-markdown/extra/
EOF;
exit(1);
}
/**
* Send custom error message to stderr
* @param $message string
* @param $callback mixed called before script exit
* @return void
*/
function error($message, $callback = null) {
$fe = fopen("php://stderr", "w");
fwrite($fe, "Error: " . $message . "\n");
if (is_callable($callback)) {
call_user_func($callback);
}
exit(1);
}

View File

@@ -0,0 +1,66 @@
<?php
/**
* @copyright Copyright (c) 2014 Carsten Brandt
* @license https://github.com/cebe/markdown/blob/master/LICENSE
* @link https://github.com/cebe/markdown#readme
*/
namespace cebe\markdown\block;
/**
* Adds the 4 space indented code blocks
*/
trait CodeTrait
{
/**
* identify a line as the beginning of a code block.
*/
protected function identifyCode($line)
{
// indentation >= 4 or one tab is code
return ($l = $line[0]) === ' ' && $line[1] === ' ' && $line[2] === ' ' && $line[3] === ' ' || $l === "\t";
}
/**
* Consume lines for a code block element
*/
protected function consumeCode($lines, $current)
{
// consume until newline
$content = [];
for ($i = $current, $count = count($lines); $i < $count; $i++) {
$line = $lines[$i];
// a line is considered to belong to this code block as long as it is intended by 4 spaces or a tab
if (isset($line[0]) && ($line[0] === "\t" || strncmp($line, ' ', 4) === 0)) {
$line = $line[0] === "\t" ? substr($line, 1) : substr($line, 4);
$content[] = $line;
// but also if it is empty and the next line is intended by 4 spaces or a tab
} elseif (($line === '' || rtrim($line) === '') && isset($lines[$i + 1][0]) &&
($lines[$i + 1][0] === "\t" || strncmp($lines[$i + 1], ' ', 4) === 0)) {
if ($line !== '') {
$line = $line[0] === "\t" ? substr($line, 1) : substr($line, 4);
}
$content[] = $line;
} else {
break;
}
}
$block = [
'code',
'content' => implode("\n", $content),
];
return [$block, --$i];
}
/**
* Renders a code block
*/
protected function renderCode($block)
{
$class = isset($block['language']) ? ' class="language-' . $block['language'] . '"' : '';
return "<pre><code$class>" . htmlspecialchars($block['content'] . "\n", ENT_NOQUOTES | ENT_SUBSTITUTE, 'UTF-8') . "</code></pre>\n";
}
}

View File

@@ -0,0 +1,54 @@
<?php
/**
* @copyright Copyright (c) 2014 Carsten Brandt
* @license https://github.com/cebe/markdown/blob/master/LICENSE
* @link https://github.com/cebe/markdown#readme
*/
namespace cebe\markdown\block;
/**
* Adds the fenced code blocks
*
* automatically included 4 space indented code blocks
*/
trait FencedCodeTrait
{
use CodeTrait;
/**
* identify a line as the beginning of a fenced code block.
*/
protected function identifyFencedCode($line)
{
return ($l = $line[0]) === '`' && strncmp($line, '```', 3) === 0 ||
$l === '~' && strncmp($line, '~~~', 3) === 0;
}
/**
* Consume lines for a fenced code block
*/
protected function consumeFencedCode($lines, $current)
{
// consume until ```
$line = rtrim($lines[$current]);
$fence = substr($line, 0, $pos = strrpos($line, $line[0]) + 1);
$language = substr($line, $pos);
$content = [];
for ($i = $current + 1, $count = count($lines); $i < $count; $i++) {
if (rtrim($line = $lines[$i]) !== $fence) {
$content[] = $line;
} else {
break;
}
}
$block = [
'code',
'content' => implode("\n", $content),
];
if (!empty($language)) {
$block['language'] = $language;
}
return [$block, $i];
}
}

View File

@@ -0,0 +1,70 @@
<?php
/**
* @copyright Copyright (c) 2014 Carsten Brandt
* @license https://github.com/cebe/markdown/blob/master/LICENSE
* @link https://github.com/cebe/markdown#readme
*/
namespace cebe\markdown\block;
/**
* Adds the headline blocks
*/
trait HeadlineTrait
{
/**
* identify a line as a headline
*/
protected function identifyHeadline($line, $lines, $current)
{
return (
// heading with #
$line[0] === '#' && !preg_match('/^#\d+/', $line)
||
// underlined headline
!empty($lines[$current + 1]) &&
(($l = $lines[$current + 1][0]) === '=' || $l === '-') &&
preg_match('/^(\-+|=+)\s*$/', $lines[$current + 1])
);
}
/**
* Consume lines for a headline
*/
protected function consumeHeadline($lines, $current)
{
if ($lines[$current][0] === '#') {
// ATX headline
$level = 1;
while (isset($lines[$current][$level]) && $lines[$current][$level] === '#' && $level < 6) {
$level++;
}
$block = [
'headline',
'content' => $this->parseInline(trim($lines[$current], "# \t")),
'level' => $level,
];
return [$block, $current];
} else {
// underlined headline
$block = [
'headline',
'content' => $this->parseInline($lines[$current]),
'level' => $lines[$current + 1][0] === '=' ? 1 : 2,
];
return [$block, $current + 1];
}
}
/**
* Renders a headline
*/
protected function renderHeadline($block)
{
$tag = 'h' . $block['level'];
return "<$tag>" . $this->renderAbsy($block['content']) . "</$tag>\n";
}
abstract protected function parseInline($text);
abstract protected function renderAbsy($absy);
}

168
vendor/cebe/markdown/block/HtmlTrait.php vendored Normal file
View File

@@ -0,0 +1,168 @@
<?php
/**
* @copyright Copyright (c) 2014 Carsten Brandt
* @license https://github.com/cebe/markdown/blob/master/LICENSE
* @link https://github.com/cebe/markdown#readme
*/
namespace cebe\markdown\block;
/**
* Adds inline and block HTML support
*/
trait HtmlTrait
{
/**
* @var array HTML elements considered as inline elements.
* @see http://www.w3.org/wiki/HTML/Elements#Text-level_semantics
*/
protected $inlineHtmlElements = [
'a', 'abbr', 'acronym',
'b', 'basefont', 'bdo', 'big', 'br', 'button', 'blink',
'cite', 'code',
'del', 'dfn',
'em',
'font',
'i', 'img', 'ins', 'input', 'iframe',
'kbd',
'label', 'listing',
'map', 'mark',
'nobr',
'object',
'q',
'rp', 'rt', 'ruby',
's', 'samp', 'script', 'select', 'small', 'spacer', 'span', 'strong', 'sub', 'sup',
'tt', 'var',
'u',
'wbr',
'time',
];
/**
* @var array HTML elements known to be self-closing.
*/
protected $selfClosingHtmlElements = [
'br', 'hr', 'img', 'input', 'nobr',
];
/**
* identify a line as the beginning of a HTML block.
*/
protected function identifyHtml($line, $lines, $current)
{
if ($line[0] !== '<' || isset($line[1]) && $line[1] == ' ') {
return false; // no html tag
}
if (strncmp($line, '<!--', 4) === 0) {
return true; // a html comment
}
$gtPos = strpos($lines[$current], '>');
$spacePos = strpos($lines[$current], ' ');
if ($gtPos === false && $spacePos === false) {
return false; // no html tag
} elseif ($spacePos === false) {
$tag = rtrim(substr($line, 1, $gtPos - 1), '/');
} else {
$tag = rtrim(substr($line, 1, min($gtPos, $spacePos) - 1), '/');
}
if (!ctype_alnum($tag) || in_array(strtolower($tag), $this->inlineHtmlElements)) {
return false; // no html tag or inline html tag
}
return true;
}
/**
* Consume lines for an HTML block
*/
protected function consumeHtml($lines, $current)
{
$content = [];
if (strncmp($lines[$current], '<!--', 4) === 0) { // html comment
for ($i = $current, $count = count($lines); $i < $count; $i++) {
$line = $lines[$i];
$content[] = $line;
if (strpos($line, '-->') !== false) {
break;
}
}
} else {
$tag = rtrim(substr($lines[$current], 1, min(strpos($lines[$current], '>'), strpos($lines[$current] . ' ', ' ')) - 1), '/');
$level = 0;
if (in_array($tag, $this->selfClosingHtmlElements)) {
$level--;
}
for ($i = $current, $count = count($lines); $i < $count; $i++) {
$line = $lines[$i];
$content[] = $line;
$level += substr_count($line, "<$tag") - substr_count($line, "</$tag>") - substr_count($line, "/>");
if ($level <= 0) {
break;
}
}
}
$block = [
'html',
'content' => implode("\n", $content),
];
return [$block, $i];
}
/**
* Renders an HTML block
*/
protected function renderHtml($block)
{
return $block['content'] . "\n";
}
/**
* Parses an & or a html entity definition.
* @marker &
*/
protected function parseEntity($text)
{
// html entities e.g. &copy; &#169; &#x00A9;
if (preg_match('/^&#?[\w\d]+;/', $text, $matches)) {
return [['inlineHtml', $matches[0]], strlen($matches[0])];
} else {
return [['text', '&amp;'], 1];
}
}
/**
* renders a html entity.
*/
protected function renderInlineHtml($block)
{
return $block[1];
}
/**
* Parses inline HTML.
* @marker <
*/
protected function parseInlineHtml($text)
{
if (strpos($text, '>') !== false) {
if (preg_match('~^</?(\w+\d?)( .*?)?>~s', $text, $matches)) {
// HTML tags
return [['inlineHtml', $matches[0]], strlen($matches[0])];
} elseif (preg_match('~^<!--.*?-->~s', $text, $matches)) {
// HTML comments
return [['inlineHtml', $matches[0]], strlen($matches[0])];
}
}
return [['text', '&lt;'], 1];
}
/**
* Escapes `>` characters.
* @marker >
*/
protected function parseGt($text)
{
return [['text', '&gt;'], 1];
}
}

197
vendor/cebe/markdown/block/ListTrait.php vendored Normal file
View File

@@ -0,0 +1,197 @@
<?php
/**
* @copyright Copyright (c) 2014 Carsten Brandt
* @license https://github.com/cebe/markdown/blob/master/LICENSE
* @link https://github.com/cebe/markdown#readme
*/
namespace cebe\markdown\block;
/**
* Adds the list blocks
*/
trait ListTrait
{
/**
* @var bool enable support `start` attribute of ordered lists. This means that lists
* will start with the number you actually type in markdown and not the HTML generated one.
* Defaults to `false` which means that numeration of all ordered lists(<ol>) starts with 1.
*/
public $keepListStartNumber = false;
/**
* identify a line as the beginning of an ordered list.
*/
protected function identifyOl($line)
{
return (($l = $line[0]) > '0' && $l <= '9' || $l === ' ') && preg_match('/^ {0,3}\d+\.[ \t]/', $line);
}
/**
* identify a line as the beginning of an unordered list.
*/
protected function identifyUl($line)
{
$l = $line[0];
return ($l === '-' || $l === '+' || $l === '*') && (isset($line[1]) && (($l1 = $line[1]) === ' ' || $l1 === "\t")) ||
($l === ' ' && preg_match('/^ {0,3}[\-\+\*][ \t]/', $line));
}
/**
* Consume lines for an ordered list
*/
protected function consumeOl($lines, $current)
{
// consume until newline
$block = [
'list',
'list' => 'ol',
'attr' => [],
'items' => [],
];
return $this->consumeList($lines, $current, $block, 'ol');
}
/**
* Consume lines for an unordered list
*/
protected function consumeUl($lines, $current)
{
// consume until newline
$block = [
'list',
'list' => 'ul',
'items' => [],
];
return $this->consumeList($lines, $current, $block, 'ul');
}
private function consumeList($lines, $current, $block, $type)
{
$item = 0;
$indent = '';
$len = 0;
$lastLineEmpty = false;
// track the indentation of list markers, if indented more than previous element
// a list marker is considered to be long to a lower level
$leadSpace = 3;
$marker = $type === 'ul' ? ltrim($lines[$current])[0] : '';
for ($i = $current, $count = count($lines); $i < $count; $i++) {
$line = $lines[$i];
// match list marker on the beginning of the line
$pattern = ($type == 'ol') ? '/^( {0,'.$leadSpace.'})(\d+)\.[ \t]+/' : '/^( {0,'.$leadSpace.'})\\'.$marker.'[ \t]+/';
if (preg_match($pattern, $line, $matches)) {
if (($len = substr_count($matches[0], "\t")) > 0) {
$indent = str_repeat("\t", $len);
$line = substr($line, strlen($matches[0]));
} else {
$len = strlen($matches[0]);
$indent = str_repeat(' ', $len);
$line = substr($line, $len);
}
if ($i === $current) {
$leadSpace = strlen($matches[1]) + 1;
}
if ($type == 'ol' && $this->keepListStartNumber) {
// attr `start` for ol
if (!isset($block['attr']['start']) && isset($matches[2])) {
$block['attr']['start'] = $matches[2];
}
}
$block['items'][++$item][] = $line;
$block['lazyItems'][$item] = $lastLineEmpty;
$lastLineEmpty = false;
} elseif (ltrim($line) === '') {
// line is empty, may be a lazy list
$lastLineEmpty = true;
// two empty lines will end the list
if (!isset($lines[$i + 1][0])) {
break;
// next item is the continuation of this list -> lazy list
} elseif (preg_match($pattern, $lines[$i + 1])) {
$block['items'][$item][] = $line;
$block['lazyItems'][$item] = true;
// next item is indented as much as this list -> lazy list if it is not a reference
} elseif (strncmp($lines[$i + 1], $indent, $len) === 0 || !empty($lines[$i + 1]) && $lines[$i + 1][0] == "\t") {
$block['items'][$item][] = $line;
$nextLine = $lines[$i + 1][0] === "\t" ? substr($lines[$i + 1], 1) : substr($lines[$i + 1], $len);
$block['lazyItems'][$item] = empty($nextLine) || !method_exists($this, 'identifyReference') || !$this->identifyReference($nextLine);
// everything else ends the list
} else {
break;
}
} else {
if ($line[0] === "\t") {
$line = substr($line, 1);
} elseif (strncmp($line, $indent, $len) === 0) {
$line = substr($line, $len);
}
$block['items'][$item][] = $line;
$lastLineEmpty = false;
}
}
foreach($block['items'] as $itemId => $itemLines) {
$content = [];
if (!$block['lazyItems'][$itemId]) {
$firstPar = [];
while (!empty($itemLines) && rtrim($itemLines[0]) !== '' && $this->detectLineType($itemLines, 0) === 'paragraph') {
$firstPar[] = array_shift($itemLines);
}
$content = $this->parseInline(implode("\n", $firstPar));
}
if (!empty($itemLines)) {
$content = array_merge($content, $this->parseBlocks($itemLines));
}
$block['items'][$itemId] = $content;
}
return [$block, $i];
}
/**
* Renders a list
*/
protected function renderList($block)
{
$type = $block['list'];
if (!empty($block['attr'])) {
$output = "<$type " . $this->generateHtmlAttributes($block['attr']) . ">\n";
} else {
$output = "<$type>\n";
}
foreach ($block['items'] as $item => $itemLines) {
$output .= '<li>' . $this->renderAbsy($itemLines). "</li>\n";
}
return $output . "</$type>\n";
}
/**
* Return html attributes string from [attrName => attrValue] list
* @param array $attributes the attribute name-value pairs.
* @return string
*/
private function generateHtmlAttributes($attributes)
{
foreach ($attributes as $name => $value) {
$attributes[$name] = "$name=\"$value\"";
}
return implode(' ', $attributes);
}
abstract protected function parseBlocks($lines);
abstract protected function parseInline($text);
abstract protected function renderAbsy($absy);
abstract protected function detectLineType($lines, $current);
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* @copyright Copyright (c) 2014 Carsten Brandt
* @license https://github.com/cebe/markdown/blob/master/LICENSE
* @link https://github.com/cebe/markdown#readme
*/
namespace cebe\markdown\block;
/**
* Adds the block quote elements
*/
trait QuoteTrait
{
/**
* identify a line as the beginning of a block quote.
*/
protected function identifyQuote($line)
{
return $line[0] === '>' && (!isset($line[1]) || ($l1 = $line[1]) === ' ' || $l1 === "\t");
}
/**
* Consume lines for a blockquote element
*/
protected function consumeQuote($lines, $current)
{
// consume until newline
$content = [];
for ($i = $current, $count = count($lines); $i < $count; $i++) {
$line = $lines[$i];
if (ltrim($line) !== '') {
if ($line[0] == '>' && !isset($line[1])) {
$line = '';
} elseif (strncmp($line, '> ', 2) === 0) {
$line = substr($line, 2);
}
$content[] = $line;
} else {
break;
}
}
$block = [
'quote',
'content' => $this->parseBlocks($content),
'simple' => true,
];
return [$block, $i];
}
/**
* Renders a blockquote
*/
protected function renderQuote($block)
{
return '<blockquote>' . $this->renderAbsy($block['content']) . "</blockquote>\n";
}
abstract protected function parseBlocks($lines);
abstract protected function renderAbsy($absy);
}

View File

@@ -0,0 +1,40 @@
<?php
/**
* @copyright Copyright (c) 2014 Carsten Brandt
* @license https://github.com/cebe/markdown/blob/master/LICENSE
* @link https://github.com/cebe/markdown#readme
*/
namespace cebe\markdown\block;
/**
* Adds horizontal rules
*/
trait RuleTrait
{
/**
* identify a line as a horizontal rule.
*/
protected function identifyHr($line)
{
// at least 3 of -, * or _ on one line make a hr
return (($l = $line[0]) === ' ' || $l === '-' || $l === '*' || $l === '_') && preg_match('/^ {0,3}([\-\*_])\s*\1\s*\1(\1|\s)*$/', $line);
}
/**
* Consume a horizontal rule
*/
protected function consumeHr($lines, $current)
{
return [['hr'], $current];
}
/**
* Renders a horizontal rule
*/
protected function renderHr($block)
{
return $this->html5 ? "<hr>\n" : "<hr />\n";
}
}

View File

@@ -0,0 +1,122 @@
<?php
/**
* @copyright Copyright (c) 2014 Carsten Brandt
* @license https://github.com/cebe/markdown/blob/master/LICENSE
* @link https://github.com/cebe/markdown#readme
*/
namespace cebe\markdown\block;
/**
* Adds the table blocks
*/
trait TableTrait
{
private $_tableCellTag = 'td';
private $_tableCellCount = 0;
private $_tableCellAlign = [];
/**
* identify a line as the beginning of a table block.
*/
protected function identifyTable($line, $lines, $current)
{
return strpos($line, '|') !== false && isset($lines[$current + 1])
&& preg_match('~^\\s*\\|?(\\s*:?-[\\-\\s]*:?\\s*\\|\\s*:?-[\\-\\s]*:?\\s*)+\\|?\\s*$~', $lines[$current + 1]);
}
/**
* Consume lines for a table
*/
protected function consumeTable($lines, $current)
{
// consume until newline
$block = [
'table',
'cols' => [],
'rows' => [],
];
$beginsWithPipe = $lines[$current][0] === '|';
for ($i = $current, $count = count($lines); $i < $count; $i++) {
$line = rtrim($lines[$i]);
// extract alignment from second line
if ($i == $current+1) {
$cols = explode('|', trim($line, ' |'));
foreach($cols as $col) {
$col = trim($col);
if (empty($col)) {
$block['cols'][] = '';
continue;
}
$l = ($col[0] === ':');
$r = (substr($col, -1, 1) === ':');
if ($l && $r) {
$block['cols'][] = 'center';
} elseif ($l) {
$block['cols'][] = 'left';
} elseif ($r) {
$block['cols'][] = 'right';
} else {
$block['cols'][] = '';
}
}
continue;
}
if ($line === '' || $beginsWithPipe && $line[0] !== '|') {
break;
}
if ($line[0] === '|') {
$line = substr($line, 1);
}
if (substr($line, -1, 1) === '|' && (substr($line, -2, 2) !== '\\|' || substr($line, -3, 3) === '\\\\|')) {
$line = substr($line, 0, -1);
}
$block['rows'][] = $line;
}
return [$block, --$i];
}
/**
* render a table block
*/
protected function renderTable($block)
{
$content = '';
$this->_tableCellAlign = $block['cols'];
$content .= "<thead>\n";
$first = true;
foreach($block['rows'] as $row) {
$this->_tableCellTag = $first ? 'th' : 'td';
$align = empty($this->_tableCellAlign[$this->_tableCellCount]) ? '' : ' align="' . $this->_tableCellAlign[$this->_tableCellCount] . '"';
$this->_tableCellCount++;
$tds = "<$this->_tableCellTag$align>" . trim($this->renderAbsy($this->parseInline($row))) . "</$this->_tableCellTag>"; // TODO move this to the consume step
$content .= "<tr>$tds</tr>\n";
if ($first) {
$content .= "</thead>\n<tbody>\n";
}
$first = false;
$this->_tableCellCount = 0;
}
return "<table>\n$content</tbody>\n</table>\n";
}
/**
* @marker |
*/
protected function parseTd($markdown)
{
if (isset($this->context[1]) && $this->context[1] === 'table') {
$align = empty($this->_tableCellAlign[$this->_tableCellCount]) ? '' : ' align="' . $this->_tableCellAlign[$this->_tableCellCount] . '"';
$this->_tableCellCount++;
return [['text', "</$this->_tableCellTag><$this->_tableCellTag$align>"], isset($markdown[1]) && $markdown[1] === ' ' ? 2 : 1]; // TODO make a absy node
}
return [['text', $markdown[0]], 1];
}
abstract protected function parseInline($text);
abstract protected function renderAbsy($absy);
}

42
vendor/cebe/markdown/composer.json vendored Normal file
View File

@@ -0,0 +1,42 @@
{
"name": "cebe/markdown",
"description": "A super fast, highly extensible markdown parser for PHP",
"keywords": ["markdown", "gfm", "markdown-extra", "fast", "extensible"],
"homepage": "https://github.com/cebe/markdown#readme",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Carsten Brandt",
"email": "mail@cebe.cc",
"homepage": "http://cebe.cc/",
"role": "Creator"
}
],
"support": {
"issues": "https://github.com/cebe/markdown/issues",
"source": "https://github.com/cebe/markdown"
},
"require": {
"php": ">=5.4.0",
"lib-pcre": "*"
},
"require-dev": {
"phpunit/phpunit": "4.1.*",
"facebook/xhprof": "*@dev",
"cebe/indent": "*"
},
"autoload": {
"psr-4": {
"cebe\\markdown\\": ""
}
},
"bin": [
"bin/markdown"
],
"extra": {
"branch-alias": {
"dev-master": "1.1.x-dev"
}
}
}

View File

@@ -0,0 +1,45 @@
<?php
/**
* @copyright Copyright (c) 2014 Carsten Brandt
* @license https://github.com/cebe/markdown/blob/master/LICENSE
* @link https://github.com/cebe/markdown#readme
*/
namespace cebe\markdown\inline;
/**
* Adds inline code elements
*/
trait CodeTrait
{
/**
* Parses an inline code span `` ` ``.
* @marker `
*/
protected function parseInlineCode($text)
{
if (preg_match('/^(``+)\s(.+?)\s\1/s', $text, $matches)) { // code with enclosed backtick
return [
[
'inlineCode',
$matches[2],
],
strlen($matches[0])
];
} elseif (preg_match('/^`(.+?)`/s', $text, $matches)) {
return [
[
'inlineCode',
$matches[1],
],
strlen($matches[0])
];
}
return [['text', $text[0]], 1];
}
protected function renderInlineCode($block)
{
return '<code>' . htmlspecialchars($block[1], ENT_NOQUOTES | ENT_SUBSTITUTE, 'UTF-8') . '</code>';
}
}

View File

@@ -0,0 +1,80 @@
<?php
/**
* @copyright Copyright (c) 2014 Carsten Brandt
* @license https://github.com/cebe/markdown/blob/master/LICENSE
* @link https://github.com/cebe/markdown#readme
*/
namespace cebe\markdown\inline;
/**
* Adds inline emphasizes and strong elements
*/
trait EmphStrongTrait
{
/**
* Parses empathized and strong elements.
* @marker _
* @marker *
*/
protected function parseEmphStrong($text)
{
$marker = $text[0];
if (!isset($text[1])) {
return [['text', $text[0]], 1];
}
if ($marker == $text[1]) { // strong
// work around a PHP bug that crashes with a segfault on too much regex backtrack
// check whether the end marker exists in the text
// https://github.com/erusev/parsedown/issues/443
// https://bugs.php.net/bug.php?id=45735
if (strpos($text, $marker . $marker, 2) === false) {
return [['text', $text[0] . $text[1]], 2];
}
if ($marker == '*' && preg_match('/^[*]{2}((?:[^*]|[*][^*]*[*])+?)[*]{2}(?![*]{2})/s', $text, $matches) ||
$marker == '_' && preg_match('/^__((?:[^_]|_[^_]*_)+?)__(?!__)/us', $text, $matches)) {
return [
[
'strong',
$this->parseInline($matches[1]),
],
strlen($matches[0])
];
}
} else { // emph
// work around a PHP bug that crashes with a segfault on too much regex backtrack
// check whether the end marker exists in the text
// https://github.com/erusev/parsedown/issues/443
// https://bugs.php.net/bug.php?id=45735
if (strpos($text, $marker, 1) === false) {
return [['text', $text[0]], 1];
}
if ($marker == '*' && preg_match('/^[*]((?:[^*]|[*][*][^*]+?[*][*])+?)[*](?![*][^*])/s', $text, $matches) ||
$marker == '_' && preg_match('/^_((?:[^_]|__[^_]*__)+?)_(?!_[^_])\b/us', $text, $matches)) {
return [
[
'emph',
$this->parseInline($matches[1]),
],
strlen($matches[0])
];
}
}
return [['text', $text[0]], 1];
}
protected function renderStrong($block)
{
return '<strong>' . $this->renderAbsy($block[1]) . '</strong>';
}
protected function renderEmph($block)
{
return '<em>' . $this->renderAbsy($block[1]) . '</em>';
}
}

View File

@@ -0,0 +1,278 @@
<?php
/**
* @copyright Copyright (c) 2014 Carsten Brandt
* @license https://github.com/cebe/markdown/blob/master/LICENSE
* @link https://github.com/cebe/markdown#readme
*/
namespace cebe\markdown\inline;
// work around https://github.com/facebook/hhvm/issues/1120
defined('ENT_HTML401') || define('ENT_HTML401', 0);
/**
* Addes links and images as well as url markers.
*
* This trait conflicts with the HtmlTrait. If both are used together,
* you have to define a resolution, by defining the HtmlTrait::parseInlineHtml
* as private so it is not used directly:
*
* ```php
* use block\HtmlTrait {
* parseInlineHtml as private parseInlineHtml;
* }
* ```
*
* If the method exists it is called internally by this trait.
*
* Also make sure to reset references on prepare():
*
* ```php
* protected function prepare()
* {
* // reset references
* $this->references = [];
* }
* ```
*/
trait LinkTrait
{
/**
* @var array a list of defined references in this document.
*/
protected $references = [];
/**
* Remove backslash from escaped characters
* @param $text
* @return string
*/
protected function replaceEscape($text)
{
$strtr = [];
foreach($this->escapeCharacters as $char) {
$strtr["\\$char"] = $char;
}
return strtr($text, $strtr);
}
/**
* Parses a link indicated by `[`.
* @marker [
*/
protected function parseLink($markdown)
{
if (!in_array('parseLink', array_slice($this->context, 1)) && ($parts = $this->parseLinkOrImage($markdown)) !== false) {
list($text, $url, $title, $offset, $key) = $parts;
return [
[
'link',
'text' => $this->parseInline($text),
'url' => $url,
'title' => $title,
'refkey' => $key,
'orig' => substr($markdown, 0, $offset),
],
$offset
];
} else {
// remove all starting [ markers to avoid next one to be parsed as link
$result = '[';
$i = 1;
while (isset($markdown[$i]) && $markdown[$i] == '[') {
$result .= '[';
$i++;
}
return [['text', $result], $i];
}
}
/**
* Parses an image indicated by `![`.
* @marker ![
*/
protected function parseImage($markdown)
{
if (($parts = $this->parseLinkOrImage(substr($markdown, 1))) !== false) {
list($text, $url, $title, $offset, $key) = $parts;
return [
[
'image',
'text' => $text,
'url' => $url,
'title' => $title,
'refkey' => $key,
'orig' => substr($markdown, 0, $offset + 1),
],
$offset + 1
];
} else {
// remove all starting [ markers to avoid next one to be parsed as link
$result = '!';
$i = 1;
while (isset($markdown[$i]) && $markdown[$i] == '[') {
$result .= '[';
$i++;
}
return [['text', $result], $i];
}
}
protected function parseLinkOrImage($markdown)
{
if (strpos($markdown, ']') !== false && preg_match('/\[((?>[^\]\[]+|(?R))*)\]/', $markdown, $textMatches)) { // TODO improve bracket regex
$text = $textMatches[1];
$offset = strlen($textMatches[0]);
$markdown = substr($markdown, $offset);
$pattern = <<<REGEXP
/(?(R) # in case of recursion match parentheses
\(((?>[^\s()]+)|(?R))*\)
| # else match a link with title
^\(\s*(((?>[^\s()]+)|(?R))*)(\s+"(.*?)")?\s*\)
)/x
REGEXP;
if (preg_match($pattern, $markdown, $refMatches)) {
// inline link
return [
$text,
isset($refMatches[2]) ? $this->replaceEscape($refMatches[2]) : '', // url
empty($refMatches[5]) ? null: $refMatches[5], // title
$offset + strlen($refMatches[0]), // offset
null, // reference key
];
} elseif (preg_match('/^([ \n]?\[(.*?)\])?/s', $markdown, $refMatches)) {
// reference style link
if (empty($refMatches[2])) {
$key = strtolower($text);
} else {
$key = strtolower($refMatches[2]);
}
return [
$text,
null, // url
null, // title
$offset + strlen($refMatches[0]), // offset
$key,
];
}
}
return false;
}
/**
* Parses inline HTML.
* @marker <
*/
protected function parseLt($text)
{
if (strpos($text, '>') !== false) {
if (!in_array('parseLink', $this->context)) { // do not allow links in links
if (preg_match('/^<([^\s]*?@[^\s]*?\.\w+?)>/', $text, $matches)) {
// email address
return [
['email', $this->replaceEscape($matches[1])],
strlen($matches[0])
];
} elseif (preg_match('/^<([a-z]{3,}:\/\/[^\s]+?)>/', $text, $matches)) {
// URL
return [
['url', $this->replaceEscape($matches[1])],
strlen($matches[0])
];
}
}
// try inline HTML if it was neither a URL nor email if HtmlTrait is included.
if (method_exists($this, 'parseInlineHtml')) {
return $this->parseInlineHtml($text);
}
}
return [['text', '&lt;'], 1];
}
protected function renderEmail($block)
{
$email = htmlspecialchars($block[1], ENT_NOQUOTES | ENT_SUBSTITUTE, 'UTF-8');
return "<a href=\"mailto:$email\">$email</a>";
}
protected function renderUrl($block)
{
$url = htmlspecialchars($block[1], ENT_COMPAT | ENT_HTML401, 'UTF-8');
$decodedUrl = urldecode($block[1]);
$secureUrlText = preg_match('//u', $decodedUrl) ? $decodedUrl : $block[1];
$text = htmlspecialchars($secureUrlText, ENT_NOQUOTES | ENT_SUBSTITUTE, 'UTF-8');
return "<a href=\"$url\">$text</a>";
}
protected function lookupReference($key)
{
$normalizedKey = preg_replace('/\s+/', ' ', $key);
if (isset($this->references[$key]) || isset($this->references[$key = $normalizedKey])) {
return $this->references[$key];
}
return false;
}
protected function renderLink($block)
{
if (isset($block['refkey'])) {
if (($ref = $this->lookupReference($block['refkey'])) !== false) {
$block = array_merge($block, $ref);
} else {
return $block['orig'];
}
}
return '<a href="' . htmlspecialchars($block['url'], ENT_COMPAT | ENT_HTML401, 'UTF-8') . '"'
. (empty($block['title']) ? '' : ' title="' . htmlspecialchars($block['title'], ENT_COMPAT | ENT_HTML401 | ENT_SUBSTITUTE, 'UTF-8') . '"')
. '>' . $this->renderAbsy($block['text']) . '</a>';
}
protected function renderImage($block)
{
if (isset($block['refkey'])) {
if (($ref = $this->lookupReference($block['refkey'])) !== false) {
$block = array_merge($block, $ref);
} else {
return $block['orig'];
}
}
return '<img src="' . htmlspecialchars($block['url'], ENT_COMPAT | ENT_HTML401, 'UTF-8') . '"'
. ' alt="' . htmlspecialchars($block['text'], ENT_COMPAT | ENT_HTML401 | ENT_SUBSTITUTE, 'UTF-8') . '"'
. (empty($block['title']) ? '' : ' title="' . htmlspecialchars($block['title'], ENT_COMPAT | ENT_HTML401 | ENT_SUBSTITUTE, 'UTF-8') . '"')
. ($this->html5 ? '>' : ' />');
}
// references
protected function identifyReference($line)
{
return ($line[0] === ' ' || $line[0] === '[') && preg_match('/^ {0,3}\[(.+?)\]:\s*([^\s]+?)(?:\s+[\'"](.+?)[\'"])?\s*$/', $line);
}
/**
* Consume link references
*/
protected function consumeReference($lines, $current)
{
while (isset($lines[$current]) && preg_match('/^ {0,3}\[(.+?)\]:\s*(.+?)(?:\s+[\(\'"](.+?)[\)\'"])?\s*$/', $lines[$current], $matches)) {
$label = strtolower($matches[1]);
$this->references[$label] = [
'url' => $this->replaceEscape($matches[2]),
];
if (isset($matches[3])) {
$this->references[$label]['title'] = $matches[3];
} else {
// title may be on the next line
if (isset($lines[$current + 1]) && preg_match('/^\s+[\(\'"](.+?)[\)\'"]\s*$/', $lines[$current + 1], $matches)) {
$this->references[$label]['title'] = $matches[1];
$current++;
}
}
$current++;
}
return [false, --$current];
}
}

View File

@@ -0,0 +1,37 @@
<?php
/**
* @copyright Copyright (c) 2014 Carsten Brandt
* @license https://github.com/cebe/markdown/blob/master/LICENSE
* @link https://github.com/cebe/markdown#readme
*/
namespace cebe\markdown\inline;
/**
* Adds strikeout inline elements
*/
trait StrikeoutTrait
{
/**
* Parses the strikethrough feature.
* @marker ~~
*/
protected function parseStrike($markdown)
{
if (preg_match('/^~~(.+?)~~/', $markdown, $matches)) {
return [
[
'strike',
$this->parseInline($matches[1])
],
strlen($matches[0])
];
}
return [['text', $markdown[0] . $markdown[1]], 2];
}
protected function renderStrike($block)
{
return '<del>' . $this->renderAbsy($block[1]) . '</del>';
}
}

View File

@@ -0,0 +1,50 @@
<?php
/**
* @copyright Copyright (c) 2014 Carsten Brandt
* @license https://github.com/cebe/markdown/blob/master/LICENSE
* @link https://github.com/cebe/markdown#readme
*/
namespace cebe\markdown\inline;
// work around https://github.com/facebook/hhvm/issues/1120
defined('ENT_HTML401') || define('ENT_HTML401', 0);
/**
* Adds auto linking for URLs
*/
trait UrlLinkTrait
{
/**
* Parses urls and adds auto linking feature.
* @marker http
* @marker ftp
*/
protected function parseUrl($markdown)
{
$pattern = <<<REGEXP
/(?(R) # in case of recursion match parentheses
\(((?>[^\s()]+)|(?R))*\)
| # else match a link with title
^(https?|ftp):\/\/(([^\s()]+)|(?R))+(?<![\.,:;\'"!\?\s])
)/x
REGEXP;
if (!in_array('parseLink', $this->context) && preg_match($pattern, $markdown, $matches)) {
return [
['autoUrl', $matches[0]],
strlen($matches[0])
];
}
return [['text', substr($markdown, 0, 4)], 4];
}
protected function renderAutoUrl($block)
{
$href = htmlspecialchars($block[1], ENT_COMPAT | ENT_HTML401, 'UTF-8');
$decodedUrl = urldecode($block[1]);
$secureUrlText = preg_match('//u', $decodedUrl) ? $decodedUrl : $block[1];
$text = htmlspecialchars($secureUrlText, ENT_NOQUOTES | ENT_SUBSTITUTE, 'UTF-8');
return "<a href=\"$href\">$text</a>";
}
}

27
vendor/cebe/markdown/phpunit.xml.dist vendored Normal file
View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<phpunit bootstrap="./tests/bootstrap.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
stopOnFailure="false">
<testsuites>
<testsuite name="Markdown Test Suite">
<file>./tests/ParserTest.php</file>
<file>./tests/MarkdownTest.php</file>
<file>./tests/MarkdownOLStartNumTest.php</file>
<file>./tests/GithubMarkdownTest.php</file>
<file>./tests/MarkdownExtraTest.php</file>
</testsuite>
</testsuites>
<filter>
<blacklist>
<directory>./vendor</directory>
<directory>./tests</directory>
</blacklist>
</filter>
</phpunit>

View File

@@ -0,0 +1,115 @@
<?php
/**
* @copyright Copyright (c) 2014 Carsten Brandt
* @license https://github.com/cebe/markdown/blob/master/LICENSE
* @link https://github.com/cebe/markdown#readme
*/
namespace cebe\markdown\tests;
use cebe\markdown\Parser;
/**
* Base class for all Test cases.
*
* @author Carsten Brandt <mail@cebe.cc>
*/
abstract class BaseMarkdownTest extends \PHPUnit_Framework_TestCase
{
protected $outputFileExtension = '.html';
abstract public function getDataPaths();
/**
* @return Parser
*/
abstract public function createMarkdown();
/**
* @dataProvider dataFiles
*/
public function testParse($path, $file)
{
list($markdown, $html) = $this->getTestData($path, $file);
// Different OS line endings should not affect test
$html = str_replace(["\r\n", "\n\r", "\r"], "\n", $html);
$m = $this->createMarkdown();
$this->assertEquals($html, $m->parse($markdown));
}
public function testUtf8()
{
$this->assertSame("<p>абвгдеёжзийклмнопрстуфхцчшщъыьэюя</p>\n", $this->createMarkdown()->parse('абвгдеёжзийклмнопрстуфхцчшщъыьэюя'));
$this->assertSame("<p>there is a charater, 配</p>\n", $this->createMarkdown()->parse('there is a charater, 配'));
$this->assertSame("<p>Arabic Latter \"م (M)\"</p>\n", $this->createMarkdown()->parse('Arabic Latter "م (M)"'));
$this->assertSame("<p>電腦</p>\n", $this->createMarkdown()->parse('電腦'));
$this->assertSame('абвгдеёжзийклмнопрстуфхцчшщъыьэюя', $this->createMarkdown()->parseParagraph('абвгдеёжзийклмнопрстуфхцчшщъыьэюя'));
$this->assertSame('there is a charater, 配', $this->createMarkdown()->parseParagraph('there is a charater, 配'));
$this->assertSame('Arabic Latter "م (M)"', $this->createMarkdown()->parseParagraph('Arabic Latter "م (M)"'));
$this->assertSame('電腦', $this->createMarkdown()->parseParagraph('電腦'));
}
public function testInvalidUtf8()
{
$m = $this->createMarkdown();
$this->assertEquals("<p><code><3E></code></p>\n", $m->parse("`\x80`"));
$this->assertEquals('<code><3E></code>', $m->parseParagraph("`\x80`"));
}
public function pregData()
{
// http://en.wikipedia.org/wiki/Newline#Representations
return [
["a\r\nb", "a\nb"],
["a\n\rb", "a\nb"], // Acorn BBC and RISC OS spooled text output :)
["a\nb", "a\nb"],
["a\rb", "a\nb"],
["a\n\nb", "a\n\nb", "a</p>\n<p>b"],
["a\r\rb", "a\n\nb", "a</p>\n<p>b"],
["a\n\r\n\rb", "a\n\nb", "a</p>\n<p>b"], // Acorn BBC and RISC OS spooled text output :)
["a\r\n\r\nb", "a\n\nb", "a</p>\n<p>b"],
];
}
/**
* @dataProvider pregData
*/
public function testPregReplaceR($input, $exptected, $pexpect = null)
{
$this->assertSame($exptected, $this->createMarkdown()->parseParagraph($input));
$this->assertSame($pexpect === null ? "<p>$exptected</p>\n" : "<p>$pexpect</p>\n", $this->createMarkdown()->parse($input));
}
public function getTestData($path, $file)
{
return [
file_get_contents($this->getDataPaths()[$path] . '/' . $file . '.md'),
file_get_contents($this->getDataPaths()[$path] . '/' . $file . $this->outputFileExtension),
];
}
public function dataFiles()
{
$files = [];
foreach ($this->getDataPaths() as $name => $src) {
$handle = opendir($src);
if ($handle === false) {
throw new \Exception('Unable to open directory: ' . $src);
}
while (($file = readdir($handle)) !== false) {
if ($file === '.' || $file === '..') {
continue;
}
if (substr($file, -3, 3) === '.md' && file_exists($src . '/' . substr($file, 0, -3) . $this->outputFileExtension)) {
$files[] = [$name, substr($file, 0, -3)];
}
}
closedir($handle);
}
return $files;
}
}

View File

@@ -0,0 +1,80 @@
<?php
/**
* @copyright Copyright (c) 2014 Carsten Brandt
* @license https://github.com/cebe/markdown/blob/master/LICENSE
* @link https://github.com/cebe/markdown#readme
*/
namespace cebe\markdown\tests;
use cebe\markdown\GithubMarkdown;
/**
* Test case for the github flavored markdown.
*
* @author Carsten Brandt <mail@cebe.cc>
* @group github
*/
class GithubMarkdownTest extends BaseMarkdownTest
{
public function createMarkdown()
{
return new GithubMarkdown();
}
public function getDataPaths()
{
return [
'markdown-data' => __DIR__ . '/markdown-data',
'github-data' => __DIR__ . '/github-data',
];
}
public function testNewlines()
{
$markdown = $this->createMarkdown();
$this->assertEquals("This is text<br />\nnewline\nnewline.", $markdown->parseParagraph("This is text \nnewline\nnewline."));
$markdown->enableNewlines = true;
$this->assertEquals("This is text<br />\nnewline<br />\nnewline.", $markdown->parseParagraph("This is text \nnewline\nnewline."));
$this->assertEquals("<p>This is text</p>\n<p>newline<br />\nnewline.</p>\n", $markdown->parse("This is text\n\nnewline\nnewline."));
}
public function dataFiles()
{
$files = parent::dataFiles();
foreach($files as $i => $f) {
// skip files that are different in github MD
if ($f[0] === 'markdown-data' && (
$f[1] === 'list-marker-in-paragraph' ||
$f[1] === 'dense-block-markers'
)) {
unset($files[$i]);
}
}
return $files;
}
public function testKeepZeroAlive()
{
$parser = $this->createMarkdown();
$this->assertEquals("0", $parser->parseParagraph("0"));
$this->assertEquals("<p>0</p>\n", $parser->parse("0"));
}
public function testAutoLinkLabelingWithEncodedUrl()
{
$parser = $this->createMarkdown();
$utfText = "\xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xe3\x81\x88\xe3\x81\x8a";
$utfNaturalUrl = "http://example.com/" . $utfText;
$utfEncodedUrl = "http://example.com/" . urlencode($utfText);
$eucEncodedUrl = "http://example.com/" . urlencode(mb_convert_encoding($utfText, 'EUC-JP', 'UTF-8'));
$this->assertStringEndsWith(">{$utfNaturalUrl}</a>", $parser->parseParagraph($utfNaturalUrl), "Natural UTF-8 URL needs no conversion.");
$this->assertStringEndsWith(">{$utfNaturalUrl}</a>", $parser->parseParagraph($utfEncodedUrl), "Encoded UTF-8 URL will be converted to readable format.");
$this->assertStringEndsWith(">{$eucEncodedUrl}</a>", $parser->parseParagraph($eucEncodedUrl), "Non UTF-8 URL should never be converted.");
// See: \cebe\markdown\inline\UrlLinkTrait::renderAutoUrl
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace cebe\markdown\tests;
use cebe\markdown\MarkdownExtra;
/**
* @author Carsten Brandt <mail@cebe.cc>
* @group extra
*/
class MarkdownExtraTest extends BaseMarkdownTest
{
public function createMarkdown()
{
return new MarkdownExtra();
}
public function getDataPaths()
{
return [
'markdown-data' => __DIR__ . '/markdown-data',
'extra-data' => __DIR__ . '/extra-data',
];
}
}

View File

@@ -0,0 +1,32 @@
<?php
/**
* @copyright Copyright (c) 2014 Carsten Brandt
* @license https://github.com/cebe/markdown/blob/master/LICENSE
* @link https://github.com/cebe/markdown#readme
*/
namespace cebe\markdown\tests;
use cebe\markdown\Markdown;
/**
* Test support ordered lists at arbitrary number(`start` html attribute)
* @author Maxim Hodyrew <maximkou@gmail.com>
* @group default
*/
class MarkdownOLStartNumTest extends BaseMarkdownTest
{
public function createMarkdown()
{
$markdown = new Markdown();
$markdown->keepListStartNumber = true;
return $markdown;
}
public function getDataPaths()
{
return [
'markdown-data' => __DIR__ . '/markdown-ol-start-num-data',
];
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* @copyright Copyright (c) 2014 Carsten Brandt
* @license https://github.com/cebe/markdown/blob/master/LICENSE
* @link https://github.com/cebe/markdown#readme
*/
namespace cebe\markdown\tests;
use cebe\markdown\Markdown;
/**
* Test case for traditional markdown.
*
* @author Carsten Brandt <mail@cebe.cc>
* @group default
*/
class MarkdownTest extends BaseMarkdownTest
{
public function createMarkdown()
{
return new Markdown();
}
public function getDataPaths()
{
return [
'markdown-data' => __DIR__ . '/markdown-data',
];
}
public function testEdgeCases()
{
$this->assertEquals("<p>&amp;</p>\n", $this->createMarkdown()->parse('&'));
$this->assertEquals("<p>&lt;</p>\n", $this->createMarkdown()->parse('<'));
}
public function testKeepZeroAlive()
{
$parser = $this->createMarkdown();
$this->assertEquals("0", $parser->parseParagraph("0"));
$this->assertEquals("<p>0</p>\n", $parser->parse("0"));
}
public function testAutoLinkLabelingWithEncodedUrl()
{
$parser = $this->createMarkdown();
$utfText = "\xe3\x81\x82\xe3\x81\x84\xe3\x81\x86\xe3\x81\x88\xe3\x81\x8a";
$utfNaturalUrl = "http://example.com/" . $utfText;
$utfEncodedUrl = "http://example.com/" . urlencode($utfText);
$eucEncodedUrl = "http://example.com/" . urlencode(mb_convert_encoding($utfText, 'EUC-JP', 'UTF-8'));
$this->assertStringEndsWith(">{$utfNaturalUrl}</a>", $parser->parseParagraph("<{$utfNaturalUrl}>"), "Natural UTF-8 URL needs no conversion.");
$this->assertStringEndsWith(">{$utfNaturalUrl}</a>", $parser->parseParagraph("<{$utfEncodedUrl}>"), "Encoded UTF-8 URL will be converted to readable format.");
$this->assertStringEndsWith(">{$eucEncodedUrl}</a>", $parser->parseParagraph("<{$eucEncodedUrl}>"), "Non UTF-8 URL should never be converted.");
// See: \cebe\markdown\inline\LinkTrait::renderUrl
}
}

View File

@@ -0,0 +1,94 @@
<?php
/**
* @copyright Copyright (c) 2014 Carsten Brandt
* @license https://github.com/cebe/markdown/blob/master/LICENSE
* @link https://github.com/cebe/markdown#readme
*/
namespace cebe\markdown\tests;
use cebe\markdown\Parser;
/**
* Test case for the parser base class.
*
* @author Carsten Brandt <mail@cebe.cc>
* @group default
*/
class ParserTest extends \PHPUnit_Framework_TestCase
{
public function testMarkerOrder()
{
$parser = new TestParser();
$parser->markers = [
'[' => 'parseMarkerA',
'[[' => 'parseMarkerB',
];
$this->assertEquals("<p>Result is A</p>\n", $parser->parse('Result is [abc]'));
$this->assertEquals("<p>Result is B</p>\n", $parser->parse('Result is [[abc]]'));
$this->assertEquals('Result is A', $parser->parseParagraph('Result is [abc]'));
$this->assertEquals('Result is B', $parser->parseParagraph('Result is [[abc]]'));
$parser = new TestParser();
$parser->markers = [
'[[' => 'parseMarkerB',
'[' => 'parseMarkerA',
];
$this->assertEquals("<p>Result is A</p>\n", $parser->parse('Result is [abc]'));
$this->assertEquals("<p>Result is B</p>\n", $parser->parse('Result is [[abc]]'));
$this->assertEquals('Result is A', $parser->parseParagraph('Result is [abc]'));
$this->assertEquals('Result is B', $parser->parseParagraph('Result is [[abc]]'));
}
public function testMaxNestingLevel()
{
$parser = new TestParser();
$parser->markers = [
'[' => 'parseMarkerC',
];
$parser->maximumNestingLevel = 3;
$this->assertEquals("(C-a(C-b(C-c)))", $parser->parseParagraph('[a[b[c]]]'));
$parser->maximumNestingLevel = 2;
$this->assertEquals("(C-a(C-b[c]))", $parser->parseParagraph('[a[b[c]]]'));
$parser->maximumNestingLevel = 1;
$this->assertEquals("(C-a[b[c]])", $parser->parseParagraph('[a[b[c]]]'));
}
public function testKeepZeroAlive()
{
$parser = new TestParser();
$this->assertEquals("0", $parser->parseParagraph("0"));
$this->assertEquals("<p>0</p>\n", $parser->parse("0"));
}
}
class TestParser extends Parser
{
public $markers = [];
protected function inlineMarkers()
{
return $this->markers;
}
protected function parseMarkerA($text)
{
return [['text', 'A'], strrpos($text, ']') + 1];
}
protected function parseMarkerB($text)
{
return [['text', 'B'], strrpos($text, ']') + 1];
}
protected function parseMarkerC($text)
{
$terminatingMarkerPos = strrpos($text, ']');
$inside = $this->parseInline(substr($text, 1, $terminatingMarkerPos - 1));
return [['text', '(C-' . $this->renderAbsy($inside) . ')'], $terminatingMarkerPos + 1];
}
}

View File

@@ -0,0 +1,5 @@
<?php
if (file_exists(__DIR__ . '/../vendor/autoload.php')) {
require(__DIR__ . '/../vendor/autoload.php');
}

View File

@@ -0,0 +1,17 @@
<pre><code>
fenced code block
</code></pre>
<pre><code>
fenced with tildes
</code></pre>
<pre><code>long fence
```
code about code
```
</code></pre>
<pre><code class="html" id="test">fenced code block
</code></pre>

View File

@@ -0,0 +1,24 @@
```
fenced code block
```
~~~
fenced with tildes
~~~
``````````
long fence
```
code about code
```
``````````
``` .html #test
fenced code block
```

View File

@@ -0,0 +1,12 @@
<h1 id="header1">Header 1</h1>
<h2 id="header2">Header 2</h2>
<h2 class="main">The Site</h2>
<h2 class="main shine" id="the-site">The Site</h2>
<p><a href="url" id="id1" class="class">link</a>
<img src="url" alt="img" id="id2" class="class" /></p>
<p><a href="http://url.de/" title="optional title" id="id" class="class">link</a> or <a href="http://url.de/" title="optional title" id="id" class="class">linkref</a>
<img src="http://url.de/" alt="img" title="optional title" id="id" class="class" /></p>
<p>this is just normal text {.main .shine #the-site}</p>
<p>some { brackets</p>
<p>some } brackets</p>
<p>some { } brackets</p>

View File

@@ -0,0 +1,25 @@
Header 1 {#header1}
========
## Header 2 ## {#header2}
## The Site ## {.main}
## The Site ## {.main .shine #the-site}
[link](url){#id1 .class}
![img](url){#id2 .class}
[link][linkref] or [linkref]
![img][linkref]
[linkref]: http://url.de/ "optional title" {#id .class}
this is just normal text {.main .shine #the-site}
some { brackets
some } brackets
some { } brackets

View File

@@ -0,0 +1,89 @@
<h2>Tables</h2>
<table>
<thead>
<tr><th>First Header </th><th>Second Header</th></tr>
</thead>
<tbody>
<tr><td>Content Cell </td><td>Content Cell</td></tr>
<tr><td>Content Cell </td><td>Content Cell</td></tr>
</tbody>
</table>
<table>
<thead>
<tr><th>First Header </th><th>Second Header</th></tr>
</thead>
<tbody>
<tr><td>Content Cell </td><td>Content Cell</td></tr>
<tr><td>Content Cell </td><td>Content Cell</td></tr>
</tbody>
</table>
<table>
<thead>
<tr><th>Name </th><th>Description</th></tr>
</thead>
<tbody>
<tr><td>Help </td><td>Display the help window.</td></tr>
<tr><td>Close </td><td>Closes a window</td></tr>
</tbody>
</table>
<table>
<thead>
<tr><th>Name </th><th>Description</th></tr>
</thead>
<tbody>
<tr><td>Help </td><td><strong>Display the</strong> help window.</td></tr>
<tr><td>Close </td><td><em>Closes</em> a window</td></tr>
</tbody>
</table>
<table>
<thead>
<tr><th>Default-Align </th><th align="left">Left-Aligned </th><th align="center">Center Aligned </th><th align="right">Right Aligned</th></tr>
</thead>
<tbody>
<tr><td>1 </td><td align="left">col 3 is </td><td align="center">some wordy text </td><td align="right">$1600</td></tr>
<tr><td>2 </td><td align="left">col 2 is </td><td align="center">centered </td><td align="right"> $12</td></tr>
<tr><td>3 </td><td align="left">zebra stripes </td><td align="center">are neat </td><td align="right"> $1</td></tr>
</tbody>
</table>
<table>
<thead>
<tr><th>Simple </th><th>Table</th></tr>
</thead>
<tbody>
<tr><td>1 </td><td>2</td></tr>
<tr><td>3 </td><td>4</td></tr>
</tbody>
</table>
<table>
<thead>
<tr><th>Simple </th><th>Table</th></tr>
</thead>
<tbody>
<tr><td>1 </td><td>2</td></tr>
<tr><td>3 </td><td>4</td></tr>
<tr><td>3 </td><td>4 |</td></tr>
<tr><td>3 </td><td>4 \</td></tr>
</tbody>
</table>
<p>Check https://github.com/erusev/parsedown/issues/184 for the following:</p>
<table>
<thead>
<tr><th>Foo </th><th>Bar </th><th>State</th></tr>
</thead>
<tbody>
<tr><td><code>Code | Pipe</code> </td><td>Broken </td><td>Blank</td></tr>
<tr><td><code>Escaped Code \| Pipe</code> </td><td>Broken </td><td>Blank</td></tr>
<tr><td>Escaped | Pipe </td><td>Broken </td><td>Blank</td></tr>
<tr><td>Escaped \</td><td>Pipe </td><td>Broken </td><td>Blank</td></tr>
<tr><td>Escaped \ </td><td>Pipe </td><td>Broken </td><td>Blank</td></tr>
</tbody>
</table>
<table>
<thead>
<tr><th align="left">Simple </th><th>Table</th></tr>
</thead>
<tbody>
<tr><td align="left">3 </td><td>4</td></tr>
</tbody>
</table>
<p>3 | 4</p>

View File

@@ -0,0 +1,56 @@
Tables
------
First Header | Second Header
------------- | -------------
Content Cell | Content Cell
Content Cell | Content Cell
| First Header | Second Header |
| ------------- | ------------- |
| Content Cell | Content Cell |
| Content Cell | Content Cell |
| Name | Description |
| ------------- | ----------- |
| Help | Display the help window.|
| Close | Closes a window |
| Name | Description |
| ------------- | ----------- |
| Help | **Display the** help window.|
| Close | _Closes_ a window |
| Default-Align | Left-Aligned | Center Aligned | Right Aligned |
| ------------- | :------------ |:---------------:| -----:|
| 1 | col 3 is | some wordy text | $1600 |
| 2 | col 2 is | centered | $12 |
| 3 | zebra stripes | are neat | $1 |
Simple | Table
------ | -----
1 | 2
3 | 4
| Simple | Table |
| ------ | ----- |
| 1 | 2 |
| 3 | 4 |
| 3 | 4 \|
| 3 | 4 \\|
Check https://github.com/erusev/parsedown/issues/184 for the following:
Foo | Bar | State
------ | ------ | -----
`Code | Pipe` | Broken | Blank
`Escaped Code \| Pipe` | Broken | Blank
Escaped \| Pipe | Broken | Blank
Escaped \\| Pipe | Broken | Blank
Escaped \\ | Pipe | Broken | Blank
| Simple | Table |
| :----- | ----- |
| 3 | 4 |
3 | 4

View File

@@ -0,0 +1,8 @@
<p>Not a headline but a code block:</p>
<pre><code>---
</code></pre>
<p>Not a headline but two HR:</p>
<hr />
<hr />
<hr />
<hr />

View File

@@ -0,0 +1,14 @@
Not a headline but a code block:
```
---
```
Not a headline but two HR:
***
---
---
***

View File

@@ -0,0 +1,5 @@
<p>this is <del>striked out</del> after</p>
<p><del>striked out</del></p>
<p>a line with ~~ in it ...</p>
<p>~~</p>
<p>~</p>

View File

@@ -0,0 +1,9 @@
this is ~~striked out~~ after
~~striked out~~
a line with ~~ in it ...
~~
~

View File

@@ -0,0 +1,52 @@
<h1>this is to test dense blocks (no newlines between them)</h1>
<hr />
<h2>what is Markdown?</h2>
<p>see <a href="http://en.wikipedia.org/wiki/Markdown">Wikipedia</a></p>
<h2>a h2</h2>
<p>paragraph</p>
<p>this is a paragraph, not a headline or list
next line</p>
<ul>
<li>whoo</li>
</ul>
<p>par</p>
<pre><code>code
code
</code></pre>
<p>par</p>
<h3>Tasks list</h3>
<ul>
<li>list items</li>
</ul>
<h2>headline1</h2>
<blockquote><p>quote
quote</p>
</blockquote>
<h2>headline2</h2>
<hr />
<h1>h1</h1>
<h2>h2</h2>
<hr />
<h3>h3</h3>
<ol>
<li>ol1</li>
<li>ol2</li>
</ol>
<h4>h4</h4>
<ul>
<li>listA</li>
<li>listB</li>
</ul>
<h5>h5</h5>
<h6>h6</h6>
<hr />
<hr />
<h2>changelog 1</h2>
<ul>
<li>17-Feb-2013 re-design</li>
</ul>
<hr />
<h2>changelog 2</h2>
<ul>
<li>17-Feb-2013 re-design</li>
</ul>

View File

@@ -0,0 +1,56 @@
# this is to test dense blocks (no newlines between them)
----
## what is Markdown?
see [Wikipedia][]
a h2
----
paragraph
this is a paragraph, not a headline or list
next line
- whoo
par
code
code
par
### Tasks list
- list items
headline1
---------
> quote
> quote
[Wikipedia]: http://en.wikipedia.org/wiki/Markdown
headline2
---------
----
# h1
## h2
---
### h3
1. ol1
2. ol2
#### h4
- listA
- listB
##### h5
###### h6
--------
----
## changelog 1
* 17-Feb-2013 re-design
----
## changelog 2
* 17-Feb-2013 re-design

View File

@@ -0,0 +1,23 @@
<p>Now we need to set:</p>
<pre><code class="language-php">'session' =&gt; [
'cookieParams' =&gt; [
'path' =&gt; '/path1/',
]
],
</code></pre>
<p>and</p>
<pre><code class="language-php">'session' =&gt; [
'cookieParams' =&gt; [
'path' =&gt; '/path2/',
]
],
</code></pre>
<p>In the following starts a Blockquote:</p>
<blockquote><p>this is a blockquote</p>
</blockquote>
<p>par</p>
<hr />
<p>par</p>
<p>This is some text</p>
<h1>Headline1</h1>
<p>more text</p>

View File

@@ -0,0 +1,27 @@
Now we need to set:
```php
'session' => [
'cookieParams' => [
'path' => '/path1/',
]
],
```
and
```php
'session' => [
'cookieParams' => [
'path' => '/path2/',
]
],
```
In the following starts a Blockquote:
> this is a blockquote
par
***
par
This is some text
# Headline1
more text

View File

@@ -0,0 +1,19 @@
<h1>GitHub Flavored Markdown</h1>
<h2>Multiple underscores in words</h2>
<p>do_this_and_do_that_and_another_thing</p>
<h2>URL autolinking</h2>
<p><a href="http://example.com">http://example.com</a></p>
<h2>Strikethrough</h2>
<p><del>Mistaken text.</del></p>
<h2>Fenced code blocks</h2>
<pre><code>function test() {
console.log("notice the blank line before this function?");
}
</code></pre>
<h2>Syntax highlighting</h2>
<pre><code class="language-ruby">require 'redcarpet'
markdown = Redcarpet.new("Hello World!")
puts markdown.to_html
</code></pre>
<pre><code>this is also code
</code></pre>

View File

@@ -0,0 +1,40 @@
GitHub Flavored Markdown
========================
Multiple underscores in words
-----------------------------
do_this_and_do_that_and_another_thing
URL autolinking
---------------
http://example.com
Strikethrough
-------------
~~Mistaken text.~~
Fenced code blocks
------------------
```
function test() {
console.log("notice the blank line before this function?");
}
```
Syntax highlighting
-------------------
```ruby
require 'redcarpet'
markdown = Redcarpet.new("Hello World!")
puts markdown.to_html
```
~~~
this is also code
~~~

View File

@@ -0,0 +1,12 @@
<ol>
<li>Item one.</li>
<li><p>Item two with some code:</p>
<pre><code>code one
</code></pre>
</li>
<li><p>Item three with code:</p>
<pre><code>code two
</code></pre>
</li>
</ol>
<p>Paragraph.</p>

View File

@@ -0,0 +1,14 @@
1. Item one.
2. Item two with some code:
```
code one
```
3. Item three with code:
```
code two
```
Paragraph.

View File

@@ -0,0 +1,117 @@
<h1>GitHub Flavored Markdown</h1>
<p><em>View the <a href="http://github.github.com/github-flavored-markdown/sample_content.html">source of this content</a>.</em></p>
<p>Let's get the whole "linebreak" thing out of the way. The next paragraph contains two phrases separated by a single newline character:</p>
<p>Roses are red
Violets are blue</p>
<p>The next paragraph has the same phrases, but now they are separated by two spaces and a newline character:</p>
<p>Roses are red<br />
Violets are blue</p>
<p>Oh, and one thing I cannot stand is the mangling of words with multiple underscores in them like perform_complicated_task or do_this_and_do_that_and_another_thing.</p>
<h2>A bit of the GitHub spice</h2>
<p>In addition to the changes in the previous section, certain references are auto-linked:</p>
<ul>
<li>SHA: be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2</li>
<li>User@SHA ref: mojombo@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2</li>
<li>User/Project@SHA: mojombo/god@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2</li>
<li>#Num: #1</li>
<li>User/#Num: mojombo#1</li>
<li>User/Project#Num: mojombo/god#1</li>
</ul>
<p>These are dangerous goodies though, and we need to make sure email addresses don't get mangled:</p>
<p>My email addy is tom@github.com.</p>
<h2>Math is hard, let's go shopping</h2>
<p>In first grade I learned that 5 &gt; 3 and 2 &lt; 7. Maybe some arrows. 1 -&gt; 2 -&gt; 3. 9 &lt;- 8 &lt;- 7.</p>
<p>Triangles man! a^2 + b^2 = c^2</p>
<h2>We all like making lists</h2>
<p>The above header should be an H2 tag. Now, for a list of fruits:</p>
<ul>
<li>Red Apples</li>
<li>Purple Grapes</li>
<li>Green Kiwifruits</li>
</ul>
<p>Let's get crazy:</p>
<ol>
<li><p>This is a list item with two paragraphs. Lorem ipsum dolor
sit amet, consectetuer adipiscing elit. Aliquam hendrerit
mi posuere lectus.</p>
<p>Vestibulum enim wisi, viverra nec, fringilla in, laoreet
vitae, risus. Donec sit amet nisl. Aliquam semper ipsum
sit amet velit.</p>
</li>
<li><p>Suspendisse id sem consectetuer libero luctus adipiscing.</p>
</li>
</ol>
<p>What about some code <strong>in</strong> a list? That's insane, right?</p>
<ol>
<li><p>In Ruby you can map like this:</p>
<pre><code> ['a', 'b'].map { |x| x.uppercase }
</code></pre>
</li>
<li><p>In Rails, you can do a shortcut:</p>
<pre><code> ['a', 'b'].map(&amp;:uppercase)
</code></pre>
</li>
</ol>
<p>Some people seem to like definition lists</p>
<dl>
<dt>Lower cost</dt>
<dd>The new version of this product costs significantly less than the previous one!</dd>
<dt>Easier to use</dt>
<dd>We've changed the product so that it's much easier to use!</dd>
</dl>
<h2>I am a robot</h2>
<p>Maybe you want to print <code>robot</code> to the console 1000 times. Why not?</p>
<pre><code>def robot_invasion
puts("robot " * 1000)
end
</code></pre>
<p>You see, that was formatted as code because it's been indented by four spaces.</p>
<p>How about we throw some angle braces and ampersands in there?</p>
<pre><code>&lt;div class="footer"&gt;
&amp;copy; 2004 Foo Corporation
&lt;/div&gt;
</code></pre>
<h2>Set in stone</h2>
<p>Preformatted blocks are useful for ASCII art:</p>
<pre>
,-.
, ,-. ,-.
/ \ ( )-( )
\ | ,.>-( )-<
\|,' ( )-( )
Y ___`-' `-'
|/__/ `-'
|
|
| -hrr-
___|_____________
</pre>
<h2>Playing the blame game</h2>
<p>If you need to blame someone, the best way to do so is by quoting them:</p>
<blockquote><p>I, at any rate, am convinced that He does not throw dice.</p>
</blockquote>
<p>Or perhaps someone a little less eloquent:</p>
<blockquote><p>I wish you'd have given me this written question ahead of time so I
could plan for it... I'm sure something will pop into my head here in
the midst of this press conference, with all the pressure of trying to
come up with answer, but it hadn't yet...</p>
<p>I don't want to sound like
I have made no mistakes. I'm confident I have. I just haven't - you
just put me under the spot here, and maybe I'm not as quick on my feet
as I should be in coming up with one.</p>
</blockquote>
<h2>Table for two</h2>
<table>
<tr>
<th>ID</th><th>Name</th><th>Rank</th>
</tr>
<tr>
<td>1</td><td>Tom Preston-Werner</td><td>Awesome</td>
</tr>
<tr>
<td>2</td><td>Albert Einstein</td><td>Nearly as awesome</td>
</tr>
</table>
<h2>Crazy linking action</h2>
<p>I get 10 times more traffic from <a href="http://google.com/" title="Google">Google</a> than from
<a href="http://search.yahoo.com/" title="Yahoo Search">Yahoo</a> or <a href="http://search.msn.com/" title="MSN Search">MSN</a>.</p>

View File

@@ -0,0 +1,159 @@
GitHub Flavored Markdown
================================
*View the [source of this content](http://github.github.com/github-flavored-markdown/sample_content.html).*
Let's get the whole "linebreak" thing out of the way. The next paragraph contains two phrases separated by a single newline character:
Roses are red
Violets are blue
The next paragraph has the same phrases, but now they are separated by two spaces and a newline character:
Roses are red
Violets are blue
Oh, and one thing I cannot stand is the mangling of words with multiple underscores in them like perform_complicated_task or do_this_and_do_that_and_another_thing.
A bit of the GitHub spice
-------------------------
In addition to the changes in the previous section, certain references are auto-linked:
* SHA: be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2
* User@SHA ref: mojombo@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2
* User/Project@SHA: mojombo/god@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2
* \#Num: #1
* User/#Num: mojombo#1
* User/Project#Num: mojombo/god#1
These are dangerous goodies though, and we need to make sure email addresses don't get mangled:
My email addy is tom@github.com.
Math is hard, let's go shopping
-------------------------------
In first grade I learned that 5 > 3 and 2 < 7. Maybe some arrows. 1 -> 2 -> 3. 9 <- 8 <- 7.
Triangles man! a^2 + b^2 = c^2
We all like making lists
------------------------
The above header should be an H2 tag. Now, for a list of fruits:
* Red Apples
* Purple Grapes
* Green Kiwifruits
Let's get crazy:
1. This is a list item with two paragraphs. Lorem ipsum dolor
sit amet, consectetuer adipiscing elit. Aliquam hendrerit
mi posuere lectus.
Vestibulum enim wisi, viverra nec, fringilla in, laoreet
vitae, risus. Donec sit amet nisl. Aliquam semper ipsum
sit amet velit.
2. Suspendisse id sem consectetuer libero luctus adipiscing.
What about some code **in** a list? That's insane, right?
1. In Ruby you can map like this:
['a', 'b'].map { |x| x.uppercase }
2. In Rails, you can do a shortcut:
['a', 'b'].map(&:uppercase)
Some people seem to like definition lists
<dl>
<dt>Lower cost</dt>
<dd>The new version of this product costs significantly less than the previous one!</dd>
<dt>Easier to use</dt>
<dd>We've changed the product so that it's much easier to use!</dd>
</dl>
I am a robot
------------
Maybe you want to print `robot` to the console 1000 times. Why not?
def robot_invasion
puts("robot " * 1000)
end
You see, that was formatted as code because it's been indented by four spaces.
How about we throw some angle braces and ampersands in there?
<div class="footer">
&copy; 2004 Foo Corporation
</div>
Set in stone
------------
Preformatted blocks are useful for ASCII art:
<pre>
,-.
, ,-. ,-.
/ \ ( )-( )
\ | ,.>-( )-<
\|,' ( )-( )
Y ___`-' `-'
|/__/ `-'
|
|
| -hrr-
___|_____________
</pre>
Playing the blame game
----------------------
If you need to blame someone, the best way to do so is by quoting them:
> I, at any rate, am convinced that He does not throw dice.
Or perhaps someone a little less eloquent:
> I wish you'd have given me this written question ahead of time so I
> could plan for it... I'm sure something will pop into my head here in
> the midst of this press conference, with all the pressure of trying to
> come up with answer, but it hadn't yet...
>
> I don't want to sound like
> I have made no mistakes. I'm confident I have. I just haven't - you
> just put me under the spot here, and maybe I'm not as quick on my feet
> as I should be in coming up with one.
Table for two
-------------
<table>
<tr>
<th>ID</th><th>Name</th><th>Rank</th>
</tr>
<tr>
<td>1</td><td>Tom Preston-Werner</td><td>Awesome</td>
</tr>
<tr>
<td>2</td><td>Albert Einstein</td><td>Nearly as awesome</td>
</tr>
</table>
Crazy linking action
--------------------
I get 10 times more traffic from [Google] [1] than from
[Yahoo] [2] or [MSN] [3].
[1]: http://google.com/ "Google"
[2]: http://search.yahoo.com/ "Yahoo Search"
[3]: http://search.msn.com/ "MSN Search"

View File

@@ -0,0 +1,5 @@
<pre><code>hey, check [this].
[this]: https://github.com/cebe/markdown
</code></pre>
<p>is a vaild reference.</p>

View File

@@ -0,0 +1,6 @@
```
hey, check [this].
[this]: https://github.com/cebe/markdown
```
is a vaild reference.

View File

@@ -0,0 +1,21 @@
<blockquote><p>some text
`<code>`
// some code
\</code>``</p>
</blockquote>
<blockquote><p>some text</p>
<pre><code>// some code
</code></pre>
</blockquote>
<blockquote><p>some text</p>
<pre><code>// some code
</code></pre>
</blockquote>
<blockquote><p>some text</p>
<pre><code>// some code
</code></pre>
</blockquote>
<blockquote><p>some text</p>
</blockquote>
<pre><code>// some code
</code></pre>

View File

@@ -0,0 +1,26 @@
> some text
\```
// some code
\```
> some text
```
// some code
```
> some text
> ```
// some code
```
> some text
>
> ```
// some code
```
> some text
```
// some code
```

View File

@@ -0,0 +1,16 @@
<p>Text before list:</p>
<ul>
<li>item 1,</li>
<li>item 2,</li>
<li>item 3.</li>
</ul>
<p>Text after list.</p>
<ul>
<li>test</li>
<li>test<ul>
<li>test</li>
<li>test</li>
</ul>
</li>
<li>test</li>
</ul>

View File

@@ -0,0 +1,12 @@
Text before list:
* item 1,
* item 2,
* item 3.
Text after list.
- test
- test
- test
- test
- test

View File

@@ -0,0 +1,5 @@
<h2>Non-tables</h2>
<p>This line contains two pipes but is not a table. [[yii\widgets\DetailView|DetailView]] widget displays the details of a single data [[yii\widgets\DetailView::$model|model]].</p>
<p>the line above contains a space.</p>
<p>looks | like | head
-:| </p>

View File

@@ -0,0 +1,9 @@
Non-tables
----------
This line contains two pipes but is not a table. [[yii\widgets\DetailView|DetailView]] widget displays the details of a single data [[yii\widgets\DetailView::$model|model]].
the line above contains a space.
looks | like | head
-:|

View File

@@ -0,0 +1,117 @@
<h2>Tables</h2>
<table>
<thead>
<tr><th>First Header </th><th>Second Header</th></tr>
</thead>
<tbody>
<tr><td>Content Cell </td><td>Content Cell</td></tr>
<tr><td>Content Cell </td><td>Content Cell</td></tr>
</tbody>
</table>
<table>
<thead>
<tr><th>First Header </th><th>Second Header</th></tr>
</thead>
<tbody>
<tr><td>Content Cell </td><td>Content Cell</td></tr>
<tr><td>Content Cell </td><td>Content Cell</td></tr>
</tbody>
</table>
<table>
<thead>
<tr><th>Name </th><th>Description</th></tr>
</thead>
<tbody>
<tr><td>Help </td><td>Display the help window.</td></tr>
<tr><td>Close </td><td>Closes a window</td></tr>
</tbody>
</table>
<table>
<thead>
<tr><th>Name </th><th>Description</th></tr>
</thead>
<tbody>
<tr><td>Help </td><td><strong>Display the</strong> help window.</td></tr>
<tr><td>Close </td><td><em>Closes</em> a window</td></tr>
</tbody>
</table>
<table>
<thead>
<tr><th>Default-Align </th><th align="left">Left-Aligned </th><th align="center">Center Aligned </th><th align="right">Right Aligned</th></tr>
</thead>
<tbody>
<tr><td>1 </td><td align="left">col 3 is </td><td align="center">some wordy text </td><td align="right">$1600</td></tr>
<tr><td>2 </td><td align="left">col 2 is </td><td align="center">centered </td><td align="right"> $12</td></tr>
<tr><td>3 </td><td align="left">zebra stripes </td><td align="center">are neat </td><td align="right"> $1</td></tr>
</tbody>
</table>
<table>
<thead>
<tr><th>Simple </th><th>Table</th></tr>
</thead>
<tbody>
<tr><td>1 </td><td>2</td></tr>
<tr><td>3 </td><td>4</td></tr>
</tbody>
</table>
<table>
<thead>
<tr><th>Simple </th><th>Table</th></tr>
</thead>
<tbody>
<tr><td>1 </td><td>2</td></tr>
<tr><td>3 </td><td>4</td></tr>
<tr><td>3 </td><td>4 |</td></tr>
<tr><td>3 </td><td>4 \</td></tr>
</tbody>
</table>
<p>Check <a href="https://github.com/erusev/parsedown/issues/184">https://github.com/erusev/parsedown/issues/184</a> for the following:</p>
<table>
<thead>
<tr><th>Foo </th><th>Bar </th><th>State</th></tr>
</thead>
<tbody>
<tr><td><code>Code | Pipe</code> </td><td>Broken </td><td>Blank</td></tr>
<tr><td><code>Escaped Code \| Pipe</code> </td><td>Broken </td><td>Blank</td></tr>
<tr><td>Escaped | Pipe </td><td>Broken </td><td>Blank</td></tr>
<tr><td>Escaped \</td><td>Pipe </td><td>Broken </td><td>Blank</td></tr>
<tr><td>Escaped \ </td><td>Pipe </td><td>Broken </td><td>Blank</td></tr>
</tbody>
</table>
<table>
<thead>
<tr><th align="left">Simple </th><th>Table</th></tr>
</thead>
<tbody>
<tr><td align="left">3 </td><td>4</td></tr>
</tbody>
</table>
<p>3 | 4</p>
<table>
<thead>
<tr><th>Table </th><th>With </th><th>Empty </th><th>Cells</th></tr>
</thead>
<tbody>
<tr><td></td><td> </td><td> </td><td></td></tr>
<tr><td>a </td><td> </td><td> b </td><td></td></tr>
<tr><td></td><td> a </td><td> </td><td> b</td></tr>
<tr><td>a </td><td> </td><td> </td><td> b</td></tr>
<tr><td></td><td> a </td><td> b </td><td></td></tr>
</tbody>
</table>
<table>
<thead>
<tr><th></th></tr>
</thead>
<tbody>
<tr><td></td></tr>
</tbody>
</table>
<table>
<thead>
<tr><th></th><th></th></tr>
</thead>
<tbody>
<tr><td></td><td></td></tr>
</tbody>
</table>

View File

@@ -0,0 +1,72 @@
Tables
------
First Header | Second Header
------------- | -------------
Content Cell | Content Cell
Content Cell | Content Cell
| First Header | Second Header |
| ------------- | ------------- |
| Content Cell | Content Cell |
| Content Cell | Content Cell |
| Name | Description |
| ------------- | ----------- |
| Help | Display the help window.|
| Close | Closes a window |
| Name | Description |
| ------------- | ----------- |
| Help | **Display the** help window.|
| Close | _Closes_ a window |
| Default-Align | Left-Aligned | Center Aligned | Right Aligned |
| ------------- | :------------ |:---------------:| -----:|
| 1 | col 3 is | some wordy text | $1600 |
| 2 | col 2 is | centered | $12 |
| 3 | zebra stripes | are neat | $1 |
Simple | Table
------ | -----
1 | 2
3 | 4
| Simple | Table |
| ------ | ----- |
| 1 | 2 |
| 3 | 4 |
| 3 | 4 \|
| 3 | 4 \\|
Check https://github.com/erusev/parsedown/issues/184 for the following:
Foo | Bar | State
------ | ------ | -----
`Code | Pipe` | Broken | Blank
`Escaped Code \| Pipe` | Broken | Blank
Escaped \| Pipe | Broken | Blank
Escaped \\| Pipe | Broken | Blank
Escaped \\ | Pipe | Broken | Blank
| Simple | Table |
| :----- | ----- |
| 3 | 4 |
3 | 4
| Table | With | Empty | Cells |
| ----- | ---- | ----- | ----- |
| | | | |
| a | | b | |
| | a | | b |
| a | | | b |
| | a | b | |
|
-- | --
|
| | |
| - | - |
| | |

View File

@@ -0,0 +1,8 @@
<p>Not a headline but a code block:</p>
<pre><code>---
</code></pre>
<p>Not a headline but two HR:</p>
<hr />
<hr />
<hr />
<hr />

View File

@@ -0,0 +1,14 @@
Not a headline but a code block:
```
---
```
Not a headline but two HR:
***
---
---
***

View File

@@ -0,0 +1,16 @@
<p>here is the url: <a href="http://www.cebe.cc/">http://www.cebe.cc/</a></p>
<p>here is the url: <a href="http://www.cebe.cc">http://www.cebe.cc</a></p>
<p>here is the url: <a href="http://www.cebe.cc/">http://www.cebe.cc/</a> and some text</p>
<p>using http is cool and http:// is the beginning of an url.</p>
<p>link should be url decoded: <a href="http://en.wikipedia.org/wiki/Mase_%28disambiguation%29">http://en.wikipedia.org/wiki/Mase_(disambiguation)</a></p>
<p>link in the end of the sentence: See this <a href="http://example.com/">http://example.com/</a>.</p>
<p>this one is in parenthesis (<a href="http://example.com/">http://example.com/</a>).</p>
<p>this one is in parenthesis (<a href="http://example.com:80/?id=1,2,3">http://example.com:80/?id=1,2,3</a>).</p>
<p>... (see <a href="http://en.wikipedia.org/wiki/Port_(computer_networking)">http://en.wikipedia.org/wiki/Port_(computer_networking)</a>).</p>
<p>... (see <a href="https://en.wikipedia.org/wiki/Port_(computer_networking)_more">https://en.wikipedia.org/wiki/Port_(computer_networking)_more</a>).</p>
<p>... (see <a href="https://en.wikipedia.org/wiki/Port_(computer_networking)_more">https://en.wikipedia.org/wiki/Port_(computer_networking)_more</a>). ... (see <a href="http://en.wikipedia.org/wiki/Port_(computer_networking)">http://en.wikipedia.org/wiki/Port_(computer_networking)</a>).</p>
<p>... (see <a href="https://en.wikipedia.org/wiki/Port_(computer_networking)_more">https://en.wikipedia.org/wiki/Port_(computer_networking)_more</a>)....(<a href="http://en.wikipedia.org/wiki/Port_(computer_networking)">http://en.wikipedia.org/wiki/Port_(computer_networking)</a>).</p>
<p>... (see <a href="http://en.wikipedia.org/wiki/Port">http://en.wikipedia.org/wiki/Port</a>)</p>
<p>... (see <a href="http://en.wikipedia.org/wiki/Port">http://en.wikipedia.org/wiki/Port</a>).</p>
<p><a href="http://www.cebe.cc">http://www.cebe.cc</a>, <a href="http://www.cebe.net">http://www.cebe.net</a>, and so on</p>
<p><a href="http://www.google.com/">link to http://www.google.com/</a></p>

View File

@@ -0,0 +1,31 @@
here is the url: http://www.cebe.cc/
here is the url: http://www.cebe.cc
here is the url: http://www.cebe.cc/ and some text
using http is cool and http:// is the beginning of an url.
link should be url decoded: http://en.wikipedia.org/wiki/Mase_%28disambiguation%29
link in the end of the sentence: See this http://example.com/.
this one is in parenthesis (http://example.com/).
this one is in parenthesis (http://example.com:80/?id=1,2,3).
... (see http://en.wikipedia.org/wiki/Port_(computer_networking)).
... (see https://en.wikipedia.org/wiki/Port_(computer_networking)_more).
... (see https://en.wikipedia.org/wiki/Port_(computer_networking)_more). ... (see http://en.wikipedia.org/wiki/Port_(computer_networking)).
... (see https://en.wikipedia.org/wiki/Port_(computer_networking)_more)....(http://en.wikipedia.org/wiki/Port_(computer_networking)).
... (see http://en.wikipedia.org/wiki/Port)
... (see http://en.wikipedia.org/wiki/Port).
http://www.cebe.cc, http://www.cebe.net, and so on
[link to http://www.google.com/](http://www.google.com/)

View File

@@ -0,0 +1 @@
All tests prefixed with `md1_` are taken from http://daringfireball.net/projects/downloads/MarkdownTest_1.0.zip

View File

@@ -0,0 +1,11 @@
<blockquote><h2>This is a header.</h2>
<ol>
<li>This is the first list item.</li>
<li>This is the second list item.</li>
</ol>
<p>Here's some example code:</p>
<pre><code>return shell_exec("echo $input | $markdown_script");
</code></pre>
<blockquote><p>quote here</p>
</blockquote>
</blockquote>

View File

@@ -0,0 +1,10 @@
> ## This is a header.
>
> 1. This is the first list item.
> 2. This is the second list item.
>
> Here's some example code:
>
> return shell_exec("echo $input | $markdown_script");
>
> > quote here

View File

@@ -0,0 +1,9 @@
<blockquote><p>test test
test</p>
</blockquote>
<blockquote><p>test
test
test</p>
</blockquote>
<p>test</p>
<p>&gt;this is not a quote</p>

View File

@@ -0,0 +1,10 @@
> test test
> test
> test
test
test
test
>this is not a quote

View File

@@ -0,0 +1,12 @@
<p>this is <code>inline code</code></p>
<p>this is <code>`inline code</code>`</p>
<p>this is <code>inline code</code></p>
<p>this is <code>inline ` code</code></p>
<p>this is <code>inline `` code</code></p>
<pre><code>code block
code block
</code></pre>
<p>this is code too: <code> co
ooo
de </code></p>

View File

@@ -0,0 +1,17 @@
this is `inline code`
this is ``inline code``
this is `` inline code ``
this is `` inline ` code ``
this is ``` inline `` code ```
code block
code block
this is code too: ` co
ooo
de `

View File

@@ -0,0 +1,50 @@
<h1>this is to test dense blocks (no newlines between them)</h1>
<hr />
<h2>what is Markdown?</h2>
<p>see <a href="http://en.wikipedia.org/wiki/Markdown">Wikipedia</a></p>
<h2>a h2</h2>
<p>paragraph</p>
<p>this is a paragraph, not a headline or list
next line
- whoo</p>
<p>par</p>
<pre><code>code
code
</code></pre>
<p>par</p>
<h3>Tasks list</h3>
<ul>
<li>list items</li>
</ul>
<h2>headline1</h2>
<blockquote><p>quote
quote</p>
</blockquote>
<h2>headline2</h2>
<hr />
<h1>h1</h1>
<h2>h2</h2>
<hr />
<h3>h3</h3>
<ol>
<li>ol1</li>
<li>ol2</li>
</ol>
<h4>h4</h4>
<ul>
<li>listA</li>
<li>listB</li>
</ul>
<h5>h5</h5>
<h6>h6</h6>
<hr />
<hr />
<h2>changelog 1</h2>
<ul>
<li>17-Feb-2013 re-design</li>
</ul>
<hr />
<h2>changelog 2</h2>
<ul>
<li>17-Feb-2013 re-design</li>
</ul>

View File

@@ -0,0 +1,56 @@
# this is to test dense blocks (no newlines between them)
----
## what is Markdown?
see [Wikipedia][]
a h2
----
paragraph
this is a paragraph, not a headline or list
next line
- whoo
par
code
code
par
### Tasks list
- list items
headline1
---------
> quote
> quote
[Wikipedia]: http://en.wikipedia.org/wiki/Markdown
headline2
---------
----
# h1
## h2
---
### h3
1. ol1
2. ol2
#### h4
- listA
- listB
##### h5
###### h6
--------
----
## changelog 1
* 17-Feb-2013 re-design
----
## changelog 2
* 17-Feb-2013 re-design

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,16 @@
<p>this is <strong>strong</strong> and this is <strong>strong</strong>.</p>
<p>this is <em>em</em> and this is <em>em</em>.</p>
<p><em><code>code</code></em> <strong><code>code</code></strong></p>
<p><em><code>code</code><strong><code>code</code></strong><code>code</code></em></p>
<p>Hey <em>this is
italic</em></p>
<p>Hey <strong>this is
bold</strong></p>
<p><strong>strong text</strong><em>emphasized text</em></p>
<p><em>emphasized text</em><strong>strong text</strong></p>
<p><strong>strong text</strong><em>emphasized text</em></p>
<p><em>emphasized text</em><strong>strong text</strong></p>
<p><strong>strong text</strong><em>emphasized text</em></p>
<p><em>emphasized text</em><strong>strong text</strong></p>
<p>simple_word_with_underscores</p>
<p>this is text, <em>this is emph</em> simple_word_with_underscores text again.</p>

View File

@@ -0,0 +1,29 @@
this is __strong__ and this is **strong**.
this is _em_ and this is *em*.
_`code`_ __`code`__
*`code`**`code`**`code`*
Hey *this is
italic*
Hey **this is
bold**
**strong text***emphasized text*
*emphasized text***strong text**
**strong text**_emphasized text_
*emphasized text*__strong text__
__strong text__*emphasized text*
_emphasized text_**strong text**
simple_word_with_underscores
this is text, _this is emph_ simple_word_with_underscores text again.

View File

@@ -0,0 +1,8 @@
<p>0</p>
<p>0
Lorem ipsum dolor</p>
<p>Lorem ipsum dolor
0</p>
<pre><code>code
</code></pre>
<p>0</p>

View File

@@ -0,0 +1,11 @@
0
0
Lorem ipsum dolor
Lorem ipsum dolor
0
code
0

View File

@@ -0,0 +1,9 @@
<h2>Creating an Action <a name="creating-action"></a></h2>
<p>For the "Hello" task, you will create a <code>say</code> <a href="structure-controllers.md#creating-actions">action</a> that reads
a <code>message</code> parameter from the request and displays that message back to the user. If the request
does not provide a <code>message</code> parameter, the action will display the default "Hello" message.</p>
<blockquote><p>Info: <a href="structure-controllers.md#creating-actions">Actions</a> are the objects that end users can directly refer to for
execution. Actions are grouped by <a href="structure-controllers.md">controllers</a>. The execution result of
an action is the response that an end user will receive.</p>
</blockquote>
<p>Actions must be declared in ...</p>

View File

@@ -0,0 +1,12 @@
Creating an Action <a name="creating-action"></a>
------------------
For the "Hello" task, you will create a `say` [action](structure-controllers.md#creating-actions) that reads
a `message` parameter from the request and displays that message back to the user. If the request
does not provide a `message` parameter, the action will display the default "Hello" message.
> Info: [Actions](structure-controllers.md#creating-actions) are the objects that end users can directly refer to for
execution. Actions are grouped by [controllers](structure-controllers.md). The execution result of
an action is the response that an end user will receive.
Actions must be declared in ...

View File

@@ -0,0 +1,3 @@
<p>nice <a href="http://www.youtube.com/video-on\e">video</a>. Nice-vide\o star *</p>
<p>nice <img src="http://www.youtube.com/video-on\e" alt="video" />. Nice-vide\o star *</p>
<p><a href="http://www.youtube.com/video-on\e">video</a> and <a href="http://www.youtube.com/video-on\e">http://www.youtube.com/video-on\e</a> and <a href="mailto:m\a-il@cebe.cc">m\a-il@cebe.cc</a></p>

View File

@@ -0,0 +1,7 @@
nice [video](http://www.youtube.com/video\-on\e). Nice\-vide\o star \*
nice ![video](http://www.youtube.com/video\-on\e). Nice\-vide\o star \*
[video]: http://www.youtube.com/video\-on\e
[video] and <http://www.youtube.com/video\-on\e> and <m\a\-il@cebe.cc>

View File

@@ -0,0 +1,21 @@
<h1>a h1 heading</h1>
<p>par1</p>
<h2>a h2 heading</h2>
<p>par2</p>
<h4>a h4 heading</h4>
<p>par3</p>
<h1>another h1</h1>
<p>par4</p>
<h2>another h2</h2>
<p>par5</p>
<h4>a h4 heading</h4>
<h4>a h4 heading</h4>
<h6>h6</h6>
<h6>h7</h6>
<p><a name="example"></a></p>
<h2>head</h2>
<p>hallo
hallo</p>
<h1>test</h1>
<p>test</p>
<p>#1 has been fixed</p>

View File

@@ -0,0 +1,41 @@
# a h1 heading
par1
## a h2 heading
par2
#### a h4 heading
par3
another h1
==========
par4
another h2
----------
par5
#### a h4 heading ####
#### a h4 heading ########
###### h6
####### h7
<a name="example"></a>
head
----
hallo
hallo
test
====
test
#1 has been fixed

View File

@@ -0,0 +1,15 @@
<hr />
<hr />
<hr />
<hr />
<hr />
<hr />
<hr />
<hr />
<hr />
<p>**</p>
<p>--</p>
<p></p>
<p>*</p>
<p>-</p>
<p></p>

View File

@@ -0,0 +1,29 @@
- - -
---
------
_ _ _
___
_____________
*********
* * *
***
**
--
*
-

View File

@@ -0,0 +1,42 @@
<p>paragraph 1 is here</p>
<table>
<tr>
<td>a</td>
<td>b</td>
</tr>
<tr>
<td>c</td>
<td>d</td>
</tr>
</table>
<p>more markdown here</p>
<p>&lt; this is not an html tag</p>
<p>&lt;thisisnotanhtmltag</p>
<p>but this is:</p>
<p><img src="file.jpg"
alt="some alt aligned with src attribute" title="some text" /></p>
<p><span class="test">some inline <strong>md</strong></span></p>
<p><span>some inline <strong>md</strong></span></p>
<p>self-closing on block level:</p>
<p>this is a paragraph</p>
<hr style="clear: both;" />
<p>something <strong>bold</strong>.</p>
<custom />
<h1>h1</h1>
<custom multi="line" something="hi" />
<h2>h2</h2>
<p>p <img src="file.jpg"
alt="some alt aligned with src attribute"
title="some text" />
something</p>
<p>p <img src="file.jpg"
alt="some alt aligned with src attribute"
title="some text" /></p>
<pre><code>something
</code></pre>
<p>p is &lt; than 5</p>
<pre><code>this is code
</code></pre>
<p>this paragraph contains a <!-- multi
line html comment -->
newline</p>

View File

@@ -0,0 +1,59 @@
paragraph 1 is here
<table>
<tr>
<td>a</td>
<td>b</td>
</tr>
<tr>
<td>c</td>
<td>d</td>
</tr>
</table>
more markdown here
< this is not an html tag
<thisisnotanhtmltag
but this is:
<img src="file.jpg"
alt="some alt aligned with src attribute" title="some text" />
<span class="test">some inline **md**</span>
<span>some inline **md**</span>
self-closing on block level:
<p>this is a paragraph</p>
<hr style="clear: both;" />
something **bold**.
<custom />
# h1
<custom multi="line" something="hi" />
## h2
p <img src="file.jpg"
alt="some alt aligned with src attribute"
title="some text" />
something
p <img src="file.jpg"
alt="some alt aligned with src attribute"
title="some text" />
something
p is < than 5
this is code
this paragraph contains a <!-- multi
line html comment -->
newline

View File

@@ -0,0 +1,14 @@
<p><img src="https://poser.pugx.org/cebe/markdown/downloads.png" alt="Total Downloads" />
<img src="https://secure.travis-ci.org/cebe/markdown.png" alt="Build Status" title="test1" /></p>
<p>Here is an image tag: <img src="https://poser.pugx.org/cebe/markdown/downloads.png" alt="Total Downloads" />.</p>
<p>Images inside of links:
<a href="https://packagist.org/packages/cebe/markdown"><img src="https://poser.pugx.org/cebe/markdown/downloads.png" alt="Total Downloads" /></a>
<!-- [![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/cebe/markdown/badges/quality-score.png?s=17448ca4d140429fd687c58ff747baeb6568d528)](https://scrutinizer-ci.com/g/cebe/markdown/) -->
<a href="http://travis-ci.org/cebe/markdown"><img src="https://secure.travis-ci.org/cebe/markdown.png" alt="Build Status" title="test2" /></a>
<a href="http://travis-ci.org/cebe/markdown" title="test4"><img src="https://secure.travis-ci.org/cebe/markdown.png" alt="Build Status" title="test3" /></a></p>
<p>This is not an image: ![[ :-)</p>
<p>This is not an image: ![[ :-)]]</p>
<p><img src="/path/to/img.jpg" alt="Alt text" /></p>
<p><img src="/path/to/img.jpg" alt="Alt text" /></p>
<p><img src="/path/to/img.jpg" alt="Alt text" /></p>
<p><img src="/path/to/img.jpg" alt="Alt text" /></p>

View File

@@ -0,0 +1,22 @@
![Total Downloads](https://poser.pugx.org/cebe/markdown/downloads.png)
![Build Status](https://secure.travis-ci.org/cebe/markdown.png "test1")
Here is an image tag: ![Total Downloads](https://poser.pugx.org/cebe/markdown/downloads.png).
Images inside of links:
[![Total Downloads](https://poser.pugx.org/cebe/markdown/downloads.png)](https://packagist.org/packages/cebe/markdown)
<!-- [![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/cebe/markdown/badges/quality-score.png?s=17448ca4d140429fd687c58ff747baeb6568d528)](https://scrutinizer-ci.com/g/cebe/markdown/) -->
[![Build Status](https://secure.travis-ci.org/cebe/markdown.png "test2")](http://travis-ci.org/cebe/markdown)
[![Build Status](https://secure.travis-ci.org/cebe/markdown.png "test3")](http://travis-ci.org/cebe/markdown "test4")
This is not an image: ![[ :-)
This is not an image: ![[ :-)]]
![Alt text](/path/to/img.jpg)
![Alt text]( /path/to/img.jpg)
![Alt text]( /path/to/img.jpg )
![Alt text](/path/to/img.jpg )

View File

@@ -0,0 +1,8 @@
<p>this is <span class="name">inline <strong>html</strong></span> trailing</p>
<p>&copy; AT&amp;T</p>
<p><del>this is deleted</del> this is not
new text on new line</p>
<p><s>this is deleted</s> this is not
new text on new line</p>
<p>this line ends with &lt;</p>
<p>this line ends with &amp;</p>

View File

@@ -0,0 +1,13 @@
this is <span class="name">inline **html**</span> trailing
&copy; AT&T
<del>this is deleted</del> this is not
new text on new line
<s>this is deleted</s> this is not
new text on new line
this line ends with <
this line ends with &

Some files were not shown because too many files have changed in this diff Show More