extlib Michelf\Markdown updated 1.4.0 to 1.4.1
authorMikael Nordfeldth <mmn@hethane.se>
Thu, 12 Feb 2015 21:45:08 +0000 (22:45 +0100)
committerMikael Nordfeldth <mmn@hethane.se>
Thu, 12 Feb 2015 21:46:25 +0000 (22:46 +0100)
extlib/Michelf/License.md
extlib/Michelf/Markdown.php
extlib/Michelf/MarkdownExtra.php [new file with mode: 0644]
extlib/Michelf/MarkdownInterface.php

index 027becbd58f5d3f0dd0051c00610f07ea908720d..8a5b85447ee4af8220915ee8e939c52f8049dd50 100644 (file)
@@ -1,5 +1,5 @@
 PHP Markdown Lib  
-Copyright (c) 2004-2013 Michel Fortin  
+Copyright (c) 2004-2014 Michel Fortin  
 <http://michelf.ca/>  
 All rights reserved.
 
index 088b7cddb662d2d160009eb138054a454b780d78..c8ced899c61da0f4f93b597a9b4b3897cb3fb125 100644 (file)
@@ -3,7 +3,7 @@
 # Markdown  -  A text-to-HTML conversion tool for web writers
 #
 # PHP Markdown  
-# Copyright (c) 2004-2013 Michel Fortin  
+# Copyright (c) 2004-2014 Michel Fortin  
 # <http://michelf.com/projects/php-markdown/>
 #
 # Original Markdown  
@@ -21,7 +21,7 @@ class Markdown implements MarkdownInterface {
 
        ### Version ###
 
-       const  MARKDOWNLIB_VERSION  =  "1.4.0";
+       const  MARKDOWNLIB_VERSION  =  "1.4.1";
 
        ### Simple Function Interface ###
 
@@ -59,6 +59,9 @@ class Markdown implements MarkdownInterface {
        public $predef_urls = array();
        public $predef_titles = array();
 
+       # Optional filter function for URLs
+       public $url_filter_func = null;
+
 
        ### Parser Implementation ###
 
@@ -209,7 +212,7 @@ class Markdown implements MarkdownInterface {
                                                        )?      # title is optional
                                                        (?:\n+|\Z)
                        }xm',
-                       array(&$this, '_stripLinkDefinitions_callback'),
+                       array($this, '_stripLinkDefinitions_callback'),
                        $text);
                return $text;
        }
@@ -242,7 +245,7 @@ class Markdown implements MarkdownInterface {
                #
                $block_tags_a_re = 'ins|del';
                $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|'.
                                                   'figure';
 
@@ -297,9 +300,9 @@ class Markdown implements MarkdownInterface {
                # match will start at the first `<div>` and stop at the first `</div>`.
                $text = preg_replace_callback('{(?>
                        (?>
-                               (?<=\n\n)               # Starting after a blank line
+                               (?<=\n)                 # Starting on its own line
                                |                               # or
-                               \A\n?                   # the beginning of the doc
+                               \A\n?                   # the at beginning of the doc
                        )
                        (                                               # save in $1
 
@@ -356,7 +359,7 @@ class Markdown implements MarkdownInterface {
                                        
                        )
                        )}Sxmi',
-                       array(&$this, '_hashHTMLBlocks_callback'),
+                       array($this, '_hashHTMLBlocks_callback'),
                        $text);
 
                return $text;
@@ -500,7 +503,7 @@ class Markdown implements MarkdownInterface {
        protected function doHardBreaks($text) {
                # Do hard breaks:
                return preg_replace_callback('/ {2,}\n/', 
-                       array(&$this, '_doHardBreaks_callback'), $text);
+                       array($this, '_doHardBreaks_callback'), $text);
        }
        protected function _doHardBreaks_callback($matches) {
                return $this->hashPart("<br$this->empty_element_suffix\n");
@@ -531,7 +534,7 @@ class Markdown implements MarkdownInterface {
                          \]
                        )
                        }xs',
-                       array(&$this, '_doAnchors_reference_callback'), $text);
+                       array($this, '_doAnchors_reference_callback'), $text);
 
                #
                # Next, inline-style links: [link text](url "optional title")
@@ -558,7 +561,7 @@ class Markdown implements MarkdownInterface {
                          \)
                        )
                        }xs',
