home *** CD-ROM | disk | FTP | other *** search
/ onj1.andrelouis.com / 2015-02-07.onj1.andrelouis.com.tar / onj1.andrelouis.com / andre / podcast.php < prev    next >
PHP Script  |  2009-08-09  |  61KB  |  2,176 lines

  1. <?php
  2. header('Content-type: application/xml');
  3. header('Content-Disposition: attachment; filename="podcast.xml"');
  4. require_once('./podcast.inc');
  5. /*You don't mean to be here.  You want to edit podcast.inc.  No, I'm serious.  You really do.  Well, unless you want to see some bad code.  But if you haven't edited podcast.inc, you really want to go do that first.  You want a podcast, right?  Then go edit podcast.inc!  */
  6.  
  7. //excuse me.  Old code is coming down the pipe.
  8.  
  9. global    $MarkdownPHPVersion, $MarkdownSyntaxVersion,
  10.         $md_empty_element_suffix, $md_tab_width,
  11.         $md_nested_brackets_depth, $md_nested_brackets, 
  12.         $md_escape_table, $md_backslash_escape_table, 
  13.         $md_list_level;
  14.  
  15. $MarkdownPHPVersion    = '1.0.1'; # Fri 17 Dec 2004
  16. $MarkdownSyntaxVersion = '1.0.1'; # Sun 12 Dec 2004
  17.  
  18.  
  19. #
  20. # Global default settings:
  21. #
  22. $md_empty_element_suffix = ">";     # Change to ">" for HTML output
  23. $md_tab_width = 4;
  24.  
  25.  
  26. # -- WordPress Plugin Interface -----------------------------------------------
  27. /*
  28. Plugin Name: Markdown
  29. Plugin URI: http://www.michelf.com/projects/php-markdown/
  30. Description: <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>
  31. Version: 1.0.1
  32. Author: Michel Fortin
  33. Author URI: http://www.michelf.com/
  34. */
  35. if (isset($wp_version)) {
  36.     # Remove default WordPress auto-paragraph filter.
  37.     remove_filter('the_content', 'wpautop');
  38.     remove_filter('the_excerpt', 'wpautop');
  39.     remove_filter('comment_text', 'wpautop');
  40.     # Add Markdown filter with priority 6 (same as Textile).
  41.     add_filter('the_content', 'Markdown', 6);
  42.     add_filter('the_excerpt', 'Markdown', 6);
  43.     add_filter('comment_text', 'Markdown', 6);
  44. }
  45.  
  46.  
  47. # -- bBlog Plugin Info --------------------------------------------------------
  48. function identify_modifier_markdown() {
  49.     global $MarkdownPHPVersion;
  50.     return array(
  51.         'name'            => 'markdown',
  52.         'type'            => 'modifier',
  53.         'nicename'        => 'Markdown',
  54.         'description'    => 'A text-to-HTML conversion tool for web writers',
  55.         'authors'        => 'Michel Fortin and John Gruber',
  56.         'licence'        => 'GPL',
  57.         'version'        => $MarkdownPHPVersion,
  58.         'help'            => '<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>'
  59.     );
  60. }
  61.  
  62. # -- Smarty Modifier Interface ------------------------------------------------
  63. function smarty_modifier_markdown($text) {
  64.     return Markdown($text);
  65. }
  66.  
  67. # -- Textile Compatibility Mode -----------------------------------------------
  68. # Rename this file to "classTextile.php" and it can replace Textile anywhere.
  69. if (strcasecmp(substr(__FILE__, -16), "classTextile.php") == 0) {
  70.     # Try to include PHP SmartyPants. Should be in the same directory.
  71.     @include_once 'smartypants.php';
  72.     # Fake Textile class. It calls Markdown instead.
  73.     class Textile {
  74.         function TextileThis($text, $lite='', $encode='', $noimage='', $strict='') {
  75.             if ($lite == '' && $encode == '')   $text = Markdown($text);
  76.             if (function_exists('SmartyPants')) $text = SmartyPants($text);
  77.                 return $text;
  78.         }
  79.     }
  80. }
  81.  
  82.  
  83.  
  84. #
  85. # Globals:
  86. #
  87.  
  88. # Regex to match balanced [brackets].
  89. # Needed to insert a maximum bracked depth while converting to PHP.
  90. $md_nested_brackets_depth = 6;
  91. $md_nested_brackets = 
  92.     str_repeat('(?>[^\[\]]+|\[', $md_nested_brackets_depth).
  93.     str_repeat('\])*', $md_nested_brackets_depth);
  94.  
  95. # Table of hash values for escaped characters:
  96. $md_escape_table = array(
  97.     "\\" => md5("\\"),
  98.     "`" => md5("`"),
  99.     "*" => md5("*"),
  100.     "_" => md5("_"),
  101.     "{" => md5("{"),
  102.     "}" => md5("}"),
  103.     "[" => md5("["),
  104.     "]" => md5("]"),
  105.     "(" => md5("("),
  106.     ")" => md5(")"),
  107.     ">" => md5(">"),
  108.     "#" => md5("#"),
  109.     "+" => md5("+"),
  110.     "-" => md5("-"),
  111.     "." => md5("."),
  112.     "!" => md5("!")
  113. );
  114. # Create an identical table but for escaped characters.
  115. $md_backslash_escape_table;
  116. foreach ($md_escape_table as $key => $char)
  117.     $md_backslash_escape_table["\\$key"] = $char;
  118.  
  119.  
  120. function Markdown($text) {
  121. #
  122. # Main function. The order in which other subs are called here is
  123. # essential. Link and image substitutions need to happen before
  124. # _EscapeSpecialChars(), so that any *'s or _'s in the <a>
  125. # and <img> tags get encoded.
  126. #
  127.     # Clear the global hashes. If we don't clear these, you get conflicts
  128.     # from other articles when generating a page which contains more than
  129.     # one article (e.g. an index page that shows the N most recent
  130.     # articles):
  131.     global $md_urls, $md_titles, $md_html_blocks;
  132.     $md_urls = array();
  133.     $md_titles = array();
  134.     $md_html_blocks = array();
  135.  
  136.     # Standardize line endings:
  137.     #   DOS to Unix and Mac to Unix
  138.     $text = str_replace(array("\r\n", "\r"), "\n", $text);
  139.  
  140.     # Make sure $text ends with a couple of newlines:
  141.     $text .= "\n\n";
  142.  
  143.     # Convert all tabs to spaces.
  144.     $text = _Detab($text);
  145.  
  146.     # Strip any lines consisting only of spaces and tabs.
  147.     # This makes subsequent regexen easier to write, because we can
  148.     # match consecutive blank lines with /\n+/ instead of something
  149.     # contorted like /[ \t]*\n+/ .
  150.     $text = preg_replace('/^[ \t]+$/m', '', $text);
  151.  
  152.     # Turn block-level HTML blocks into hash entries
  153.     $text = _HashHTMLBlocks($text);
  154.  
  155.     # Strip link definitions, store in hashes.
  156.     $text = _StripLinkDefinitions($text);
  157.  
  158.     $text = _RunBlockGamut($text);
  159.  
  160.     $text = _UnescapeSpecialChars($text);
  161.  
  162.     return $text . "\n";
  163. }
  164.  
  165.  
  166. function _StripLinkDefinitions($text) {
  167. #
  168. # Strips link definitions from text, stores the URLs and titles in
  169. # hash references.
  170. #
  171.     global $md_tab_width;
  172.     $less_than_tab = $md_tab_width - 1;
  173.  
  174.     # Link defs are in the form: ^[id]: url "optional title"
  175.     $text = preg_replace_callback('{
  176.                         ^[ ]{0,'.$less_than_tab.'}\[(.+)\]:    # id = $1
  177.                           [ \t]*
  178.                           \n?                # maybe *one* newline
  179.                           [ \t]*
  180.                         <?(\S+?)>?            # url = $2
  181.                           [ \t]*
  182.                           \n?                # maybe one newline
  183.                           [ \t]*
  184.                         (?:
  185.                             (?<=\s)            # lookbehind for whitespace
  186.                             ["(]
  187.                             (.+?)            # title = $3
  188.                             [")]
  189.                             [ \t]*
  190.                         )?    # title is optional
  191.                         (?:\n+|\Z)
  192.         }xm',
  193.         '_StripLinkDefinitions_callback',
  194.         $text);
  195.     return $text;
  196. }
  197. function _StripLinkDefinitions_callback($matches) {
  198.     global $md_urls, $md_titles;
  199.     $link_id = strtolower($matches[1]);
  200.     $md_urls[$link_id] = _EncodeAmpsAndAngles($matches[2]);
  201.     if (isset($matches[3]))
  202.         $md_titles[$link_id] = str_replace('"', '"', $matches[3]);
  203.     return ''; # String that will replace the block
  204. }
  205.  
  206.  
  207. function _HashHTMLBlocks($text) {
  208.     global $md_tab_width;
  209.     $less_than_tab = $md_tab_width - 1;
  210.  
  211.     # Hashify HTML blocks:
  212.     # We only want to do this for block-level HTML tags, such as headers,
  213.     # lists, and tables. That's because we still want to wrap <p>s around
  214.     # "paragraphs" that are wrapped in non-block-level tags, such as anchors,
  215.     # phrase emphasis, and spans. The list of tags we're looking for is
  216.     # hard-coded:
  217.     $block_tags_a = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|'.
  218.                     'script|noscript|form|fieldset|iframe|math|ins|del';
  219.     $block_tags_b = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|'.
  220.                     'script|noscript|form|fieldset|iframe|math';
  221.  
  222.     # First, look for nested blocks, e.g.:
  223.     #     <div>
  224.     #         <div>
  225.     #         tags for inner block must be indented.
  226.     #         </div>
  227.     #     </div>
  228.     #
  229.     # The outermost tags must start at the left margin for this to match, and
  230.     # the inner nested divs must be indented.
  231.     # We need to do this before the next, more liberal match, because the next
  232.     # match will start at the first `<div>` and stop at the first `</div>`.
  233.     $text = preg_replace_callback("{
  234.                 (                        # save in $1
  235.                     ^                    # start of line  (with /m)
  236.                     <($block_tags_a)    # start tag = $2
  237.                     \\b                    # word break
  238.                     (.*\\n)*?            # any number of lines, minimally matching
  239.                     </\\2>                # the matching end tag
  240.                     [ \\t]*                # trailing spaces/tabs
  241.                     (?=\\n+|\\Z)    # followed by a newline or end of document
  242.                 )
  243.         }xm",
  244.         '_HashHTMLBlocks_callback',
  245.         $text);
  246.  
  247.     #
  248.     # Now match more liberally, simply from `\n<tag>` to `</tag>\n`
  249.     #
  250.     $text = preg_replace_callback("{
  251.                 (                        # save in $1
  252.                     ^                    # start of line  (with /m)
  253.                     <($block_tags_b)    # start tag = $2
  254.                     \\b                    # word break
  255.                     (.*\\n)*?            # any number of lines, minimally matching
  256.                     .*</\\2>                # the matching end tag
  257.                     [ \\t]*                # trailing spaces/tabs
  258.                     (?=\\n+|\\Z)    # followed by a newline or end of document
  259.                 )
  260.         }xm",
  261.         '_HashHTMLBlocks_callback',
  262.         $text);
  263.  
  264.     # Special case just for <hr />. It was easier to make a special case than
  265.     # to make the other regex more complicated.
  266.     $text = preg_replace_callback('{
  267.                 (?:
  268.                     (?<=\n\n)        # Starting after a blank line
  269.                     |                # or
  270.                     \A\n?            # the beginning of the doc
  271.                 )
  272.                 (                        # save in $1
  273.                     [ ]{0,'.$less_than_tab.'}
  274.                     <(hr)                # start tag = $2
  275.                     \b                    # word break
  276.                     ([^<>])*?            # 
  277.                     /?>                    # the matching end tag
  278.                     [ \t]*
  279.                     (?=\n{2,}|\Z)        # followed by a blank line or end of document
  280.                 )
  281.         }x',
  282.         '_HashHTMLBlocks_callback',
  283.         $text);
  284.  
  285.     # Special case for standalone HTML comments:
  286.     $text = preg_replace_callback('{
  287.                 (?:
  288.                     (?<=\n\n)        # Starting after a blank line
  289.                     |                # or
  290.                     \A\n?            # the beginning of the doc
  291.                 )
  292.                 (                        # save in $1
  293.                     [ ]{0,'.$less_than_tab.'}
  294.                     (?s:
  295.                         <!
  296.                         (--.*?--\s*)+
  297.                         >
  298.                     )
  299.                     [ \t]*
  300.                     (?=\n{2,}|\Z)        # followed by a blank line or end of document
  301.                 )
  302.             }x',
  303.             '_HashHTMLBlocks_callback',
  304.             $text);
  305.  
  306.     return $text;
  307. }
  308. function _HashHTMLBlocks_callback($matches) {
  309.     global $md_html_blocks;
  310.     $text = $matches[1];
  311.     $key = md5($text);
  312.     $md_html_blocks[$key] = $text;
  313.     return "\n\n$key\n\n"; # String that will replace the block
  314. }
  315.  
  316.  
  317. function _RunBlockGamut($text) {
  318. #
  319. # These are all the transformations that form block-level
  320. # tags like paragraphs, headers, and list items.
  321. #
  322.     global $md_empty_element_suffix;
  323.  
  324.     $text = _DoHeaders($text);
  325.  
  326.     # Do Horizontal Rules:
  327.     $text = preg_replace(
  328.         array('{^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$}mx',
  329.               '{^[ ]{0,2}([ ]? -[ ]?){3,}[ \t]*$}mx',
  330.               '{^[ ]{0,2}([ ]? _[ ]?){3,}[ \t]*$}mx'),
  331.         "\n<hr$md_empty_element_suffix\n", 
  332.         $text);
  333.  
  334.     $text = _DoLists($text);
  335.  
  336.     $text = _DoCodeBlocks($text);
  337.  
  338.     $text = _DoBlockQuotes($text);
  339.  
  340.     # We already ran _HashHTMLBlocks() before, in Markdown(), but that
  341.     # was to escape raw HTML in the original Markdown source. This time,
  342.     # we're escaping the markup we've just created, so that we don't wrap
  343.     # <p> tags around block-level tags.
  344.     $text = _HashHTMLBlocks($text);
  345.  
  346.     $text = _FormParagraphs($text);
  347.  
  348.     return $text;
  349. }
  350.  
  351.  
  352. function _RunSpanGamut($text) {
  353. #
  354. # These are all the transformations that occur *within* block-level
  355. # tags like paragraphs, headers, and list items.
  356. #
  357.     global $md_empty_element_suffix;
  358.  
  359.     $text = _DoCodeSpans($text);
  360.  
  361.     $text = _EscapeSpecialChars($text);
  362.  
  363.     # Process anchor and image tags. Images must come first,
  364.     # because ![foo][f] looks like an anchor.
  365.     $text = _DoImages($text);
  366.     $text = _DoAnchors($text);
  367.  
  368.     # Make links out of things like `<http://example.com/>`
  369.     # Must come after _DoAnchors(), because you can use < and >
  370.     # delimiters in inline links like [this](<url>).
  371.     $text = _DoAutoLinks($text);
  372.  
  373.     # Fix unencoded ampersands and <'s:
  374.     $text = _EncodeAmpsAndAngles($text);
  375.  
  376.     $text = _DoItalicsAndBold($text);
  377.  
  378.     # Do hard breaks:
  379.     $text = preg_replace('/ {2,}\n/', "<br$md_empty_element_suffix\n", $text);
  380.  
  381.     return $text;
  382. }
  383.  
  384.  
  385. function _EscapeSpecialChars($text) {
  386.     global $md_escape_table;
  387.     $tokens = _TokenizeHTML($text);
  388.  
  389.     $text = '';   # rebuild $text from the tokens
  390. #    $in_pre = 0;  # Keep track of when we're inside <pre> or <code> tags.
  391. #    $tags_to_skip = "!<(/?)(?:pre|code|kbd|script|math)[\s>]!";
  392.  
  393.     foreach ($tokens as $cur_token) {
  394.         if ($cur_token[0] == 'tag') {
  395.             # Within tags, encode * and _ so they don't conflict
  396.             # with their use in Markdown for italics and strong.
  397.             # We're replacing each such character with its
  398.             # corresponding MD5 checksum value; this is likely
  399.             # overkill, but it should prevent us from colliding
  400.             # with the escape values by accident.
  401.             $cur_token[1] = str_replace(array('*', '_'),
  402.                 array($md_escape_table['*'], $md_escape_table['_']),
  403.                 $cur_token[1]);
  404.             $text .= $cur_token[1];
  405.         } else {
  406.             $t = $cur_token[1];
  407.             $t = _EncodeBackslashEscapes($t);
  408.             $text .= $t;
  409.         }
  410.     }
  411.     return $text;
  412. }
  413.  
  414.  
  415. function _DoAnchors($text) {
  416. #
  417. # Turn Markdown link shortcuts into XHTML <a> tags.
  418. #
  419.     global $md_nested_brackets;
  420.     #
  421.     # First, handle reference-style links: [link text] [id]
  422.     #
  423.     $text = preg_replace_callback("{
  424.         (                    # wrap whole match in $1
  425.           \\[
  426.             ($md_nested_brackets)    # link text = $2
  427.           \\]
  428.  
  429.           [ ]?                # one optional space
  430.           (?:\\n[ ]*)?        # one optional newline followed by spaces
  431.  
  432.           \\[
  433.             (.*?)        # id = $3
  434.           \\]
  435.         )
  436.         }xs",
  437.         '_DoAnchors_reference_callback', $text);
  438.  
  439.     #
  440.     # Next, inline-style links: [link text](url "optional title")
  441.     #
  442.     $text = preg_replace_callback("{
  443.         (                # wrap whole match in $1
  444.           \\[
  445.             ($md_nested_brackets)    # link text = $2
  446.           \\]
  447.           \\(            # literal paren
  448.             [ \\t]*
  449.             <?(.*?)>?    # href = $3
  450.             [ \\t]*
  451.             (            # $4
  452.               (['\"])    # quote char = $5
  453.               (.*?)        # Title = $6
  454.               \\5        # matching quote
  455.             )?            # title is optional
  456.           \\)
  457.         )
  458.         }xs",
  459.         '_DoAnchors_inline_callback', $text);
  460.  
  461.     return $text;
  462. }
  463. function _DoAnchors_reference_callback($matches) {
  464.     global $md_urls, $md_titles, $md_escape_table;
  465.     $whole_match = $matches[1];
  466.     $link_text   = $matches[2];
  467.     $link_id     = strtolower($matches[3]);
  468.  
  469.     if ($link_id == "") {
  470.         $link_id = strtolower($link_text); # for shortcut links like [this][].
  471.     }
  472.  
  473.     if (isset($md_urls[$link_id])) {
  474.         $url = $md_urls[$link_id];
  475.         # We've got to encode these to avoid conflicting with italics/bold.
  476.         $url = str_replace(array('*', '_'),
  477.                            array($md_escape_table['*'], $md_escape_table['_']),
  478.                            $url);
  479.         $result = "<a href=\"$url\"";
  480.         if ( isset( $md_titles[$link_id] ) ) {
  481.             $title = $md_titles[$link_id];
  482.             $title = str_replace(array('*',     '_'),
  483.                                  array($md_escape_table['*'], 
  484.                                        $md_escape_table['_']), $title);
  485.             $result .=  " title=\"$title\"";
  486.         }
  487.         $result .= ">$link_text</a>";
  488.     }
  489.     else {
  490.         $result = $whole_match;
  491.     }
  492.     return $result;
  493. }
  494. function _DoAnchors_inline_callback($matches) {
  495.     global $md_escape_table;
  496.     $whole_match    = $matches[1];
  497.     $link_text        = $matches[2];
  498.     $url            = $matches[3];
  499.     $title            =& $matches[6];
  500.  
  501.     # We've got to encode these to avoid conflicting with italics/bold.
  502.     $url = str_replace(array('*', '_'),
  503.                        array($md_escape_table['*'], $md_escape_table['_']), 
  504.                        $url);
  505.     $result = "<a href=\"$url\"";
  506.     if (isset($title)) {
  507.         $title = str_replace('"', '"', $title);
  508.         $title = str_replace(array('*', '_'),
  509.                              array($md_escape_table['*'], $md_escape_table['_']),
  510.                              $title);
  511.         $result .=  " title=\"$title\"";
  512.     }
  513.     
  514.     $result .= ">$link_text</a>";
  515.  
  516.     return $result;
  517. }
  518.  
  519.  
  520. function _DoImages($text) {
  521. #
  522. # Turn Markdown image shortcuts into <img> tags.
  523. #
  524.     #
  525.     # First, handle reference-style labeled images: ![alt text][id]
  526.     #
  527.     $text = preg_replace_callback('{
  528.         (                # wrap whole match in $1
  529.           !\[
  530.             (.*?)        # alt text = $2
  531.           \]
  532.  
  533.           [ ]?                # one optional space
  534.           (?:\n[ ]*)?        # one optional newline followed by spaces
  535.  
  536.           \[
  537.             (.*?)        # id = $3
  538.           \]
  539.  
  540.         )
  541.         }xs', 
  542.         '_DoImages_reference_callback', $text);
  543.  
  544.     #
  545.     # Next, handle inline images:  ![alt text](url "optional title")
  546.     # Don't forget: encode * and _
  547.  
  548.     $text = preg_replace_callback("{
  549.         (                # wrap whole match in $1
  550.           !\\[
  551.             (.*?)        # alt text = $2
  552.           \\]
  553.           \\(            # literal paren
  554.             [ \\t]*
  555.             <?(\S+?)>?    # src url = $3
  556.             [ \\t]*
  557.             (            # $4
  558.               (['\"])    # quote char = $5
  559.               (.*?)        # title = $6
  560.               \\5        # matching quote
  561.               [ \\t]*
  562.             )?            # title is optional
  563.           \\)
  564.         )
  565.         }xs",
  566.         '_DoImages_inline_callback', $text);
  567.  
  568.     return $text;
  569. }
  570. function _DoImages_reference_callback($matches) {
  571.     global $md_urls, $md_titles, $md_empty_element_suffix, $md_escape_table;
  572.     $whole_match = $matches[1];
  573.     $alt_text    = $matches[2];
  574.     $link_id     = strtolower($matches[3]);
  575.  
  576.     if ($link_id == "") {
  577.         $link_id = strtolower($alt_text); # for shortcut links like ![this][].
  578.     }
  579.  
  580.     $alt_text = str_replace('"', '"', $alt_text);
  581.     if (isset($md_urls[$link_id])) {
  582.         $url = $md_urls[$link_id];
  583.         # We've got to encode these to avoid conflicting with italics/bold.
  584.         $url = str_replace(array('*', '_'),
  585.                            array($md_escape_table['*'], $md_escape_table['_']),
  586.                            $url);
  587.         $result = "<img src=\"$url\" alt=\"$alt_text\"";
  588.         if (isset($md_titles[$link_id])) {
  589.             $title = $md_titles[$link_id];
  590.             $title = str_replace(array('*', '_'),
  591.                                  array($md_escape_table['*'], 
  592.                                        $md_escape_table['_']), $title);
  593.             $result .=  " title=\"$title\"";
  594.         }
  595.         $result .= $md_empty_element_suffix;
  596.     }
  597.     else {
  598.         # If there's no such link ID, leave intact:
  599.         $result = $whole_match;
  600.     }
  601.  
  602.     return $result;
  603. }
  604. function _DoImages_inline_callback($matches) {
  605.     global $md_empty_element_suffix, $md_escape_table;
  606.     $whole_match    = $matches[1];
  607.     $alt_text        = $matches[2];
  608.     $url            = $matches[3];
  609.     $title            = '';
  610.     if (isset($matches[6])) {
  611.         $title        = $matches[6];
  612.     }
  613.  
  614.     $alt_text = str_replace('"', '"', $alt_text);
  615.     $title    = str_replace('"', '"', $title);
  616.     # We've got to encode these to avoid conflicting with italics/bold.
  617.     $url = str_replace(array('*', '_'),
  618.                        array($md_escape_table['*'], $md_escape_table['_']),
  619.                        $url);
  620.     $result = "<img src=\"$url\" alt=\"$alt_text\"";
  621.     if (isset($title)) {
  622.         $title = str_replace(array('*', '_'),
  623.                              array($md_escape_table['*'], $md_escape_table['_']),
  624.                              $title);
  625.         $result .=  " title=\"$title\""; # $title already quoted
  626.     }
  627.     $result .= $md_empty_element_suffix;
  628.  
  629.     return $result;
  630. }
  631.  
  632.  
  633. function _DoHeaders($text) {
  634.     # Setext-style headers:
  635.     #      Header 1
  636.     #      ========
  637.     #  
  638.     #      Header 2
  639.     #      --------
  640.     #
  641.     $text = preg_replace(
  642.         array('{ ^(.+)[ \t]*\n=+[ \t]*\n+ }emx',
  643.               '{ ^(.+)[ \t]*\n-+[ \t]*\n+ }emx'),
  644.         array("'<h1>'._RunSpanGamut(_UnslashQuotes('\\1')).'</h1>\n\n'",
  645.               "'<h2>'._RunSpanGamut(_UnslashQuotes('\\1')).'</h2>\n\n'"),
  646.         $text);
  647.  
  648.     # atx-style headers:
  649.     #    # Header 1
  650.     #    ## Header 2
  651.     #    ## Header 2 with closing hashes ##
  652.     #    ...
  653.     #    ###### Header 6
  654.     #
  655.     $text = preg_replace("{
  656.             ^(\\#{1,6})    # $1 = string of #'s
  657.             [ \\t]*
  658.             (.+?)        # $2 = Header text
  659.             [ \\t]*
  660.             \\#*            # optional closing #'s (not counted)
  661.             \\n+
  662.         }xme",
  663.         "'<h'.strlen('\\1').'>'._RunSpanGamut(_UnslashQuotes('\\2')).'</h'.strlen('\\1').'>\n\n'",
  664.         $text);
  665.  
  666.     return $text;
  667. }
  668.  
  669.  
  670. function _DoLists($text) {
  671. #
  672. # Form HTML ordered (numbered) and unordered (bulleted) lists.
  673. #
  674.     global $md_tab_width, $md_list_level;
  675.     $less_than_tab = $md_tab_width - 1;
  676.  
  677.     # Re-usable patterns to match list item bullets and number markers:
  678.     $marker_ul  = '[*+-]';
  679.     $marker_ol  = '\d+[.]';
  680.     $marker_any = "(?:$marker_ul|$marker_ol)";
  681.  
  682.     # Re-usable pattern to match any entirel ul or ol list:
  683.     $whole_list = '
  684.         (                                # $1 = whole list
  685.           (                                # $2
  686.             [ ]{0,'.$less_than_tab.'}
  687.             ('.$marker_any.')                # $3 = first list item marker
  688.             [ \t]+
  689.           )
  690.           (?s:.+?)
  691.           (                                # $4
  692.               \z
  693.             |
  694.               \n{2,}
  695.               (?=\S)
  696.               (?!                        # Negative lookahead for another list item marker
  697.                 [ \t]*
  698.                 '.$marker_any.'[ \t]+
  699.               )
  700.           )
  701.         )
  702.     '; // mx
  703.     
  704.     # We use a different prefix before nested lists than top-level lists.
  705.     # See extended comment in _ProcessListItems().
  706.  
  707.     if ($md_list_level) {
  708.         $text = preg_replace_callback('{
  709.                 ^
  710.                 '.$whole_list.'
  711.             }mx',
  712.             '_DoLists_callback', $text);
  713.     }
  714.     else {
  715.         $text = preg_replace_callback('{
  716.                 (?:(?<=\n\n)|\A\n?)
  717.                 '.$whole_list.'
  718.             }mx',
  719.             '_DoLists_callback', $text);
  720.     }
  721.  
  722.     return $text;
  723. }
  724. function _DoLists_callback($matches) {
  725.     # Re-usable patterns to match list item bullets and number markers:
  726.     $marker_ul  = '[*+-]';
  727.     $marker_ol  = '\d+[.]';
  728.     $marker_any = "(?:$marker_ul|$marker_ol)";
  729.     
  730.     $list = $matches[1];
  731.     $list_type = preg_match("/$marker_ul/", $matches[3]) ? "ul" : "ol";
  732.     # Turn double returns into triple returns, so that we can make a
  733.     # paragraph for the last item in a list, if necessary:
  734.     $list = preg_replace("/\n{2,}/", "\n\n\n", $list);
  735.     $result = _ProcessListItems($list, $marker_any);
  736.     $result = "<$list_type>\n" . $result . "</$list_type>\n";
  737.     return $result;
  738. }
  739.  
  740.  
  741. function _ProcessListItems($list_str, $marker_any) {
  742. #
  743. #    Process the contents of a single ordered or unordered list, splitting it
  744. #    into individual list items.
  745. #
  746.     global $md_list_level;
  747.     
  748.     # The $md_list_level global keeps track of when we're inside a list.
  749.     # Each time we enter a list, we increment it; when we leave a list,
  750.     # we decrement. If it's zero, we're not in a list anymore.
  751.     #
  752.     # We do this because when we're not inside a list, we want to treat
  753.     # something like this:
  754.     #
  755.     #        I recommend upgrading to version
  756.     #        8. Oops, now this line is treated
  757.     #        as a sub-list.
  758.     #
  759.     # As a single paragraph, despite the fact that the second line starts
  760.     # with a digit-period-space sequence.
  761.     #
  762.     # Whereas when we're inside a list (or sub-list), that line will be
  763.     # treated as the start of a sub-list. What a kludge, huh? This is
  764.     # an aspect of Markdown's syntax that's hard to parse perfectly
  765.     # without resorting to mind-reading. Perhaps the solution is to
  766.     # change the syntax rules such that sub-lists must start with a
  767.     # starting cardinal number; e.g. "1." or "a.".
  768.     
  769.     $md_list_level++;
  770.  
  771.     # trim trailing blank lines:
  772.     $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str);
  773.  
  774.     $list_str = preg_replace_callback('{
  775.         (\n)?                            # leading line = $1
  776.         (^[ \t]*)                        # leading whitespace = $2
  777.         ('.$marker_any.') [ \t]+        # list marker = $3
  778.         ((?s:.+?)                        # list item text   = $4
  779.         (\n{1,2}))
  780.         (?= \n* (\z | \2 ('.$marker_any.') [ \t]+))
  781.         }xm',
  782.         '_ProcessListItems_callback', $list_str);
  783.  
  784.     $md_list_level--;
  785.     return $list_str;
  786. }
  787. function _ProcessListItems_callback($matches) {
  788.     $item = $matches[4];
  789.     $leading_line =& $matches[1];
  790.     $leading_space =& $matches[2];
  791.  
  792.     if ($leading_line || preg_match('/\n{2,}/', $item)) {
  793.         $item = _RunBlockGamut(_Outdent($item));
  794.     }
  795.     else {
  796.         # Recursion for sub-lists:
  797.         $item = _DoLists(_Outdent($item));
  798.         $item = rtrim($item, "\n");
  799.         $item = _RunSpanGamut($item);
  800.     }
  801.  
  802.     return "<li>" . $item . "</li>\n";
  803. }
  804.  
  805.  
  806. function _DoCodeBlocks($text) {
  807. #
  808. #    Process Markdown `<pre><code>` blocks.
  809. #
  810.     global $md_tab_width;
  811.     $text = preg_replace_callback("{
  812.             (?:\\n\\n|\\A)
  813.             (                # $1 = the code block -- one or more lines, starting with a space/tab
  814.               (?:
  815.                 (?:[ ]\{$md_tab_width} | \\t)  # Lines must start with a tab or a tab-width of spaces
  816.                 .*\\n+
  817.               )+
  818.             )
  819.             ((?=^[ ]{0,$md_tab_width}\\S)|\\Z)    # Lookahead for non-space at line-start, or end of doc
  820.         }xm",
  821.         '_DoCodeBlocks_callback', $text);
  822.  
  823.     return $text;
  824. }
  825. function _DoCodeBlocks_callback($matches) {
  826.     $codeblock = $matches[1];
  827.  
  828.     $codeblock = _EncodeCode(_Outdent($codeblock));
  829. //    $codeblock = _Detab($codeblock);
  830.     # trim leading newlines and trailing whitespace
  831.     $codeblock = preg_replace(array('/\A\n+/', '/\s+\z/'), '', $codeblock);
  832.  
  833.     $result = "\n\n<pre><code>" . $codeblock . "\n</code></pre>\n\n";
  834.  
  835.     return $result;
  836. }
  837.  
  838.  
  839. function _DoCodeSpans($text) {
  840. #
  841. #     *    Backtick quotes are used for <code></code> spans.
  842. #
  843. #     *    You can use multiple backticks as the delimiters if you want to
  844. #         include literal backticks in the code span. So, this input:
  845. #
  846. #          Just type ``foo `bar` baz`` at the prompt.
  847. #
  848. #          Will translate to:
  849. #
  850. #          <p>Just type <code>foo `bar` baz</code> at the prompt.</p>
  851. #
  852. #        There's no arbitrary limit to the number of backticks you
  853. #        can use as delimters. If you need three consecutive backticks
  854. #        in your code, use four for delimiters, etc.
  855. #
  856. #    *    You can use spaces to get literal backticks at the edges:
  857. #
  858. #          ... type `` `bar` `` ...
  859. #
  860. #          Turns to:
  861. #
  862. #          ... type <code>`bar`</code> ...
  863. #
  864.     $text = preg_replace_callback("@
  865.             (`+)        # $1 = Opening run of `
  866.             (.+?)        # $2 = The code block
  867.             (?<!`)
  868.             \\1
  869.             (?!`)
  870.         @xs",
  871.         '_DoCodeSpans_callback', $text);
  872.  
  873.     return $text;
  874. }
  875. function _DoCodeSpans_callback($matches) {
  876.     $c = $matches[2];
  877.     $c = preg_replace('/^[ \t]*/', '', $c); # leading whitespace
  878.     $c = preg_replace('/[ \t]*$/', '', $c); # trailing whitespace
  879.     $c = _EncodeCode($c);
  880.     return "<code>$c</code>";
  881. }
  882.  
  883.  
  884. function _EncodeCode($_) {
  885. #
  886. # Encode/escape certain characters inside Markdown code runs.
  887. # The point is that in code, these characters are literals,
  888. # and lose their special Markdown meanings.
  889. #
  890.     global $md_escape_table;
  891.  
  892.     # Encode all ampersands; HTML entities are not
  893.     # entities within a Markdown code span.
  894.     $_ = str_replace('&', '&', $_);
  895.  
  896.     # Do the angle bracket song and dance:
  897.     $_ = str_replace(array('<',    '>'), 
  898.                      array('<', '>'), $_);
  899.  
  900.     # Now, escape characters that are magic in Markdown:
  901.     $_ = str_replace(array_keys($md_escape_table), 
  902.                      array_values($md_escape_table), $_);
  903.  
  904.     return $_;
  905. }
  906.  
  907.  
  908. function _DoItalicsAndBold($text) {
  909.     # <strong> must go first:
  910.     $text = preg_replace('{ (\*\*|__) (?=\S) (.+?[*_]*) (?<=\S) \1 }sx',
  911.         '<strong>\2</strong>', $text);
  912.     # Then <em>:
  913.     $text = preg_replace('{ (\*|_) (?=\S) (.+?) (?<=\S) \1 }sx',
  914.         '<em>\2</em>', $text);
  915.  
  916.     return $text;
  917. }
  918.  
  919.  
  920. function _DoBlockQuotes($text) {
  921.     $text = preg_replace_callback('/
  922.           (                                # Wrap whole match in $1
  923.             (
  924.               ^[ \t]*>[ \t]?            # ">" at the start of a line
  925.                 .+\n                    # rest of the first line
  926.               (.+\n)*                    # subsequent consecutive lines
  927.               \n*                        # blanks
  928.             )+
  929.           )
  930.         /xm',
  931.         '_DoBlockQuotes_callback', $text);
  932.  
  933.     return $text;
  934. }
  935. function _DoBlockQuotes_callback($matches) {
  936.     $bq = $matches[1];
  937.     # trim one level of quoting - trim whitespace-only lines
  938.     $bq = preg_replace(array('/^[ \t]*>[ \t]?/m', '/^[ \t]+$/m'), '', $bq);
  939.     $bq = _RunBlockGamut($bq);        # recurse
  940.  
  941.     $bq = preg_replace('/^/m', "  ", $bq);
  942.     # These leading spaces screw with <pre> content, so we need to fix that:
  943.     $bq = preg_replace_callback('{(\s*<pre>.+?</pre>)}sx', 
  944.                                 '_DoBlockQuotes_callback2', $bq);
  945.  
  946.     return "<blockquote>\n$bq\n</blockquote>\n\n";
  947. }
  948. function _DoBlockQuotes_callback2($matches) {
  949.     $pre = $matches[1];
  950.     $pre = preg_replace('/^  /m', '', $pre);
  951.     return $pre;
  952. }
  953.  
  954.  
  955. function _FormParagraphs($text) {
  956. #
  957. #    Params:
  958. #        $text - string to process with html <p> tags
  959. #
  960.     global $md_html_blocks;
  961.  
  962.     # Strip leading and trailing lines:
  963.     $text = preg_replace(array('/\A\n+/', '/\n+\z/'), '', $text);
  964.  
  965.     $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY);
  966.  
  967.     #
  968.     # Wrap <p> tags.
  969.     #
  970.     foreach ($grafs as $key => $value) {
  971.         if (!isset( $md_html_blocks[$value] )) {
  972.             $value = _RunSpanGamut($value);
  973.             $value = preg_replace('/^([ \t]*)/', '<p>', $value);
  974.             $value .= "</p>";
  975.             $grafs[$key] = $value;
  976.         }
  977.     }
  978.  
  979.     #
  980.     # Unhashify HTML blocks
  981.     #
  982.     foreach ($grafs as $key => $value) {
  983.         if (isset( $md_html_blocks[$value] )) {
  984.             $grafs[$key] = $md_html_blocks[$value];
  985.         }
  986.     }
  987.  
  988.     return implode("\n\n", $grafs);
  989. }
  990.  
  991.  
  992. function _EncodeAmpsAndAngles($text) {
  993. # Smart processing for ampersands and angle brackets that need to be encoded.
  994.  
  995.     # Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
  996.     #   http://bumppo.net/projects/amputator/
  997.     $text = preg_replace('/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/', 
  998.                          '&', $text);;
  999.  
  1000.     # Encode naked <'s
  1001.     $text = preg_replace('{<(?![a-z/?\$!])}i', '<', $text);
  1002.  
  1003.     return $text;
  1004. }
  1005.  
  1006.  
  1007. function _EncodeBackslashEscapes($text) {
  1008. #
  1009. #    Parameter:  String.
  1010. #    Returns:    The string, with after processing the following backslash
  1011. #                escape sequences.
  1012. #
  1013.     global $md_escape_table, $md_backslash_escape_table;
  1014.     # Must process escaped backslashes first.
  1015.     return str_replace(array_keys($md_backslash_escape_table),
  1016.                        array_values($md_backslash_escape_table), $text);
  1017. }
  1018.  
  1019.  
  1020. function _DoAutoLinks($text) {
  1021.     $text = preg_replace("!<((https?|ftp):[^'\">\\s]+)>!", 
  1022.                          '<a href="\1">\1</a>', $text);
  1023.  
  1024.     # Email addresses: <address@domain.foo>
  1025.     $text = preg_replace('{
  1026.         <
  1027.         (?:mailto:)?
  1028.         (
  1029.             [-.\w]+
  1030.             \@
  1031.             [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+
  1032.         )
  1033.         >
  1034.         }exi',
  1035.         "_EncodeEmailAddress(_UnescapeSpecialChars(_UnslashQuotes('\\1')))",
  1036.         $text);
  1037.  
  1038.     return $text;
  1039. }
  1040.  
  1041.  
  1042. function _EncodeEmailAddress($addr) {
  1043. #
  1044. #    Input: an email address, e.g. "foo@example.com"
  1045. #
  1046. #    Output: the email address as a mailto link, with each character
  1047. #        of the address encoded as either a decimal or hex entity, in
  1048. #        the hopes of foiling most address harvesting spam bots. E.g.:
  1049. #
  1050. #      <a href="mailto:foo@e
  1051. #        xample.com">foo
  1052. #        @example.com</a>
  1053. #
  1054. #    Based by a filter by Matthew Wickline, posted to the BBEdit-Talk
  1055. #    mailing list: <http://tinyurl.com/yu7ue>
  1056. #
  1057.     $addr = "mailto:" . $addr;
  1058.     $length = strlen($addr);
  1059.  
  1060.     # leave ':' alone (to spot mailto: later)
  1061.     $addr = preg_replace_callback('/([^\:])/', 
  1062.                                   '_EncodeEmailAddress_callback', $addr);
  1063.  
  1064.     $addr = "<a href=\"$addr\">$addr</a>";
  1065.     # strip the mailto: from the visible part
  1066.     $addr = preg_replace('/">.+?:/', '">', $addr);
  1067.  
  1068.     return $addr;
  1069. }
  1070. function _EncodeEmailAddress_callback($matches) {
  1071.     $char = $matches[1];
  1072.     $r = rand(0, 100);
  1073.     # roughly 10% raw, 45% hex, 45% dec
  1074.     # '@' *must* be encoded. I insist.
  1075.     if ($r > 90 && $char != '@') return $char;
  1076.     if ($r < 45) return '&#x'.dechex(ord($char)).';';
  1077.     return '&#'.ord($char).';';
  1078. }
  1079.  
  1080.  
  1081. function _UnescapeSpecialChars($text) {
  1082. #
  1083. # Swap back in all the special characters we've hidden.
  1084. #
  1085.     global $md_escape_table;
  1086.     return str_replace(array_values($md_escape_table), 
  1087.                        array_keys($md_escape_table), $text);
  1088. }
  1089.  
  1090.  
  1091. # _TokenizeHTML is shared between PHP Markdown and PHP SmartyPants.
  1092. # We only define it if it is not already defined.
  1093. if (!function_exists('_TokenizeHTML')) :
  1094. function _TokenizeHTML($str) {
  1095. #
  1096. #   Parameter:  String containing HTML markup.
  1097. #   Returns:    An array of the tokens comprising the input
  1098. #               string. Each token is either a tag (possibly with nested,
  1099. #               tags contained therein, such as <a href="<MTFoo>">, or a
  1100. #               run of text between tags. Each element of the array is a
  1101. #               two-element array; the first is either 'tag' or 'text';
  1102. #               the second is the actual value.
  1103. #
  1104. #
  1105. #   Regular expression derived from the _tokenize() subroutine in 
  1106. #   Brad Choate's MTRegex plugin.
  1107. #   <http://www.bradchoate.com/past/mtregex.php>
  1108. #
  1109.     $index = 0;
  1110.     $tokens = array();
  1111.  
  1112.     $match = '(?s:<!(?:--.*?--\s*)+>)|'.    # comment
  1113.              '(?s:<\?.*?\?>)|'.                # processing instruction
  1114.              '(?:</?[\w:$]+\b(?>[^"\'>]+|"[^"]*"|\'[^\']*\')*>)'; # regular tags
  1115.  
  1116.     $parts = preg_split("{($match)}", $str, -1, PREG_SPLIT_DELIM_CAPTURE);
  1117.  
  1118.     foreach ($parts as $part) {
  1119.         if (++$index % 2 && $part != '') 
  1120.             array_push($tokens, array('text', $part));
  1121.         else
  1122.             array_push($tokens, array('tag', $part));
  1123.     }
  1124.  
  1125.     return $tokens;
  1126. }
  1127. endif;
  1128.  
  1129.  
  1130. function _Outdent($text) {
  1131. #
  1132. # Remove one level of line-leading tabs or spaces
  1133. #
  1134.     global $md_tab_width;
  1135.     return preg_replace("/^(\\t|[ ]{1,$md_tab_width})/m", "", $text);
  1136. }
  1137.  
  1138.  
  1139. function _Detab($text) {
  1140. #
  1141. # Replace tabs with the appropriate amount of space.
  1142. #
  1143.     global $md_tab_width;
  1144.  
  1145.     # For each line we separate the line in blocks delemited by
  1146.     # tab characters. Then we reconstruct the line adding the appropriate
  1147.     # number of space charcters.
  1148.     
  1149.     $lines = explode("\n", $text);
  1150.     $text = "";
  1151.     
  1152.     foreach ($lines as $line) {
  1153.         # Split in blocks.
  1154.         $blocks = explode("\t", $line);
  1155.         # Add each blocks to the line.
  1156.         $line = $blocks[0];
  1157.         unset($blocks[0]); # Do not add first block twice.
  1158.         foreach ($blocks as $block) {
  1159.             # Calculate amount of space, insert spaces, insert block.
  1160.             $amount = $md_tab_width - strlen($line) % $md_tab_width;
  1161.             $line .= str_repeat(" ", $amount) . $block;
  1162.         }
  1163.         $text .= "$line\n";
  1164.     }
  1165.     return $text;
  1166. }
  1167.  
  1168.  
  1169. function _UnslashQuotes($text) {
  1170. #
  1171. #    This function is useful to remove automaticaly slashed double quotes
  1172. #    when using preg_replace and evaluating an expression.
  1173. #    Parameter:  String.
  1174. #    Returns:    The string with any slash-double-quote (\") sequence replaced
  1175. #                by a single double quote.
  1176. #
  1177.     return str_replace('\"', '"', $text);
  1178. }
  1179.  
  1180.  
  1181. /*
  1182.  
  1183. PHP Markdown
  1184. ============
  1185.  
  1186. Description
  1187. -----------
  1188.  
  1189. This is a PHP translation of the original Markdown formatter written in
  1190. Perl by John Gruber.
  1191.  
  1192. Markdown is a text-to-HTML filter; it translates an easy-to-read /
  1193. easy-to-write structured text format into HTML. Markdown's text format
  1194. is most similar to that of plain text email, and supports features such
  1195. as headers, *emphasis*, code blocks, blockquotes, and links.
  1196.  
  1197. Markdown's syntax is designed not as a generic markup language, but
  1198. specifically to serve as a front-end to (X)HTML. You can use span-level
  1199. HTML tags anywhere in a Markdown document, and you can use block level
  1200. HTML tags (like <div> and <table> as well).
  1201.  
  1202. For more information about Markdown's syntax, see:
  1203.  
  1204. <http://daringfireball.net/projects/markdown/>
  1205.  
  1206.  
  1207. Bugs
  1208. ----
  1209.  
  1210. To file bug reports please send email to:
  1211.  
  1212. <michel.fortin@michelf.com>
  1213.  
  1214. Please include with your report: (1) the example input; (2) the output you
  1215. expected; (3) the output Markdown actually produced.
  1216.  
  1217.  
  1218. Version History
  1219. --------------- 
  1220.  
  1221. See the readme file for detailed release notes for this version.
  1222.  
  1223. 1.0.1 - 17 Dec 2004
  1224.  
  1225. 1.0 - 21 Aug 2004
  1226.  
  1227.  
  1228. Author & Contributors
  1229. ---------------------
  1230.  
  1231. Original Perl version by John Gruber  
  1232. <http://daringfireball.net/>
  1233.  
  1234. PHP port and other contributions by Michel Fortin  
  1235. <http://www.michelf.com/>
  1236.  
  1237.  
  1238. Copyright and License
  1239. ---------------------
  1240.  
  1241. Copyright (c) 2004 Michel Fortin  
  1242. <http://www.michelf.com/>  
  1243. All rights reserved.
  1244.  
  1245. Copyright (c) 2003-2004 John Gruber   
  1246. <http://daringfireball.net/>   
  1247. All rights reserved.
  1248.  
  1249. Redistribution and use in source and binary forms, with or without
  1250. modification, are permitted provided that the following conditions are
  1251. met:
  1252.  
  1253. *    Redistributions of source code must retain the above copyright notice,
  1254.     this list of conditions and the following disclaimer.
  1255.  
  1256. *    Redistributions in binary form must reproduce the above copyright
  1257.     notice, this list of conditions and the following disclaimer in the
  1258.     documentation and/or other materials provided with the distribution.
  1259.  
  1260. *    Neither the name "Markdown" nor the names of its contributors may
  1261.     be used to endorse or promote products derived from this software
  1262.     without specific prior written permission.
  1263.  
  1264. This software is provided by the copyright holders and contributors "as
  1265. is" and any express or implied warranties, including, but not limited
  1266. to, the implied warranties of merchantability and fitness for a
  1267. particular purpose are disclaimed. In no event shall the copyright owner
  1268. or contributors be liable for any direct, indirect, incidental, special,
  1269. exemplary, or consequential damages (including, but not limited to,
  1270. procurement of substitute goods or services; loss of use, data, or
  1271. profits; or business interruption) however caused and on any theory of
  1272. liability, whether in contract, strict liability, or tort (including
  1273. negligence or otherwise) arising in any way out of the use of this
  1274. software, even if advised of the possibility of such damage.
  1275.  
  1276. */
  1277.  
  1278. global    $SmartyPantsPHPVersion, $SmartyPantsSyntaxVersion,
  1279.         $smartypants_attr, $sp_tags_to_skip;
  1280.  
  1281. $SmartyPantsPHPVersion    = '1.5.1c'; # Mon 13 Dec 2004
  1282. $SmartyPantsSyntaxVersion = '1.5.1'; # Fri 12 Mar 2004
  1283.  
  1284.  
  1285. # Configurable variables:
  1286. $smartypants_attr = "1";  # Change this to configure.
  1287.                           #  1 =>  "--" for em-dashes; no en-dash support
  1288.                           #  2 =>  "---" for em-dashes; "--" for en-dashes
  1289.                           #  3 =>  "--" for em-dashes; "---" for en-dashes
  1290.                           #  See docs for more configuration options.
  1291.  
  1292. # Globals:
  1293. $sp_tags_to_skip = '<(/?)(?:pre|code|kbd|script|math)[\s>]';
  1294.  
  1295.  
  1296. # -- WordPress plugin interface -----------------------------------------------
  1297. /*
  1298. Plugin Name: SmartyPants
  1299. Plugin URI: http://www.michelf.com/projects/php-smartypants/
  1300. Description: SmartyPants is a web publishing utility that translates plain ASCII punctuation characters into “smart” typographic punctuation HTML entities. This plugin <strong>replace the default WordPress Texturize algorithm</strong> for the content and the title of your posts, the comments body and author name, and everywhere else Texturize normally apply. Based on the original Perl version by <a href="http://daringfireball.net/">John Gruber</a>.
  1301. Version: 1.5.1c
  1302. Author: Michel Fortin
  1303. Author URI: http://www.michelf.com/
  1304. */
  1305. if (isset($wp_version)) {
  1306.     # Remove default Texturize filter that would conflict with SmartyPants.
  1307.     remove_filter('category_description', 'wptexturize');
  1308.     remove_filter('list_cats', 'wptexturize');
  1309.     remove_filter('comment_author', 'wptexturize');
  1310.     remove_filter('comment_text', 'wptexturize');
  1311.     remove_filter('single_post_title', 'wptexturize');
  1312.     remove_filter('the_title', 'wptexturize');
  1313.     remove_filter('the_content', 'wptexturize');
  1314.     remove_filter('the_excerpt', 'wptexturize');
  1315.     # Add SmartyPants filter with priority 10 (same as Texturize).
  1316.     add_filter('category_description', 'SmartyPants', 10);
  1317.     add_filter('list_cats', 'SmartyPants', 10);
  1318.     add_filter('comment_author', 'SmartyPants', 10);
  1319.     add_filter('comment_text', 'SmartyPants', 10);
  1320.     add_filter('single_post_title', 'SmartyPants', 10);
  1321.     add_filter('the_title', 'SmartyPants', 10);
  1322.     add_filter('the_content', 'SmartyPants', 10);
  1323.     add_filter('the_excerpt', 'SmartyPants', 10);
  1324. }
  1325.  
  1326. # -- Smarty Modifier Interface ------------------------------------------------
  1327. function smarty_modifier_smartypants($text, $attr = NULL) {
  1328.     return SmartyPants($text, $attr);
  1329. }
  1330.  
  1331.  
  1332.  
  1333. function SmartyPants($text, $attr = NULL, $ctx = NULL) {
  1334.     global $smartypants_attr, $sp_tags_to_skip;
  1335.     # Paramaters:
  1336.     $text;   # text to be parsed
  1337.     $attr;   # value of the smart_quotes="" attribute
  1338.     $ctx;    # MT context object (unused)
  1339.     if ($attr == NULL) $attr = $smartypants_attr;
  1340.  
  1341.     # Options to specify which transformations to make:
  1342.     $do_stupefy = FALSE;
  1343.     $convert_quot = 0;  # should we translate " entities into normal quotes?
  1344.  
  1345.     # Parse attributes:
  1346.     # 0 : do nothing
  1347.     # 1 : set all
  1348.     # 2 : set all, using old school en- and em- dash shortcuts
  1349.     # 3 : set all, using inverted old school en and em- dash shortcuts
  1350.     # 
  1351.     # q : quotes
  1352.     # b : backtick quotes (``double'' only)
  1353.     # B : backtick quotes (``double'' and `single')
  1354.     # d : dashes
  1355.     # D : old school dashes
  1356.     # i : inverted old school dashes
  1357.     # e : ellipses
  1358.     # w : convert " entities to " for Dreamweaver users
  1359.  
  1360.     if ($attr == "0") {
  1361.         # Do nothing.
  1362.         return $text;
  1363.     }
  1364.     else if ($attr == "1") {
  1365.         # Do everything, turn all options on.
  1366.         $do_quotes    = 1;
  1367.         $do_backticks = 1;
  1368.         $do_dashes    = 1;
  1369.         $do_ellipses  = 1;
  1370.     }
  1371.     else if ($attr == "2") {
  1372.         # Do everything, turn all options on, use old school dash shorthand.
  1373.         $do_quotes    = 1;
  1374.         $do_backticks = 1;
  1375.         $do_dashes    = 2;
  1376.         $do_ellipses  = 1;
  1377.     }
  1378.     else if ($attr == "3") {
  1379.         # Do everything, turn all options on, use inverted old school dash shorthand.
  1380.         $do_quotes    = 1;
  1381.         $do_backticks = 1;
  1382.         $do_dashes    = 3;
  1383.         $do_ellipses  = 1;
  1384.     }
  1385.     else if ($attr == "-1") {
  1386.         # Special "stupefy" mode.
  1387.         $do_stupefy   = 1;
  1388.     }
  1389.     else {
  1390.         $chars = preg_split('//', $attr);
  1391.         foreach ($chars as $c){
  1392.             if      ($c == "q") { $do_quotes    = 1; }
  1393.             else if ($c == "b") { $do_backticks = 1; }
  1394.             else if ($c == "B") { $do_backticks = 2; }
  1395.             else if ($c == "d") { $do_dashes    = 1; }
  1396.             else if ($c == "D") { $do_dashes    = 2; }
  1397.             else if ($c == "i") { $do_dashes    = 3; }
  1398.             else if ($c == "e") { $do_ellipses  = 1; }
  1399.             else if ($c == "w") { $convert_quot = 1; }
  1400.             else {
  1401.                 # Unknown attribute option, ignore.
  1402.             }
  1403.         }
  1404.     }
  1405.  
  1406.     $tokens = _TokenizeHTML($text);
  1407.     $result = '';
  1408.     $in_pre = 0;  # Keep track of when we're inside <pre> or <code> tags.
  1409.  
  1410.     $prev_token_last_char = "";     # This is a cheat, used to get some context
  1411.                                     # for one-character tokens that consist of 
  1412.                                     # just a quote char. What we do is remember
  1413.                                     # the last character of the previous text
  1414.                                     # token, to use as context to curl single-
  1415.                                     # character quote tokens correctly.
  1416.  
  1417.     foreach ($tokens as $cur_token) {
  1418.         if ($cur_token[0] == "tag") {
  1419.             # Don't mess with quotes inside tags.
  1420.             $result .= $cur_token[1];
  1421.             if (preg_match("@$sp_tags_to_skip@", $cur_token[1], $matches)) {
  1422.                 $in_pre = isset($matches[1]) && $matches[1] == '/' ? 0 : 1;
  1423.             }
  1424.         } else {
  1425.             $t = $cur_token[1];
  1426.             $last_char = substr($t, -1); # Remember last char of this token before processing.
  1427.             if (! $in_pre) {
  1428.                 $t = ProcessEscapes($t);
  1429.  
  1430.                 if ($convert_quot) {
  1431.                     $t = preg_replace('/"/', '"', $t);
  1432.                 }
  1433.  
  1434.                 if ($do_dashes) {
  1435.                     if ($do_dashes == 1) $t = EducateDashes($t);
  1436.                     if ($do_dashes == 2) $t = EducateDashesOldSchool($t);
  1437.                     if ($do_dashes == 3) $t = EducateDashesOldSchoolInverted($t);
  1438.                 }
  1439.  
  1440.                 if ($do_ellipses) $t = EducateEllipses($t);
  1441.  
  1442.                 # Note: backticks need to be processed before quotes.
  1443.                 if ($do_backticks) {
  1444.                     $t = EducateBackticks($t);
  1445.                     if ($do_backticks == 2) $t = EducateSingleBackticks($t);
  1446.                 }
  1447.  
  1448.                 if ($do_quotes) {
  1449.                     if ($t == "'") {
  1450.                         # Special case: single-character ' token
  1451.                         if (preg_match('/\S/', $prev_token_last_char)) {
  1452.                             $t = "’";
  1453.                         }
  1454.                         else {
  1455.                             $t = "‘";
  1456.                         }
  1457.                     }
  1458.                     else if ($t == '"') {
  1459.                         # Special case: single-character " token
  1460.                         if (preg_match('/\S/', $prev_token_last_char)) {
  1461.                             $t = "”";
  1462.                         }
  1463.                         else {
  1464.                             $t = "“";
  1465.                         }
  1466.                     }
  1467.                     else {
  1468.                         # Normal case:
  1469.                         $t = EducateQuotes($t);
  1470.                     }
  1471.                 }
  1472.  
  1473.                 if ($do_stupefy) $t = StupefyEntities($t);
  1474.             }
  1475.             $prev_token_last_char = $last_char;
  1476.             $result .= $t;
  1477.         }
  1478.     }
  1479.  
  1480.     return $result;
  1481. }
  1482.  
  1483.  
  1484. function SmartQuotes($text, $attr = NULL, $ctx = NULL) {
  1485.     global $smartypants_attr, $sp_tags_to_skip;
  1486.     # Paramaters:
  1487.     $text;   # text to be parsed
  1488.     $attr;   # value of the smart_quotes="" attribute
  1489.     $ctx;    # MT context object (unused)
  1490.     if ($attr == NULL) $attr = $smartypants_attr;
  1491.  
  1492.     $do_backticks;   # should we educate ``backticks'' -style quotes?
  1493.  
  1494.     if ($attr == 0) {
  1495.         # do nothing;
  1496.         return $text;
  1497.     }
  1498.     else if ($attr == 2) {
  1499.         # smarten ``backticks'' -style quotes
  1500.         $do_backticks = 1;
  1501.     }
  1502.     else {
  1503.         $do_backticks = 0;
  1504.     }
  1505.  
  1506.     # Special case to handle quotes at the very end of $text when preceded by
  1507.     # an HTML tag. Add a space to give the quote education algorithm a bit of
  1508.     # context, so that it can guess correctly that it's a closing quote:
  1509.     $add_extra_space = 0;
  1510.     if (preg_match("/>['\"]\\z/", $text)) {
  1511.         $add_extra_space = 1; # Remember, so we can trim the extra space later.
  1512.         $text .= " ";
  1513.     }
  1514.  
  1515.     $tokens = _TokenizeHTML($text);
  1516.     $result = '';
  1517.     $in_pre = 0;  # Keep track of when we're inside <pre> or <code> tags
  1518.  
  1519.     $prev_token_last_char = "";     # This is a cheat, used to get some context
  1520.                                     # for one-character tokens that consist of 
  1521.                                     # just a quote char. What we do is remember
  1522.                                     # the last character of the previous text
  1523.                                     # token, to use as context to curl single-
  1524.                                     # character quote tokens correctly.
  1525.  
  1526.     foreach ($tokens as $cur_token) {
  1527.         if ($cur_token[0] == "tag") {
  1528.             # Don't mess with quotes inside tags
  1529.             $result .= $cur_token[1];
  1530.             if (preg_match("@$sp_tags_to_skip@", $cur_token[1], $matches)) {
  1531.                 $in_pre = isset($matches[1]) && $matches[1] == '/' ? 0 : 1;
  1532.             }
  1533.         } else {
  1534.             $t = $cur_token[1];
  1535.             $last_char = substr($t, -1); # Remember last char of this token before processing.
  1536.             if (! $in_pre) {
  1537.                 $t = ProcessEscapes($t);
  1538.                 if ($do_backticks) {
  1539.                     $t = EducateBackticks($t);
  1540.                 }
  1541.  
  1542.                 if ($t == "'") {
  1543.                     # Special case: single-character ' token
  1544.                     if (preg_match('/\S/', $prev_token_last_char)) {
  1545.                         $t = "’";
  1546.                     }
  1547.                     else {
  1548.                         $t = "‘";
  1549.                     }
  1550.                 }
  1551.                 else if ($t == '"') {
  1552.                     # Special case: single-character " token
  1553.                     if (preg_match('/\S/', $prev_token_last_char)) {
  1554.                         $t = "”";
  1555.                     }
  1556.                     else {
  1557.                         $t = "“";
  1558.                     }
  1559.                 }
  1560.                 else {
  1561.                     # Normal case:
  1562.                     $t = EducateQuotes($t);
  1563.                 }
  1564.  
  1565.             }
  1566.             $prev_token_last_char = $last_char;
  1567.             $result .= $t;
  1568.         }
  1569.     }
  1570.  
  1571.     if ($add_extra_space) {
  1572.         preg_replace('/ \z/', '', $result);  # Trim trailing space if we added one earlier.
  1573.     }
  1574.     return $result;
  1575. }
  1576.  
  1577.  
  1578. function SmartDashes($text, $attr = NULL, $ctx = NULL) {
  1579.     global $smartypants_attr, $sp_tags_to_skip;
  1580.     # Paramaters:
  1581.     $text;   # text to be parsed
  1582.     $attr;   # value of the smart_dashes="" attribute
  1583.     $ctx;    # MT context object (unused)
  1584.     if ($attr == NULL) $attr = $smartypants_attr;
  1585.  
  1586.     # reference to the subroutine to use for dash education, default to EducateDashes:
  1587.     $dash_sub_ref = 'EducateDashes';
  1588.  
  1589.     if ($attr == 0) {
  1590.         # do nothing;
  1591.         return $text;
  1592.     }
  1593.     else if ($attr == 2) {
  1594.         # use old smart dash shortcuts, "--" for en, "---" for em
  1595.         $dash_sub_ref = 'EducateDashesOldSchool'; 
  1596.     }
  1597.     else if ($attr == 3) {
  1598.         # inverse of 2, "--" for em, "---" for en
  1599.         $dash_sub_ref = 'EducateDashesOldSchoolInverted'; 
  1600.     }
  1601.  
  1602.     $tokens;
  1603.     $tokens = _TokenizeHTML($text);
  1604.  
  1605.     $result = '';
  1606.     $in_pre = 0;  # Keep track of when we're inside <pre> or <code> tags
  1607.     foreach ($tokens as $cur_token) {
  1608.         if ($cur_token[0] == "tag") {
  1609.             # Don't mess with quotes inside tags
  1610.             $result .= $cur_token[1];
  1611.             if (preg_match("@$sp_tags_to_skip@", $cur_token[1], $matches)) {
  1612.                 $in_pre = isset($matches[1]) && $matches[1] == '/' ? 0 : 1;
  1613.             }
  1614.         } else {
  1615.             $t = $cur_token[1];
  1616.             if (! $in_pre) {
  1617.                 $t = ProcessEscapes($t);
  1618.                 $t = $dash_sub_ref($t);
  1619.             }
  1620.             $result .= $t;
  1621.         }
  1622.     }
  1623.     return $result;
  1624. }
  1625.  
  1626.  
  1627. function SmartEllipses($text, $attr = NULL, $ctx = NULL) {
  1628.     # Paramaters:
  1629.     $text;   # text to be parsed
  1630.     $attr;   # value of the smart_ellipses="" attribute
  1631.     $ctx;    # MT context object (unused)
  1632.     if ($attr == NULL) $attr = $smartypants_attr;
  1633.  
  1634.     if ($attr == 0) {
  1635.         # do nothing;
  1636.         return $text;
  1637.     }
  1638.  
  1639.     $tokens;
  1640.     $tokens = _TokenizeHTML($text);
  1641.  
  1642.     $result = '';
  1643.     $in_pre = 0;  # Keep track of when we're inside <pre> or <code> tags
  1644.     foreach ($tokens as $cur_token) {
  1645.         if ($cur_token[0] == "tag") {
  1646.             # Don't mess with quotes inside tags
  1647.             $result .= $cur_token[1];
  1648.             if (preg_match("@$sp_tags_to_skip@", $cur_token[1], $matches)) {
  1649.                 $in_pre = isset($matches[1]) && $matches[1] == '/' ? 0 : 1;
  1650.             }
  1651.         } else {
  1652.             $t = $cur_token[1];
  1653.             if (! $in_pre) {
  1654.                 $t = ProcessEscapes($t);
  1655.                 $t = EducateEllipses($t);
  1656.             }
  1657.             $result .= $t;
  1658.         }
  1659.     }
  1660.     return $result;
  1661. }
  1662.  
  1663.  
  1664. function EducateQuotes($_) {
  1665. #
  1666. #   Parameter:  String.
  1667. #
  1668. #   Returns:    The string, with "educated" curly quote HTML entities.
  1669. #
  1670. #   Example input:  "Isn't this fun?"
  1671. #   Example output: “Isn’t this fun?”
  1672. #
  1673.     # Make our own "punctuation" character class, because the POSIX-style
  1674.     # [:PUNCT:] is only available in Perl 5.6 or later:
  1675.     $punct_class = "[!\"#\\$\\%'()*+,-.\\/:;<=>?\\@\\[\\\\\]\\^_`{|}~]";
  1676.  
  1677.     # Special case if the very first character is a quote
  1678.     # followed by punctuation at a non-word-break. Close the quotes by brute force:
  1679.     $_ = preg_replace(
  1680.         array("/^'(?=$punct_class\\B)/", "/^\"(?=$punct_class\\B)/"),
  1681.         array('’',                 '”'), $_);
  1682.  
  1683.  
  1684.     # Special case for double sets of quotes, e.g.:
  1685.     #   <p>He said, "'Quoted' words in a larger quote."</p>
  1686.     $_ = preg_replace(
  1687.         array("/\"'(?=\w)/",    "/'\"(?=\w)/"),
  1688.         array('“‘', '‘“'), $_);
  1689.  
  1690.     # Special case for decade abbreviations (the '80s):
  1691.     $_ = preg_replace("/'(?=\\d{2}s)/", '’', $_);
  1692.  
  1693.     $close_class = '[^\ \t\r\n\[\{\(\-]';
  1694.     $dec_dashes = '&\#8211;|&\#8212;';
  1695.  
  1696.     # Get most opening single quotes:
  1697.     $_ = preg_replace("{
  1698.         (
  1699.             \\s          |   # a whitespace char, or
  1700.                    |   # a non-breaking space entity, or
  1701.             --          |   # dashes, or
  1702.             &[mn]dash;  |   # named dash entities
  1703.             $dec_dashes |   # or decimal entities
  1704.             &\\#x201[34];    # or hex
  1705.         )
  1706.         '                   # the quote
  1707.         (?=\\w)              # followed by a word character
  1708.         }x", '\1‘', $_);
  1709.     # Single closing quotes:
  1710.     $_ = preg_replace("{
  1711.         ($close_class)?
  1712.         '
  1713.         (?(1)|          # If $1 captured, then do nothing;
  1714.           (?=\\s | s\\b)  # otherwise, positive lookahead for a whitespace
  1715.         )               # char or an 's' at a word ending position. This
  1716.                         # is a special case to handle something like:
  1717.                         # \"<i>Custer</i>'s Last Stand.\"
  1718.         }xi", '\1’', $_);
  1719.  
  1720.     # Any remaining single quotes should be opening ones:
  1721.     $_ = str_replace("'", '‘', $_);
  1722.  
  1723.  
  1724.     # Get most opening double quotes:
  1725.     $_ = preg_replace("{
  1726.         (
  1727.             \\s          |   # a whitespace char, or
  1728.                    |   # a non-breaking space entity, or
  1729.             --          |   # dashes, or
  1730.             &[mn]dash;  |   # named dash entities
  1731.             $dec_dashes |   # or decimal entities
  1732.             &\\#x201[34];    # or hex
  1733.         )
  1734.         \"                   # the quote
  1735.         (?=\\w)              # followed by a word character
  1736.         }x", '\1“', $_);
  1737.  
  1738.     # Double closing quotes:
  1739.     $_ = preg_replace("{
  1740.         ($close_class)?
  1741.         \"
  1742.         (?(1)|(?=\\s))   # If $1 captured, then do nothing;
  1743.                            # if not, then make sure the next char is whitespace.
  1744.         }x", '\1”', $_);
  1745.  
  1746.     # Any remaining quotes should be opening ones.
  1747.     $_ = str_replace('"', '“', $_);
  1748.  
  1749.     return $_;
  1750. }
  1751.  
  1752.  
  1753. function EducateBackticks($_) {
  1754. #
  1755. #   Parameter:  String.
  1756. #   Returns:    The string, with ``backticks'' -style double quotes
  1757. #               translated into HTML curly quote entities.
  1758. #
  1759. #   Example input:  ``Isn't this fun?''
  1760. #   Example output: “Isn't this fun?”
  1761. #
  1762.  
  1763.     $_ = str_replace(array("``",       "''",),
  1764.                      array('“', '”'), $_);
  1765.     return $_;
  1766. }
  1767.  
  1768.  
  1769. function EducateSingleBackticks($_) {
  1770. #
  1771. #   Parameter:  String.
  1772. #   Returns:    The string, with `backticks' -style single quotes
  1773. #               translated into HTML curly quote entities.
  1774. #
  1775. #   Example input:  `Isn't this fun?'
  1776. #   Example output: ‘Isn’t this fun?’
  1777. #
  1778.  
  1779.     $_ = str_replace(array("`",       "'",),
  1780.                      array('‘', '’'), $_);
  1781.     return $_;
  1782. }
  1783.  
  1784.  
  1785. function EducateDashes($_) {
  1786. #
  1787. #   Parameter:  String.
  1788. #
  1789. #   Returns:    The string, with each instance of "--" translated to
  1790. #               an em-dash HTML entity.
  1791. #
  1792.  
  1793.     $_ = str_replace('--', '—', $_);
  1794.     return $_;
  1795. }
  1796.  
  1797.  
  1798. function EducateDashesOldSchool($_) {
  1799. #
  1800. #   Parameter:  String.
  1801. #
  1802. #   Returns:    The string, with each instance of "--" translated to
  1803. #               an en-dash HTML entity, and each "---" translated to
  1804. #               an em-dash HTML entity.
  1805. #
  1806.  
  1807.     #                      em         en
  1808.     $_ = str_replace(array("---",     "--",),
  1809.                      array('—', '–'), $_);
  1810.     return $_;
  1811. }
  1812.  
  1813.  
  1814. function EducateDashesOldSchoolInverted($_) {
  1815. #
  1816. #   Parameter:  String.
  1817. #
  1818. #   Returns:    The string, with each instance of "--" translated to
  1819. #               an em-dash HTML entity, and each "---" translated to
  1820. #               an en-dash HTML entity. Two reasons why: First, unlike the
  1821. #               en- and em-dash syntax supported by
  1822. #               EducateDashesOldSchool(), it's compatible with existing
  1823. #               entries written before SmartyPants 1.1, back when "--" was
  1824. #               only used for em-dashes.  Second, em-dashes are more
  1825. #               common than en-dashes, and so it sort of makes sense that
  1826. #               the shortcut should be shorter to type. (Thanks to Aaron
  1827. #               Swartz for the idea.)
  1828. #
  1829.  
  1830.     #                      en         em
  1831.     $_ = str_replace(array("---",     "--",),
  1832.                      array('–', '—'), $_);
  1833.     return $_;
  1834. }
  1835.  
  1836.  
  1837. function EducateEllipses($_) {
  1838. #
  1839. #   Parameter:  String.
  1840. #   Returns:    The string, with each instance of "..." translated to
  1841. #               an ellipsis HTML entity. Also converts the case where
  1842. #               there are spaces between the dots.
  1843. #
  1844. #   Example input:  Huh...?
  1845. #   Example output: Huh…?
  1846. #
  1847.  
  1848.     $_ = str_replace(array("...",     ". . .",), '…', $_);
  1849.     return $_;
  1850. }
  1851.  
  1852.  
  1853. function StupefyEntities($_) {
  1854. #
  1855. #   Parameter:  String.
  1856. #   Returns:    The string, with each SmartyPants HTML entity translated to
  1857. #               its ASCII counterpart.
  1858. #
  1859. #   Example input:  “Hello — world.”
  1860. #   Example output: "Hello -- world."
  1861. #
  1862.  
  1863.                         #  en-dash    em-dash
  1864.     $_ = str_replace(array('–', '—'),
  1865.                      array('-',       '--'), $_);
  1866.  
  1867.     # single quote         open       close
  1868.     $_ = str_replace(array('‘', '’'), "'", $_);
  1869.  
  1870.     # double quote         open       close
  1871.     $_ = str_replace(array('“', '”'), '"', $_);
  1872.  
  1873.     $_ = str_replace('…', '...', $_); # ellipsis
  1874.  
  1875.     return $_;
  1876. }
  1877.  
  1878.  
  1879. function ProcessEscapes($_) {
  1880. #
  1881. #   Parameter:  String.
  1882. #   Returns:    The string, with after processing the following backslash
  1883. #               escape sequences. This is useful if you want to force a "dumb"
  1884. #               quote or other character to appear.
  1885. #
  1886. #               Escape  Value
  1887. #               ------  -----
  1888. #               \\      \
  1889. #               \"      "
  1890. #               \'      '
  1891. #               \.      .
  1892. #               \-      -
  1893. #               \`      `
  1894. #
  1895.     $_ = str_replace(
  1896.         array('\\',    '\"',    "\'",    '\.',    '\-',    '\`'),
  1897.         array('\', '"', ''', '.', '-', '`'), $_);
  1898.  
  1899.     return $_;
  1900. }
  1901.  
  1902.  
  1903. # _TokenizeHTML is shared between PHP SmartyPants and PHP Markdown.
  1904. # We only define it if it is not already defined.
  1905. if (!function_exists('_TokenizeHTML')) :
  1906. function _TokenizeHTML($str) {
  1907. #
  1908. #   Parameter:  String containing HTML markup.
  1909. #   Returns:    An array of the tokens comprising the input
  1910. #               string. Each token is either a tag (possibly with nested,
  1911. #               tags contained therein, such as <a href="<MTFoo>">, or a
  1912. #               run of text between tags. Each element of the array is a
  1913. #               two-element array; the first is either 'tag' or 'text';
  1914. #               the second is the actual value.
  1915. #
  1916. #
  1917. #   Regular expression derived from the _tokenize() subroutine in 
  1918. #   Brad Choate's MTRegex plugin.
  1919. #   <http://www.bradchoate.com/past/mtregex.php>
  1920. #
  1921.     $index = 0;
  1922.     $tokens = array();
  1923.  
  1924.     $match = '(?s:<!(?:--.*?--\s*)+>)|'.    # comment
  1925.              '(?s:<\?.*?\?>)|'.                # processing instruction
  1926.              '(?:</?[\w:$]+\b(?>[^"\'>]+|"[^"]*"|\'[^\']*\')*>)'; # regular tags
  1927.  
  1928.     $parts = preg_split("{($match)}", $str, -1, PREG_SPLIT_DELIM_CAPTURE);
  1929.  
  1930.     foreach ($parts as $part) {
  1931.         if (++$index % 2 && $part != '') 
  1932.             array_push($tokens, array('text', $part));
  1933.         else
  1934.             array_push($tokens, array('tag', $part));
  1935.     }
  1936.  
  1937.     return $tokens;
  1938. }
  1939. endif;
  1940.  
  1941.  
  1942. /*
  1943.  
  1944. PHP SmartyPants
  1945. ===============
  1946.  
  1947. Description
  1948. -----------
  1949.  
  1950. This is a PHP translation of the original SmartyPants quote educator written in
  1951. Perl by John Gruber.
  1952.  
  1953. SmartyPants is a web publishing utility that translates plain ASCII
  1954. punctuation characters into "smart" typographic punctuation HTML
  1955. entities. SmartyPants can perform the following transformations:
  1956.  
  1957. *    Straight quotes (`"` and `'`) into "curly" quote HTML entities
  1958. *    Backticks-style quotes (` ``like this'' `) into "curly" quote HTML 
  1959.     entities
  1960. *    Dashes (`--` and `---`) into en- and em-dash entities
  1961. *    Three consecutive dots (`...`) into an ellipsis entity
  1962.  
  1963. SmartyPants does not modify characters within `<pre>`, `<code>`, `<kbd>`, 
  1964. `<script>`, or `<math>` tag blocks. Typically, these tags are used to 
  1965. display text where smart quotes and other "smart punctuation" would not 
  1966. be appropriate, such as source code or example markup.
  1967.  
  1968.  
  1969. ### Backslash Escapes ###
  1970.  
  1971. If you need to use literal straight quotes (or plain hyphens and
  1972. periods), SmartyPants accepts the following backslash escape sequences
  1973. to force non-smart punctuation. It does so by transforming the escape
  1974. sequence into a decimal-encoded HTML entity:
  1975.  
  1976.     Escape  Value  Character
  1977.     ------  -----  ---------
  1978.       \\    \    \
  1979.       \"    "    "
  1980.       \'    '    '
  1981.       \.    .    .
  1982.       \-    -    -
  1983.       \`    `    `
  1984.  
  1985. This is useful, for example, when you want to use straight quotes as
  1986. foot and inch marks: 6'2" tall; a 17" iMac.
  1987.  
  1988.  
  1989. Bugs
  1990. ----
  1991.  
  1992. To file bug reports or feature requests (other than topics listed in the
  1993. Caveats section above) please send email to:
  1994.  
  1995. <michel.fortin@michelf.com>
  1996.  
  1997. If the bug involves quotes being curled the wrong way, please send example
  1998. text to illustrate.
  1999.  
  2000.  
  2001. ### Algorithmic Shortcomings ###
  2002.  
  2003. One situation in which quotes will get curled the wrong way is when
  2004. apostrophes are used at the start of leading contractions. For example:
  2005.  
  2006.     'Twas the night before Christmas.
  2007.  
  2008. In the case above, SmartyPants will turn the apostrophe into an opening
  2009. single-quote, when in fact it should be a closing one. I don't think
  2010. this problem can be solved in the general case -- every word processor
  2011. I've tried gets this wrong as well. In such cases, it's best to use the
  2012. proper HTML entity for closing single-quotes (`’`) by hand.
  2013.  
  2014.  
  2015. Version History
  2016. ---------------
  2017.  
  2018. See the readme file for detailed release notes for this version.
  2019.  
  2020. 1.5.1c - 13 Dec 2004
  2021.  
  2022. 1.5.1b - 6 Sep 2004
  2023.  
  2024. 1.5.1a - 30 Jun 2004
  2025.  
  2026. 1.5.1 - 6 Jun 2004
  2027.  
  2028.  
  2029. Author
  2030. ------
  2031.  
  2032. Original Perl version by John Gruber  
  2033. <http://daringfireball.net/>
  2034.  
  2035. PHP port by Michel Fortin  
  2036. <http://www.michelf.com/>
  2037.  
  2038.  
  2039. Additional Credits
  2040. ------------------
  2041.  
  2042. Jeremy Hedley (<http://antipixel.com/>) and Charles Wiltgen
  2043. (<http://playbacktime.com/>) deserve mention for exemplary beta testing.
  2044.  
  2045.  
  2046. Copyright and License
  2047. ---------------------
  2048.  
  2049. Copyright (c) 2003 John Gruber  
  2050. <http://daringfireball.net/>  
  2051. All rights reserved.
  2052.  
  2053. Copyright (c) 2004 Michel Fortin  
  2054. <http://www.michelf.com>  
  2055. All rights reserved.
  2056.  
  2057. Redistribution and use in source and binary forms, with or without
  2058. modification, are permitted provided that the following conditions are met:
  2059.  
  2060. *    Redistributions of source code must retain the above copyright
  2061.     notice, this list of conditions and the following disclaimer.
  2062.  
  2063. *    Redistributions in binary form must reproduce the above copyright
  2064.     notice, this list of conditions and the following disclaimer in the
  2065.     documentation and/or other materials provided with the distribution.
  2066.  
  2067. *    Neither the name "SmartyPants" nor the names of its contributors may
  2068.     be used to endorse or promote products derived from this software
  2069.     without specific prior written permission.
  2070.  
  2071. This software is provided by the copyright holders and contributors "as is"
  2072. and any express or implied warranties, including, but not limited to, the 
  2073. implied warranties of merchantability and fitness for a particular purpose 
  2074. are disclaimed. In no event shall the copyright owner or contributors be 
  2075. liable for any direct, indirect, incidental, special, exemplary, or 
  2076. consequential damages (including, but not limited to, procurement of 
  2077. substitute goods or services; loss of use, data, or profits; or business 
  2078. interruption) however caused and on any theory of liability, whether in 
  2079. contract, strict liability, or tort (including negligence or otherwise) 
  2080. arising in any way out of the use of this software, even if advised of the
  2081. possibility of such damage.
  2082.  
  2083. */
  2084.  
  2085. function textformat($my_text) {
  2086. $my_html = SmartyPants(Markdown($my_text));
  2087. return $my_html;
  2088. }
  2089.  
  2090. if ($dopodcast=="yes") {
  2091. // let's play the figure out where the hell we are right now game!
  2092.  
  2093. if (substr(PHP_OS, 0, 3) == 'WIN') {
  2094. // welcome to windows!  
  2095. $mypath = strtolower(getcwd());
  2096. }
  2097. else {
  2098. //It's a unix system!  Or something.
  2099. $mypath = getcwd();
  2100. }
  2101.  
  2102. // no php5 for you.  You've been a bad boy.  Do it the hard way.  
  2103. exec('ls "$mypath"',$f_list);
  2104. $dir_str = $mypath;
  2105. $filelist[0] = $mypath;
  2106. $i = 1;
  2107. for ($count=0; $count<count($f_list); $count++) {
  2108.    if ($f_list[$count] == "") {
  2109. continue;
  2110. }
  2111.    if ( substr($f_list[$count], strlen($f_list[$count])-1,1) == ":") {
  2112.      $dir_str = substr($f_list[$count], 0, strlen ($f_list[$count])-1);
  2113.      $filelist[$i] = $dir_str;
  2114.      $i++;
  2115. }
  2116. else {
  2117.      $file_str = "$dir_str/$f_list[$count]";
  2118.      if (is_file($file_str)) {
  2119.        $filelist[$i] = $file_str;
  2120.        $i++;
  2121.      }
  2122.    }
  2123. }
  2124. // Well, not as hard as it looks.  Assuming of course that it works.  
  2125. // Don't talk to me about ls!  I never want to see that output again.  Ever.
  2126.  
  2127. // now, welcome to fake xml!  here come the headers.
  2128. echo '<?xml version="1.0"?>';
  2129. ?>
  2130.     <rss version="2.0">
  2131.     <channel>
  2132.         <title><?php echo $show_name;?></title>
  2133.         <link><?php echo $show_page; ?></link>
  2134.         <description><?php echo textformat($show_desc); ?></description>
  2135.         <language><?php echo $show_lang; ?></language>
  2136.         <copyright><?php echo $show_copy; ?></copyright>
  2137.         <lastBuildDate><?php echo date("l dS of F Y h:i:s A"); ?></lastBuildDate>
  2138.         <generator>The Beyond Radio Network Podcast server</generator>
  2139. <webMaster><?php echo $show_addr; ?></webMaster>
  2140.         <ttl>1</ttl>
  2141.  
  2142. <?php
  2143. //now we go through the files
  2144. for ($c=0; $c<count($filelist); $c++) {
  2145. if (is_file($filelist[$c]) == true){
  2146. if (strpos($filelist[$c], ".php") === false) {
  2147. if (strpos($filelist[$c], ".inc") === false) {
  2148. $cur_file = basename($filelist[$c], ".mp3");
  2149. ?>
  2150.         <item>
  2151. <title><?php echo $cur_file; ?></title>
  2152. <description>
  2153. <?php
  2154. if(file_exists($filelist[$c] . ".txt")) {
  2155. $shownotes = textformat(file_get_contents($filelist[$c] . ".txt"));
  2156. echo $shownotes;
  2157. }
  2158. else {
  2159. echo "No show notes available for this show.";
  2160. }
  2161. ?>
  2162. </description>
  2163. <pubDate><?php echo date ("F d Y H:i:s.", filemtime($filelist[$c])); ?></pubDate>
  2164. <enclosure url="<?php echo $show_url . $cur_file;?>" length="<?php echo filesize($filelist[$c]); ?>" type="<?php echo $show_format; ?>"/>
  2165. </item>
  2166. <?php
  2167. }
  2168. }
  2169. }
  2170. }
  2171. ?>
  2172. </channel>
  2173. </rss>
  2174. <?php
  2175. }
  2176. ?>