extlib Michelf\Markdown updated 1.4.0 to 1.4.1

This commit is contained in:
Mikael Nordfeldth 2015-02-12 22:45:08 +01:00
parent a66973e158
commit 69e04e5cbd
4 changed files with 195 additions and 115 deletions

View File

@ -1,5 +1,5 @@
PHP Markdown Lib PHP Markdown Lib
Copyright (c) 2004-2013 Michel Fortin Copyright (c) 2004-2014 Michel Fortin
<http://michelf.ca/> <http://michelf.ca/>
All rights reserved. All rights reserved.

View File

@ -3,7 +3,7 @@
# Markdown - A text-to-HTML conversion tool for web writers # Markdown - A text-to-HTML conversion tool for web writers
# #
# PHP Markdown # PHP Markdown
# Copyright (c) 2004-2013 Michel Fortin # Copyright (c) 2004-2014 Michel Fortin
# <http://michelf.com/projects/php-markdown/> # <http://michelf.com/projects/php-markdown/>
# #
# Original Markdown # Original Markdown
@ -21,7 +21,7 @@ class Markdown implements MarkdownInterface {
### Version ### ### Version ###
const MARKDOWNLIB_VERSION = "1.4.0"; const MARKDOWNLIB_VERSION = "1.4.1";
### Simple Function Interface ### ### Simple Function Interface ###
@ -59,6 +59,9 @@ class Markdown implements MarkdownInterface {
public $predef_urls = array(); public $predef_urls = array();
public $predef_titles = array(); public $predef_titles = array();
# Optional filter function for URLs
public $url_filter_func = null;
### Parser Implementation ### ### Parser Implementation ###
@ -209,7 +212,7 @@ class Markdown implements MarkdownInterface {
)? # title is optional )? # title is optional
(?:\n+|\Z) (?:\n+|\Z)
}xm', }xm',
array(&$this, '_stripLinkDefinitions_callback'), array($this, '_stripLinkDefinitions_callback'),
$text); $text);
return $text; return $text;
} }
@ -242,7 +245,7 @@ class Markdown implements MarkdownInterface {
# #
$block_tags_a_re = 'ins|del'; $block_tags_a_re = 'ins|del';
$block_tags_b_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|'. $block_tags_b_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|'.
'script|noscript|form|fieldset|iframe|math|svg|'. 'script|noscript|style|form|fieldset|iframe|math|svg|'.
'article|section|nav|aside|hgroup|header|footer|'. 'article|section|nav|aside|hgroup|header|footer|'.
'figure'; 'figure';
@ -297,9 +300,9 @@ class Markdown implements MarkdownInterface {
# match will start at the first `<div>` and stop at the first `</div>`. # match will start at the first `<div>` and stop at the first `</div>`.
$text = preg_replace_callback('{(?> $text = preg_replace_callback('{(?>
(?> (?>
(?<=\n\n) # Starting after a blank line (?<=\n) # Starting on its own line
| # or | # or
\A\n? # the beginning of the doc \A\n? # the at beginning of the doc
) )
( # save in $1 ( # save in $1
@ -356,7 +359,7 @@ class Markdown implements MarkdownInterface {
) )
)}Sxmi', )}Sxmi',
array(&$this, '_hashHTMLBlocks_callback'), array($this, '_hashHTMLBlocks_callback'),
$text); $text);
return $text; return $text;
@ -500,7 +503,7 @@ class Markdown implements MarkdownInterface {
protected function doHardBreaks($text) { protected function doHardBreaks($text) {
# Do hard breaks: # Do hard breaks:
return preg_replace_callback('/ {2,}\n/', return preg_replace_callback('/ {2,}\n/',
array(&$this, '_doHardBreaks_callback'), $text); array($this, '_doHardBreaks_callback'), $text);
} }
protected function _doHardBreaks_callback($matches) { protected function _doHardBreaks_callback($matches) {
return $this->hashPart("<br$this->empty_element_suffix\n"); return $this->hashPart("<br$this->empty_element_suffix\n");
@ -531,7 +534,7 @@ class Markdown implements MarkdownInterface {
\] \]
) )
}xs', }xs',
array(&$this, '_doAnchors_reference_callback'), $text); array($this, '_doAnchors_reference_callback'), $text);
# #
# Next, inline-style links: [link text](url "optional title") # Next, inline-style links: [link text](url "optional title")
@ -558,7 +561,7 @@ class Markdown implements MarkdownInterface {
\) \)
) )
}xs', }xs',
array(&$this, '_doAnchors_inline_callback'), $text); array($this, '_doAnchors_inline_callback'), $text);
# #
# Last, handle reference-style shortcuts: [link text] # Last, handle reference-style shortcuts: [link text]
@ -572,7 +575,7 @@ class Markdown implements MarkdownInterface {
\] \]
) )
}xs', }xs',
array(&$this, '_doAnchors_reference_callback'), $text); array($this, '_doAnchors_reference_callback'), $text);
$this->in_anchor = false; $this->in_anchor = false;
return $text; return $text;
@ -593,7 +596,7 @@ class Markdown implements MarkdownInterface {
if (isset($this->urls[$link_id])) { if (isset($this->urls[$link_id])) {
$url = $this->urls[$link_id]; $url = $this->urls[$link_id];
$url = $this->encodeAttribute($url); $url = $this->encodeURLAttribute($url);
$result = "<a href=\"$url\""; $result = "<a href=\"$url\"";
if ( isset( $this->titles[$link_id] ) ) { if ( isset( $this->titles[$link_id] ) ) {
@ -617,7 +620,13 @@ class Markdown implements MarkdownInterface {
$url = $matches[3] == '' ? $matches[4] : $matches[3]; $url = $matches[3] == '' ? $matches[4] : $matches[3];
$title =& $matches[7]; $title =& $matches[7];
$url = $this->encodeAttribute($url); // if the URL was of the form <s p a c e s> it got caught by the HTML
// tag parser and hashed. Need to reverse the process before using the URL.
$unhashed = $this->unhash($url);
if ($unhashed != $url)
$url = preg_replace('/^<(.*)>$/', '\1', $unhashed);
$url = $this->encodeURLAttribute($url);
$result = "<a href=\"$url\""; $result = "<a href=\"$url\"";
if (isset($title)) { if (isset($title)) {
@ -654,7 +663,7 @@ class Markdown implements MarkdownInterface {
) )
}xs', }xs',
array(&$this, '_doImages_reference_callback'), $text); array($this, '_doImages_reference_callback'), $text);
# #
# Next, handle inline images: ![alt text](url "optional title") # Next, handle inline images: ![alt text](url "optional title")
@ -683,7 +692,7 @@ class Markdown implements MarkdownInterface {
\) \)
) )
}xs', }xs',
array(&$this, '_doImages_inline_callback'), $text); array($this, '_doImages_inline_callback'), $text);
return $text; return $text;
} }
@ -698,7 +707,7 @@ class Markdown implements MarkdownInterface {
$alt_text = $this->encodeAttribute($alt_text); $alt_text = $this->encodeAttribute($alt_text);
if (isset($this->urls[$link_id])) { if (isset($this->urls[$link_id])) {
$url = $this->encodeAttribute($this->urls[$link_id]); $url = $this->encodeURLAttribute($this->urls[$link_id]);
$result = "<img src=\"$url\" alt=\"$alt_text\""; $result = "<img src=\"$url\" alt=\"$alt_text\"";
if (isset($this->titles[$link_id])) { if (isset($this->titles[$link_id])) {
$title = $this->titles[$link_id]; $title = $this->titles[$link_id];
@ -722,7 +731,7 @@ class Markdown implements MarkdownInterface {
$title =& $matches[7]; $title =& $matches[7];
$alt_text = $this->encodeAttribute($alt_text); $alt_text = $this->encodeAttribute($alt_text);
$url = $this->encodeAttribute($url); $url = $this->encodeURLAttribute($url);
$result = "<img src=\"$url\" alt=\"$alt_text\""; $result = "<img src=\"$url\" alt=\"$alt_text\"";
if (isset($title)) { if (isset($title)) {
$title = $this->encodeAttribute($title); $title = $this->encodeAttribute($title);
@ -743,7 +752,7 @@ class Markdown implements MarkdownInterface {
# -------- # --------
# #
$text = preg_replace_callback('{ ^(.+?)[ ]*\n(=+|-+)[ ]*\n+ }mx', $text = preg_replace_callback('{ ^(.+?)[ ]*\n(=+|-+)[ ]*\n+ }mx',
array(&$this, '_doHeaders_callback_setext'), $text); array($this, '_doHeaders_callback_setext'), $text);
# atx-style headers: # atx-style headers:
# # Header 1 # # Header 1
@ -760,7 +769,7 @@ class Markdown implements MarkdownInterface {
\#* # optional closing #\'s (not counted) \#* # optional closing #\'s (not counted)
\n+ \n+
}xm', }xm',
array(&$this, '_doHeaders_callback_atx'), $text); array($this, '_doHeaders_callback_atx'), $text);
return $text; return $text;
} }
@ -789,7 +798,6 @@ class Markdown implements MarkdownInterface {
# Re-usable patterns to match list item bullets and number markers: # Re-usable patterns to match list item bullets and number markers:
$marker_ul_re = '[*+-]'; $marker_ul_re = '[*+-]';
$marker_ol_re = '\d+[\.]'; $marker_ol_re = '\d+[\.]';
$marker_any_re = "(?:$marker_ul_re|$marker_ol_re)";
$markers_relist = array( $markers_relist = array(
$marker_ul_re => $marker_ol_re, $marker_ul_re => $marker_ol_re,
@ -833,14 +841,14 @@ class Markdown implements MarkdownInterface {
^ ^
'.$whole_list_re.' '.$whole_list_re.'
}mx', }mx',
array(&$this, '_doLists_callback'), $text); array($this, '_doLists_callback'), $text);
} }
else { else {
$text = preg_replace_callback('{ $text = preg_replace_callback('{
(?:(?<=\n)\n|\A\n?) # Must eat the newline (?:(?<=\n)\n|\A\n?) # Must eat the newline
'.$whole_list_re.' '.$whole_list_re.'
}mx', }mx',
array(&$this, '_doLists_callback'), $text); array($this, '_doLists_callback'), $text);
} }
} }
@ -907,7 +915,7 @@ class Markdown implements MarkdownInterface {
(?:(\n+(?=\n))|\n) # tailing blank line = $5 (?:(\n+(?=\n))|\n) # tailing blank line = $5
(?= \n* (\z | \2 ('.$marker_any_re.') (?:[ ]+|(?=\n)))) (?= \n* (\z | \2 ('.$marker_any_re.') (?:[ ]+|(?=\n))))
}xm', }xm',
array(&$this, '_processListItems_callback'), $list_str); array($this, '_processListItems_callback'), $list_str);
$this->list_level--; $this->list_level--;
return $list_str; return $list_str;
@ -951,7 +959,7 @@ class Markdown implements MarkdownInterface {
) )
((?=^[ ]{0,'.$this->tab_width.'}\S)|\Z) # Lookahead for non-space at line-start, or end of doc ((?=^[ ]{0,'.$this->tab_width.'}\S)|\Z) # Lookahead for non-space at line-start, or end of doc
}xm', }xm',
array(&$this, '_doCodeBlocks_callback'), $text); array($this, '_doCodeBlocks_callback'), $text);
return $text; return $text;
} }
@ -979,19 +987,19 @@ class Markdown implements MarkdownInterface {
protected $em_relist = array( protected $em_relist = array(
'' => '(?:(?<!\*)\*(?!\*)|(?<!_)_(?!_))(?=\S|$)(?![\.,:;]\s)', '' => '(?:(?<!\*)\*(?!\*)|(?<!_)_(?!_))(?![\.,:;]?\s)',
'*' => '(?<=\S|^)(?<!\*)\*(?!\*)', '*' => '(?<![\s*])\*(?!\*)',
'_' => '(?<=\S|^)(?<!_)_(?!_)', '_' => '(?<![\s_])_(?!_)',
); );
protected $strong_relist = array( protected $strong_relist = array(
'' => '(?:(?<!\*)\*\*(?!\*)|(?<!_)__(?!_))(?=\S|$)(?![\.,:;]\s)', '' => '(?:(?<!\*)\*\*(?!\*)|(?<!_)__(?!_))(?![\.,:;]?\s)',
'**' => '(?<=\S|^)(?<!\*)\*\*(?!\*)', '**' => '(?<![\s*])\*\*(?!\*)',
'__' => '(?<=\S|^)(?<!_)__(?!_)', '__' => '(?<![\s_])__(?!_)',
); );
protected $em_strong_relist = array( protected $em_strong_relist = array(
'' => '(?:(?<!\*)\*\*\*(?!\*)|(?<!_)___(?!_))(?=\S|$)(?![\.,:;]\s)', '' => '(?:(?<!\*)\*\*\*(?!\*)|(?<!_)___(?!_))(?![\.,:;]?\s)',
'***' => '(?<=\S|^)(?<!\*)\*\*\*(?!\*)', '***' => '(?<![\s*])\*\*\*(?!\*)',
'___' => '(?<=\S|^)(?<!_)___(?!_)', '___' => '(?<![\s_])___(?!_)',
); );
protected $em_strong_prepared_relist; protected $em_strong_prepared_relist;
@ -1151,7 +1159,7 @@ class Markdown implements MarkdownInterface {
)+ )+
) )
/xm', /xm',
array(&$this, '_doBlockQuotes_callback'), $text); array($this, '_doBlockQuotes_callback'), $text);
return $text; return $text;
} }
@ -1165,7 +1173,7 @@ class Markdown implements MarkdownInterface {
# These leading spaces cause problem with <pre> content, # These leading spaces cause problem with <pre> content,
# so we need to fix that: # so we need to fix that:
$bq = preg_replace_callback('{(\s*<pre>.+?</pre>)}sx', $bq = preg_replace_callback('{(\s*<pre>.+?</pre>)}sx',
array(&$this, '_doBlockQuotes_callback2'), $bq); array($this, '_doBlockQuotes_callback2'), $bq);
return "\n". $this->hashBlock("<blockquote>\n$bq\n</blockquote>")."\n\n"; return "\n". $this->hashBlock("<blockquote>\n$bq\n</blockquote>")."\n\n";
} }
@ -1255,6 +1263,33 @@ class Markdown implements MarkdownInterface {
$text = str_replace('"', '&quot;', $text); $text = str_replace('"', '&quot;', $text);
return $text; return $text;
} }
protected function encodeURLAttribute($url, &$text = null) {
#
# Encode text for a double-quoted HTML attribute containing a URL,
# applying the URL filter if set. Also generates the textual
# representation for the URL (removing mailto: or tel:) storing it in $text.
# This function is *not* suitable for attributes enclosed in single quotes.
#
if ($this->url_filter_func)
$url = call_user_func($this->url_filter_func, $url);
if (preg_match('{^mailto:}i', $url))
$url = $this->encodeEntityObfuscatedAttribute($url, $text, 7);
else if (preg_match('{^tel:}i', $url))
{
$url = $this->encodeAttribute($url);
$text = substr($url, 4);
}
else
{
$url = $this->encodeAttribute($url);
$text = $url;
}
return $url;
}
protected function encodeAmpsAndAngles($text) { protected function encodeAmpsAndAngles($text) {
@ -1279,8 +1314,8 @@ class Markdown implements MarkdownInterface {
protected function doAutoLinks($text) { protected function doAutoLinks($text) {
$text = preg_replace_callback('{<((https?|ftp|dict):[^\'">\s]+)>}i', $text = preg_replace_callback('{<((https?|ftp|dict|tel):[^\'">\s]+)>}i',
array(&$this, '_doAutoLinks_url_callback'), $text); array($this, '_doAutoLinks_url_callback'), $text);
# Email addresses: <address@domain.foo> # Email addresses: <address@domain.foo>
$text = preg_replace_callback('{ $text = preg_replace_callback('{
@ -1301,49 +1336,47 @@ class Markdown implements MarkdownInterface {
) )
> >
}xi', }xi',
array(&$this, '_doAutoLinks_email_callback'), $text); array($this, '_doAutoLinks_email_callback'), $text);
$text = preg_replace_callback('{<(tel:([^\'">\s]+))>}i',array(&$this, '_doAutoLinks_tel_callback'), $text);
return $text; return $text;
} }
protected function _doAutoLinks_tel_callback($matches) {
$url = $this->encodeAttribute($matches[1]);
$tel = $this->encodeAttribute($matches[2]);
$link = "<a href=\"$url\">$tel</a>";
return $this->hashPart($link);
}
protected function _doAutoLinks_url_callback($matches) { protected function _doAutoLinks_url_callback($matches) {
$url = $this->encodeAttribute($matches[1]); $url = $this->encodeURLAttribute($matches[1], $text);
$link = "<a href=\"$url\">$url</a>"; $link = "<a href=\"$url\">$text</a>";
return $this->hashPart($link); return $this->hashPart($link);
} }
protected function _doAutoLinks_email_callback($matches) { protected function _doAutoLinks_email_callback($matches) {
$address = $matches[1]; $addr = $matches[1];
$link = $this->encodeEmailAddress($address); $url = $this->encodeURLAttribute("mailto:$addr", $text);
$link = "<a href=\"$url\">$text</a>";
return $this->hashPart($link); return $this->hashPart($link);
} }
protected function encodeEmailAddress($addr) { protected function encodeEntityObfuscatedAttribute($text, &$tail = null, $head_length = 0) {
# #
# Input: an email address, e.g. "foo@example.com" # Input: some text to obfuscate, e.g. "mailto:foo@example.com"
# #
# Output: the email address as a mailto link, with each character # Output: the same text but with most characters encoded as either a
# of the address encoded as either a decimal or hex entity, in # decimal or hex entity, in the hopes of foiling most address
# the hopes of foiling most address harvesting spam bots. E.g.: # harvesting spam bots. E.g.:
# #
# <p><a href="&#109;&#x61;&#105;&#x6c;&#116;&#x6f;&#58;&#x66;o&#111; # &#109;&#x61;&#105;&#x6c;&#116;&#x6f;&#58;&#x66;o&#111;
# &#x40;&#101;&#x78;&#97;&#x6d;&#112;&#x6c;&#101;&#46;&#x63;&#111; # &#x40;&#101;&#x78;&#97;&#x6d;&#112;&#x6c;&#101;&#46;&#x63;&#111;
# &#x6d;">&#x66;o&#111;&#x40;&#101;&#x78;&#97;&#x6d;&#112;&#x6c; # &#x6d;
# &#101;&#46;&#x63;&#111;&#x6d;</a></p> #
# Note: the additional output $tail is assigned the same value as the
# ouput, minus the number of characters specified by $head_length.
# #
# Based by a filter by Matthew Wickline, posted to BBEdit-Talk. # Based by a filter by Matthew Wickline, posted to BBEdit-Talk.
# With some optimizations by Milian Wolff. # With some optimizations by Milian Wolff. Forced encoding of HTML
# attribute special characters by Allan Odgaard.
# #
$addr = "mailto:" . $addr; if ($text == "") return $tail = "";
$chars = preg_split('/(?<!^)(?!$)/', $addr);
$seed = (int)abs(crc32($addr) / strlen($addr)); # Deterministic seed. $chars = preg_split('/(?<!^)(?!$)/', $text);
$seed = (int)abs(crc32($text) / strlen($text)); # Deterministic seed.
foreach ($chars as $key => $char) { foreach ($chars as $key => $char) {
$ord = ord($char); $ord = ord($char);
# Ignore non-ascii chars. # Ignore non-ascii chars.
@ -1351,17 +1384,17 @@ class Markdown implements MarkdownInterface {
$r = ($seed * (1 + $key)) % 100; # Pseudo-random function. $r = ($seed * (1 + $key)) % 100; # Pseudo-random function.
# roughly 10% raw, 45% hex, 45% dec # roughly 10% raw, 45% hex, 45% dec
# '@' *must* be encoded. I insist. # '@' *must* be encoded. I insist.
if ($r > 90 && $char != '@') /* do nothing */; # '"' and '>' have to be encoded inside the attribute
if ($r > 90 && strpos('@"&>', $char) === false) /* do nothing */;
else if ($r < 45) $chars[$key] = '&#x'.dechex($ord).';'; else if ($r < 45) $chars[$key] = '&#x'.dechex($ord).';';
else $chars[$key] = '&#'.$ord.';'; else $chars[$key] = '&#'.$ord.';';
} }
} }
$addr = implode('', $chars);
$text = implode('', array_slice($chars, 7)); # text without `mailto:`
$addr = "<a href=\"$addr\">$text</a>";
return $addr; $text = implode('', $chars);
$tail = $head_length ? implode('', array_slice($chars, $head_length)) : $text;
return $text;
} }
@ -1470,7 +1503,7 @@ class Markdown implements MarkdownInterface {
# appropriate number of space between each blocks. # appropriate number of space between each blocks.
$text = preg_replace_callback('/^.*\t.*$/m', $text = preg_replace_callback('/^.*\t.*$/m',
array(&$this, '_detab_callback'), $text); array($this, '_detab_callback'), $text);
return $text; return $text;
} }
@ -1510,7 +1543,7 @@ class Markdown implements MarkdownInterface {
# Swap back in all the tags hashed by _HashHTMLBlocks. # Swap back in all the tags hashed by _HashHTMLBlocks.
# #
return preg_replace_callback('/(.)\x1A[0-9]+\1/', return preg_replace_callback('/(.)\x1A[0-9]+\1/',
array(&$this, '_unhash_callback'), $text); array($this, '_unhash_callback'), $text);
} }
protected function _unhash_callback($matches) { protected function _unhash_callback($matches) {
return $this->html_hashes[$matches[0]]; return $this->html_hashes[$matches[0]];
@ -1645,9 +1678,9 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
### Extra Attribute Parser ### ### Extra Attribute Parser ###
# Expression to use to catch attributes (includes the braces) # Expression to use to catch attributes (includes the braces)
protected $id_class_attr_catch_re = '\{((?:[ ]*[#.][-_:a-zA-Z0-9]+){1,})[ ]*\}'; protected $id_class_attr_catch_re = '\{((?:[ ]*[#.a-z][-_:a-zA-Z0-9=]+){1,})[ ]*\}';
# Expression to use when parsing in a context when no capture is desired # Expression to use when parsing in a context when no capture is desired
protected $id_class_attr_nocatch_re = '\{(?:[ ]*[#.][-_:a-zA-Z0-9]+){1,}[ ]*\}'; protected $id_class_attr_nocatch_re = '\{(?:[ ]*[#.a-z][-_:a-zA-Z0-9=]+){1,}[ ]*\}';
protected function doExtraAttributes($tag_name, $attr) { protected function doExtraAttributes($tag_name, $attr) {
# #
@ -1659,17 +1692,21 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
if (empty($attr)) return ""; if (empty($attr)) return "";
# Split on components # Split on components
preg_match_all('/[#.][-_:a-zA-Z0-9]+/', $attr, $matches); preg_match_all('/[#.a-z][-_:a-zA-Z0-9=]+/', $attr, $matches);
$elements = $matches[0]; $elements = $matches[0];
# handle classes and ids (only first id taken into account) # handle classes and ids (only first id taken into account)
$classes = array(); $classes = array();
$attributes = array();
$id = false; $id = false;
foreach ($elements as $element) { foreach ($elements as $element) {
if ($element{0} == '.') { if ($element{0} == '.') {
$classes[] = substr($element, 1); $classes[] = substr($element, 1);
} else if ($element{0} == '#') { } else if ($element{0} == '#') {
if ($id === false) $id = substr($element, 1); if ($id === false) $id = substr($element, 1);
} else if (strpos($element, '=') > 0) {
$parts = explode('=', $element, 2);
$attributes[] = $parts[0] . '="' . $parts[1] . '"';
} }
} }
@ -1681,6 +1718,9 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
if (!empty($classes)) { if (!empty($classes)) {
$attr_str .= ' class="'.implode(" ", $classes).'"'; $attr_str .= ' class="'.implode(" ", $classes).'"';
} }
if (!$this->no_markup && !empty($attributes)) {
$attr_str .= ' '.implode(" ", $attributes);
}
return $attr_str; return $attr_str;
} }
@ -1716,7 +1756,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
(?:[ ]* '.$this->id_class_attr_catch_re.' )? # $5 = extra id & class attr (?:[ ]* '.$this->id_class_attr_catch_re.' )? # $5 = extra id & class attr
(?:\n+|\Z) (?:\n+|\Z)
}xm', }xm',
array(&$this, '_stripLinkDefinitions_callback'), array($this, '_stripLinkDefinitions_callback'),
$text); $text);
return $text; return $text;
} }
@ -1733,17 +1773,17 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
### HTML Block Parser ### ### HTML Block Parser ###
# Tags that are always treated as block tags: # Tags that are always treated as block tags:
protected $block_tags_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|form|fieldset|iframe|hr|legend|article|section|nav|aside|hgroup|header|footer|figcaption'; protected $block_tags_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|form|fieldset|iframe|hr|legend|article|section|nav|aside|hgroup|header|footer|figcaption|figure';
# Tags treated as block tags only if the opening tag is alone on its line: # Tags treated as block tags only if the opening tag is alone on its line:
protected $context_block_tags_re = 'script|noscript|ins|del|iframe|object|source|track|param|math|svg|canvas|audio|video'; protected $context_block_tags_re = 'script|noscript|style|ins|del|iframe|object|source|track|param|math|svg|canvas|audio|video';
# Tags where markdown="1" default to span mode: # Tags where markdown="1" default to span mode:
protected $contain_span_tags_re = 'p|h[1-6]|li|dd|dt|td|th|legend|address'; protected $contain_span_tags_re = 'p|h[1-6]|li|dd|dt|td|th|legend|address';
# Tags which must not have their contents modified, no matter where # Tags which must not have their contents modified, no matter where
# they appear: # they appear:
protected $clean_tags_re = 'script|math|svg'; protected $clean_tags_re = 'script|style|math|svg';
# Tags that do not need to be closed. # Tags that do not need to be closed.
protected $auto_close_tags_re = 'hr|img|param|source|track'; protected $auto_close_tags_re = 'hr|img|param|source|track';
@ -2227,7 +2267,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
\] \]
) )
}xs', }xs',
array(&$this, '_doAnchors_reference_callback'), $text); array($this, '_doAnchors_reference_callback'), $text);
# #
# Next, inline-style links: [link text](url "optional title") # Next, inline-style links: [link text](url "optional title")
@ -2255,7 +2295,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
(?:[ ]? '.$this->id_class_attr_catch_re.' )? # $8 = id/class attributes (?:[ ]? '.$this->id_class_attr_catch_re.' )? # $8 = id/class attributes
) )
}xs', }xs',
array(&$this, '_doAnchors_inline_callback'), $text); array($this, '_doAnchors_inline_callback'), $text);
# #
# Last, handle reference-style shortcuts: [link text] # Last, handle reference-style shortcuts: [link text]
@ -2269,7 +2309,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
\] \]
) )
}xs', }xs',
array(&$this, '_doAnchors_reference_callback'), $text); array($this, '_doAnchors_reference_callback'), $text);
$this->in_anchor = false; $this->in_anchor = false;
return $text; return $text;
@ -2290,7 +2330,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
if (isset($this->urls[$link_id])) { if (isset($this->urls[$link_id])) {
$url = $this->urls[$link_id]; $url = $this->urls[$link_id];
$url = $this->encodeAttribute($url); $url = $this->encodeURLAttribute($url);
$result = "<a href=\"$url\""; $result = "<a href=\"$url\"";
if ( isset( $this->titles[$link_id] ) ) { if ( isset( $this->titles[$link_id] ) ) {
@ -2317,8 +2357,13 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
$title =& $matches[7]; $title =& $matches[7];
$attr = $this->doExtraAttributes("a", $dummy =& $matches[8]); $attr = $this->doExtraAttributes("a", $dummy =& $matches[8]);
// if the URL was of the form <s p a c e s> it got caught by the HTML
// tag parser and hashed. Need to reverse the process before using the URL.
$unhashed = $this->unhash($url);
if ($unhashed != $url)
$url = preg_replace('/^<(.*)>$/', '\1', $unhashed);
$url = $this->encodeAttribute($url); $url = $this->encodeURLAttribute($url);
$result = "<a href=\"$url\""; $result = "<a href=\"$url\"";
if (isset($title)) { if (isset($title)) {
@ -2356,7 +2401,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
) )
}xs', }xs',
array(&$this, '_doImages_reference_callback'), $text); array($this, '_doImages_reference_callback'), $text);
# #
# Next, handle inline images: ![alt text](url "optional title") # Next, handle inline images: ![alt text](url "optional title")
@ -2386,7 +2431,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
(?:[ ]? '.$this->id_class_attr_catch_re.' )? # $8 = id/class attributes (?:[ ]? '.$this->id_class_attr_catch_re.' )? # $8 = id/class attributes
) )
}xs', }xs',
array(&$this, '_doImages_inline_callback'), $text); array($this, '_doImages_inline_callback'), $text);
return $text; return $text;
} }
@ -2401,7 +2446,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
$alt_text = $this->encodeAttribute($alt_text); $alt_text = $this->encodeAttribute($alt_text);
if (isset($this->urls[$link_id])) { if (isset($this->urls[$link_id])) {
$url = $this->encodeAttribute($this->urls[$link_id]); $url = $this->encodeURLAttribute($this->urls[$link_id]);
$result = "<img src=\"$url\" alt=\"$alt_text\""; $result = "<img src=\"$url\" alt=\"$alt_text\"";
if (isset($this->titles[$link_id])) { if (isset($this->titles[$link_id])) {
$title = $this->titles[$link_id]; $title = $this->titles[$link_id];
@ -2428,7 +2473,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
$attr = $this->doExtraAttributes("img", $dummy =& $matches[8]); $attr = $this->doExtraAttributes("img", $dummy =& $matches[8]);
$alt_text = $this->encodeAttribute($alt_text); $alt_text = $this->encodeAttribute($alt_text);
$url = $this->encodeAttribute($url); $url = $this->encodeURLAttribute($url);
$result = "<img src=\"$url\" alt=\"$alt_text\""; $result = "<img src=\"$url\" alt=\"$alt_text\"";
if (isset($title)) { if (isset($title)) {
$title = $this->encodeAttribute($title); $title = $this->encodeAttribute($title);
@ -2458,7 +2503,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
(?:[ ]+ '.$this->id_class_attr_catch_re.' )? # $3 = id/class attributes (?:[ ]+ '.$this->id_class_attr_catch_re.' )? # $3 = id/class attributes
[ ]*\n(=+|-+)[ ]*\n+ # $3: Header footer [ ]*\n(=+|-+)[ ]*\n+ # $3: Header footer
}mx', }mx',
array(&$this, '_doHeaders_callback_setext'), $text); array($this, '_doHeaders_callback_setext'), $text);
# atx-style headers: # atx-style headers:
# # Header 1 {#header1} # # Header 1 {#header1}
@ -2477,7 +2522,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
[ ]* [ ]*
\n+ \n+
}xm', }xm',
array(&$this, '_doHeaders_callback_atx'), $text); array($this, '_doHeaders_callback_atx'), $text);
return $text; return $text;
} }
@ -2528,7 +2573,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
) )
(?=\n|\Z) # Stop at final double newline. (?=\n|\Z) # Stop at final double newline.
}xm', }xm',
array(&$this, '_doTable_leadingPipe_callback'), $text); array($this, '_doTable_leadingPipe_callback'), $text);
# #
# Find tables without leading pipe. # Find tables without leading pipe.
@ -2554,7 +2599,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
) )
(?=\n|\Z) # Stop at final double newline. (?=\n|\Z) # Stop at final double newline.
}xm', }xm',
array(&$this, '_DoTable_callback'), $text); array($this, '_DoTable_callback'), $text);
return $text; return $text;
} }
@ -2678,7 +2723,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
(?>\A\n?|(?<=\n\n)) (?>\A\n?|(?<=\n\n))
'.$whole_list_re.' '.$whole_list_re.'
}mx', }mx',
array(&$this, '_doDefLists_callback'), $text); array($this, '_doDefLists_callback'), $text);
return $text; return $text;
} }
@ -2716,7 +2761,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
(?=\n?[ ]{0,3}:[ ]) # lookahead for following line feed (?=\n?[ ]{0,3}:[ ]) # lookahead for following line feed
# with a definition mark. # with a definition mark.
}xm', }xm',
array(&$this, '_processDefListItems_callback_dt'), $list_str); array($this, '_processDefListItems_callback_dt'), $list_str);
# Process actual definitions. # Process actual definitions.
$list_str = preg_replace_callback('{ $list_str = preg_replace_callback('{
@ -2733,7 +2778,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
) )
) )
}xm', }xm',
array(&$this, '_processDefListItems_callback_dd'), $list_str); array($this, '_processDefListItems_callback_dd'), $list_str);
return $list_str; return $list_str;
} }
@ -2801,7 +2846,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
# Closing marker. # Closing marker.
\1 [ ]* (?= \n ) \1 [ ]* (?= \n )
}xm', }xm',
array(&$this, '_doFencedCodeBlocks_callback'), $text); array($this, '_doFencedCodeBlocks_callback'), $text);
return $text; return $text;
} }
@ -2811,7 +2856,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
$codeblock = $matches[4]; $codeblock = $matches[4];
$codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES); $codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
$codeblock = preg_replace_callback('/^\n+/', $codeblock = preg_replace_callback('/^\n+/',
array(&$this, '_doFencedCodeBlocks_newlines'), $codeblock); array($this, '_doFencedCodeBlocks_newlines'), $codeblock);
if ($classname != "") { if ($classname != "") {
if ($classname{0} == '.') if ($classname{0} == '.')
@ -2837,19 +2882,19 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
# work in the middle of a word. # work in the middle of a word.
# #
protected $em_relist = array( protected $em_relist = array(
'' => '(?:(?<!\*)\*(?!\*)|(?<![a-zA-Z0-9_])_(?!_))(?=\S|$)(?![\.,:;]\s)', '' => '(?:(?<!\*)\*(?!\*)|(?<![a-zA-Z0-9_])_(?!_))(?![\.,:;]?\s)',
'*' => '(?<=\S|^)(?<!\*)\*(?!\*)', '*' => '(?<![\s*])\*(?!\*)',
'_' => '(?<=\S|^)(?<!_)_(?![a-zA-Z0-9_])', '_' => '(?<![\s_])_(?![a-zA-Z0-9_])',
); );
protected $strong_relist = array( protected $strong_relist = array(
'' => '(?:(?<!\*)\*\*(?!\*)|(?<![a-zA-Z0-9_])__(?!_))(?=\S|$)(?![\.,:;]\s)', '' => '(?:(?<!\*)\*\*(?!\*)|(?<![a-zA-Z0-9_])__(?!_))(?![\.,:;]?\s)',
'**' => '(?<=\S|^)(?<!\*)\*\*(?!\*)', '**' => '(?<![\s*])\*\*(?!\*)',
'__' => '(?<=\S|^)(?<!_)__(?![a-zA-Z0-9_])', '__' => '(?<![\s_])__(?![a-zA-Z0-9_])',
); );
protected $em_strong_relist = array( protected $em_strong_relist = array(
'' => '(?:(?<!\*)\*\*\*(?!\*)|(?<![a-zA-Z0-9_])___(?!_))(?=\S|$)(?![\.,:;]\s)', '' => '(?:(?<!\*)\*\*\*(?!\*)|(?<![a-zA-Z0-9_])___(?!_))(?![\.,:;]?\s)',
'***' => '(?<=\S|^)(?<!\*)\*\*\*(?!\*)', '***' => '(?<![\s*])\*\*\*(?!\*)',
'___' => '(?<=\S|^)(?<!_)___(?![a-zA-Z0-9_])', '___' => '(?<![\s_])___(?![a-zA-Z0-9_])',
); );
@ -2908,13 +2953,13 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
.+ # actual text .+ # actual text
| |
\n # newlines but \n # newlines but
(?!\[\^.+?\]:\s)# negative lookahead for footnote marker. (?!\[.+?\][ ]?:\s)# negative lookahead for footnote or link definition marker.
(?!\n+[ ]{0,3}\S)# ensure line is not blank and followed (?!\n+[ ]{0,3}\S)# ensure line is not blank and followed
# by non-indented content # by non-indented content
)* )*
) )
}xm', }xm',
array(&$this, '_stripFootnotes_callback'), array($this, '_stripFootnotes_callback'),
$text); $text);
return $text; return $text;
} }
@ -2942,7 +2987,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
# Append footnote list to text. # Append footnote list to text.
# #
$text = preg_replace_callback('{F\x1Afn:(.*?)\x1A:}', $text = preg_replace_callback('{F\x1Afn:(.*?)\x1A:}',
array(&$this, '_appendFootnotes_callback'), $text); array($this, '_appendFootnotes_callback'), $text);
if (!empty($this->footnotes_ordered)) { if (!empty($this->footnotes_ordered)) {
$text .= "\n\n"; $text .= "\n\n";
@ -2974,7 +3019,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
$footnote .= "\n"; # Need to append newline before parsing. $footnote .= "\n"; # Need to append newline before parsing.
$footnote = $this->runBlockGamut("$footnote\n"); $footnote = $this->runBlockGamut("$footnote\n");
$footnote = preg_replace_callback('{F\x1Afn:(.*?)\x1A:}', $footnote = preg_replace_callback('{F\x1Afn:(.*?)\x1A:}',
array(&$this, '_appendFootnotes_callback'), $footnote); array($this, '_appendFootnotes_callback'), $footnote);
$attr = str_replace("%%", ++$num, $attr); $attr = str_replace("%%", ++$num, $attr);
$note_id = $this->encodeAttribute($note_id); $note_id = $this->encodeAttribute($note_id);
@ -3057,7 +3102,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
^[ ]{0,'.$less_than_tab.'}\*\[(.+?)\][ ]?: # abbr_id = $1 ^[ ]{0,'.$less_than_tab.'}\*\[(.+?)\][ ]?: # abbr_id = $1
(.*) # text = $2 (no blank lines allowed) (.*) # text = $2 (no blank lines allowed)
}xm', }xm',
array(&$this, '_stripAbbreviations_callback'), array($this, '_stripAbbreviations_callback'),
$text); $text);
return $text; return $text;
} }
@ -3084,7 +3129,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
'(?:'.$this->abbr_word_re.')'. '(?:'.$this->abbr_word_re.')'.
'(?![\w\x1A])'. '(?![\w\x1A])'.
'}', '}',
array(&$this, '_doAbbreviations_callback'), $text); array($this, '_doAbbreviations_callback'), $text);
} }
return $text; return $text;
} }