-                       array(&$this, '_doAnchors_inline_callback'), $text);
+                       array($this, '_doAnchors_inline_callback'), $text);
 
                #
                # Last, handle reference-style shortcuts: [link text]
@@ -572,7 +575,7 @@ class Markdown implements MarkdownInterface {
                          \]
                        )
                        }xs',
-                       array(&$this, '_doAnchors_reference_callback'), $text);
+                       array($this, '_doAnchors_reference_callback'), $text);
 
                $this->in_anchor = false;
                return $text;
@@ -593,7 +596,7 @@ class Markdown implements MarkdownInterface {
 
                if (isset($this->urls[$link_id])) {
                        $url = $this->urls[$link_id];
-                       $url = $this->encodeAttribute($url);
+                       $url = $this->encodeURLAttribute($url);
                        
                        $result = "<a href=\"$url\"";
                        if ( isset( $this->titles[$link_id] ) ) {
@@ -617,7 +620,13 @@ class Markdown implements MarkdownInterface {
                $url                    =  $matches[3] == '' ? $matches[4] : $matches[3];
                $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\"";
                if (isset($title)) {
@@ -654,7 +663,7 @@ class Markdown implements MarkdownInterface {
 
                        )
                        }xs', 
-                       array(&$this, '_doImages_reference_callback'), $text);
+                       array($this, '_doImages_reference_callback'), $text);
 
                #
                # Next, handle inline images:  ![alt text](url "optional title")
@@ -683,7 +692,7 @@ class Markdown implements MarkdownInterface {
                          \)
                        )
                        }xs',
-                       array(&$this, '_doImages_inline_callback'), $text);
+                       array($this, '_doImages_inline_callback'), $text);
 
                return $text;
        }
@@ -698,7 +707,7 @@ class Markdown implements MarkdownInterface {
 
                $alt_text = $this->encodeAttribute($alt_text);
                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\"";
                        if (isset($this->titles[$link_id])) {
                                $title = $this->titles[$link_id];
@@ -722,7 +731,7 @@ class Markdown implements MarkdownInterface {
                $title                  =& $matches[7];
 
                $alt_text = $this->encodeAttribute($alt_text);
-               $url = $this->encodeAttribute($url);
+               $url = $this->encodeURLAttribute($url);
                $result = "<img src=\"$url\" alt=\"$alt_text\"";
                if (isset($title)) {
                        $title = $this->encodeAttribute($title);
@@ -743,7 +752,7 @@ class Markdown implements MarkdownInterface {
                #         --------
                #
                $text = preg_replace_callback('{ ^(.+?)[ ]*\n(=+|-+)[ ]*\n+ }mx',
-                       array(&$this, '_doHeaders_callback_setext'), $text);
+                       array($this, '_doHeaders_callback_setext'), $text);
 
                # atx-style headers:
                #       # Header 1
@@ -760,7 +769,7 @@ class Markdown implements MarkdownInterface {
                                \#*                     # optional closing #\'s (not counted)
                                \n+
                        }xm',
-                       array(&$this, '_doHeaders_callback_atx'), $text);
+                       array($this, '_doHeaders_callback_atx'), $text);
 
                return $text;
        }
@@ -789,7 +798,6 @@ class Markdown implements MarkdownInterface {
                # Re-usable patterns to match list item bullets and number markers:
                $marker_ul_re  = '[*+-]';
                $marker_ol_re  = '\d+[\.]';
-               $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)";
 
                $markers_relist = array(
                        $marker_ul_re => $marker_ol_re,
@@ -833,14 +841,14 @@ class Markdown implements MarkdownInterface {
                                                ^
                                                '.$whole_list_re.'
                                        }mx',
-                                       array(&$this, '_doLists_callback'), $text);
+                                       array($this, '_doLists_callback'), $text);
                        }
                        else {
                                $text = preg_replace_callback('{
                                                (?:(?<=\n)\n|\A\n?) # Must eat the newline
                                                '.$whole_list_re.'
                                        }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* (\z | \2 ('.$marker_any_re.') (?:[ ]+|(?=\n))))
                        }xm',
-                       array(&$this, '_processListItems_callback'), $list_str);
+                       array($this, '_processListItems_callback'), $list_str);
 
                $this->list_level--;
                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
                        }xm',
-                       array(&$this, '_doCodeBlocks_callback'), $text);
+                       array($this, '_doCodeBlocks_callback'), $text);
 
                return $text;
        }
