|
|
@ -59,9 +59,6 @@ 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 ###
|
|
|
|
|
|
|
|
|
@ -596,7 +593,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->encodeURLAttribute($url); |
|
|
|
|
|
|
|
|
$url = $this->encodeAttribute($url); |
|
|
|
|
|
|
|
|
$result = "<a href=\"$url\"";
|
|
|
$result = "<a href=\"$url\"";
|
|
|
if ( isset( $this->titles[$link_id] ) ) { |
|
|
if ( isset( $this->titles[$link_id] ) ) { |
|
|
@ -626,7 +623,7 @@ class Markdown implements MarkdownInterface { |
|
|
if ($unhashed != $url) |
|
|
if ($unhashed != $url) |
|
|
$url = preg_replace('/^<(.*)>$/', '\1', $unhashed); |
|
|
$url = preg_replace('/^<(.*)>$/', '\1', $unhashed); |
|
|
|
|
|
|
|
|
$url = $this->encodeURLAttribute($url); |
|
|
|
|
|
|
|
|
$url = $this->encodeAttribute($url); |
|
|
|
|
|
|
|
|
$result = "<a href=\"$url\"";
|
|
|
$result = "<a href=\"$url\"";
|
|
|
if (isset($title)) { |
|
|
if (isset($title)) { |
|
|
@ -707,7 +704,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->encodeURLAttribute($this->urls[$link_id]); |
|
|
|
|
|
|
|
|
$url = $this->encodeAttribute($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]; |
|
|
@ -731,7 +728,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->encodeURLAttribute($url); |
|
|
|
|
|
|
|
|
$url = $this->encodeAttribute($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); |
|
|
@ -1263,33 +1260,6 @@ class Markdown implements MarkdownInterface { |
|
|
$text = str_replace('"', '"', $text); |
|
|
$text = str_replace('"', '"', $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) { |
|
|
@ -1314,7 +1284,7 @@ class Markdown implements MarkdownInterface { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected function doAutoLinks($text) { |
|
|
protected function doAutoLinks($text) { |
|
|
$text = preg_replace_callback('{<((https?|ftp|dict|tel):[^\'">\s]+)>}i', |
|
|
|
|
|
|
|
|
$text = preg_replace_callback('{<((https?|ftp|dict):[^\'">\s]+)>}i', |
|
|
array($this, '_doAutoLinks_url_callback'), $text); |
|
|
array($this, '_doAutoLinks_url_callback'), $text); |
|
|
|
|
|
|
|
|
# Email addresses: <address@domain.foo>
|
|
|
# Email addresses: <address@domain.foo>
|
|
|
@ -1337,46 +1307,48 @@ 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->encodeURLAttribute($matches[1], $text); |
|
|
|
|
|
$link = "<a href=\"$url\">$text</a>";
|
|
|
|
|
|
|
|
|
$url = $this->encodeAttribute($matches[1]); |
|
|
|
|
|
$link = "<a href=\"$url\">$url</a>";
|
|
|
return $this->hashPart($link); |
|
|
return $this->hashPart($link); |
|
|
} |
|
|
} |
|
|
protected function _doAutoLinks_email_callback($matches) { |
|
|
protected function _doAutoLinks_email_callback($matches) { |
|
|
$addr = $matches[1]; |
|
|
|
|
|
$url = $this->encodeURLAttribute("mailto:$addr", $text); |
|
|
|
|
|
$link = "<a href=\"$url\">$text</a>";
|
|
|
|
|
|
|
|
|
$address = $matches[1]; |
|
|
|
|
|
$link = $this->encodeEmailAddress($address); |
|
|
return $this->hashPart($link); |
|
|
return $this->hashPart($link); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected function encodeEntityObfuscatedAttribute($text, &$tail = null, $head_length = 0) { |
|
|
|
|
|
|
|
|
protected function encodeEmailAddress($addr) { |
|
|
#
|
|
|
#
|
|
|
# Input: some text to obfuscate, e.g. "mailto:foo@example.com"
|
|
|
|
|
|
|
|
|
# Input: an email address, e.g. "foo@example.com"
|
|
|
#
|
|
|
#
|
|
|
# Output: the same text but with most characters encoded as either a
|
|
|
|
|
|
# decimal or hex entity, in the hopes of foiling most address
|
|
|
|
|
|
# harvesting spam bots. E.g.:
|
|
|
|
|
|
|
|
|
# Output: the email address as a mailto link, with each character
|
|
|
|
|
|
# of the address encoded as either a decimal or hex entity, in
|
|
|
|
|
|
# the hopes of foiling most address harvesting spam bots. E.g.:
|
|
|
#
|
|
|
#
|
|
|
# mailto:foo
|
|
|
|
|
|
|
|
|
# <p><a href="mailto:foo
|
|
|
# @example.co
|
|
|
# @example.co
|
|
|
# m
|
|
|
|
|
|
#
|
|
|
|
|
|
# Note: the additional output $tail is assigned the same value as the
|
|
|
|
|
|
# ouput, minus the number of characters specified by $head_length.
|
|
|
|
|
|
|
|
|
# m">foo@exampl
|
|
|
|
|
|
# e.com</a></p>
|
|
|
#
|
|
|
#
|
|
|
# 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. Forced encoding of HTML
|
|
|
|
|
|
# attribute special characters by Allan Odgaard.
|
|
|
|
|
|
|
|
|
# With some optimizations by Milian Wolff.
|
|
|
#
|
|
|
#
|
|
|
if ($text == "") return $tail = ""; |
|
|
|
|
|
|
|
|
|
|
|
$chars = preg_split('/(?<!^)(?!$)/', $text); |
|
|
|
|
|
$seed = (int)abs(crc32($text) / strlen($text)); # Deterministic seed.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$addr = "mailto:" . $addr; |
|
|
|
|
|
$chars = preg_split('/(?<!^)(?!$)/', $addr); |
|
|
|
|
|
$seed = (int)abs(crc32($addr) / strlen($addr)); # 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.
|
|
|
@ -1384,17 +1356,18 @@ 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.
|
|
|
# '"' and '>' have to be encoded inside the attribute
|
|
|
|
|
|
if ($r > 90 && strpos('@"&>', $char) === false) /* do nothing */; |
|
|
|
|
|
|
|
|
# '"' has to be encoded inside the attribute
|
|
|
|
|
|
if ($r > 90 && $char != '@' && $char != '"') /* 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>";
|
|
|
|
|
|
|
|
|
$text = implode('', $chars); |
|
|
|
|
|
$tail = $head_length ? implode('', array_slice($chars, $head_length)) : $text; |
|
|
|
|
|
|
|
|
|
|
|
return $text; |
|
|
|
|
|
|
|
|
return $addr; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -1678,9 +1651,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-z][-_:a-zA-Z0-9=]+){1,})[ ]*\}'; |
|
|
|
|
|
|
|
|
protected $id_class_attr_catch_re = '\{((?:[ ]*[#.][-_: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-z][-_:a-zA-Z0-9=]+){1,}[ ]*\}'; |
|
|
|
|
|
|
|
|
protected $id_class_attr_nocatch_re = '\{(?:[ ]*[#.][-_:a-zA-Z0-9]+){1,}[ ]*\}'; |
|
|
|
|
|
|
|
|
protected function doExtraAttributes($tag_name, $attr) { |
|
|
protected function doExtraAttributes($tag_name, $attr) { |
|
|
#
|
|
|
#
|
|
|
@ -1692,21 +1665,17 @@ 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-z][-_:a-zA-Z0-9=]+/', $attr, $matches); |
|
|
|
|
|
|
|
|
preg_match_all('/[#.][-_: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] . '"'; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -1718,9 +1687,6 @@ 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; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -2330,7 +2296,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->encodeURLAttribute($url); |
|
|
|
|
|
|
|
|
$url = $this->encodeAttribute($url); |
|
|
|
|
|
|
|
|
$result = "<a href=\"$url\"";
|
|
|
$result = "<a href=\"$url\"";
|
|
|
if ( isset( $this->titles[$link_id] ) ) { |
|
|
if ( isset( $this->titles[$link_id] ) ) { |
|
|
@ -2363,7 +2329,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown { |
|
|
if ($unhashed != $url) |
|
|
if ($unhashed != $url) |
|
|
$url = preg_replace('/^<(.*)>$/', '\1', $unhashed); |
|
|
$url = preg_replace('/^<(.*)>$/', '\1', $unhashed); |
|
|
|
|
|
|
|
|
$url = $this->encodeURLAttribute($url); |
|
|
|
|
|
|
|
|
$url = $this->encodeAttribute($url); |
|
|
|
|
|
|
|
|
$result = "<a href=\"$url\"";
|
|
|
$result = "<a href=\"$url\"";
|
|
|
if (isset($title)) { |
|
|
if (isset($title)) { |
|
|
@ -2446,7 +2412,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->encodeURLAttribute($this->urls[$link_id]); |
|
|
|
|
|
|
|
|
$url = $this->encodeAttribute($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]; |
|
|
@ -2473,7 +2439,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->encodeURLAttribute($url); |
|
|
|
|
|
|
|
|
$url = $this->encodeAttribute($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); |
|
|
|