View File

@ -0,0 +1,38 @@
<?php
#
# Markdown Extra - A text-to-HTML conversion tool for web writers
#
# PHP Markdown Extra
# Copyright (c) 2004-2014 Michel Fortin
# <http://michelf.com/projects/php-markdown/>
#
# Original Markdown
# Copyright (c) 2004-2006 John Gruber
# <http://daringfireball.net/projects/markdown/>
#
namespace Michelf;
# Just force Michelf/Markdown.php to load. This is needed to load
# the temporary implementation class. See below for details.
\Michelf\Markdown::MARKDOWNLIB_VERSION;
#
# Markdown Extra Parser Class
#
# Note: Currently the implementation resides in the temporary class
# \Michelf\MarkdownExtra_TmpImpl (in the same file as \Michelf\Markdown).
# This makes it easier to propagate the changes between the three different
# packaging styles of PHP Markdown. Once this issue is resolved, the
# _MarkdownExtra_TmpImpl will disappear and this one will contain the code.
#
class MarkdownExtra extends \Michelf\_MarkdownExtra_TmpImpl {
### Parser Implementation ###
# Temporarily, the implemenation is in the _MarkdownExtra_TmpImpl class.
# See note above.
}

View File

@ -3,7 +3,7 @@
# Markdown - A text-to-HTML conversion tool for web writers # Markdown - A text-to-HTML conversion tool for web writers
# #
# PHP Markdown # PHP Markdown
# Copyright (c) 2004-2013 Michel Fortin # Copyright (c) 2004-2014 Michel Fortin
# <http://michelf.com/projects/php-markdown/> # <http://michelf.com/projects/php-markdown/>
# #
# Original Markdown # Original Markdown
@ -32,6 +32,3 @@ interface MarkdownInterface {
public function transform($text); public function transform($text);
} }
?>