@@ -979,19 +987,19 @@ class Markdown implements MarkdownInterface {
 
 
        protected $em_relist = array(
-               ''  => '(?:(?<!\*)\*(?!\*)|(?<!_)_(?!_))(?=\S|$)(?![\.,:;]\s)',
-               '*' => '(?<=\S|^)(?<!\*)\*(?!\*)',
-               '_' => '(?<=\S|^)(?<!_)_(?!_)',
+               ''  => '(?:(?<!\*)\*(?!\*)|(?<!_)_(?!_))(?![\.,:;]?\s)',
+               '*' => '(?<![\s*])\*(?!\*)',
+               '_' => '(?<![\s_])_(?!_)',
                );
        protected $strong_relist = array(
-               ''   => '(?:(?<!\*)\*\*(?!\*)|(?<!_)__(?!_))(?=\S|$)(?![\.,:;]\s)',
-               '**' => '(?<=\S|^)(?<!\*)\*\*(?!\*)',
-               '__' => '(?<=\S|^)(?<!_)__(?!_)',
+               ''   => '(?:(?<!\*)\*\*(?!\*)|(?<!_)__(?!_))(?![\.,:;]?\s)',
+               '**' => '(?<![\s*])\*\*(?!\*)',
+               '__' => '(?<![\s_])__(?!_)',
                );
        protected $em_strong_relist = array(
-               ''    => '(?:(?<!\*)\*\*\*(?!\*)|(?<!_)___(?!_))(?=\S|$)(?![\.,:;]\s)',
-               '***' => '(?<=\S|^)(?<!\*)\*\*\*(?!\*)',
-               '___' => '(?<=\S|^)(?<!_)___(?!_)',
+               ''    => '(?:(?<!\*)\*\*\*(?!\*)|(?<!_)___(?!_))(?![\.,:;]?\s)',
+               '***' => '(?<![\s*])\*\*\*(?!\*)',
+               '___' => '(?<![\s_])___(?!_)',
                );
        protected $em_strong_prepared_relist;
        
@@ -1151,7 +1159,7 @@ class Markdown implements MarkdownInterface {
                                )+
                          )
                        /xm',
-                       array(&$this, '_doBlockQuotes_callback'), $text);
+                       array($this, '_doBlockQuotes_callback'), $text);
 
                return $text;
        }
@@ -1165,7 +1173,7 @@ class Markdown implements MarkdownInterface {
                # These leading spaces cause problem with <pre> content, 
                # so we need to fix that:
                $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";
        }
