Development

Changeset 6191

You must first sign up to be able to contribute.

Changeset 6191

Show
Ignore:
Timestamp:
11/27/07 23:47:01 (1 year ago)
Author:
Simone.Carletti
Message:

sfMarkdownPlugin:
- updated parser version to PHP Markdown 1.1.7
- sfMarkdownPlugin 0.1.1

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • plugins/sfMarkdownPlugin/README

    r4712 r6191  
    156156== Changelog == 
    157157 
     1582007-11-27: 0.1.1 alpha 
     159 
     160 * Updated: PHP Markdown 1.1.7 
     161 
    1581622007-07-22: 0.1.0 alpha 
    159163 
  • plugins/sfMarkdownPlugin/lib/markdown.php

    r4712 r6191  
    1313 
    1414 
    15 define( 'MARKDOWN_VERSION',       "1.0.1g" ); # Tue 3 Jul 2007 
    16 define( 'MARKDOWNEXTRA_VERSION',  "1.1.3" );  # Tue 3 Jul 2007 
     15define( 'MARKDOWN_VERSION',  "1.0.1k" ); # Wed 26 Sep 2007 
     16define( 'MARKDOWNEXTRA_VERSION',  "1.1.7" ); # Wed 26 Sep 2007 
    1717 
    1818 
     
    2222 
    2323# Change to ">" for HTML output 
    24 define( 'MARKDOWN_EMPTY_ELEMENT_SUFFIX',  " />"); 
     24@define( 'MARKDOWN_EMPTY_ELEMENT_SUFFIX',  " />"); 
    2525 
    2626# Define the width of a tab for code blocks. 
    27 define( 'MARKDOWN_TAB_WIDTH',     4 ); 
     27@define( 'MARKDOWN_TAB_WIDTH',     4 ); 
    2828 
    2929# Optional title attribute for footnote links and backlinks. 
    30 define( 'MARKDOWN_FN_LINK_TITLE',         "" ); 
    31 define( 'MARKDOWN_FN_BACKLINK_TITLE',     "" ); 
     30@define( 'MARKDOWN_FN_LINK_TITLE',         "" ); 
     31@define( 'MARKDOWN_FN_BACKLINK_TITLE',     "" ); 
    3232 
    3333# Optional class attribute for footnote links and backlinks. 
    34 define( 'MARKDOWN_FN_LINK_CLASS',         "" ); 
    35 define( 'MARKDOWN_FN_BACKLINK_CLASS',     "" ); 
     34@define( 'MARKDOWN_FN_LINK_CLASS',         "" ); 
     35@define( 'MARKDOWN_FN_BACKLINK_CLASS',     "" ); 
    3636 
    3737 
     
    4141 
    4242# Change to false to remove Markdown from posts and/or comments. 
    43 define( 'MARKDOWN_WP_POSTS',      true ); 
    44 define( 'MARKDOWN_WP_COMMENTS',   true ); 
     43@define( 'MARKDOWN_WP_POSTS',      true ); 
     44@define( 'MARKDOWN_WP_COMMENTS',   true ); 
    4545 
    4646 
     
    4848### Standard Function Interface ### 
    4949 
    50 define( 'MARKDOWN_PARSER_CLASS',  'MarkdownExtra_Parser' ); 
     50@define( 'MARKDOWN_PARSER_CLASS',  'MarkdownExtra_Parser' ); 
    5151 
    5252function Markdown($text) { 
     
    7272Plugin URI: http://www.michelf.com/projects/php-markdown/ 
    7373Description: <a href="http://daringfireball.net/projects/markdown/syntax">Markdown syntax</a> allows you to write using an easy-to-read, easy-to-write plain text format. Based on the original Perl version by <a href="http://daringfireball.net/">John Gruber</a>. <a href="http://www.michelf.com/projects/php-markdown/">More...</a> 
    74 Version: 1.1.3 
     74Version: 1.1.7 
    7575Author: Michel Fortin 
    7676Author URI: http://www.michelf.com/ 
     
    117117    add_filter('get_comment_excerpt', 'mdwp_strip_p', 7); 
    118118   
    119     global $wp_markdown_hidden
    120     $wp_markdown_hidden[1] = 
    121       '<p> </p> <pre> </pre> <ol> </ol> <ul> </ul> <li> </li>'
    122     $wp_markdown_hidden[2] = explode(' ', str_rot13( 
     119    global $mdwp_hidden_tags, $mdwp_placeholders
     120    $mdwp_hidden_tags = explode(' ', 
     121      '<p> </p> <pre> </pre> <ol> </ol> <ul> </ul> <li> </li>')
     122    $mdwp_placeholders = explode(' ', str_rot13( 
    123123      'pEj07ZbbBZ U1kqgh4w4p pre2zmeN6K QTi31t9pre ol0MP1jzJR '. 
    124124      'ML5IjmbRol ulANi1NsGY J7zRLJqPul liA8ctl16T K9nhooUHli')); 
     
    136136 
    137137  function mdwp_hide_tags($text) { 
    138     global $wp_markdown_hidden; 
    139     return str_replace(explode($wp_markdown_hidden),  
    140               explode($wp_markdown_hidden), $text); 
     138    global $mdwp_hidden_tags, $mdwp_placeholders; 
     139    return str_replace($mdwp_hidden_tags, $mdwp_placeholders, $text); 
    141140  } 
    142141  function mdwp_show_tags($text) { 
    143     global $markdown_hidden_tags; 
    144     return str_replace(array_values($markdown_hidden_tags),  
    145               array_keys($markdown_hidden_tags), $text); 
     142    global $mdwp_hidden_tags, $mdwp_placeholders; 
     143    return str_replace($mdwp_placeholders, $mdwp_hidden_tags, $text); 
    146144  } 
    147145} 
     
    212210  # Table of hash values for escaped characters: 
    213211  var $escape_chars = '\`*_{}[]()>#+-.!'; 
    214   var $escape_table = array(); 
    215   var $backslash_escape_table = array(); 
    216212 
    217213  # Change to ">" for HTML output. 
    218214  var $empty_element_suffix = MARKDOWN_EMPTY_ELEMENT_SUFFIX; 
    219215  var $tab_width = MARKDOWN_TAB_WIDTH; 
     216   
     217  # Change to `true` to disallow markup or entities. 
     218  var $no_markup = false; 
     219  var $no_entities = false; 
    220220 
    221221 
     
    233233      str_repeat('(?>[^()\s]+|\(', $this->nested_url_parenthesis_depth). 
    234234      str_repeat('(?>\)))*', $this->nested_url_parenthesis_depth); 
    235      
    236     # Create an identical table but for escaped characters. 
    237     foreach (preg_split('/(?!^|$)/', $this->escape_chars) as $char) { 
    238       $entity = "&#". ord($char). ";"; 
    239       $this->escape_table[$char] = $entity; 
    240       $this->backslash_escape_table["\\$char"] = $entity; 
    241     } 
    242235     
    243236    # Sort document, block, and span gamut in ascendent priority order. 
     
    251244  var $urls = array(); 
    252245  var $titles = array(); 
    253   var $html_blocks = array(); 
    254   var $html_hashes = array(); # Contains both blocks and span hashes. 
     246  var $html_hashes = array(); 
    255247   
    256248  # Status flag to avoid invalid nesting. 
     
    271263    $this->urls = array(); 
    272264    $this->titles = array(); 
    273     $this->html_blocks = array(); 
    274265    $this->html_hashes = array(); 
    275266 
    276267    # Standardize line endings: 
    277268    #   DOS to Unix and Mac to Unix 
    278     $text = str_replace(array("\r\n", "\r"), "\n", $text); 
     269    $text = preg_replace('{\r\n?}', "\n", $text); 
    279270 
    280271    # Make sure $text ends with a couple of newlines: 
     
    349340 
    350341  function hashHTMLBlocks($text) { 
     342    if ($this->no_markup)  return $text; 
     343 
    351344    $less_than_tab = $this->tab_width - 1; 
    352345 
     
    357350    # phrase emphasis, and spans. The list of tags we're looking for is 
    358351    # hard-coded: 
    359     $block_tags_a = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|'. 
    360             'script|noscript|form|fieldset|iframe|math|ins|del'; 
     352    # 
     353    # *  List "a" is made of tags which can be both inline or block-level. 
     354    #    These will be treated block-level when the start tag is alone on  
     355    #    its line, otherwise they're not matched here and will be taken as  
     356    #    inline later. 
     357    # *  List "b" is made of tags which are always block-level; 
     358    # 
     359    $block_tags_a = 'ins|del'; 
    361360    $block_tags_b = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|'. 
    362361            'script|noscript|form|fieldset|iframe|math'; 
     
    385384          <\2     # nested opening tag 
    386385          '.$attr.' # attributes 
    387           (?: 
     386          (?> 
    388387            /> 
    389388          | 
     
    398397        )*', 
    399398        $nested_tags_level); 
     399    $content2 = str_replace('\2', '\3', $content); 
    400400 
    401401    # First, look for nested blocks, e.g.: 
     
    410410    # We need to do this before the next, more liberal match, because the next 
    411411    # match will start at the first `<div>` and stop at the first `</div>`. 
    412     $text = preg_replace_callback('{ 
    413           (           # save in $1 
    414             ^         # start of line  (with /m) 
    415             <('.$block_tags_a.')# start tag = $2 
    416             '.$attr.'>\n    # attributes followed by > and \n 
     412    $text = preg_replace_callback('{(?> 
     413      (?> 
     414        (?<=\n\n)   # Starting after a blank line 
     415        |       # or 
     416        \A\n?     # the beginning of the doc 
     417      ) 
     418      (           # save in $1 
     419 
     420        # Match from `\n<tag>` to `</tag>\n`, handling nested tags  
     421        # in between. 
     422           
     423            [ ]{0,'.$less_than_tab.'} 
     424            <('.$block_tags_b.')# start tag = $2 
     425            '.$attr.'>      # attributes followed by > and \n 
    417426            '.$content.'    # content, support nesting 
    418427            </\2>       # the matching end tag 
    419428            [ ]*        # trailing spaces/tabs 
    420429            (?=\n+|\Z)  # followed by a newline or end of document 
    421           ) 
    422       }xmi', 
    423       array(&$this, '_hashHTMLBlocks_callback'), 
    424       $text); 
    425  
    426     # 
    427     # Match from `\n<tag>` to `</tag>\n`, handling nested tags in between. 
    428     # 
    429     $text = preg_replace_callback('{ 
    430           (           # save in $1 
    431             ^         # start of line  (with /m) 
    432             <('.$block_tags_b.')# start tag = $2 
    433             '.$attr.'>      # attributes followed by > 
    434             '.$content.'    # content, support nesting 
    435             </\2>       # the matching end tag 
     430 
     431      | # Special version for tags of group a. 
     432 
     433            [ ]{0,'.$less_than_tab.'} 
     434            <('.$block_tags_a.')# start tag = $3 
     435            '.$attr.'>[ ]*\n  # attributes followed by > 
     436            '.$content2.'   # content, support nesting 
     437            </\3>       # the matching end tag 
    436438            [ ]*        # trailing spaces/tabs 
    437439            (?=\n+|\Z)  # followed by a newline or end of document 
    438           ) 
    439       }xmi', 
    440       array(&$this, '_hashHTMLBlocks_callback'), 
    441       $text); 
    442  
    443     # Special case just for <hr />. It was easier to make a special case than 
    444     # to make the other regex more complicated. 
    445     $text = preg_replace_callback('{ 
    446           (?: 
    447             (?<=\n\n)   # Starting after a blank line 
    448             |       # or 
    449             \A\n?     # the beginning of the doc 
    450           ) 
    451           (           # save in $1 
     440           
     441      | # Special case just for <hr />. It was easier to make a special  
     442        # case than to make the other regex more complicated. 
     443       
    452444            [ ]{0,'.$less_than_tab.'} 
    453445            <(hr)       # start tag = $2 
     
    457449            [ ]* 
    458450            (?=\n{2,}|\Z)   # followed by a blank line or end of document 
    459           ) 
    460       }xi', 
    461       array(&$this, '_hashHTMLBlocks_callback'), 
    462       $text); 
    463  
    464     # Special case for standalone HTML comments: 
    465     $text = preg_replace_callback('{ 
    466         (?: 
    467           (?<=\n\n)   # Starting after a blank line 
    468           |       # or 
    469           \A\n?     # the beginning of the doc 
    470         ) 
    471         (           # save in $1 
     451       
     452      | # Special case for standalone HTML comments: 
     453       
    472454          [ ]{0,'.$less_than_tab.'} 
    473455          (?s: 
     
    476458          [ ]* 
    477459          (?=\n{2,}|\Z)   # followed by a blank line or end of document 
    478         ) 
    479       }x', 
    480       array(&$this, '_hashHTMLBlocks_callback'), 
    481       $text); 
    482  
    483     # PHP and ASP-style processor instructions (<? and <%) 
    484     $text = preg_replace_callback('{ 
    485         (?: 
    486           (?<=\n\n)   # Starting after a blank line 
    487           |       # or 
    488           \A\n?     # the beginning of the doc 
    489         ) 
    490         (           # save in $1 
     460       
     461      | # PHP and ASP-style processor instructions (<? and <%) 
     462       
    491463          [ ]{0,'.$less_than_tab.'} 
    492464          (?s: 
     
    497469          [ ]* 
    498470          (?=\n{2,}|\Z)   # followed by a blank line or end of document 
    499         ) 
    500       }x', 
     471           
     472      ) 
     473      )}Sxmi', 
    501474      array(&$this, '_hashHTMLBlocks_callback'), 
    502475      $text); 
     
    509482    return "\n\n$key\n\n"; 
    510483  } 
    511  
    512  
    513   function hashBlock($text) { 
    514   # 
    515   # Called whenever a tag must be hashed when a function insert a block-level  
    516   # tag in $text, it pass through this function and is automaticaly escaped,  
    517   # which remove the need to call _HashHTMLBlocks at every step. 
     484   
     485   
     486  function hashPart($text, $boundary = 'X') { 
     487  # 
     488  # Called whenever a tag must be hashed when a function insert an atomic  
     489  # element in the text stream. Passing $text to through this function gives 
     490  # a unique text-token which will be reverted back when calling unhash. 
     491  # 
     492  # The $boundary argument specify what character should be used to surround 
     493  # the token. By convension, "B" is used for block elements that needs not 
     494  # to be wrapped into paragraph tags at the end, ":" is used for elements 
     495  # that are word separators and "S" is used for general span-level elements. 
    518496  # 
    519497    # Swap back any tag hash found in $text so we do not have to `unhash` 
     
    522500     
    523501    # Then hash the block. 
    524     $key = "B\x1A". md5($text); 
     502    static $i = 0; 
     503    $key = "$boundary\x1A" . ++$i . $boundary; 
    525504    $this->html_hashes[$key] = $text; 
    526     $this->html_blocks[$key] = $text; 
    527505    return $key; # String that will replace the tag. 
    528506  } 
    529507 
    530508 
    531   function hashSpan($text, $word_separator = false) { 
    532   # 
    533   # Called whenever a tag must be hashed when a function insert a span-level  
    534   # element in $text, it pass through this function and is automaticaly  
    535   # escaped, blocking invalid nested overlap. If optional argument  
    536   # $word_separator is true, surround the hash value by spaces. 
    537   # 
    538     # Swap back any tag hash found in $text so we do not have to `unhash` 
    539     # multiple times at the end. 
    540     $text = $this->unhash($text); 
    541      
    542     # Then hash the span. 
    543     $key = "S\x1A". md5($text); 
    544     if ($word_separator) $key = ":$key:"; 
    545      
    546     $this->html_hashes[$key] = $text; 
    547     return $key; # String that will replace the span tag. 
     509  function hashBlock($text) { 
     510  # 
     511  # Shortcut function for hashPart with block-level boundaries. 
     512  # 
     513    return $this->hashPart($text, 'B'); 
    548514  } 
    549515 
     
    596562    # Do Horizontal Rules: 
    597563    return preg_replace( 
    598       array('{^[ ]{0,2}([ ]?\*[ ]?){3,}[ ]*$}mx', 
    599           '{^[ ]{0,2}([ ]? -[ ]?){3,}[ ]*$}mx', 
    600           '{^[ ]{0,2}([ ]? _[ ]?){3,}[ ]*$}mx'), 
     564      '{ 
     565        ^[ ]{0,3} # Leading space 
     566        ([-*_])   # $1: First marker 
     567        (?>     # Repeated marker group 
     568          [ ]{0,2}  # Zero, one, or two spaces. 
     569          \1      # Marker character 
     570        ){2,}   # Group repeated at least twice 
     571        [ ]*    # Tailing spaces 
     572        $     # End of line. 
     573      }mx', 
    601574      "\n".$this->hashBlock("<hr$this->empty_element_suffix")."\n",  
    602575      $text); 
     
    609582  # tags like paragraphs, headers, and list items. 
    610583  # 
    611     "escapeSpecialCharsWithinTagAttributes"  => -20, 
    612     "doCodeSpans"              => -10, 
    613     "encodeBackslashEscapes"       =>  -5
     584    # Process character escapes, code spans, and inline HTML 
     585    # in one shot. 
     586    "parseSpan"           => -30
    614587 
    615588    # Process anchor and image tags. Images must come first, 
     
    642615  function doHardBreaks($text) { 
    643616    # Do hard breaks: 
    644     $br_tag = $this->hashSpan("<br$this->empty_element_suffix\n"); 
    645     return preg_replace('/ {2,}\n/', $br_tag, $text); 
    646   } 
    647  
    648  
    649   function escapeSpecialCharsWithinTagAttributes($text) { 
    650   # 
    651   # Within tags -- meaning between < and > -- encode [\ ` * _] so they 
    652   # don't conflict with their use in Markdown for code, italics and strong. 
    653   # We're replacing each such character with its corresponding MD5 checksum 
    654   # value; this is likely overkill, but it should prevent us from colliding 
    655   # with the escape values by accident. 
    656   # 
    657     $tokens = $this->tokenizeHTML($text); 
    658     $text = '';   # rebuild $text from the tokens 
    659  
    660     foreach ($tokens as $cur_token) { 
    661       if ($cur_token[0] == 'tag') { 
    662         $cur_token[1] = str_replace('\\', $this->escape_table['\\'], $cur_token[1]); 
    663         $cur_token[1] = str_replace('`', $this->escape_table['`'], $cur_token[1]); 
    664         $cur_token[1] = str_replace('*', $this->escape_table['*'], $cur_token[1]); 
    665         $cur_token[1] = str_replace('_', $this->escape_table['_'], $cur_token[1]); 
    666       } 
    667       $text .= $cur_token[1]; 
    668     } 
    669     return $text; 
     617    return preg_replace_callback('/ {2,}\n/',  
     618      array(&$this, '_doHardBreaks_callback'), $text); 
     619  } 
     620  function _doHardBreaks_callback($matches) { 
     621    return $this->hashPart("<br$this->empty_element_suffix\n"); 
    670622  } 
    671623 
     
    768720      $link_text = $this->runSpanGamut($link_text); 
    769721      $result .= ">$link_text</a>"; 
    770       $result = $this->hashSpan($result); 
     722      $result = $this->hashPart($result); 
    771723    } 
    772724    else { 
     
    793745    $result .= ">$link_text</a>"; 
    794746 
    795     return $this->hashSpan($result); 
     747    return $this->hashPart($result); 
    796748  } 
    797749 
     
    870822      } 
    871823      $result .= $this->empty_element_suffix; 
    872       $result = $this->hashSpan($result); 
     824      $result = $this->hashPart($result); 
    873825    } 
    874826    else { 
     
    893845    $result .= $this->empty_element_suffix; 
    894846 
    895     return $this->hashSpan($result); 
     847    return $this->hashPart($result); 
    896848  } 
    897849 
     
    905857    #   -------- 
    906858    # 
    907     $text = preg_replace_callback('{ ^(.+?)[ ]*\n=+[ ]*\n+ }mx', 
    908       array(&$this, '_doHeaders_callback_setext_h1'), $text); 
    909     $text = preg_replace_callback('{ ^(.+?)[ ]*\n-+[ ]*\n+ }mx', 
    910       array(&$this, '_doHeaders_callback_setext_h2'), $text); 
     859    $text = preg_replace_callback('{ ^(.+?)[ ]*\n(=+|-+)[ ]*\n+ }mx', 
     860      array(&$this, '_doHeaders_callback_setext'), $text); 
    911861 
    912862    # atx-style headers: 
     
    929879    return $text; 
    930880  } 
    931   function _doHeaders_callback_setext_h1($matches) { 
    932     $block = "<h1>".$this->runSpanGamut($matches[1])."</h1>"; 
    933     return "\n" . $this->hashBlock($block) . "\n\n"; 
    934   } 
    935   function _doHeaders_callback_setext_h2($matches) { 
    936     $block = "<h2>".$this->runSpanGamut($matches[1])."</h2>"; 
     881  function _doHeaders_callback_setext($matches) { 
     882    $level = $matches[2]{0} == '=' ? 1 : 2; 
     883    $block = "<h$level>".$this->runSpanGamut($matches[1])."</h$level>"; 
    937884    return "\n" . $this->hashBlock($block) . "\n\n"; 
    938885  } 
     
    10941041        (?:\n\n|\A) 
    10951042        (             # $1 = the code block -- one or more lines, starting with a space/tab 
    1096           (?: 
    1097           (?:[ ]{'.$this->tab_width.'} | \t)  # Lines must start with a tab or a tab-width of spaces 
     1043          (?> 
     1044          [ ]{'.$this->tab_width.'}  # Lines must start with a tab or a tab-width of spaces 
    10981045          .*\n+ 
    10991046          )+ 
     
    11081055    $codeblock = $matches[1]; 
    11091056 
    1110     $codeblock = $this->encodeCode($this->outdent($codeblock)); 
    1111 //    $codeblock = $this->detab($codeblock); 
    1112     # trim leading newlines and trailing whitespace 
    1113     $codeblock = preg_replace(array('/\A\n+/', '/\n+\z/'), '', $codeblock); 
    1114  
    1115     $result = "\n\n".$this->hashBlock("<pre><code>" . $codeblock . "\n</code></pre>")."\n\n"; 
    1116  
    1117     return $result; 
    1118   } 
    1119  
    1120  
    1121   function doCodeSpans($text) { 
    1122   # 
    1123   #   * Backtick quotes are used for <code></code> spans. 
    1124   # 
    1125   #   * You can use multiple backticks as the delimiters if you want to 
    1126   #     include literal backticks in the code span. So, this input: 
    1127   # 
    1128   #     Just type ``foo `bar` baz`` at the prompt. 
    1129   # 
    1130   #     Will translate to: 
    1131   # 
    1132   #     <p>Just type <code>foo `bar` baz</code> at the prompt.</p> 
    1133   # 
    1134   #   There's no arbitrary limit to the number of backticks you 
    1135   #   can use as delimters. If you need three consecutive backticks 
    1136   #   in your code, use four for delimiters, etc. 
    1137   # 
    1138   # * You can use spaces to get literal backticks at the edges: 
    1139   # 
    1140   #     ... type `` `bar` `` ... 
    1141   # 
    1142   #     Turns to: 
    1143   # 
    1144   #     ... type <code>`bar`</code> ... 
    1145   # 
    1146     $text = preg_replace_callback('@ 
    1147         (?<!\\\)  # Character before opening ` can\'t be a backslash 
    1148         (`+)    # $1 = Opening run of ` 
    1149         (.+?)   # $2 = The code block 
    1150         (?<!`) 
    1151         \1      # Matching closer 
    1152         (?!`) 
    1153       @xs', 
    1154       array(&$this, '_doCodeSpans_callback'), $text); 
    1155  
    1156     return $text; 
    1157   } 
    1158   function _doCodeSpans_callback($matches) { 
    1159     $c = $matches[2]; 
    1160     $c = preg_replace('/^[ ]*/', '', $c); # leading whitespace 
    1161     $c = preg_replace('/[ ]*$/', '', $c); # trailing whitespace 
    1162     $c = $this->encodeCode($c); 
    1163     return $this->hashSpan("<code>$c</code>"); 
    1164   } 
    1165  
    1166  
    1167   function encodeCode($_) { 
    1168   # 
    1169   # Encode/escape certain characters inside Markdown code runs. 
    1170   # The point is that in code, these characters are literals, 
    1171   # and lose their special Markdown meanings. 
    1172   # 
    1173     # Encode all ampersands; HTML entities are not 
    1174     # entities within a Markdown code span. 
    1175     $_ = str_replace('&', '&amp;', $_); 
    1176  
    1177     # Do the angle bracket song and dance: 
    1178     $_ = str_replace(array('<',    '>'),  
    1179              array('&lt;', '&gt;'), $_); 
    1180  
    1181     # Now, escape characters that are magic in Markdown: 
    1182 //    $_ = str_replace(array_keys($this->escape_table),  
    1183 //             array_values($this->escape_table), $_); 
    1184  
    1185     return $_; 
     1057    $codeblock = $this->outdent($codeblock); 
     1058    $codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES); 
     1059 
     1060    # trim leading newlines and trailing newlines 
     1061    $codeblock = preg_replace('/\A\n+|\n+\z/', '', $codeblock); 
     1062 
     1063    $codeblock = "<pre><code>$codeblock\n</code></pre>"; 
     1064    return "\n\n".$this->hashBlock($codeblock)."\n\n"; 
     1065  } 
     1066 
     1067 
     1068  function makeCodeSpan($code) { 
     1069  # 
     1070  # Create a code span markup for $code. Called from handleSpanToken. 
     1071  # 
     1072    $code = htmlspecialchars(trim($code), ENT_NOQUOTES); 
     1073    return $this->hashPart("<code>$code</code>"); 
    11861074  } 
    11871075 
     
    12201108    $text = $matches[2]; 
    12211109    $text = $this->runSpanGamut($text); 
    1222     return $this->hashSpan("<em>$text</em>"); 
     1110    return $this->hashPart("<em>$text</em>"); 
    12231111  } 
    12241112  function _doItalicAndBold_strong_callback($matches) { 
    12251113    $text = $matches[2]; 
    12261114    $text = $this->runSpanGamut($text); 
    1227     return $this->hashSpan("<strong>$text</strong>"); 
     1115    return $this->hashPart("<strong>$text</strong>"); 
    12281116  } 
    12291117 
     
    12321120    $text = preg_replace_callback('/ 
    12331121        (               # Wrap whole match in $1 
    1234         ( 
     1122        (?> 
    12351123          ^[ ]*>[ ]?      # ">" at the start of a line 
    12361124          .+\n          # rest of the first line 
     
    12471135    $bq = $matches[1]; 
    12481136    # trim one level of quoting - trim whitespace-only lines 
    1249     $bq = preg_replace(array('/^[ ]*>[ ]?/m', '/^[ ]+$/m'), '', $bq); 
     1137    $bq = preg_replace('/^[ ]*>[ ]?|^[ ]+$/m', '', $bq); 
    12501138    $bq = $this->runBlockGamut($bq);    # recurse 
    12511139 
     
    12711159  # 
    12721160    # Strip leading and trailing lines: 
    1273     $text = preg_replace(array('/\A\n+/', '/\n+\z/'), '', $text); 
     1161    $text = preg_replace('/\A\n+|\n+\z/', '', $text); 
    12741162 
    12751163    $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY); 
    12761164 
    12771165    # 
    1278     # Wrap <p> tags. 
     1166    # Wrap <p> tags and unhashify HTML blocks 
    12791167    # 
    12801168    foreach ($grafs as $key => $value) { 
    1281       if (!isset( $this->html_blocks[$value] )) { 
     1169      if (!preg_match('/^B\x1A[0-9]+B$/', $value)) { 
     1170        # Is a paragraph. 
    12821171        $value = $this->runSpanGamut($value); 
    12831172        $value = preg_replace('/^([ ]*)/', "<p>", $value); 
     
    12851174        $grafs[$key] = $this->unhash($value); 
    12861175      } 
    1287     } 
    1288  
    1289     # 
    1290     # Unhashify HTML blocks 
    1291     # 
    1292     foreach ($grafs as $key => $graf) { 
    1293       # Modify elements of @grafs in-place... 
    1294       if (isset($this->html_blocks[$graf])) { 
    1295         $block = $this->html_blocks[$graf]; 
     1176      else { 
     1177        # Is a block. 
     1178        # Modify elements of @grafs in-place... 
     1179        $graf = $value; 
     1180        $block = $this->html_hashes[$graf]; 
    12961181        $graf = $block; 
    12971182//        if (preg_match('{ 
     
    13401225  function encodeAmpsAndAngles($text) { 
    13411226  # Smart processing for ampersands and angle brackets that need to be encoded. 
     1227    if ($this->no_entities) { 
     1228      $text = str_replace('&', '&amp;', $text); 
     1229      $text = str_replace('<', '&lt;', $text); 
     1230      return $text; 
     1231    } 
    13421232 
    13431233    # Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin: 
     
    13501240 
    13511241    return $text; 
    1352   } 
    1353  
    1354  
    1355   function encodeBackslashEscapes($text) { 
    1356   # 
    1357   # Parameter:  String. 
    1358   # Returns:    The string, with after processing the following backslash 
    1359   #       escape sequences. 
    1360   # 
    1361     # Must process escaped backslashes first. 
    1362     return str_replace(array_keys($this->backslash_escape_table), 
    1363                array_values($this->backslash_escape_table), $text); 
    13641242  } 
    13651243 
     
    13871265    $url = $this->encodeAmpsAndAngles($matches[1]); 
    13881266    $link = "<a href=\"$url\">$url</a>"; 
    1389     return $this->hashSpan($link); 
     1267    return $this->hashPart($link); 
    13901268  } 
    13911269  function _doAutoLinks_email_callback($matches) { 
    13921270    $address = $matches[1]; 
    13931271    $link = $this->encodeEmailAddress($address); 
    1394     return $this->hashSpan($link); 
     1272    return $this->hashPart($link); 
    13951273  } 
    13961274 
     
    14371315 
    14381316 
    1439   function tokenizeHTML($str) { 
    1440   # 
    1441   #   Parameter:  String containing HTML + Markdown markup. 
    1442   #   Returns:    An array of the tokens comprising the input 
    1443   #               string. Each token is either a tag or a run of text  
    1444   #               between tags. Each element of the array is a 
    1445   #               two-element array; the first is either 'tag' or 'text'; 
    1446   #               the second is the actual value. 
    1447   #   Note:       Markdown code spans are taken into account: no tag token is  
    1448   #               generated within a code span. 
    1449   # 
    1450     $tokens = array(); 
    1451  
    1452     while ($str != "") { 
    1453       # 
    1454       # Each loop iteration seach for either the next tag or the next  
    1455       # openning code span marker. If a code span marker is found, the  
    1456       # code span is extracted in entierty and will result in an extra 
    1457       # text token. 
    1458       # 
    1459       $parts = preg_split('{ 
     1317  function parseSpan($str) { 
     1318  # 
     1319  # Take the string $str and parse it into tokens, hashing embeded HTML, 
     1320  # escaped characters and handling code spans. 
     1321  # 
     1322    $output = ''; 
     1323     
     1324    $regex = '{ 
    14601325        ( 
     1326          \\\\['.preg_quote($this->escape_chars).'] 
     1327        | 
    14611328          (?<![`\\\\]) 
    14621329          `+            # code span marker 
     1330      '.( $this->no_markup ? '' : ' 
    14631331        | 
    14641332          <!--    .*?     -->   # comment 
     
    14671335        | 
    14681336          <[/!$]?[-a-zA-Z0-9:]+ # regular tags 
    1469           (?: 
     1337          (?> 
    14701338            \s 
    14711339            (?>[^"\'>]+|"[^"]*"|\'[^\']*\')* 
    14721340          )? 
    14731341          > 
     1342      ').' 
    14741343        ) 
    1475         }xs', $str, 2, PREG_SPLIT_DELIM_CAPTURE); 
     1344        }xs'; 
     1345 
     1346    while (1) { 
     1347      # 
     1348      # Each loop iteration seach for either the next tag, the next  
     1349      # openning code span marker, or the next escaped character.  
     1350      # Each token is then passed to handleSpanToken. 
     1351      # 
     1352      $parts = preg_split($regex, $str, 2, PREG_SPLIT_DELIM_CAPTURE); 
    14761353       
    14771354      # Create token from text preceding tag. 
    14781355      if ($parts[0] != "") { 
    1479         $tokens[] = array('text', $parts[0])
     1356        $output .= $parts[0]
    14801357      } 
    14811358       
    14821359      # Check if we reach the end. 
    1483       if (count($parts) < 3) { 
     1360      if (isset($parts[1])) { 
     1361        $output .= $this->handleSpanToken($parts[1], $parts[2]); 
     1362        $str = $parts[2]; 
     1363      } 
     1364      else { 
    14841365        break; 
    14851366      } 
    1486        
    1487       # Create token from tag or code span. 
    1488       if ($parts[1]{0} == "`") { 
    1489         $tokens[] = array('text', $parts[1]); 
    1490         $str = $parts[2]; 
    1491          
    1492         # Skip the whole code span, pass as text token. 
    1493         if (preg_match('/^(.*(?<!`\\\\)'.$parts[1].'(?!`))(.*)$/sm',  
     1367    } 
     1368     
     1369    return $output; 
     1370  } 
     1371   
     1372   
     1373  function handleSpanToken($token, &$str) { 
     1374  # 
     1375  # Handle $token provided by parseSpan by determining its nature and  
     1376  # returning the corresponding value that should replace it. 
     1377  # 
     1378    switch ($token{0}) { 
     1379      case "\\": 
     1380        return $this->hashPart("&#". ord($token{1}). ";"); 
     1381      case "`": 
     1382        # Search for end marker in remaining text. 
     1383        if (preg_match('/^(.*?[^`])'.$token.'(?!`)(.*)$/sm',  
    14941384          $str, $matches)) 
    14951385        { 
    1496           $tokens[] = array('text', $matches[1]); 
    14971386          $str = $matches[2]; 
     1387          $codespan = $this->makeCodeSpan($matches[1]); 
     1388          return $this->hashPart($codespan); 
    14981389        } 
    1499       } else { 
    1500         $tokens[] = array('tag', $parts[1]); 
    1501         $str = $parts[2]; 
    1502       } 
    1503     } 
    1504      
    1505     return $tokens; 
     1390        return $token; // return as text since no ending marker found. 
     1391      default: 
     1392        return $this->hashPart($token); 
     1393    } 
    15061394  } 
    15071395 
     
    15111399  # Remove one level of line-leading tabs or spaces 
    15121400  # 
    1513     return preg_replace("/^(\\t|[ ]{1,$this->tab_width})/m", "", $text); 
     1401    return preg_replace('/^(\t|[ ]{1,'.$this->tab_width.'})/m', '', $text); 
    15141402  } 
    15151403 
     
    15271415    # appropriate number of space between each blocks. 
    15281416     
     1417    $text = preg_replace_callback('/^.*\t.*$/m', 
     1418      array(&$this, '_detab_callback'), $text); 
     1419 
     1420    return $text; 
     1421  } 
     1422  function _detab_callback($matches) { 
     1423    $line = $matches[0]; 
    15291424    $strlen = $this->utf8_strlen; # strlen function for UTF-8. 
    1530     $lines = explode("\n", $text); 
    1531     $text = ""; 
    1532      
    1533     foreach ($lines as $line) { 
    1534       # Split in blocks. 
    1535       $blocks = explode("\t", $line); 
    1536       # Add each blocks to the line. 
    1537       $line = $blocks[0]; 
    1538       unset($blocks[0]); # Do not add first block twice. 
    1539       foreach ($blocks as $block) { 
    1540         # Calculate amount of space, insert spaces, insert block. 
    1541         $amount = $this->tab_width -  
    1542           $strlen($line, 'UTF-8') % $this->tab_width; 
    1543         $line .= str_repeat(" ", $amount) . $block; 
    1544       } 
    1545       $text .= "$line\n"; 
    1546     } 
    1547     return $text; 
     1425     
     1426    # Split in blocks. 
     1427    $blocks = explode("\t", $line); 
     1428    # Add each blocks to the line. 
     1429    $line = $blocks[0]; 
     1430    unset($blocks[0]); # Do not add first block twice. 
     1431    foreach ($blocks as $block) { 
     1432      # Calculate amount of space, insert spaces, insert block. 
     1433      $amount = $this->tab_width -  
     1434        $strlen($line, 'UTF-8') % $this->tab_width; 
     1435      $line .= str_repeat(" ", $amount) . $block; 
     1436    } 
     1437    return $line; 
    15481438  } 
    15491439  function _initDetab() { 
     
    15651455  # Swap back in all the tags hashed by _HashHTMLBlocks. 
    15661456  # 
    1567     return str_replace(array_keys($this->html_hashes),  
    1568                array_values($this->html_hashes), $text); 
     1457    return preg_replace_callback('/(.)\x1A[0-9]+\1/',  
     1458      array(&$this, '_unhash_callback'), $text); 
     1459  } 
     1460  function _unhash_callback($matches) { 
     1461    return $this->html_hashes[$matches[0]]; 
    15691462  } 
    15701463 
     
    16231516  var $abbr_desciptions = array(); 
    16241517  var $abbr_matches = array(); 
    1625   var $html_cleans = array(); 
    16261518   
    16271519  # Status flag to avoid invalid nesting. 
     
    16421534    $this->abbr_desciptions = array(); 
    16431535    $this->abbr_matches = array(); 
    1644     $this->html_cleans = array(); 
    16451536 
    16461537    return parent::transform($text); 
     
    17771668      # after each newline to prevent triggering any block element. 
    17781669      if ($span) { 
    1779         $void = $this->hashSpan("", true)
    1780         $newline = $this->hashSpan("", true) . "\n"; 
     1670        $void = $this->hashPart("", ':')
     1671        $newline = "$void\n"; 
    17811672        $parts[0] = $void . str_replace("\n", $newline, $parts[0]) . $void; 
    17821673      } 
     
    17971688      # 
    17981689      if (# Find current paragraph 
    1799         preg_match('/(?>^\n?|\n\n)((?>.\n?)+?)$/', $parsed, $matches) && 
     1690        preg_match('/(?>^\n?|\n\n)((?>.+\n?)*?)$/', $parsed, $matches) && 
    18001691        ( 
    18011692        # Then match in it either a code block... 
     
    20481939  # blocking invalid nested overlap. 
    20491940  # 
    2050     # Swap back any tag hash found in $text so we do not have to `unhash` 
    2051     # multiple times at the end. 
    2052     $text = $this->unhash($text); 
    2053      
    2054     # Then hash the tag. 
    2055     $key = "C\x1A". md5($text); 
    2056     $this->html_cleans[$key] = $text; 
    2057     $this->html_hashes[$key] = $text; 
    2058     return $key; # String that will replace the clean tag. 
     1941    return $this->hashPart($text, 'C'); 
    20591942  } 
    20601943 
     
    20721955    # 
    20731956    $text = preg_replace_callback( 
    2074       '{ (^.+?) (?:[ ]+\{\#([-_:a-zA-Z0-9]+)\})? [ ]*\n=+[ ]*\n+ }mx', 
    2075       array(&$this, '_doHeaders_callback_setext_h1'), $text); 
    2076     $text = preg_replace_callback( 
    2077       '{ (^.+?) (?:[ ]+\{\#([-_:a-zA-Z0-9]+)\})? [ ]*\n-+[ ]*\n+ }mx', 
    2078       array(&$this, '_doHeaders_callback_setext_h2'), $text); 
     1957      '{ 
     1958        (^.+?)                # $1: Header text 
     1959        (?:[ ]+\{\#([-_:a-zA-Z0-9]+)\})?  # $2: Id attribute 
     1960        [ ]*\n(=+|-+)[ ]*\n+        # $3: Header footer 
     1961      }mx', 
     1962      array(&$this, '_doHeaders_callback_setext'), $text); 
    20791963 
    20801964    # atx-style headers: 
     
    21031987    return " id=\"$attr\""; 
    21041988  } 
    2105   function _doHeaders_callback_setext_h1($matches) { 
     1989  function _doHeaders_callback_setext($matches) { 
     1990    $level = $matches[3]{0} == '=' ? 1 : 2; 
    21061991    $attr  = $this->_doHeaders_attr($id =& $matches[2]); 
    2107     $block = "<h1$attr>".$this->runSpanGamut($matches[1])."</h1>"; 
    2108     return "\n" . $this->hashBlock($block) . "\n\n"; 
    2109   } 
    2110   function _doHeaders_callback_setext_h2($matches) { 
    2111     $attr  = $this->_doHeaders_attr($id =& $matches[2]); 
    2112     $block = "<h2$attr>".$this->runSpanGamut($matches[1])."</h2>"; 
     1992    $block = "<h$level$attr>".$this->runSpanGamut($matches[1])."</h$level>"; 
    21131993    return "\n" . $this->hashBlock($block) . "\n\n"; 
    21141994  } 
     
    21452025         
    21462026        (             # $3: Cells 
    2147           (?: 
     2027          (?> 
    21482028            [ ]*        # Allowed whitespace. 
    21492029            [|] .* \n     # Row content. 
     
    21722052         
    21732053        (             # $3: Cells 
    2174           (?: 
     2054          (?> 
    21752055            .* [|] .* \n    # Row content 
    21762056          )* 
     
    22112091    } 
    22122092     
    2213     # Creating code spans before splitting the row is an easy way to  
    2214     # handle a code span containg pipes
    2215     $head = $this->doCodeSpans($head); 
     2093    # Parsing span elements, including code spans, character escapes,  
     2094    # and inline HTML tags, so that pipes inside those gets ignored
     2095    $head = $this->parseSpan($head); 
    22162096    $headers  = preg_split('/ *[|] */', $head); 
    22172097    $col_count  = count($headers); 
     
    22312111    $text .= "<tbody>\n"; 
    22322112    foreach ($rows as $row) { 
    2233       # Creating code spans before splitting the row is an easy way to  
    2234       # handle a code span containg pipes
    2235       $row = $this->doCodeSpans($row); 
     2113      # Parsing span elements, including code spans, character escapes,  
     2114      # and inline HTML tags, so that pipes inside those gets ignored
     2115      $row = $this->parseSpan($row); 
    22362116       
    22372117      # Split row by cell. 
     
    22582138 
    22592139    # Re-usable pattern to match any entire dl list: 
    2260     $whole_list = ' 
     2140    $whole_list = '(?> 
    22612141      (               # $1 = whole list 
    22622142        (               # $2 
     
    22832163        ) 
    22842164      ) 
    2285     '; // mx 
     2165    )'; // mx 
    22862166 
    22872167    $text = preg_replace_callback('{ 
     
    24372317  # 
    24382318    # Strip leading and trailing lines: 
    2439     $text = preg_replace(array('/\A\n+/', '/\n+\z/'), '', $text); 
     2319    $text = preg_replace('/\A\n+|\n+\z/', '', $text); 
    24402320     
    24412321    $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY); 
     
    24492329      # Check if this should be enclosed in a paragraph. 
    24502330      # Clean tag hashes & block tag hashes are left alone. 
    2451       $clean_key = $value; 
    2452       $block_key = substr($value, 0, 34); 
    2453        
    2454       $is_p = (!isset($this->html_blocks[$block_key]) &&  
    2455            !isset($this->html_cleans[$clean_key]));