+
+Please include with your report: (1) the example input; (2) the output you
+expected; (3) the output PHP Markdown actually produced.
+
+
+Version History
+---------------
+
+1.0.1oo (19 May 2006)
+
+* Converted PHP Markdown and PHP Markdown Extra to a object-oriented design.
+
+
+1.0.1 (9 December 2005)
+
+* Fixed a problem occurring with PHP 5.1.1 due to a small
+ change to strings variable replacement behaviour in
+ this version.
+
+
+1.0 (5 September 2005)
+
+* Added support for setting the id attributes for headers like this:
+
+ Header 1 {#header1}
+ ========
+
+ ## Header 2 ## {#header2}
+
+ This only work only for headers for now.
+
+* Tables will now work correctly as the first element of a definition
+ list. For example, this input:
+
+ Term
+
+ : Header | Header
+ ------- | -------
+ Cell | Cell
+
+ used to produce no definition list and a table where the first
+ header was named ": Header". This is now fixed.
+
+* Fix for a problem where a paragraph following a table was not
+ placed between `` tags.
+
+
+1.0b4 (1 August 2005)
+
+* Fixed some issues where whitespace around HTML blocks were trigging
+ empty paragraph tags.
+
+* Fixed an HTML block parsing issue that would cause a block element
+ following a code span or block with unmatched opening bracket to be
+ placed inside a paragraph.
+
+* Removed some PHP notices that could appear when parsing definition
+ lists and tables with PHP notice reporting flag set.
+
+
+1.0b3 (29 July 2005)
+
+* Definition lists now require a blank line before each term. Solves
+ an ambiguity where the last line of lazy-indented definitions could
+ be mistaken by PHP Markdown as a new term in the list.
+
+* Definition lists now support multiple terms per definition.
+
+* Some special tags were replaced in the output by their md5 hash
+ key. Things such as this now work as expected:
+
+ ## Header ##
+
+
+1.0b2 (26 July 2005)
+
+* Definition lists can now take two or more definitions for one term.
+ This should have been the case before, but a bug prevented this
+ from working right.
+
+* Fixed a problem where single column table with a pipe only at the
+ end where not parsed as table. Here is such a table:
+
+ | header
+ | ------
+ | cell
+
+* Fixed problems with empty cells in the first column of a table with
+ no leading pipe, like this one:
+
+ header | header
+ ------ | ------
+ | cell
+
+* Code spans containing pipes did not within a table. This is now
+ fixed by parsing code spans before splitting rows into cells.
+
+* Added the pipe character to the backlash escape character lists.
+
+
+1.0b1 (25 Jun 2005)
+
+* First public release of PHP Markdown Extra.
+
+
+Copyright and License
+---------------------
+
+Copyright (c) 2004-2006 Michel Fortin
+
+All rights reserved.
+
+Based on Markdown
+Copyright (c) 2003-2005 John Gruber
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the
+ distribution.
+
+* Neither the name "Markdown" nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+This software is provided by the copyright holders and contributors "as
+is" and any express or implied warranties, including, but not limited
+to, the implied warranties of merchantability and fitness for a
+particular purpose are disclaimed. In no event shall the copyright owner
+or contributors be liable for any direct, indirect, incidental, special,
+exemplary, or consequential damages (including, but not limited to,
+procurement of substitute goods or services; loss of use, data, or
+profits; or business interruption) however caused and on any theory of
+liability, whether in contract, strict liability, or tort (including
+negligence or otherwise) arising in any way out of the use of this
+software, even if advised of the possibility of such damage.
diff --git a/PHP Markdown Readme.text b/PHP Markdown Readme.text
deleted file mode 100644
index 101dff1..0000000
--- a/PHP Markdown Readme.text
+++ /dev/null
@@ -1,478 +0,0 @@
-PHP Markdown
-============
-
-Version 1.0.1oo - Fri 19 May 2006
-
-by Michel Fortin
-
-
-based on work by John Gruber
-
-
-
-Introduction
-------------
-
-Markdown is a text-to-HTML conversion tool for web writers. Markdown
-allows you to write using an easy-to-read, easy-to-write plain text
-format, then convert it to structurally valid XHTML (or HTML).
-
-"Markdown" is two things: a plain text markup syntax, and a software
-tool, written in Perl, that converts the plain text markup to HTML.
-PHP Markdown is a port to PHP of the original Markdown program by
-John Gruber.
-
-PHP Markdown can work as a plug-in for WordPress and bBlog, as a
-modifier for the Smarty templating engine, or as a remplacement for
-textile formatting in any software that support textile.
-
-Full documentation of Markdown's syntax is available on John's
-Markdown page:
-
-
-Installation and Requirement
-----------------------------
-
-PHP Markdown requires PHP version 4.0.5 or later.
-
-
-### WordPress ###
-
-PHP Markdown works with [WordPress][wp], version 1.2 or later.
-PHP Markdown is already bundled with WordPress. Still, you can find
-here the latest version that may be newer than the latest WordPress
-version.
-
- [wp]: http://wordpress.org/
-
-1. To use PHP Markdown with WordPress, place the "makrdown.php" file
- in the "plugins" folder. This folder is located inside
- "wp-content" at the root of your site:
-
- (site home)/wp-content/plugins/
-
-2. Activate the plugin with the administrative interface of
- WordPress. In the "Plugins" section you will now find Markdown.
- To activate the plugin, click on the "Activate" button on the
- same line than Markdown. Your entries will now be formatted by
- PHP Markdown.
-
-You can configure PHP Markdown to not apply to the comments on your
-WordPress weblog. See the "Configuration" section below.
-
-Note: It is not possible at this time to apply a different set of
-filters to different entries. All your entries will be formated by
-PHP Markdown. This is currently a limitation of WordPress. If your old
-entries are written in HTML (as opposed to another formatting syntax),
-your site should not suffer much from installing PHP Markdown.
-
-
-### bBlog ###
-
-PHP Markdown also works with the latest version of [bBlog][bb].
-
- [bb]: http://www.bblog.com/
-
-1. To use PHP Markdown with bBlog, rename "markdown.php" to
- "modifier.markdown.php" and place the file in the "bBlog_plugins"
- folder. This folder is located inside the "bblog" directory of
- your site, like this:
-
- (site home)/bblog/bBlog_plugins/modifier.markdown.php
-
-2. Select "Markdown" as the "Entry Modifier" when you post a new
- entry. This setting will only apply to the entry you are editing.
-
-
-### Replacing Textile ###
-
-Many web programs written in PHP use [Textile][tx] to format your text.
-To use PHP Markdown with these programs without having to change the
-code, you can use PHP Markdown in "Textile Compatibility Mode."
-
- [tx]: http://www.textism.com/tools/textile/
-
-1. Rename the "markdown.php" file to "classTextile.php".
-
-2. Locate the "classTextile.php" file hidden somewhere inside the
- installation of your program (see table below). Replace it with
- the PHP Markdown file you just renamed.
-
-As an helper, here you can learn where is the "classTextile.php" file
-in some web programs:
-
- Program Location
- ----------------------------------------------------------------
- TextPattern (site home)/textpattern/lib/classTextile.php
- Pivot (site home)/pivot/includes/textile/classtextile.php
-
-Contrary to Textile, Markdown does not convert quotes to curly ones
-and does not convert multiple hyphens (`--` and `---`) into en- and
-em-dashes. If you use PHP Markdown in Textile Compatibility Mode, you
-can solve this problem by installing the "smartypants.php" file from
-[PHP SmartyPants][psp] beside the "classTextile.php" file. The Textile
-Compatibility Mode function will use SmartyPants automatically without
-further modification.
-
- [psp]: http://www.michelf.com/projects/php-smartypants/
-
-
-### In Your Own Programs ###
-
-You can use PHP Markdown easily in your current PHP program. Simply
-include the file and then call the Markdown function on the text you
-want to convert:
-
- include_once "markdown.php";
- $my_html = Markdown($my_text);
-
-If you wish to use PHP Markdown with another text filter function
-built to parse HTML, you should filter the text *after* the Markdown
-function call. This is an example with [PHP SmartyPants][psp]:
-
- $my_html = SmartyPants(Markdown($my_text));
-
-
-### With Smarty ###
-
-If your program use the [Smarty][sm] template engine, PHP Markdown
-can now be used as a modifier for your templates. Rename "markdown.php"
-to "modifier.markdown.php" and put it in your smarty plugins folder.
-
- [sm]: http://smarty.php.net/
-
-If you are using MovableType 3.1 or later, the Smarty plugin folder is
-located at `(MT CGI root)/php/extlib/smarty/plugins`. This will allow
-Markdown to work on dynamic pages.
-
-
-Configuration
--------------
-
-By default, PHP Markdown produces XHTML output for tags with empty
-elements. E.g.:
-
-
-
-Markdown can be configured to produce HTML-style tags; e.g.:
-
-
-
-To do this, you must edit the "$md_empty_element_suffix" variable
-below the "Global default settings" header at the start of the
-"markdown.php" file.
-
-
-### WordPress-Specific Settings ###
-
-By default, the Markdown plugin applies to both posts and comments on
-your WordPress weblog. To deactivate one or the other, edit the
-`$md_wp_posts` or `$md_wp_comments` variable under the "WordPress
-settings" header at the start of the "markdown.php" file.
-
-
-Bugs
-----
-
-To file bug reports please send email to:
-
-
-Please include with your report: (1) the example input; (2) the output you
-expected; (3) the output PHP Markdown actually produced.
-
-
-Version History
----------------
-
-1.0.1oo (19 May 2006)
-
-* Converted PHP Markdown to a object-oriented design.
-
-
-1.0.1c (9 Dec 2005)
-
-* Fixed a problem occurring with PHP 5.1.1 due to a small
- change to strings variable replacement behaviour in
- this version.
-
-
-1.0.1b (6 Jun 2005)
-
-* Fixed a bug where an inline image followed by a reference link would
- give a completely wrong result.
-
-* Fix for escaped backticks still triggering code spans:
-
- There are two raw backticks here: \` and here: \`, not a code span
-
-* Fix for an ordered list following an unordered list, and the
- reverse. There is now a loop in _DoList that does the two
- separately.
-
-* Fix for nested sub-lists in list-paragraph mode. Previously we got
- a spurious extra level of `` tags for something like this:
-
- * this
-
- * sub
-
- that
-
-* Fixed some incorrect behaviour with emphasis. This will now work
- as it should:
-
- *test **thing***
- **test *thing***
- ***thing* test**
- ***thing** test*
-
- Name: __________
- Address: _______
-
-* Correct a small bug in `_TokenizeHTML` where a Doctype declaration
- was not seen as HTML.
-
-* Major rewrite of the WordPress integration code that should
- correct many problems by preventing default WordPress filters from
- tampering with Markdown-formatted text. More details here:
-
-
-* Added a configuration variable for WordPress that can disable the
- Markdown filter on comments.
-
-
-1.0.1a (15 Apr 2005)
-
-* Fixed an issue where PHP warnings were trigged when converting
- text with list items running on PHP 4.0.6. This was comming from
- the `rtrim` function which did not support the second argument
- prior version 4.1. Replaced by a regular expression.
-
-* Markdown now filter correctly post excerpts and comment
- excerpts in WordPress.
-
-* Automatic links and some code sample were "corrected" by
- the balenceTag filter in WordPress meant to ensure HTML
- is well formed. This new version of PHP Markdown postpone this
- filter so that it runs after Markdown.
-
-* Blockquote syntax and some code sample were stripped by
- a new WordPress 1.5 filter meant to remove unwanted HTML
- in comments. This new version of PHP Markdown postpone this
- filter so that it runs after Markdown.
-
-
-1.0.2b1 (5 Mar 2005)
-
-* Fix for backticks within HTML tag:
-
- like this
-
-* Fix for escaped backticks still triggering code spans:
-
- There are two raw backticks here: \` and here: \`, not a code span
-
-* Improved integration with WordPress. With WordPress 1.5, the
- balenceTags filter now runs after Markdown, so it won't
- interfere anymore. You can still disable balanceTags from the admin
- interface (in Options > Writing) if you want to.
-
-* PHP Markdown now correctly filter text for excerpts in WordPress.
- There is still one glitch: autolinks and tags in code samples are
- stripped by WordPress when trimming it. A fix for this is possible
- with WordPress 1.5, but would require duplicating WordPress entry
- trimming code within Markdown, which I can't do because of a license
- issue. (Nor do I think it is a good solution to fix this.)
-
-* Improved Textile compatibility mode. Markdown will now honor the
- no-image and the lite parameters. In lite mode, no header, blockquote,
- list, or code block will be made, and inline HTML is limited
- to the following tags:
-
- ![]()
-
- This is acheived by backslash-escaping block markers before sending
- text through the Markdown filter.
-
- The improved Textile comatibility means that the Markdown syntax will now
- be processed for comments in TextPattern (only for span elements due to
- TextPattern using the lite mode for comments). Sadly, due to TextPattern
- tag stripping, sample code in code span and auto-links will be stripped
- before the Markdown filter can see them. So I guess I should say it
- half-work for comments TextPattern.
-
-
-1.0.1 (16 Dec 2004):
-
-* Changed the syntax rules for code blocks and spans. Previously,
- backslash escapes for special Markdown characters were processed
- everywhere other than within inline HTML tags. Now, the contents of
- code blocks and spans are no longer processed for backslash escapes.
- This means that code blocks and spans are now treated literally,
- with no special rules to worry about regarding backslashes.
-
- **IMPORTANT**: This breaks the syntax from all previous versions of
- Markdown. Code blocks and spans involving backslash characters will
- now generate different output than before.
-
- Implementation-wise, this change was made by moving the call to
- `_EscapeSpecialChars()` from the top-level `Markdown()` function to
- within `_RunSpanGamut()`.
-
-* Significants performance improvement in `_DoHeader`, `_Detab`
- and `_TokenizeHTML`.
-
-* Added `>`, `+`, and `-` to the list of backslash-escapable
- characters. These should have been done when these characters
- were added as unordered list item markers.
-
-* Inline links using `<` and `>` URL delimiters weren't working:
-
- like [this]()
-
- Fixed by moving `_DoAutoLinks()` after `_DoAnchors()` in
- `_RunSpanGamut()`.
-
-* Fixed bug where auto-links were being processed within code spans:
-
- like this: ``
-
- Fixed by moving `_DoAutoLinks()` from `_RunBlockGamut()` to
- `_RunSpanGamut()`.
-
-* Sort-of fixed a bug where lines in the middle of hard-wrapped
- paragraphs, which lines look like the start of a list item,
- would accidentally trigger the creation of a list. E.g. a
- paragraph that looked like this:
-
- I recommend upgrading to version
- 8. Oops, now this line is treated
- as a sub-list.
-
- This is fixed for top-level lists, but it can still happen for
- sub-lists. E.g., the following list item will not be parsed
- properly:
-
- * I recommend upgrading to version
- 8. Oops, now this line is treated
- as a sub-list.
-
- Given Markdown's list-creation rules, I'm not sure this can
- be fixed.
-
-* Fix for horizontal rules preceded by 2 or 3 spaces or followed by
- trailing spaces and tabs.
-
-* Standalone HTML comments are now handled; previously, they'd get
- wrapped in a spurious `` tag.
-
-* `_HashHTMLBlocks()` now tolerates trailing spaces and tabs following
- HTML comments and `
` tags.
-
-* Changed special case pattern for hashing `
` tags in
- `_HashHTMLBlocks()` so that they must occur within three spaces
- of left margin. (With 4 spaces or a tab, they should be
- code blocks, but weren't before this fix.)
-
-* Auto-linked email address can now optionally contain
- a 'mailto:' protocol. I.e. these are equivalent:
-
-
-
-
-* Fixed annoying bug where nested lists would wind up with
- spurious (and invalid) `` tags.
-
-* Changed `_StripLinkDefinitions()` so that link definitions must
- occur within three spaces of the left margin. Thus if you indent
- a link definition by four spaces or a tab, it will now be a code
- block.
-
-* You can now write empty links:
-
- [like this]()
-
- and they'll be turned into anchor tags with empty href attributes.
- This should have worked before, but didn't.
-
-* `***this***` and `___this___` are now turned into
-
- this
-
- Instead of
-
- this
-
- which isn't valid.
-
-* Fixed problem for links defined with urls that include parens, e.g.:
-
- [1]: http://sources.wikipedia.org/wiki/Middle_East_Policy_(Chomsky)
-
- "Chomsky" was being erroneously treated as the URL's title.
-
-* Double quotes in the title of an inline link used to give strange
- results (incorrectly made entities). Fixed.
-
-* Tabs are now correctly changed into spaces. Previously, only
- the first tab was converted. In code blocks, the second one was too,
- but was not always correctly aligned.
-
-* Fixed a bug where a tab character inserted after a quote on the same
- line could add a slash before the quotes.
-
- This is "before" [tab] and "after" a tab.
-
- Previously gave this result:
-
- This is \"before\" [tab] and "after" a tab.
-
-* Removed a call to `htmlentities`. This fixes a bug where multibyte
- characters present in the title of a link reference could lead to
- invalid utf-8 characters.
-
-* Changed a regular expression in `_TokenizeHTML` that could lead to
- a segmentation fault with PHP 4.3.8 on Linux.
-
-* Fixed some notices that could show up if PHP error reporting
- E_NOTICE flag was set.
-
-
-Copyright and License
----------------------
-
-Copyright (c) 2004-2006 Michel Fortin
-
-All rights reserved.
-
-Based on Markdown
-Copyright (c) 2003-2005 John Gruber
-
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
-* Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
-* Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
-* Neither the name "Markdown" nor the names of its contributors may
- be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-This software is provided by the copyright holders and contributors "as
-is" and any express or implied warranties, including, but not limited
-to, the implied warranties of merchantability and fitness for a
-particular purpose are disclaimed. In no event shall the copyright owner
-or contributors be liable for any direct, indirect, incidental, special,
-exemplary, or consequential damages (including, but not limited to,
-procurement of substitute goods or services; loss of use, data, or
-profits; or business interruption) however caused and on any theory of
-liability, whether in contract, strict liability, or tort (including
-negligence or otherwise) arising in any way out of the use of this
-software, even if advised of the possibility of such damage.
diff --git a/markdown.php b/markdown.php
index 426e7fc..271f6d4 100644
--- a/markdown.php
+++ b/markdown.php
@@ -1,8 +1,8 @@
#
@@ -12,7 +12,8 @@
#
-define( 'MARKDOWN_VERSION', "1.0.1oo" ); # Fri 19 May 2006
+define( 'MARKDOWN_VERSION', "1.0.1oo" ); # Fri 19 May 2006
+define( 'MARKDOWNEXTRA_VERSION', "1.0.1oo" ); # Fri 19 May 2006
#
@@ -38,7 +39,7 @@ define( 'MARKDOWN_WP_COMMENTS', true );
### Standard Function Interface ###
-define( 'MARKDOWN_PARSER_CLASS', 'Markdown_Parser' );
+define( 'MARKDOWN_PARSER_CLASS', 'MarkdownExtra_Parser' );
function Markdown($text) {
#
@@ -59,7 +60,7 @@ function Markdown($text) {
### WordPress Plugin Interface ###
/*
-Plugin Name: Markdown
+Plugin Name: Markdown Extra
Plugin URI: http://www.michelf.com/projects/php-markdown/
Description: Markdown syntax allows you to write using an easy-to-read, easy-to-write plain text format. Based on the original Perl version by John Gruber. More...
Version: 1.0.1oo
@@ -140,15 +141,15 @@ if (isset($wp_version)) {
function identify_modifier_markdown() {
return array(
- 'name' => 'markdown',
- 'type' => 'modifier',
- 'nicename' => 'Markdown',
- 'description' => 'A text-to-HTML conversion tool for web writers',
- 'authors' => 'Michel Fortin and John Gruber',
- 'licence' => 'GPL',
- 'version' => MARKDOWN_VERSION,
- 'help' => 'Markdown syntax allows you to write using an easy-to-read, easy-to-write plain text format. Based on the original Perl version by John Gruber. More...'
- );
+ 'name' => 'markdown',
+ 'type' => 'modifier',
+ 'nicename' => 'PHP Markdown Extra',
+ 'description' => 'A text-to-HTML conversion tool for web writers',
+ 'authors' => 'Michel Fortin and John Gruber',
+ 'licence' => 'GPL',
+ 'version' => MARKDOWNEXTRA_VERSION,
+ 'help' => 'Markdown syntax allows you to write using an easy-to-read, easy-to-write plain text format. Based on the original Perl version by John Gruber. More...',
+ );
}
@@ -1302,16 +1303,974 @@ class Markdown_Parser {
}
+#
+# Markdown Extra Parser Class
+#
+
+class MarkdownExtra_Parser extends Markdown_Parser {
+
+ # Extra hash used during transformation.
+ var $html_hashes = array();
+
+
+ function MarkdownExtra_Parser() {
+ #
+ # Constructor function. Initialize the parser object.
+ #
+ # Add extra escapable characters before parent constructor
+ # initialize the table.
+ $this->escape_chars .= ':|';
+
+ parent::Markdown_Parser();
+ }
+
+
+ function transform($text) {
+ #
+ # Added clear to the new $html_hashes, reordered `hashHTMLBlocks` before
+ # blank line stripping and added extra parameter to `runBlockGamut`.
+ #
+ # Clear the global hashes. If we don't clear these, you get conflicts
+ # from other articles when generating a page which contains more than
+ # one article (e.g. an index page that shows the N most recent
+ # articles):
+ $this->urls = array();
+ $this->titles = array();
+ $this->html_blocks = array();
+ $this->html_hashes = array();
+
+ # Standardize line endings:
+ # DOS to Unix and Mac to Unix
+ $text = str_replace(array("\r\n", "\r"), "\n", $text);
+
+ # Make sure $text ends with a couple of newlines:
+ $text .= "\n\n";
+
+ # Convert all tabs to spaces.
+ $text = $this->detab($text);
+
+ # Turn block-level HTML blocks into hash entries
+ $text = $this->hashHTMLBlocks($text);
+
+ # Strip any lines consisting only of spaces and tabs.
+ # This makes subsequent regexen easier to write, because we can
+ # match consecutive blank lines with /\n+/ instead of something
+ # contorted like /[ \t]*\n+/ .
+ $text = preg_replace('/^[ \t]+$/m', '', $text);
+
+ # Strip link definitions, store in hashes.
+ $text = $this->stripLinkDefinitions($text);
+
+ $text = $this->runBlockGamut($text, false);
+
+ $text = $this->unescapeSpecialChars($text);
+
+ return $text . "\n";
+ }
+
+
+
+ ### HTML Block Parser ###
+
+ # Tags that are always treated as block tags:
+ var $block_tags =
+ 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|form|fieldset|iframe|hr|legend';
+
+ # Tags treated as block tags only if the opening tag is alone on it's line:
+ var $context_block_tags = 'script|noscript|math|ins|del';
+
+ # Tags where markdown="1" default to span mode:
+ var $contain_span_tags = 'p|h[1-6]|li|dd|dt|td|th|legend';
+
+ # Tags which must not have their contents modified, no matter where
+ # they appear:
+ var $clean_tags = 'script|math';
+
+ # Tags that do not need to be closed.
+ var $auto_close_tags = 'hr|img';
+
+
+ function hashHTMLBlocks($text) {
+ #
+ # Hashify HTML Blocks and "clean tags".
+ #
+ # We only want to do this for block-level HTML tags, such as headers,
+ # lists, and tables. That's because we still want to wrap
s around
+ # "paragraphs" that are wrapped in non-block-level tags, such as anchors,
+ # phrase emphasis, and spans. The list of tags we're looking for is
+ # hard-coded.
+ #
+ # This works by calling _HashHTMLBlocks_InMarkdown, which then calls
+ # _HashHTMLBlocks_InHTML when it encounter block tags. When the markdown="1"
+ # attribute is found whitin a tag, _HashHTMLBlocks_InHTML calls back
+ # _HashHTMLBlocks_InMarkdown to handle the Markdown syntax within the tag.
+ # These two functions are calling each other. It's recursive!
+ #
+ #
+ # Call the HTML-in-Markdown hasher.
+ #
+ list($text, ) = $this->_hashHTMLBlocks_inMarkdown($text);
+
+ return $text;
+ }
+ function _hashHTMLBlocks_inMarkdown($text, $indent = 0,
+ $enclosing_tag = '', $span = false)
+ {
+ #
+ # Parse markdown text, calling _HashHTMLBlocks_InHTML for block tags.
+ #
+ # * $indent is the number of space to be ignored when checking for code
+ # blocks. This is important because if we don't take the indent into
+ # account, something like this (which looks right) won't work as expected:
+ #
+ #
+ #
+ # Hello World. <-- Is this a Markdown code block or text?
+ #
<-- Is this a Markdown code block or a real tag?
+ #
+ #
+ # If you don't like this, just don't indent the tag on which
+ # you apply the markdown="1" attribute.
+ #
+ # * If $enclosing_tag is not empty, stops at the first unmatched closing
+ # tag with that name. Nested tags supported.
+ #
+ # * If $span is true, text inside must treated as span. So any double
+ # newline will be replaced by a single newline so that it does not create
+ # paragraphs.
+ #
+ # Returns an array of that form: ( processed text , remaining text )
+ #
+ if ($text === '') return array('', '');
+
+ # Regex to check for the presense of newlines around a block tag.
+ $newline_match_before = '/(?:^\n?|\n\n)*$/';
+ $newline_match_after =
+ '{
+ ^ # Start of text following the tag.
+ (?:[ ]*)? # Optional comment.
+ [ ]*\n # Must be followed by newline.
+ }xs';
+
+ # Regex to match any tag.
+ $block_tag_match =
+ '{
+ ( # $2: Capture hole tag.
+ ? # Any opening or closing tag.
+ (?: # Tag name.
+ '.$this->block_tags.' |
+ '.$this->context_block_tags.' |
+ '.$this->clean_tags.' |
+ (?!\s)'.$enclosing_tag.'
+ )
+ \s* # Whitespace.
+ (?:
+ ".*?" | # Double quotes (can contain `>`)
+ \'.*?\' | # Single quotes (can contain `>`)
+ .+? # Anything but quotes and `>`.
+ )*?
+ > # End of tag.
+ |
+ # HTML Comment
+ |
+ <\? .*? \?> # Processing instruction
+ |
+ # CData Block
+ )
+ }xs';
+
+
+ $depth = 0; # Current depth inside the tag tree.
+ $parsed = ""; # Parsed text that will be returned.
+
+ #
+ # Loop through every tag until we find the closing tag of the parent
+ # or loop until reaching the end of text if no parent tag specified.
+ #
+ do {
+ #
+ # Split the text using the first $tag_match pattern found.
+ # Text before pattern will be first in the array, text after
+ # pattern will be at the end, and between will be any catches made
+ # by the pattern.
+ #
+ $parts = preg_split($block_tag_match, $text, 2,
+ PREG_SPLIT_DELIM_CAPTURE);
+
+ # If in Markdown span mode, replace any multiple newlines that would
+ # trigger a new paragraph.
+ if ($span) {
+ $parts[0] = preg_replace('/\n\n/', "\n", $parts[0]);
+ }
+
+ $parsed .= $parts[0]; # Text before current tag.
+
+ # If end of $text has been reached. Stop loop.
+ if (count($parts) < 3) {
+ $text = "";
+ break;
+ }
+
+ $tag = $parts[1]; # Tag to handle.
+ $text = $parts[2]; # Remaining text after current tag.
+
+ #
+ # Check for: Tag inside code block or span
+ #
+ if (# Find current paragraph
+ preg_match('/(?>^\n?|\n\n)((?>.\n?)+?)$/', $parsed, $matches) &&
+ (
+ # Then match in it either a code block...
+ preg_match('/^ {'.($indent+4).'}.*(?>\n {'.($indent+4).'}.*)*'.
+ '(?!\n)$/', $matches[1], $x) ||
+ # ...or unbalenced code span markers. (the regex matches balenced)
+ !preg_match('/^(?>[^`]+|(`+)(?>[^`]+|(?!\1[^`])`)*?\1(?!`))*$/s',
+ $matches[1])
+ ))
+ {
+ # Tag is in code block or span and may not be a tag at all. So we
+ # simply skip the first char (should be a `<`).
+ $parsed .= $tag{0};
+ $text = substr($tag, 1) . $text; # Put back $tag minus first char.
+ }
+ #
+ # Check for: Opening Block level tag or
+ # Opening Content Block tag (like ins and del)
+ # used as a block tag (tag is alone on it's line).
+ #
+ else if (preg_match("{^<(?:$this->block_tags)\b}", $tag) ||
+ ( preg_match("{^<(?:$this->context_block_tags)\b}", $tag) &&
+ preg_match($newline_match_before, $parsed) &&
+ preg_match($newline_match_after, $text) )
+ )
+ {
+ # Need to parse tag and following text using the HTML parser.
+ list($block_text, $text) =
+ $this->_hashHTMLBlocks_inHTML($tag . $text,
+ "_hashHTMLBlocks_hashBlock", TRUE);
+
+ # Make sure it stays outside of any paragraph by adding newlines.
+ $parsed .= "\n\n$block_text\n\n";
+ }
+ #
+ # Check for: Clean tag (like script, math)
+ # HTML Comments, processing instructions.
+ #
+ else if (preg_match("{^<(?:$this->clean_tags)\b}", $tag) ||
+ $tag{1} == '!' || $tag{1} == '?')
+ {
+ # Need to parse tag and following text using the HTML parser.
+ # (don't check for markdown attribute)
+ list($block_text, $text) =
+ $this->_hashHTMLBlocks_inHTML($tag . $text,
+ "_hashHTMLBlocks_hashClean", FALSE);
+
+ $parsed .= $block_text;
+ }
+ #
+ # Check for: Tag with same name as enclosing tag.
+ #
+ else if ($enclosing_tag !== '' &&
+ # Same name as enclosing tag.
+ preg_match("{^?(?:$enclosing_tag)\b}", $tag))
+ {
+ #
+ # Increase/decrease nested tag count.
+ #
+ if ($tag{1} == '/') $depth--;
+ else if ($tag{strlen($tag)-2} != '/') $depth++;
+
+ if ($depth < 0) {
+ #
+ # Going out of parent element. Clean up and break so we
+ # return to the calling function.
+ #
+ $text = $tag . $text;
+ break;
+ }
+
+ $parsed .= $tag;
+ }
+ else {
+ $parsed .= $tag;
+ }
+ } while ($depth >= 0);
+
+ return array($parsed, $text);
+ }
+ function _hashHTMLBlocks_inHTML($text, $hash_method, $md_attr) {
+ #
+ # Parse HTML, calling _HashHTMLBlocks_InMarkdown for block tags.
+ #
+ # * Calls $hash_method to convert any blocks.
+ # * Stops when the first opening tag closes.
+ # * $md_attr indicate if the use of the `markdown="1"` attribute is allowed.
+ # (it is not inside clean tags)
+ #
+ # Returns an array of that form: ( processed text , remaining text )
+ #
+ if ($text === '') return array('', '');
+
+ # Regex to match `markdown` attribute inside of a tag.
+ $markdown_attr_match = '
+ {
+ \s* # Eat whitespace before the `markdown` attribute
+ markdown
+ \s*=\s*
+ (["\']) # $1: quote delimiter
+ (.*?) # $2: attribute value
+ \1 # matching delimiter
+ }xs';
+
+ # Regex to match any tag.
+ $tag_match = '{
+ ( # $2: Capture hole tag.
+ ? # Any opening or closing tag.
+ [\w:$]+ # Tag name.
+ \s* # Whitespace.
+ (?:
+ ".*?" | # Double quotes (can contain `>`)
+ \'.*?\' | # Single quotes (can contain `>`)
+ .+? # Anything but quotes and `>`.
+ )*?
+ > # End of tag.
+ |
+ # HTML Comment
+ |
+ <\? .*? \?> # Processing instruction
+ |
+ # CData Block
+ )
+ }xs';
+
+ $original_text = $text; # Save original text in case of faliure.
+
+ $depth = 0; # Current depth inside the tag tree.
+ $block_text = ""; # Temporary text holder for current text.
+ $parsed = ""; # Parsed text that will be returned.
+
+ #
+ # Get the name of the starting tag.
+ #
+ if (preg_match("/^<([\w:$]*)\b/", $text, $matches))
+ $base_tag_name = $matches[1];
+
+ #
+ # Loop through every tag until we find the corresponding closing tag.
+ #
+ do {
+ #
+ # Split the text using the first $tag_match pattern found.
+ # Text before pattern will be first in the array, text after
+ # pattern will be at the end, and between will be any catches made
+ # by the pattern.
+ #
+ $parts = preg_split($tag_match, $text, 2, PREG_SPLIT_DELIM_CAPTURE);
+
+ if (count($parts) < 3) {
+ #
+ # End of $text reached with unbalenced tag(s).
+ # In that case, we return original text unchanged and pass the
+ # first character as filtered to prevent an infinite loop in the
+ # parent function.
+ #
+ return array($original_text{0}, substr($original_text, 1));
+ }
+
+ $block_text .= $parts[0]; # Text before current tag.
+ $tag = $parts[1]; # Tag to handle.
+ $text = $parts[2]; # Remaining text after current tag.
+
+ #
+ # Check for: Auto-close tag (like
)
+ # Comments and Processing Instructions.
+ #
+ if (preg_match("{^?(?:$this->auto_close_tags)\b}", $tag) ||
+ $tag{1} == '!' || $tag{1} == '?')
+ {
+ # Just add the tag to the block as if it was text.
+ $block_text .= $tag;
+ }
+ else {
+ #
+ # Increase/decrease nested tag count. Only do so if
+ # the tag's name match base tag's.
+ #
+ if (preg_match("{^?$base_tag_name\b}", $tag)) {
+ if ($tag{1} == '/') $depth--;
+ else if ($tag{strlen($tag)-2} != '/') $depth++;
+ }
+
+ #
+ # Check for `markdown="1"` attribute and handle it.
+ #
+ if ($md_attr &&
+ preg_match($markdown_attr_match, $tag, $attr_matches) &&
+ preg_match('/^(?:1|block|span)$/', $attr_matches[2]))
+ {
+ # Remove `markdown` attribute from opening tag.
+ $tag = preg_replace($markdown_attr_match, '', $tag);
+
+ # Check if text inside this tag must be parsed in span mode.
+ $this->mode = $attr_matches[2];
+ $span_mode = $this->mode == 'span' || $this->mode != 'block' &&
+ preg_match("{^<(?:$this->contain_span_tags)\b}", $tag);
+
+ # Calculate indent before tag.
+ preg_match('/(?:^|\n)( *?)(?! ).*?$/', $block_text, $matches);
+ $indent = strlen($matches[1]);
+
+ # End preceding block with this tag.
+ $block_text .= $tag;
+ $parsed .= $this->$hash_method($block_text, $span_mode);
+
+ # Get enclosing tag name for the ParseMarkdown function.
+ preg_match('/^<([\w:$]*)\b/', $tag, $matches);
+ $tag_name = $matches[1];
+
+ # Parse the content using the HTML-in-Markdown parser.
+ list ($block_text, $text)
+ = $this->_hashHTMLBlocks_inMarkdown($text, $indent,
+ $tag_name, $span_mode);
+
+ # Outdent markdown text.
+ if ($indent > 0) {
+ $block_text = preg_replace("/^[ ]{1,$indent}/m", "",
+ $block_text);
+ }
+
+ # Append tag content to parsed text.
+ if (!$span_mode) $parsed .= "\n\n$block_text\n\n";
+ else $parsed .= "$block_text";
+
+ # Start over a new block.
+ $block_text = "";
+ }
+ else $block_text .= $tag;
+ }
+
+ } while ($depth > 0);
+
+ #
+ # Hash last block text that wasn't processed inside the loop.
+ #
+ $parsed .= $this->$hash_method($block_text);
+
+ return array($parsed, $text);
+ }
+ function _hashHTMLBlocks_HashBlock($text) {
+ $key = md5($text);
+ $this->html_hashes[$key] = $text;
+ $this->html_blocks[$key] = $text;
+ return $key; # String that will replace the tag.
+ }
+ function _hashHTMLBlocks_HashClean($text) {
+ $key = md5($text);
+ $this->html_hashes[$key] = $text;
+ return $key; # String that will replace the clean tag.
+ }
+
+
+ function hashBlock($text) {
+ #
+ # Called whenever a tag must be hashed when a function insert a block-level
+ # tag in $text, it pass through this function and is automaticaly escaped,
+ # which remove the need to call _HashHTMLBlocks at every step.
+ #
+ # Swap back any tag hash found in $text so we do not have to _UnhashTags
+ # multiple times at the end.
+ $text = $this->unhashTags($text);
+
+ # Then hash the block as usual.
+ $text = $this->_hashHTMLBlocks_HashBlock($text);
+
+ return $text;
+ }
+
+
+ function runBlockGamut($text, $hash_html_blocks = true) {
+ #
+ # Redefined to add definition lists and move HTML block hashing at the start.
+ #
+ if ($hash_html_blocks) {
+ # We need to escape raw HTML in Markdown source before doing anything
+ # else. This need to be done for each block, and not only at the
+ # begining in the Markdown function since hashed blocks can be part of
+ # a list item and could have been indented. Indented blocks would have
+ # been seen as a code block in previous pass of _HashHTMLBlocks.
+ $text = $this->hashHTMLBlocks($text);
+ }
+
+ $text = $this->doHeaders($text);
+ $text = $this->doTables($text);
+
+ # Do Horizontal Rules:
+ $text = preg_replace(
+ array('{^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$}mx',
+ '{^[ ]{0,2}([ ]? -[ ]?){3,}[ \t]*$}mx',
+ '{^[ ]{0,2}([ ]? _[ ]?){3,}[ \t]*$}mx'),
+ $this->hashBlock("\n
empty_element_suffix\n"), $text);
+
+ $text = $this->doLists($text);
+ $text = $this->doDefLists($text);
+ $text = $this->doCodeBlocks($text);
+ $text = $this->doBlockQuotes($text);
+ $text = $this->formParagraphs($text);
+
+ return $text;
+ }
+
+
+ function doHeaders($text) {
+ #
+ # Redefined to add id attribute support.
+ #
+ # Setext-style headers:
+ # Header 1 {#header1}
+ # ========
+ #
+ # Header 2 {#header2}
+ # --------
+ #
+ $text = preg_replace_callback(
+ '{ (^.+?) (?:[ ]+\{\#([-_:a-zA-Z0-9]+)\})? [ \t]*\n=+[ \t]*\n+ }mx',
+ array(&$this, '_doHeaders_callback_setext_h1'), $text);
+ $text = preg_replace_callback(
+ '{ (^.+?) (?:[ ]+\{\#([-_:a-zA-Z0-9]+)\})? [ \t]*\n-+[ \t]*\n+ }mx',
+ array(&$this, '_doHeaders_callback_setext_h2'), $text);
+
+ # atx-style headers:
+ # # Header 1 {#header1}
+ # ## Header 2 {#header2}
+ # ## Header 2 with closing hashes ## {#header3}
+ # ...
+ # ###### Header 6 {#header2}
+ #
+ $text = preg_replace_callback('{
+ ^(\#{1,6}) # $1 = string of #\'s
+ [ \t]*
+ (.+?) # $2 = Header text
+ [ \t]*
+ \#* # optional closing #\'s (not counted)
+ (?:[ ]+\{\#([-_:a-zA-Z0-9]+)\}[ ]*)? # id attribute
+ \n+
+ }xm',
+ array(&$this, '_doHeaders_callback_atx'), $text);
+
+ return $text;
+ }
+ function _doHeaders_attr($attr) {
+ if (empty($attr)) return "";
+ return " id=\"$attr\"";
+ }
+ function _doHeaders_callback_setext_h1($matches) {
+ $attr = $this->_doHeaders_attr($id =& $matches[2]);
+ $block = "
".$this->runSpanGamut($matches[1])."
";
+ return $this->hashBlock($block) . "\n\n";
+ }
+ function _doHeaders_callback_setext_h2($matches) {
+ $attr = $this->_doHeaders_attr($id =& $matches[2]);
+ $block = "
".$this->runSpanGamut($matches[1])."
";
+ return $this->hashBlock($block) . "\n\n";
+ }
+ function _doHeaders_callback_atx($matches) {
+ $level = strlen($matches[1]);
+ $attr = $this->_doHeaders_attr($id =& $matches[3]);
+ $block = "
".$this->runSpanGamut($matches[2])."";
+ return $this->hashBlock($block) . "\n\n";
+ }
+
+
+ function doTables($text) {
+ #
+ # Form HTML tables.
+ #
+ $less_than_tab = $this->tab_width - 1;
+ #
+ # Find tables with leading pipe.
+ #
+ # | Header 1 | Header 2
+ # | -------- | --------
+ # | Cell 1 | Cell 2
+ # | Cell 3 | Cell 4
+ #
+ $text = preg_replace_callback('
+ {
+ ^ # Start of a line
+ [ ]{0,'.$less_than_tab.'} # Allowed whitespace.
+ [|] # Optional leading pipe (present)
+ (.+) \n # $1: Header row (at least one pipe)
+
+ [ ]{0,'.$less_than_tab.'} # Allowed whitespace.
+ [|] ([ ]*[-:]+[-| :]*) \n # $2: Header underline
+
+ ( # $3: Cells
+ (?:
+ [ ]* # Allowed whitespace.
+ [|] .* \n # Row content.
+ )*
+ )
+ (?=\n|\Z) # Stop at final double newline.
+ }xm',
+ array(&$this, '_doTable_leadingPipe_callback'), $text);
+
+ #
+ # Find tables without leading pipe.
+ #
+ # Header 1 | Header 2
+ # -------- | --------
+ # Cell 1 | Cell 2
+ # Cell 3 | Cell 4
+ #
+ $text = preg_replace_callback('
+ {
+ ^ # Start of a line
+ [ ]{0,'.$less_than_tab.'} # Allowed whitespace.
+ (\S.*[|].*) \n # $1: Header row (at least one pipe)
+
+ [ ]{0,'.$less_than_tab.'} # Allowed whitespace.
+ ([-:]+[ ]*[|][-| :]*) \n # $2: Header underline
+
+ ( # $3: Cells
+ (?:
+ .* [|] .* \n # Row content
+ )*
+ )
+ (?=\n|\Z) # Stop at final double newline.
+ }xm',
+ array(&$this, '_DoTable_callback'), $text);
+
+ return $text;
+ }
+ function _doTable_leadingPipe_callback($matches) {
+ $head = $matches[1];
+ $underline = $matches[2];
+ $content = $matches[3];
+
+ # Remove leading pipe for each row.
+ $content = preg_replace('/^ *[|]/m', '', $content);
+
+ return $this->_doTable_callback(array($matches[0], $head, $underline, $content));
+ }
+ function _doTable_callback($matches) {
+ $head = $matches[1];
+ $underline = $matches[2];
+ $content = $matches[3];
+
+ # Remove any tailing pipes for each line.
+ $head = preg_replace('/[|] *$/m', '', $head);
+ $underline = preg_replace('/[|] *$/m', '', $underline);
+ $content = preg_replace('/[|] *$/m', '', $content);
+
+ # Reading alignement from header underline.
+ $separators = preg_split('/ *[|] */', $underline);
+ foreach ($separators as $n => $s) {
+ if (preg_match('/^ *-+: *$/', $s)) $attr[$n] = ' align="right"';
+ else if (preg_match('/^ *:-+: *$/', $s))$attr[$n] = ' align="center"';
+ else if (preg_match('/^ *:-+ *$/', $s)) $attr[$n] = ' align="left"';
+ else $attr[$n] = '';
+ }
+
+ # Creating code spans before splitting the row is an easy way to
+ # handle a code span containg pipes.
+ $head = $this->doCodeSpans($head);
+ $headers = preg_split('/ *[|] */', $head);
+ $col_count = count($headers);
+
+ # Write column headers.
+ $text = "
\n";
+ $text .= "\n";
+ $text .= "\n";
+ foreach ($headers as $n => $header)
+ $text .= " | ".$this->runSpanGamut(trim($header))." | \n";
+ $text .= "
\n";
+ $text .= "\n";
+
+ # Split content by row.
+ $rows = explode("\n", trim($content, "\n"));
+
+ $text .= "\n";
+ foreach ($rows as $row) {
+ # Creating code spans before splitting the row is an easy way to
+ # handle a code span containg pipes.
+ $row = $this->doCodeSpans($row);
+
+ # Split row by cell.
+ $row_cells = preg_split('/ *[|] */', $row, $col_count);
+ $row_cells = array_pad($row_cells, $col_count, '');
+
+ $text .= "\n";
+ foreach ($row_cells as $n => $cell)
+ $text .= " | ".$this->runSpanGamut(trim($cell))." | \n";
+ $text .= "
\n";
+ }
+ $text .= "\n";
+ $text .= "
";
+
+ return $this->hashBlock($text) . "\n";
+ }
+
+
+ #
+ # Redefined callbacks that should now return hashed output.
+ #
+ function _doLists_callback_top($matches) {
+ # Calling the nested variant as there is no point in trimming
+ # whitespace when hashing output blocks.
+ return $this->_doLists_callback_nested($matches);
+ }
+ function _doLists_callback_nested($matches) {
+ $block = parent::_doLists_callback_nested($matches);
+ return "\n" . $this->hashBlock(trim($block)) . "\n\n";
+ }
+
+ function _doCodeBlocks_callback($matches) {
+ $result = parent::_doCodeBlocks_callback($matches);
+ return "\n\n" . $this->hashBlock(trim($result)) . "\n\n";
+ }
+
+ function _doBlockQuotes_callback($matches) {
+ $result = parent::_doBlockQuotes_callback($matches);
+ return $this->hashBlock(trim($result)) . "\n\n";
+ }
+
+
+
+ function doDefLists($text) {
+ #
+ # Form HTML definition lists.
+ #
+ $less_than_tab = $this->tab_width - 1;
+
+ # Re-usable pattern to match any entire dl list:
+ $whole_list = '
+ ( # $1 = whole list
+ ( # $2
+ [ ]{0,'.$less_than_tab.'}
+ ((?>.*\S.*\n)+) # $3 = defined term
+ \n?
+ [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition
+ )
+ (?s:.+?)
+ ( # $4
+ \z
+ |
+ \n{2,}
+ (?=\S)
+ (?! # Negative lookahead for another term
+ [ ]{0,'.$less_than_tab.'}
+ (?: \S.*\n )+? # defined term
+ \n?
+ [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition
+ )
+ (?! # Negative lookahead for another definition
+ [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition
+ )
+ )
+ )
+ '; // mx
+
+ $text = preg_replace_callback('{
+ (?:(?<=\n\n)|\A\n?)
+ '.$whole_list.'
+ }mx',
+ array(&$this, '_doDefLists_callback'), $text);
+
+ return $text;
+ }
+ function _doDefLists_callback($matches) {
+ # Re-usable patterns to match list item bullets and number markers:
+ $list = $matches[1];
+
+ # Turn double returns into triple returns, so that we can make a
+ # paragraph for the last item in a list, if necessary:
+ $result = trim($this->processDefListItems($list));
+ $result = "
\n" . $result . "\n
";
+ return $this->hashBlock($result) . "\n\n";
+ }
+
+
+ function processDefListItems($list_str) {
+ #
+ # Process the contents of a single definition list, splitting it
+ # into individual term and definition list items.
+ #
+ $less_than_tab = $this->tab_width - 1;
+
+ # trim trailing blank lines:
+ $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str);
+
+ # Process definition terms.
+ $list_str = preg_replace_callback('{
+ (?:\n\n+|\A\n?) # leading line
+ ( # definition terms = $1
+ [ ]{0,'.$less_than_tab.'} # leading whitespace
+ (?![:][ ]|[ ]) # negative lookahead for a definition
+ # mark (colon) or more whitespace.
+ (?: \S.* \n)+? # actual term (not whitespace).
+ )
+ (?=\n?[ ]{0,3}:[ ]) # lookahead for following line feed
+ # with a definition mark.
+ }xm',
+ array(&$this, '_ProcessDefListItems_callback_dt'), $list_str);
+
+ # Process actual definitions.
+ $list_str = preg_replace_callback('{
+ \n(\n+)? # leading line = $1
+ [ ]{0,'.$less_than_tab.'} # whitespace before colon
+ [:][ ]+ # definition mark (colon)
+ ((?s:.+?)) # definition text = $2
+ (?= \n+ # stop at next definition mark,
+ (?: # next term or end of text
+ [ ]{0,'.$less_than_tab.'} [:][ ] |
+
| \z
+ )
+ )
+ }xm',
+ array(&$this, '_ProcessDefListItems_callback_dd'), $list_str);
+
+ return $list_str;
+ }
+ function _processDefListItems_callback_dt($matches) {
+ $terms = explode("\n", trim($matches[1]));
+ $text = '';
+ foreach ($terms as $term) {
+ $term = $this->runSpanGamut(trim($term));
+ $text .= "\n" . $term . "";
+ }
+ return $text . "\n";
+ }
+ function _processDefListItems_callback_dd($matches) {
+ $leading_line = $matches[1];
+ $def = $matches[2];
+
+ if ($leading_line || preg_match('/\n{2,}/', $def)) {
+ $def = $this->runBlockGamut($this->outdent($def . "\n\n"));
+ $def = "\n". $def ."\n";
+ }
+ else {
+ $def = rtrim($def);
+ $def = $this->runSpanGamut($this->outdent($def));
+ }
+
+ return "\n" . $def . "\n";
+ }
+
+
+ function doItalicsAndBold($text) {
+ #
+ # Redefined to change emphasis by underscore behaviour so that it does not
+ # work in the middle of a word.
+ #
+ #
must go first:
+ $text = preg_replace(array(
+ '{
+ ( (?
+ [^_]+? # Anthing not em markers.
+ |
+ # Balence any regular _ emphasis inside.
+ (?
+ [^*]+? # Anthing not em markers.
+ |
+ # Balence any regular * emphasis inside.
+ \* (?=\S) (?! \*) (.+?) (?<=\S) \*
+ )+?
+ )
+ (?<=\S) \*\* # End mark not preceded by whitespace.
+ }sx',
+ ),
+ '\2', $text);
+ # Then :
+ $text = preg_replace(array(
+ '{ ( (?\2', $text);
+
+ return $text;
+ }
+
+
+ function formParagraphs($text) {
+ #
+ # Params:
+ # $text - string to process with html tags
+ #
+ # Strip leading and trailing lines:
+ $text = preg_replace(array('/\A\n+/', '/\n+\z/'), '', $text);
+
+ $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY);
+
+ #
+ # Wrap
tags and unhashify HTML blocks
+ #
+ foreach ($grafs as $key => $value) {
+ $value = trim($this->runSpanGamut($value));
+
+ # Check if this should be enclosed in a paragraph.
+ # Text equaling to a clean tag hash are not enclosed.
+ # Text starting with a block tag hash are not either.
+ $clean_key = $value;
+ $block_key = substr($value, 0, 32);
+
+ $is_p = (!isset($this->html_blocks[$block_key]) &&
+ !isset($this->html_hashes[$clean_key]));
+
+ if ($is_p) {
+ $value = "
$value
";
+ }
+ $grafs[$key] = $value;
+ }
+
+ # Join grafs in one text, then unhash HTML tags.
+ $text = implode("\n\n", $grafs);
+
+ # Finish by removing any tag hashes still present in $text.
+ $text = $this->unhashTags($text);
+
+ return $text;
+ }
+
+
+ function unhashTags($text) {
+ #
+ # Swap back in all the tags hashed by _HashHTMLBlocks.
+ #
+ return str_replace(array_keys($this->html_hashes),
+ array_values($this->html_hashes), $text);
+ }
+
+}
+
+
/*
-PHP Markdown
-============
+PHP Markdown Extra
+==================
Description
-----------
-This is a PHP translation of the original Markdown formatter written in
-Perl by John Gruber.
+This is a PHP port of the original Markdown formatter written in Perl
+by John Gruber. This special "Extra" version of PHP Markdown features
+further enhancements to the syntax for making additional constructs
+such as tables and definition list.
Markdown is a text-to-HTML filter; it translates an easy-to-read /
easy-to-write structured text format into HTML. Markdown's text format
@@ -1342,22 +2301,24 @@ expected; (3) the output Markdown actually produced.
Version History
---------------
-See the readme file for detailed release notes for this version.
+See Readme file for details.
1.0.1oo (19 May 2006)
-* Converted PHP Markdown to a object-oriented design.
+* Converted PHP Markdown and PHP Markdown Extra to a object-oriented design.
-1.0.1c (9 Dec 2005)
+1.0.1 (9 December 2005)
-1.0.1b (6 Jun 2005)
+1.0 (5 September 2005)
-1.0.1a (15 Apr 2005)
+1.0b4 (1 August 2005)
-1.0.1 (16 Dec 2004)
+1.0b3 (29 July 2005)
-1.0 (21 Aug 2004)
+1.0b2 (26 July 2005)
+
+1.0b1 (25 July 2005)
Author & Contributors
@@ -1377,6 +2338,7 @@ Copyright (c) 2004-2006 Michel Fortin
All rights reserved.
+Based on Markdown
Copyright (c) 2003-2004 John Gruber
All rights reserved.
@@ -1409,4 +2371,4 @@ negligence or otherwise) arising in any way out of the use of this
software, even if advised of the possibility of such damage.
*/
-?>
+?>
\ No newline at end of file