@@ -1255,6 +1263,33 @@ class Markdown implements MarkdownInterface {
                $text = str_replace('"', '&quot;', $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) {
@@ -1279,8 +1314,8 @@ class Markdown implements MarkdownInterface {
 
 
        protected function doAutoLinks($text) {
-               $text = preg_replace_callback('{<((https?|ftp|dict):[^\'">\s]+)>}i', 
-                       array(&$this, '_doAutoLinks_url_callback'), $text);
+               $text = preg_replace_callback('{<((https?|ftp|dict|tel):[^\'">\s]+)>}i',
+                       array($this, '_doAutoLinks_url_callback'), $text);
 
                # Email addresses: <address@domain.foo>
                $text = preg_replace_callback('{
@@ -1301,49 +1336,47 @@ class Markdown implements MarkdownInterface {
                        )
                        >
                        }xi',
-                       array(&$this, '_doAutoLinks_email_callback'), $text);
-               $text = preg_replace_callback('{<(tel:([^\'">\s]+))>}i',array(&$this, '_doAutoLinks_tel_callback'), $text);
+                       array($this, '_doAutoLinks_email_callback'), $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) {
-               $url = $this->encodeAttribute($matches[1]);
-               $link = "<a href=\"$url\">$url</a>";
+               $url = $this->encodeURLAttribute($matches[1], $text);
+               $link = "<a href=\"$url\">$text</a>";
                return $this->hashPart($link);
        }
        protected function _doAutoLinks_email_callback($matches) {
-               $address = $matches[1];
-               $link = $this->encodeEmailAddress($address);
+               $addr = $matches[1];
+               $url = $this->encodeURLAttribute("mailto:$addr", $text);
+               $link = "<a href=\"$url\">$text</a>";
                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
-       #               of the address encoded as either a decimal or hex entity, in
-       #               the hopes of foiling most address harvesting spam bots. E.g.:
+       #       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.:
        #
-       #         <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;
-       #        &#x6d;">&#x66;o&#111;&#x40;&#101;&#x78;&#97;&#x6d;&#112;&#x6c;
-       #        &#101;&#46;&#x63;&#111;&#x6d;</a></p>
+       #        &#x6d;
+       #
+       #       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.
-       #   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;
-               $chars = preg_split('/(?<!^)(?!$)/', $addr);
-               $seed = (int)abs(crc32($addr) / strlen($addr)); # Deterministic seed.
-               
+               if ($text == "") return $tail = "";
+
+               $chars = preg_split('/(?<!^)(?!$)/', $text);
+               $seed = (int)abs(crc32($text) / strlen($text)); # Deterministic seed.
+
                foreach ($chars as $key => $char) {
                        $ord = ord($char);
                        # Ignore non-ascii chars.
@@ -1351,17 +1384,17 @@ class Markdown implements MarkdownInterface {
                                $r = ($seed * (1 + $key)) % 100; # Pseudo-random function.
                                # roughly 10% raw, 45% hex, 45% dec
                                # '@' *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              $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.
                
                $text = preg_replace_callback('/^.*\t.*$/m',
-                       array(&$this, '_detab_callback'), $text);
+                       array($this, '_detab_callback'), $text);
 
                return $text;
        }
@@ -1510,7 +1543,7 @@ class Markdown implements MarkdownInterface {
        # Swap back in all the tags hashed by _HashHTMLBlocks.
        #
                return preg_replace_callback('/(.)\x1A[0-9]+\1/', 
-                       array(&$this, '_unhash_callback'), $text);
+                       array($this, '_unhash_callback'), $text);
        }
        protected function _unhash_callback($matches) {
                return $this->html_hashes[$matches[0]];
@@ -1645,9 +1678,9 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
        ### Extra Attribute Parser ###
 
        # 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
-       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) {
        #
@@ -1659,17 +1692,21 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
                if (empty($attr)) return "";
                
                # 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];
 
                # handle classes and ids (only first id taken into account)
                $classes = array();
+               $attributes = array();
                $id = false;
                foreach ($elements as $element) {
                        if ($element{0} == '.') {
                                $classes[] = substr($element, 1);
                        } else if ($element{0} == '#') {
                                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)) {
                        $attr_str .= ' class="'.implode(" ", $classes).'"';
                }
+               if (!$this->no_markup && !empty($attributes)) {
+                       $attr_str .= ' '.implode(" ", $attributes);
+               }
                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
                                                        (?:\n+|\Z)
                        }xm',
-                       array(&$this, '_stripLinkDefinitions_callback'),
+                       array($this, '_stripLinkDefinitions_callback'),
                        $text);
                return $text;
        }
@@ -1733,17 +1773,17 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
        ### HTML Block Parser ###
        
        # 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:
-       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:
        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 
        # 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.
        protected $auto_close_tags_re = 'hr|img|param|source|track';
@@ -2227,7 +2267,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
                          \]
                        )
                        }xs',
-                       array(&$this, '_doAnchors_reference_callback'), $text);
+                       array($this, '_doAnchors_reference_callback'), $text);
 
                #
                # 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
                        )
                        }xs',
-                       array(&$this, '_doAnchors_inline_callback'), $text);
+                       array($this, '_doAnchors_inline_callback'), $text);
 
                #
                # Last, handle reference-style shortcuts: [link text]
@@ -2269,7 +2309,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
                          \]
                        )
                        }xs',
-                       array(&$this, '_doAnchors_reference_callback'), $text);
+                       array($this, '_doAnchors_reference_callback'), $text);
 
                $this->in_anchor = false;
                return $text;
@@ -2290,7 +2330,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
 
                if (isset($this->urls[$link_id])) {
                        $url = $this->urls[$link_id];
-                       $url = $this->encodeAttribute($url);
+                       $url = $this->encodeURLAttribute($url);
                        
                        $result = "<a href=\"$url\"";
                        if ( isset( $this->titles[$link_id] ) ) {
@@ -2317,8 +2357,13 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
                $title                  =& $matches[7];
                $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\"";
                if (isset($title)) {
@@ -2356,7 +2401,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
 
                        )
                        }xs', 
-                       array(&$this, '_doImages_reference_callback'), $text);
+                       array($this, '_doImages_reference_callback'), $text);
 
                #
                # 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
                        )
                        }xs',
-                       array(&$this, '_doImages_inline_callback'), $text);
+                       array($this, '_doImages_inline_callback'), $text);
 
                return $text;
        }
@@ -2401,7 +2446,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
 
                $alt_text = $this->encodeAttribute($alt_text);
                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\"";
                        if (isset($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]);
 
                $alt_text = $this->encodeAttribute($alt_text);
-               $url = $this->encodeAttribute($url);
+               $url = $this->encodeURLAttribute($url);
                $result = "<img src=\"$url\" alt=\"$alt_text\"";
                if (isset($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
                                [ ]*\n(=+|-+)[ ]*\n+                            # $3: Header footer
                        }mx',
-                       array(&$this, '_doHeaders_callback_setext'), $text);
+                       array($this, '_doHeaders_callback_setext'), $text);
 
                # atx-style headers:
                #       # Header 1        {#header1}
@@ -2477,7 +2522,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
                                [ ]*
                                \n+
                        }xm',
-                       array(&$this, '_doHeaders_callback_atx'), $text);
+                       array($this, '_doHeaders_callback_atx'), $text);
 
                return $text;
        }
@@ -2528,7 +2573,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
                                )
                                (?=\n|\Z)                                       # Stop at final double newline.
                        }xm',
-                       array(&$this, '_doTable_leadingPipe_callback'), $text);
+                       array($this, '_doTable_leadingPipe_callback'), $text);
                
                #
                # Find tables without leading pipe.
@@ -2554,7 +2599,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
                                )
                                (?=\n|\Z)                                       # Stop at final double newline.
                        }xm',
-                       array(&$this, '_DoTable_callback'), $text);
+                       array($this, '_DoTable_callback'), $text);
 
                return $text;
        }
@@ -2678,7 +2723,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
                                (?>\A\n?|(?<=\n\n))
                                '.$whole_list_re.'
                        }mx',
-                       array(&$this, '_doDefLists_callback'), $text);
+                       array($this, '_doDefLists_callback'), $text);
 
                return $text;
        }
@@ -2716,7 +2761,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
                        (?=\n?[ ]{0,3}:[ ])                             # lookahead for following line feed 
                                                                                        #   with a definition mark.
                        }xm',
-                       array(&$this, '_processDefListItems_callback_dt'), $list_str);
+                       array($this, '_processDefListItems_callback_dt'), $list_str);
 
                # Process actual definitions.
                $list_str = preg_replace_callback('{
@@ -2733,7 +2778,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
                                )                                               
                        )                                       
                        }xm',
-                       array(&$this, '_processDefListItems_callback_dd'), $list_str);
+                       array($this, '_processDefListItems_callback_dd'), $list_str);
 
                return $list_str;
        }
@@ -2801,7 +2846,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
                                # Closing marker.
                                \1 [ ]* (?= \n )
                        }xm',
-                       array(&$this, '_doFencedCodeBlocks_callback'), $text);
+                       array($this, '_doFencedCodeBlocks_callback'), $text);
 
                return $text;
        }
@@ -2811,7 +2856,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
                $codeblock = $matches[4];
                $codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
                $codeblock = preg_replace_callback('/^\n+/',
-                       array(&$this, '_doFencedCodeBlocks_newlines'), $codeblock);
+                       array($this, '_doFencedCodeBlocks_newlines'), $codeblock);
 
                if ($classname != "") {
                        if ($classname{0} == '.')
@@ -2837,19 +2882,19 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
        # work in the middle of a word.
        #
        protected $em_relist = array(
-               ''  => '(?:(?<!\*)\*(?!\*)|(?<![a-zA-Z0-9_])_(?!_))(?=\S|$)(?![\.,:;]\s)',
-               '*' => '(?<=\S|^)(?<!\*)\*(?!\*)',
-               '_' => '(?<=\S|^)(?<!_)_(?![a-zA-Z0-9_])',
+               ''  => '(?:(?<!\*)\*(?!\*)|(?<![a-zA-Z0-9_])_(?!_))(?![\.,:;]?\s)',
+               '*' => '(?<![\s*])\*(?!\*)',
+               '_' => '(?<![\s_])_(?![a-zA-Z0-9_])',
                );
        protected $strong_relist = array(
-               ''   => '(?:(?<!\*)\*\*(?!\*)|(?<![a-zA-Z0-9_])__(?!_))(?=\S|$)(?![\.,:;]\s)',
-               '**' => '(?<=\S|^)(?<!\*)\*\*(?!\*)',
-               '__' => '(?<=\S|^)(?<!_)__(?![a-zA-Z0-9_])',
+               ''   => '(?:(?<!\*)\*\*(?!\*)|(?<![a-zA-Z0-9_])__(?!_))(?![\.,:;]?\s)',
+               '**' => '(?<![\s*])\*\*(?!\*)',
+               '__' => '(?<![\s_])__(?![a-zA-Z0-9_])',
                );
        protected $em_strong_relist = array(
-               ''    => '(?:(?<!\*)\*\*\*(?!\*)|(?<![a-zA-Z0-9_])___(?!_))(?=\S|$)(?![\.,:;]\s)',
-               '***' => '(?<=\S|^)(?<!\*)\*\*\*(?!\*)',
-               '___' => '(?<=\S|^)(?<!_)___(?![a-zA-Z0-9_])',
+               ''    => '(?:(?<!\*)\*\*\*(?!\*)|(?<![a-zA-Z0-9_])___(?!_))(?![\.,:;]?\s)',
+               '***' => '(?<![\s*])\*\*\*(?!\*)',
+               '___' => '(?<![\s_])___(?![a-zA-Z0-9_])',
                );
 
 
@@ -2908,13 +2953,13 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
                                        .+                              # actual text
                                |
                                        \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 
                                                                        # by non-indented content
                                )*
                        )               
                        }xm',
-                       array(&$this, '_stripFootnotes_callback'),
+                       array($this, '_stripFootnotes_callback'),
                        $text);
                return $text;
        }
@@ -2942,7 +2987,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
        # Append footnote list to text.
        #
                $text = preg_replace_callback('{F\x1Afn:(.*?)\x1A:}', 
-                       array(&$this, '_appendFootnotes_callback'), $text);
+                       array($this, '_appendFootnotes_callback'), $text);
        
                if (!empty($this->footnotes_ordered)) {
                        $text .= "\n\n";
@@ -2974,7 +3019,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
                                $footnote .= "\n"; # Need to append newline before parsing.
                                $footnote = $this->runBlockGamut("$footnote\n");                                
                                $footnote = preg_replace_callback('{F\x1Afn:(.*?)\x1A:}', 
-                                       array(&$this, '_appendFootnotes_callback'), $footnote);
+                                       array($this, '_appendFootnotes_callback'), $footnote);
                                
                                $attr = str_replace("%%", ++$num, $attr);
                                $note_id = $this->encodeAttribute($note_id);
@@ -3057,7 +3102,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
                        ^[ ]{0,'.$less_than_tab.'}\*\[(.+?)\][ ]?:      # abbr_id = $1
                        (.*)                                    # text = $2 (no blank lines allowed)    
                        }xm',
-                       array(&$this, '_stripAbbreviations_callback'),
+                       array($this, '_stripAbbreviations_callback'),
                        $text);
                return $text;
        }
@@ -3084,7 +3129,7 @@ abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
                                '(?:'.$this->abbr_word_re.')'.
                                '(?![\w\x1A])'.
                                '}', 
-                               array(&$this, '_doAbbreviations_callback'), $text);
+                               array($this, '_doAbbreviations_callback'), $text);
                }
                return $text;
        }
diff --git a/extlib/Michelf/MarkdownExtra.php b/extlib/Michelf/MarkdownExtra.php
new file mode 100644 (file)
index 0000000..89822e4
--- /dev/null
@@ -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.
+
+}
+
index 22c571a0d09cea891911e390dd9c1da6adb67e78..e4c2931f32b01a626e7e51275a66a8ee041b0bb1 100644 (file)
@@ -3,7 +3,7 @@
 # Markdown  -  A text-to-HTML conversion tool for web writers
 #
 # PHP Markdown
-# Copyright (c) 2004-2013 Michel Fortin
+# Copyright (c) 2004-2014 Michel Fortin
 # <http://michelf.com/projects/php-markdown/>
 #
 # Original Markdown
@@ -32,6 +32,3 @@ interface MarkdownInterface {
   public function transform($text);
 
 }
-
-
-?>
\ No newline at end of file