` has been added to the list of block-level elements and is now
+ treated as an HTML block instead of being wrapped within paragraph tags.
+
+* Now only trim trailing newlines from code blocks, instead of trimming
+ all trailing whitespace characters.
+
+* Fixed bug where this:
+
+ [text](http://m.com "title" )
+
+ wasn't working as expected, because the parser wasn't allowing for spaces
+ before the closing paren.
+
+* Filthy hack to support markdown='1' in div tags.
+
+* _DoAutoLinks() now supports the 'dict://' URL scheme.
+
+* PHP- and ASP-style processor instructions are now protected as
+ raw HTML blocks.
+
+ ... ?>
+ <% ... %>
+
+* Fix for escaped backticks still triggering code spans:
+
+ There are two raw backticks here: \` and here: \`, not a code span
+
+
+Extra 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.
+
+
+Extra 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.
+
+
+Extra 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 ##
+
+
+Extra 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.
+
+Extra 1.0b1 (25 Jun 2005)
+
+* First public release of PHP Markdown Extra.
+
+
+Copyright and License
+---------------------
+
+PHP Markdown & Extra
+Copyright (c) 2004-2013 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/serendipity_event_markdown/PHP Markdown Readme.text b/serendipity_event_markdown/PHP Markdown Readme.text
new file mode 100644
index 00000000..87ea7765
--- /dev/null
+++ b/serendipity_event_markdown/PHP Markdown Readme.text
@@ -0,0 +1,796 @@
+PHP Markdown
+============
+
+Version 1.0.2 - 29 Nov 2013
+
+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, as a modifier for
+the Smarty templating engine, or as a replacement for Textile
+formatting in any software that supports 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.
+
+Before PHP 5.3.7, pcre.backtrack_limit defaults to 100 000, which is too small
+in many situations. You might need to set it to higher values. Later PHP
+releases defaults to 1 000 000, which is usually fine.
+
+
+### WordPress ###
+
+PHP Markdown works with [WordPress][wp], version 1.2 or later.
+
+ [wp]: http://wordpress.org/
+
+1. To use PHP Markdown with WordPress, place the "markdown.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 as Markdown. Your entries will now be formatted by
+ PHP Markdown.
+
+3. To post Markdown content, you'll first have to disable the
+ "visual" editor in the User section of WordPress.
+
+You can configure PHP Markdown to not apply to the comments on your
+WordPress weblog. See the "Configuration" section below.
+
+It is not possible at this time to apply a different set of
+filters to different entries. All your entries will be formatted by
+PHP Markdown. This is a limitation of WordPress. If your old entries
+are written in HTML (as opposed to another formatting syntax, like
+Textile), they'll probably stay fine after installing Markdown.
+
+
+### Replacing Textile in TextPattern ###
+
+[TextPattern][tp] use [Textile][tx] to format your text. You can
+replace Textile by Markdown in TextPattern without having to change
+any code by using the *Textile Compatibility Mode*. This may work
+with other software that expect Textile too.
+
+ [tx]: http://www.textism.com/tools/textile/
+ [tp]: http://www.textpattern.com/
+
+1. Rename the "markdown.php" file to "classTextile.php". This will
+ make PHP Markdown behave as if it was the actual Textile parser.
+
+2. Replace the "classTextile.php" file TextPattern installed in your
+ web directory. It can be found in the "lib" directory:
+
+ (site home)/textpattern/lib/
+
+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://michelf.ca/projects/php-smartypants/
+
+
+### Updating Markdown in Other Programs ###
+
+Many web applications now ship with PHP Markdown, or have plugins to
+perform the conversion to HTML. You can update PHP Markdown in many of
+these programs by swapping the old "markdown.php" file for the new one.
+
+Here is a short non-exhaustive list of some programs and where they
+hide the "markdown.php" file.
+
+| Program | Path to Markdown
+| ------- | ----------------
+| [Pivot][] | `(site home)/pivot/includes/markdown/markdown.php`
+
+If you're unsure if you can do this with your application, ask the
+developer, or wait for the developer to update his application or
+plugin with the new version of PHP Markdown.
+
+ [Pivot]: http://pivotlog.net/
+
+
+### 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 "MARKDOWN_EMPTY_ELEMENT_SUFFIX"
+definition 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
+`MARKDOWN_WP_POSTS` or `MARKDOWN_WP_COMMENTS` definitions 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.
+
+If you have a problem where Markdown gives you an empty result, first check
+that the backtrack limit is not too low by running `php --info | grep pcre`.
+See Installation and Requirement above for details.
+
+
+Version History
+---------------
+
+1.0.2 (29 Nov 2013)
+
+* Added support for the `tel:` URL scheme in automatic links.
+
+
+
+ It gets converted to this (note the `tel:` prefix becomes invisible):
+
+ +1-111-111-1111
+
+
+1.0.1q (11 Apr 2013):
+
+* Fixed a small mistake that could cause the parser to retain an invalid
+ state related to parsing links across multiple runs. This was never
+ observed (that I know of), but it's still worth fixing.
+
+
+1.0.1p (13 Jan 2013):
+
+* Fixed an issue where some XML-style empty tags (such as `
`) were not
+ recognized correctly as such when inserted into Markdown-formatted text.
+
+* The following HTML 5 elements are treated as block elements when at the
+ root of an HTML block: `article`, `section`, `nav`, `aside`, `hgroup`,
+ `header`, `footer`, and `figure`. `svg` too.
+
+
+1.0.1o (8 Jan 2012):
+
+* Silenced a new warning introduced around PHP 5.3 complaining about
+ POSIX characters classes not being implemented. PHP Markdown does not
+ use POSIX character classes, but it nevertheless trigged that warning.
+
+
+1.0.1n (10 Oct 2009):
+
+* Enabled reference-style shortcut links. Now you can write reference-style
+ links with less brakets:
+
+ This is [my website].
+
+ [my website]: http://example.com/
+
+ This was added in the 1.0.2 betas, but commented out in the 1.0.1 branch,
+ waiting for the feature to be officialized. [But half of the other Markdown
+ implementations are supporting this syntax][half], so it makes sense for
+ compatibility's sake to allow it in PHP Markdown too.
+
+ [half]: http://babelmark.bobtfish.net/?markdown=This+is+%5Bmy+website%5D.%0D%0A%09%09%0D%0A%5Bmy+website%5D%3A+http%3A%2F%2Fexample.com%2F%0D%0A&src=1&dest=2
+
+* Now accepting many valid email addresses in autolinks that were
+ previously rejected, such as:
+
+
+
+ <"abc@def"@example.com>
+ <"Fred Bloggs"@example.com>
+
+
+* Now accepting spaces in URLs for inline and reference-style links. Such
+ URLs need to be surrounded by angle brakets. For instance:
+
+ [link text]( "optional title")
+
+ [link text][ref]
+ [ref]: "optional title"
+
+ There is still a quirk which may prevent this from working correctly with
+ relative URLs in inline-style links however.
+
+* Fix for adjacent list of different kind where the second list could
+ end as a sublist of the first when not separated by an empty line.
+
+* Fixed a bug where inline-style links wouldn't be recognized when the link
+ definition contains a line break between the url and the title.
+
+* Fixed a bug where tags where the name contains an underscore aren't parsed
+ correctly.
+
+* Fixed some corner-cases mixing underscore-ephasis and asterisk-emphasis.
+
+
+1.0.1m (21 Jun 2008):
+
+* Lists can now have empty items.
+
+* Rewrote the emphasis and strong emphasis parser to fix some issues
+ with odly placed and overlong markers.
+
+
+1.0.1l (11 May 2008):
+
+* Now removing the UTF-8 BOM at the start of a document, if present.
+
+* Now accepting capitalized URI schemes (such as HTTP:) in automatic
+ links, such as ``.
+
+* Fixed a problem where `
` was seen as a horizontal
+ rule instead of an automatic link.
+
+* Fixed an issue where some characters in Markdown-generated HTML
+ attributes weren't properly escaped with entities.
+
+* Fix for code blocks as first element of a list item. Previously,
+ this didn't create any code block for item 2:
+
+ * Item 1 (regular paragraph)
+
+ * Item 2 (code block)
+
+* A code block starting on the second line of a document wasn't seen
+ as a code block. This has been fixed.
+
+* Added programatically-settable parser properties `predef_urls` and
+ `predef_titles` for predefined URLs and titles for reference-style
+ links. To use this, your PHP code must call the parser this way:
+
+ $parser = new Markdwon_Parser;
+ $parser->predef_urls = array('linkref' => 'http://example.com');
+ $html = $parser->transform($text);
+
+ You can then use the URL as a normal link reference:
+
+ [my link][linkref]
+ [my link][linkRef]
+
+ Reference names in the parser properties *must* be lowercase.
+ Reference names in the Markdown source may have any case.
+
+* Added `setup` and `teardown` methods which can be used by subclassers
+ as hook points to arrange the state of some parser variables before and
+ after parsing.
+
+
+1.0.1k (26 Sep 2007):
+
+* Fixed a problem introduced in 1.0.1i where three or more identical
+ uppercase letters, as well as a few other symbols, would trigger
+ a horizontal line.
+
+
+1.0.1j (4 Sep 2007):
+
+* Fixed a problem introduced in 1.0.1i where the closing `code` and
+ `pre` tags at the end of a code block were appearing in the wrong
+ order.
+
+* Overriding configuration settings by defining constants from an
+ external before markdown.php is included is now possible without
+ producing a PHP warning.
+
+
+1.0.1i (31 Aug 2007):
+
+* Fixed a problem where an escaped backslash before a code span
+ would prevent the code span from being created. This should now
+ work as expected:
+
+ Litteral backslash: \\`code span`
+
+* Overall speed improvements, especially with long documents.
+
+
+1.0.1h (3 Aug 2007):
+
+* Added two properties (`no_markup` and `no_entities`) to the parser
+ allowing HTML tags and entities to be disabled.
+
+* Fix for a problem introduced in 1.0.1g where posting comments in
+ WordPress would trigger PHP warnings and cause some markup to be
+ incorrectly filtered by the kses filter in WordPress.
+
+
+1.0.1g (3 Jul 2007):
+
+* Fix for PHP 5 compiled without the mbstring module. Previous fix to
+ calculate the length of UTF-8 strings in `detab` when `mb_strlen` is
+ not available was only working with PHP 4.
+
+* Fixed a problem with WordPress 2.x where full-content posts in RSS feeds
+ were not processed correctly by Markdown.
+
+* Now supports URLs containing literal parentheses for inline links
+ and images, such as:
+
+ [WIMP](http://en.wikipedia.org/wiki/WIMP_(computing))
+
+ Such parentheses may be arbitrarily nested, but must be
+ balanced. Unbalenced parentheses are allowed however when the URL
+ when escaped or when the URL is enclosed in angle brakets `<>`.
+
+* Fixed a performance problem where the regular expression for strong
+ emphasis introduced in version 1.0.1d could sometime be long to process,
+ give slightly wrong results, and in some circumstances could remove
+ entirely the content for a whole paragraph.
+
+* Some change in version 1.0.1d made possible the incorrect nesting of
+ anchors within each other. This is now fixed.
+
+* Fixed a rare issue where certain MD5 hashes in the content could
+ be changed to their corresponding text. For instance, this:
+
+ The MD5 value for "+" is "26b17225b626fb9238849fd60eabdf60".
+
+ was incorrectly changed to this in previous versions of PHP Markdown:
+
+ The MD5 value for "+" is "+".
+
+* Now convert escaped characters to their numeric character
+ references equivalent.
+
+ This fix an integration issue with SmartyPants and backslash escapes.
+ Since Markdown and SmartyPants have some escapable characters in common,
+ it was sometime necessary to escape them twice. Previously, two
+ backslashes were sometime required to prevent Markdown from "eating" the
+ backslash before SmartyPants sees it:
+
+ Here are two hyphens: \\--
+
+ Now, only one backslash will do:
+
+ Here are two hyphens: \--
+
+
+1.0.1f (7 Feb 2007):
+
+* Fixed an issue with WordPress where manually-entered excerpts, but
+ not the auto-generated ones, would contain nested paragraphs.
+
+* Fixed an issue introduced in 1.0.1d where headers and blockquotes
+ preceded too closely by a paragraph (not separated by a blank line)
+ where incorrectly put inside the paragraph.
+
+* Fixed an issue introduced in 1.0.1d in the tokenizeHTML method where
+ two consecutive code spans would be merged into one when together they
+ form a valid tag in a multiline paragraph.
+
+* Fixed an long-prevailing issue where blank lines in code blocks would
+ be doubled when the code block is in a list item.
+
+ This was due to the list processing functions relying on artificially
+ doubled blank lines to correctly determine when list items should
+ contain block-level content. The list item processing model was thus
+ changed to avoid the need for double blank lines.
+
+* Fixed an issue with `<% asp-style %>` instructions used as inline
+ content where the opening `<` was encoded as `<`.
+
+* Fixed a parse error occuring when PHP is configured to accept
+ ASP-style delimiters as boundaries for PHP scripts.
+
+* Fixed a bug introduced in 1.0.1d where underscores in automatic links
+ got swapped with emphasis tags.
+
+
+1.0.1e (28 Dec 2006)
+
+* Added support for internationalized domain names for email addresses in
+ automatic link. Improved the speed at which email addresses are converted
+ to entities. Thanks to Milian Wolff for his optimisations.
+
+* Made deterministic the conversion to entities of email addresses in
+ automatic links. This means that a given email address will always be
+ encoded the same way.
+
+* PHP Markdown will now use its own function to calculate the length of an
+ UTF-8 string in `detab` when `mb_strlen` is not available instead of
+ giving a fatal error.
+
+
+1.0.1d (1 Dec 2006)
+
+* Fixed a bug where inline images always had an empty title attribute. The
+ title attribute is now present only when explicitly defined.
+
+* Link references definitions can now have an empty title, previously if the
+ title was defined but left empty the link definition was ignored. This can
+ be useful if you want an empty title attribute in images to hide the
+ tooltip in Internet Explorer.
+
+* Made `detab` aware of UTF-8 characters. UTF-8 multi-byte sequences are now
+ correctly mapped to one character instead of the number of bytes.
+
+* Fixed a small bug with WordPress where WordPress' default filter `wpautop`
+ was not properly deactivated on comment text, resulting in hard line breaks
+ where Markdown do not prescribes them.
+
+* Added a `TextileRestrited` method to the textile compatibility mode. There
+ is no restriction however, as Markdown does not have a restricted mode at
+ this point. This should make PHP Markdown work again in the latest
+ versions of TextPattern.
+
+* Converted PHP Markdown to a object-oriented design.
+
+* Changed span and block gamut methods so that they loop over a
+ customizable list of methods. This makes subclassing the parser a more
+ interesting option for creating syntax extensions.
+
+* Also added a "document" gamut loop which can be used to hook document-level
+ methods (like for striping link definitions).
+
+* Changed all methods which were inserting HTML code so that they now return
+ a hashed representation of the code. New methods `hashSpan` and `hashBlock`
+ are used to hash respectivly span- and block-level generated content. This
+ has a couple of significant effects:
+
+ 1. It prevents invalid nesting of Markdown-generated elements which
+ could occur occuring with constructs like `*something [link*][1]`.
+ 2. It prevents problems occuring with deeply nested lists on which
+ paragraphs were ill-formed.
+ 3. It removes the need to call `hashHTMLBlocks` twice during the the
+ block gamut.
+
+ Hashes are turned back to HTML prior output.
+
+* Made the block-level HTML parser smarter using a specially-crafted regular
+ expression capable of handling nested tags.
+
+* Solved backtick issues in tag attributes by rewriting the HTML tokenizer to
+ be aware of code spans. All these lines should work correctly now:
+
+ bar
+ bar
+ ``
+
+* Changed the parsing of HTML comments to match simply from ``
+ instead using of the more complicated SGML-style rule with paired `--`.
+ This is how most browsers parse comments and how XML defines them too.
+
+* `` has been added to the list of block-level elements and is now
+ treated as an HTML block instead of being wrapped within paragraph tags.
+
+* Now only trim trailing newlines from code blocks, instead of trimming
+ all trailing whitespace characters.
+
+* Fixed bug where this:
+
+ [text](http://m.com "title" )
+
+ wasn't working as expected, because the parser wasn't allowing for spaces
+ before the closing paren.
+
+* Filthy hack to support markdown='1' in div tags.
+
+* _DoAutoLinks() now supports the 'dict://' URL scheme.
+
+* PHP- and ASP-style processor instructions are now protected as
+ raw HTML blocks.
+
+ ... ?>
+ <% ... %>
+
+* Fix for escaped backticks still triggering code spans:
+
+ There are two raw backticks here: \` and here: \`, not a code span
+
+
+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:
+
+
+
+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.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
+---------------------
+
+PHP Markdown
+Copyright (c) 2004-2013 Michel Fortin
+
+All rights reserved.
+
+Based on Markdown
+Copyright (c) 2003-2006 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/serendipity_event_markdown/UTF-8/documentation_cs.html b/serendipity_event_markdown/UTF-8/documentation_cs.html
index 48959d4d..48bcec66 100644
--- a/serendipity_event_markdown/UTF-8/documentation_cs.html
+++ b/serendipity_event_markdown/UTF-8/documentation_cs.html
@@ -19,7 +19,7 @@
Popis pluginu
- Převádí značky Markdown Extra na HTML v textu příspěvku (Viz.: http://www.michelf.com/projects/php-markdown/extra/)
+ Převádí značky Markdown Extra na HTML v textu příspěvku (Viz.: http://michelf.ca/projects/php-markdown/extra/)
Historie verzí (ChangeLog)
diff --git a/serendipity_event_markdown/UTF-8/documentation_cz.html b/serendipity_event_markdown/UTF-8/documentation_cz.html
index 48959d4d..48bcec66 100644
--- a/serendipity_event_markdown/UTF-8/documentation_cz.html
+++ b/serendipity_event_markdown/UTF-8/documentation_cz.html
@@ -19,7 +19,7 @@
Popis pluginu
- Převádí značky Markdown Extra na HTML v textu příspěvku (Viz.: http://www.michelf.com/projects/php-markdown/extra/)
+ Převádí značky Markdown Extra na HTML v textu příspěvku (Viz.: http://michelf.ca/projects/php-markdown/extra/)
Historie verzí (ChangeLog)
diff --git a/serendipity_event_markdown/UTF-8/lang_bg.inc.php b/serendipity_event_markdown/UTF-8/lang_bg.inc.php
index 4b55e3ab..d42f6996 100644
--- a/serendipity_event_markdown/UTF-8/lang_bg.inc.php
+++ b/serendipity_event_markdown/UTF-8/lang_bg.inc.php
@@ -6,8 +6,9 @@
* EN-Revision: 1.12
*/
- @define('PLUGIN_EVENT_MARKDOWN_NAME', 'Форматиране на текст: Markdown');
- @define('PLUGIN_EVENT_MARKDOWN_DESC', 'Прилага текстово форматиране Markdown');
- @define('PLUGIN_EVENT_MARKDOWN_EXTRA_NAME', 'Форматиране на текст: Markdown Extra');
- @define('PLUGIN_EVENT_MARKDOWN_EXTRA_DESC', 'Прилагане на форматиране Markdown Extra върху текста на статиите (Виж: http://www.michelf.com/projects/php-markdown/extra/)');
- @define('PLUGIN_EVENT_MARKDOWN_TRANSFORM', 'Форматиране Markdown е позволено');
+@define('PLUGIN_EVENT_MARKDOWN_NAME', 'Форматиране на текст: Markdown');
+@define('PLUGIN_EVENT_MARKDOWN_DESC', 'Прилага текстово форматиране Markdown');
+@define('PLUGIN_EVENT_MARKDOWN_EXTRA_NAME', 'Форматиране на текст: Markdown Extra');
+@define('PLUGIN_EVENT_MARKDOWN_EXTRA_DESC', 'Прилагане на форматиране Markdown Extra върху текста на статиите (Виж: http://michelf.ca/projects/php-markdown/extra/)');
+@define('PLUGIN_EVENT_MARKDOWN_TRANSFORM', 'Форматиране Markdown е позволено');
+
diff --git a/serendipity_event_markdown/UTF-8/lang_cs.inc.php b/serendipity_event_markdown/UTF-8/lang_cs.inc.php
index ea88bbe2..ef12ab5c 100644
--- a/serendipity_event_markdown/UTF-8/lang_cs.inc.php
+++ b/serendipity_event_markdown/UTF-8/lang_cs.inc.php
@@ -12,5 +12,6 @@
@define('PLUGIN_EVENT_MARKDOWN_NAME', 'Markup: Markdown');
@define('PLUGIN_EVENT_MARKDOWN_DESC', 'Převádí značky Markdown na HTML v textu příspěvku');
@define('PLUGIN_EVENT_MARKDOWN_EXTRA_NAME', 'Markup: Markdown Extra');
-@define('PLUGIN_EVENT_MARKDOWN_EXTRA_DESC', 'Převádí značky Markdown Extra na HTML v textu příspěvku (Viz.: http://www.michelf.com/projects/php-markdown/extra/)');
-@define('PLUGIN_EVENT_MARKDOWN_TRANSFORM', 'Formátování Markdown je povoleno');
\ No newline at end of file
+@define('PLUGIN_EVENT_MARKDOWN_EXTRA_DESC', 'Převádí značky Markdown Extra na HTML v textu příspěvku (Viz.: http://michelf.ca/projects/php-markdown/extra/)');
+@define('PLUGIN_EVENT_MARKDOWN_TRANSFORM', 'Formátování Markdown je povoleno');
+
diff --git a/serendipity_event_markdown/UTF-8/lang_cz.inc.php b/serendipity_event_markdown/UTF-8/lang_cz.inc.php
index ea88bbe2..ef12ab5c 100644
--- a/serendipity_event_markdown/UTF-8/lang_cz.inc.php
+++ b/serendipity_event_markdown/UTF-8/lang_cz.inc.php
@@ -12,5 +12,6 @@
@define('PLUGIN_EVENT_MARKDOWN_NAME', 'Markup: Markdown');
@define('PLUGIN_EVENT_MARKDOWN_DESC', 'Převádí značky Markdown na HTML v textu příspěvku');
@define('PLUGIN_EVENT_MARKDOWN_EXTRA_NAME', 'Markup: Markdown Extra');
-@define('PLUGIN_EVENT_MARKDOWN_EXTRA_DESC', 'Převádí značky Markdown Extra na HTML v textu příspěvku (Viz.: http://www.michelf.com/projects/php-markdown/extra/)');
-@define('PLUGIN_EVENT_MARKDOWN_TRANSFORM', 'Formátování Markdown je povoleno');
\ No newline at end of file
+@define('PLUGIN_EVENT_MARKDOWN_EXTRA_DESC', 'Převádí značky Markdown Extra na HTML v textu příspěvku (Viz.: http://michelf.ca/projects/php-markdown/extra/)');
+@define('PLUGIN_EVENT_MARKDOWN_TRANSFORM', 'Formátování Markdown je povoleno');
+
diff --git a/serendipity_event_markdown/UTF-8/lang_de.inc.php b/serendipity_event_markdown/UTF-8/lang_de.inc.php
index fa3f2e48..6b789671 100644
--- a/serendipity_event_markdown/UTF-8/lang_de.inc.php
+++ b/serendipity_event_markdown/UTF-8/lang_de.inc.php
@@ -1,5 +1,6 @@
Markdown-Formatierung erlaubt');
+@define('PLUGIN_EVENT_MARKDOWN_NAME', 'Textformatierung: Markdown');
+@define('PLUGIN_EVENT_MARKDOWN_DESC', 'Markdown Textformatierung durchführen');
+@define('PLUGIN_EVENT_MARKDOWN_TRANSFORM', 'Markdown-Formatierung erlaubt');
+
diff --git a/serendipity_event_markdown/UTF-8/lang_ja.inc.php b/serendipity_event_markdown/UTF-8/lang_ja.inc.php
index 1203ea6d..c6dfe025 100644
--- a/serendipity_event_markdown/UTF-8/lang_ja.inc.php
+++ b/serendipity_event_markdown/UTF-8/lang_ja.inc.php
@@ -10,4 +10,3 @@
@define('PLUGIN_EVENT_MARKDOWN_DESC', 'エントリテキストに Markdown マークアップを適用します。');
@define('PLUGIN_EVENT_MARKDOWN_TRANSFORM', 'Markdown 書式を許可します。');
-?>
diff --git a/serendipity_event_markdown/documentation_cs.html b/serendipity_event_markdown/documentation_cs.html
index 5d72d5ff..56bd7902 100644
--- a/serendipity_event_markdown/documentation_cs.html
+++ b/serendipity_event_markdown/documentation_cs.html
@@ -19,7 +19,7 @@
Popis pluginu
- Pevd znaky Markdown Extra na HTML v textu pspvku (Viz.: http://www.michelf.com/projects/php-markdown/extra/)
+ Pevd znaky Markdown Extra na HTML v textu pspvku (Viz.: http://michelf.ca/projects/php-markdown/extra/)
Historie verz (ChangeLog)
diff --git a/serendipity_event_markdown/documentation_cz.html b/serendipity_event_markdown/documentation_cz.html
index 708568f1..dd99394e 100644
--- a/serendipity_event_markdown/documentation_cz.html
+++ b/serendipity_event_markdown/documentation_cz.html
@@ -19,7 +19,7 @@
Popis pluginu
- Pevd znaky Markdown Extra na HTML v textu pspvku (Viz.: http://www.michelf.com/projects/php-markdown/extra/)
+ Pevd znaky Markdown Extra na HTML v textu pspvku (Viz.: http://michelf.ca/projects/php-markdown/extra/)
Historie verz (ChangeLog)
diff --git a/serendipity_event_markdown/lang_bg.inc.php b/serendipity_event_markdown/lang_bg.inc.php
index ab4e69f9..89c60816 100644
--- a/serendipity_event_markdown/lang_bg.inc.php
+++ b/serendipity_event_markdown/lang_bg.inc.php
@@ -6,8 +6,9 @@
* EN-Revision: 1.12
*/
- @define('PLUGIN_EVENT_MARKDOWN_NAME', ' : Markdown');
- @define('PLUGIN_EVENT_MARKDOWN_DESC', ' Markdown');
- @define('PLUGIN_EVENT_MARKDOWN_EXTRA_NAME', ' : Markdown Extra');
- @define('PLUGIN_EVENT_MARKDOWN_EXTRA_DESC', ' Markdown Extra (: http://www.michelf.com/projects/php-markdown/extra/)');
- @define('PLUGIN_EVENT_MARKDOWN_TRANSFORM', ' Markdown ');
+@define('PLUGIN_EVENT_MARKDOWN_NAME', ' : Markdown');
+@define('PLUGIN_EVENT_MARKDOWN_DESC', ' Markdown');
+@define('PLUGIN_EVENT_MARKDOWN_EXTRA_NAME', ' : Markdown Extra');
+@define('PLUGIN_EVENT_MARKDOWN_EXTRA_DESC', ' Markdown Extra (: http://michelf.ca/projects/php-markdown/extra/)');
+@define('PLUGIN_EVENT_MARKDOWN_TRANSFORM', ' Markdown ');
+
diff --git a/serendipity_event_markdown/lang_cs.inc.php b/serendipity_event_markdown/lang_cs.inc.php
index 233b0e36..304eeee8 100644
--- a/serendipity_event_markdown/lang_cs.inc.php
+++ b/serendipity_event_markdown/lang_cs.inc.php
@@ -12,5 +12,6 @@
@define('PLUGIN_EVENT_MARKDOWN_NAME', 'Markup: Markdown');
@define('PLUGIN_EVENT_MARKDOWN_DESC', 'Pevd znaky Markdown na HTML v textu pspvku');
@define('PLUGIN_EVENT_MARKDOWN_EXTRA_NAME', 'Markup: Markdown Extra');
-@define('PLUGIN_EVENT_MARKDOWN_EXTRA_DESC', 'Pevd znaky Markdown Extra na HTML v textu pspvku (Viz.: http://www.michelf.com/projects/php-markdown/extra/)');
-@define('PLUGIN_EVENT_MARKDOWN_TRANSFORM', 'Formtovn Markdown je povoleno');
\ No newline at end of file
+@define('PLUGIN_EVENT_MARKDOWN_EXTRA_DESC', 'Pevd znaky Markdown Extra na HTML v textu pspvku (Viz.: http://michelf.ca/projects/php-markdown/extra/)');
+@define('PLUGIN_EVENT_MARKDOWN_TRANSFORM', 'Formtovn Markdown je povoleno');
+
diff --git a/serendipity_event_markdown/lang_cz.inc.php b/serendipity_event_markdown/lang_cz.inc.php
index 233b0e36..304eeee8 100644
--- a/serendipity_event_markdown/lang_cz.inc.php
+++ b/serendipity_event_markdown/lang_cz.inc.php
@@ -12,5 +12,6 @@
@define('PLUGIN_EVENT_MARKDOWN_NAME', 'Markup: Markdown');
@define('PLUGIN_EVENT_MARKDOWN_DESC', 'Pevd znaky Markdown na HTML v textu pspvku');
@define('PLUGIN_EVENT_MARKDOWN_EXTRA_NAME', 'Markup: Markdown Extra');
-@define('PLUGIN_EVENT_MARKDOWN_EXTRA_DESC', 'Pevd znaky Markdown Extra na HTML v textu pspvku (Viz.: http://www.michelf.com/projects/php-markdown/extra/)');
-@define('PLUGIN_EVENT_MARKDOWN_TRANSFORM', 'Formtovn Markdown je povoleno');
\ No newline at end of file
+@define('PLUGIN_EVENT_MARKDOWN_EXTRA_DESC', 'Pevd znaky Markdown Extra na HTML v textu pspvku (Viz.: http://michelf.ca/projects/php-markdown/extra/)');
+@define('PLUGIN_EVENT_MARKDOWN_TRANSFORM', 'Formtovn Markdown je povoleno');
+
diff --git a/serendipity_event_markdown/lang_de.inc.php b/serendipity_event_markdown/lang_de.inc.php
index 4de85a46..33e84316 100644
--- a/serendipity_event_markdown/lang_de.inc.php
+++ b/serendipity_event_markdown/lang_de.inc.php
@@ -1,5 +1,6 @@
Markdown-Formatierung erlaubt');
+@define('PLUGIN_EVENT_MARKDOWN_NAME', 'Textformatierung: Markdown');
+@define('PLUGIN_EVENT_MARKDOWN_DESC', 'Markdown Textformatierung durchfhren');
+@define('PLUGIN_EVENT_MARKDOWN_TRANSFORM', 'Markdown-Formatierung erlaubt');
+
diff --git a/serendipity_event_markdown/lang_en.inc.php b/serendipity_event_markdown/lang_en.inc.php
index 320cc481..fbebd94c 100644
--- a/serendipity_event_markdown/lang_en.inc.php
+++ b/serendipity_event_markdown/lang_en.inc.php
@@ -9,7 +9,14 @@
@define('PLUGIN_EVENT_MARKDOWN_NAME', 'Markup: Markdown');
@define('PLUGIN_EVENT_MARKDOWN_DESC', 'Apply Markdown markup to entry text');
@define('PLUGIN_EVENT_MARKDOWN_EXTRA_NAME', 'Markup: Markdown Extra');
-@define('PLUGIN_EVENT_MARKDOWN_EXTRA_DESC', 'Apply Markdown Extra markup to entry text (See: http://www.michelf.com/projects/php-markdown/extra/)');
+@define('PLUGIN_EVENT_MARKDOWN_EXTRA_DESC', 'Apply Markdown Extra markup to entry text (See: http://michelf.ca/projects/php-markdown/extra/)');
@define('PLUGIN_EVENT_MARKDOWN_TRANSFORM', 'Markdown format allowed');
-?>
+@define('PLUGIN_EVENT_MARKDOWN_VERSION', 'Markdown Version');
+@define('PLUGIN_EVENT_MARKDOWN_VERSION_BLABLAH', 'Which version of Markdown do you want to use? (http://michelf.ca/projects/php-markdown/ and http://michelf.ca/blog/2013/php-markdown-lib/)');
+
+@define('PLUGIN_EVENT_MARKDOWN_SMARTYPANTS_NAME', 'Markup: SmartyPants');
+@define('PLUGIN_EVENT_MARKDOWN_SMARTYPANTS_DESC', 'Apply SmartyPants (+Typographer) markup to entry text (See: http://michelf.ca/projects/php-smartypants/). Use with lib only!');
+@define('PLUGIN_EVENT_MARKDOWN_SMARTYPANTS_EXTENDED', '+Typographer');
+@define('PLUGIN_EVENT_MARKDOWN_SMARTYPANTS_NEVER', 'Disabled');
+
diff --git a/serendipity_event_markdown/lang_ja.inc.php b/serendipity_event_markdown/lang_ja.inc.php
index 1203ea6d..c6dfe025 100644
--- a/serendipity_event_markdown/lang_ja.inc.php
+++ b/serendipity_event_markdown/lang_ja.inc.php
@@ -10,4 +10,3 @@
@define('PLUGIN_EVENT_MARKDOWN_DESC', 'エントリテキストに Markdown マークアップを適用します。');
@define('PLUGIN_EVENT_MARKDOWN_TRANSFORM', 'Markdown 書式を許可します。');
-?>
diff --git a/serendipity_event_markdown/lib/License.md b/serendipity_event_markdown/lib/License.md
new file mode 100644
index 00000000..027becbd
--- /dev/null
+++ b/serendipity_event_markdown/lib/License.md
@@ -0,0 +1,36 @@
+PHP Markdown Lib
+Copyright (c) 2004-2013 Michel Fortin
+
+All rights reserved.
+
+Based on Markdown
+Copyright (c) 2003-2006 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/serendipity_event_markdown/lib/Michelf/Markdown.inc.php b/serendipity_event_markdown/lib/Michelf/Markdown.inc.php
new file mode 100644
index 00000000..8c281094
--- /dev/null
+++ b/serendipity_event_markdown/lib/Michelf/Markdown.inc.php
@@ -0,0 +1,10 @@
+
+#
+# Original Markdown
+# Copyright (c) 2004-2006 John Gruber
+#
+#
+namespace Michelf;
+
+
+#
+# Markdown Parser Class
+#
+
+class Markdown implements MarkdownInterface {
+
+ ### Version ###
+
+ const MARKDOWNLIB_VERSION = "1.4.0";
+
+ ### Simple Function Interface ###
+
+ public static function defaultTransform($text) {
+ #
+ # Initialize the parser and return the result of its transform method.
+ # This will work fine for derived classes too.
+ #
+ # Take parser class on which this function was called.
+ $parser_class = \get_called_class();
+
+ # try to take parser from the static parser list
+ static $parser_list;
+ $parser =& $parser_list[$parser_class];
+
+ # create the parser it not already set
+ if (!$parser)
+ $parser = new $parser_class;
+
+ # Transform text using parser.
+ return $parser->transform($text);
+ }
+
+ ### Configuration Variables ###
+
+ # Change to ">" for HTML output.
+ public $empty_element_suffix = " />";
+ public $tab_width = 4;
+
+ # Change to `true` to disallow markup or entities.
+ public $no_markup = false;
+ public $no_entities = false;
+
+ # Predefined urls and titles for reference links and images.
+ public $predef_urls = array();
+ public $predef_titles = array();
+
+
+ ### Parser Implementation ###
+
+ # Regex to match balanced [brackets].
+ # Needed to insert a maximum bracked depth while converting to PHP.
+ protected $nested_brackets_depth = 6;
+ protected $nested_brackets_re;
+
+ protected $nested_url_parenthesis_depth = 4;
+ protected $nested_url_parenthesis_re;
+
+ # Table of hash values for escaped characters:
+ protected $escape_chars = '\`*_{}[]()>#+-.!';
+ protected $escape_chars_re;
+
+
+ public function __construct() {
+ #
+ # Constructor function. Initialize appropriate member variables.
+ #
+ $this->_initDetab();
+ $this->prepareItalicsAndBold();
+
+ $this->nested_brackets_re =
+ str_repeat('(?>[^\[\]]+|\[', $this->nested_brackets_depth).
+ str_repeat('\])*', $this->nested_brackets_depth);
+
+ $this->nested_url_parenthesis_re =
+ str_repeat('(?>[^()\s]+|\(', $this->nested_url_parenthesis_depth).
+ str_repeat('(?>\)))*', $this->nested_url_parenthesis_depth);
+
+ $this->escape_chars_re = '['.preg_quote($this->escape_chars).']';
+
+ # Sort document, block, and span gamut in ascendent priority order.
+ asort($this->document_gamut);
+ asort($this->block_gamut);
+ asort($this->span_gamut);
+ }
+
+
+ # Internal hashes used during transformation.
+ protected $urls = array();
+ protected $titles = array();
+ protected $html_hashes = array();
+
+ # Status flag to avoid invalid nesting.
+ protected $in_anchor = false;
+
+
+ protected function setup() {
+ #
+ # Called before the transformation process starts to setup parser
+ # states.
+ #
+ # Clear global hashes.
+ $this->urls = $this->predef_urls;
+ $this->titles = $this->predef_titles;
+ $this->html_hashes = array();
+
+ $this->in_anchor = false;
+ }
+
+ protected function teardown() {
+ #
+ # Called after the transformation process to clear any variable
+ # which may be taking up memory unnecessarly.
+ #
+ $this->urls = array();
+ $this->titles = array();
+ $this->html_hashes = array();
+ }
+
+
+ public function transform($text) {
+ #
+ # Main function. Performs some preprocessing on the input text
+ # and pass it through the document gamut.
+ #
+ $this->setup();
+
+ # Remove UTF-8 BOM and marker character in input, if present.
+ $text = preg_replace('{^\xEF\xBB\xBF|\x1A}', '', $text);
+
+ # Standardize line endings:
+ # DOS to Unix and Mac to Unix
+ $text = preg_replace('{\r\n?}', "\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 /[ ]*\n+/ .
+ $text = preg_replace('/^[ ]+$/m', '', $text);
+
+ # Run document gamut methods.
+ foreach ($this->document_gamut as $method => $priority) {
+ $text = $this->$method($text);
+ }
+
+ $this->teardown();
+
+ return $text . "\n";
+ }
+
+ protected $document_gamut = array(
+ # Strip link definitions, store in hashes.
+ "stripLinkDefinitions" => 20,
+
+ "runBasicBlockGamut" => 30,
+ );
+
+
+ protected function stripLinkDefinitions($text) {
+ #
+ # Strips link definitions from text, stores the URLs and titles in
+ # hash references.
+ #
+ $less_than_tab = $this->tab_width - 1;
+
+ # Link defs are in the form: ^[id]: url "optional title"
+ $text = preg_replace_callback('{
+ ^[ ]{0,'.$less_than_tab.'}\[(.+)\][ ]?: # id = $1
+ [ ]*
+ \n? # maybe *one* newline
+ [ ]*
+ (?:
+ <(.+?)> # url = $2
+ |
+ (\S+?) # url = $3
+ )
+ [ ]*
+ \n? # maybe one newline
+ [ ]*
+ (?:
+ (?<=\s) # lookbehind for whitespace
+ ["(]
+ (.*?) # title = $4
+ [")]
+ [ ]*
+ )? # title is optional
+ (?:\n+|\Z)
+ }xm',
+ array(&$this, '_stripLinkDefinitions_callback'),
+ $text);
+ return $text;
+ }
+ protected function _stripLinkDefinitions_callback($matches) {
+ $link_id = strtolower($matches[1]);
+ $url = $matches[2] == '' ? $matches[3] : $matches[2];
+ $this->urls[$link_id] = $url;
+ $this->titles[$link_id] =& $matches[4];
+ return ''; # String that will replace the block
+ }
+
+
+ protected function hashHTMLBlocks($text) {
+ if ($this->no_markup) return $text;
+
+ $less_than_tab = $this->tab_width - 1;
+
+ # Hashify HTML blocks:
+ # 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:
+ #
+ # * List "a" is made of tags which can be both inline or block-level.
+ # These will be treated block-level when the start tag is alone on
+ # its line, otherwise they're not matched here and will be taken as
+ # inline later.
+ # * List "b" is made of tags which are always block-level;
+ #
+ $block_tags_a_re = 'ins|del';
+ $block_tags_b_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|'.
+ 'script|noscript|form|fieldset|iframe|math|svg|'.
+ 'article|section|nav|aside|hgroup|header|footer|'.
+ 'figure';
+
+ # Regular expression for the content of a block tag.
+ $nested_tags_level = 4;
+ $attr = '
+ (?> # optional tag attributes
+ \s # starts with whitespace
+ (?>
+ [^>"/]+ # text outside quotes
+ |
+ /+(?!>) # slash not followed by ">"
+ |
+ "[^"]*" # text inside double quotes (tolerate ">")
+ |
+ \'[^\']*\' # text inside single quotes (tolerate ">")
+ )*
+ )?
+ ';
+ $content =
+ str_repeat('
+ (?>
+ [^<]+ # content without tag
+ |
+ <\2 # nested opening tag
+ '.$attr.' # attributes
+ (?>
+ />
+ |
+ >', $nested_tags_level). # end of opening tag
+ '.*?'. # last level nested tag content
+ str_repeat('
+ \2\s*> # closing nested tag
+ )
+ |
+ <(?!/\2\s*> # other tags with a different name
+ )
+ )*',
+ $nested_tags_level);
+ $content2 = str_replace('\2', '\3', $content);
+
+ # First, look for nested blocks, e.g.:
+ #
+ #
+ # tags for inner block must be indented.
+ #
+ #
+ #
+ # The outermost tags must start at the left margin for this to match, and
+ # the inner nested divs must be indented.
+ # We need to do this before the next, more liberal match, because the next
+ # match will start at the first `` and stop at the first `
`.
+ $text = preg_replace_callback('{(?>
+ (?>
+ (?<=\n\n) # Starting after a blank line
+ | # or
+ \A\n? # the beginning of the doc
+ )
+ ( # save in $1
+
+ # Match from `\n` to `\n`, handling nested tags
+ # in between.
+
+ [ ]{0,'.$less_than_tab.'}
+ <('.$block_tags_b_re.')# start tag = $2
+ '.$attr.'> # attributes followed by > and \n
+ '.$content.' # content, support nesting
+ \2> # the matching end tag
+ [ ]* # trailing spaces/tabs
+ (?=\n+|\Z) # followed by a newline or end of document
+
+ | # Special version for tags of group a.
+
+ [ ]{0,'.$less_than_tab.'}
+ <('.$block_tags_a_re.')# start tag = $3
+ '.$attr.'>[ ]*\n # attributes followed by >
+ '.$content2.' # content, support nesting
+ \3> # the matching end tag
+ [ ]* # trailing spaces/tabs
+ (?=\n+|\Z) # followed by a newline or end of document
+
+ | # Special case just for
. It was easier to make a special
+ # case than to make the other regex more complicated.
+
+ [ ]{0,'.$less_than_tab.'}
+ <(hr) # start tag = $2
+ '.$attr.' # attributes
+ /?> # the matching end tag
+ [ ]*
+ (?=\n{2,}|\Z) # followed by a blank line or end of document
+
+ | # Special case for standalone HTML comments:
+
+ [ ]{0,'.$less_than_tab.'}
+ (?s:
+
+ )
+ [ ]*
+ (?=\n{2,}|\Z) # followed by a blank line or end of document
+
+ | # PHP and ASP-style processor instructions ( and <%)
+
+ [ ]{0,'.$less_than_tab.'}
+ (?s:
+ <([?%]) # $2
+ .*?
+ \2>
+ )
+ [ ]*
+ (?=\n{2,}|\Z) # followed by a blank line or end of document
+
+ )
+ )}Sxmi',
+ array(&$this, '_hashHTMLBlocks_callback'),
+ $text);
+
+ return $text;
+ }
+ protected function _hashHTMLBlocks_callback($matches) {
+ $text = $matches[1];
+ $key = $this->hashBlock($text);
+ return "\n\n$key\n\n";
+ }
+
+
+ protected function hashPart($text, $boundary = 'X') {
+ #
+ # Called whenever a tag must be hashed when a function insert an atomic
+ # element in the text stream. Passing $text to through this function gives
+ # a unique text-token which will be reverted back when calling unhash.
+ #
+ # The $boundary argument specify what character should be used to surround
+ # the token. By convension, "B" is used for block elements that needs not
+ # to be wrapped into paragraph tags at the end, ":" is used for elements
+ # that are word separators and "X" is used in the general case.
+ #
+ # Swap back any tag hash found in $text so we do not have to `unhash`
+ # multiple times at the end.
+ $text = $this->unhash($text);
+
+ # Then hash the block.
+ static $i = 0;
+ $key = "$boundary\x1A" . ++$i . $boundary;
+ $this->html_hashes[$key] = $text;
+ return $key; # String that will replace the tag.
+ }
+
+
+ protected function hashBlock($text) {
+ #
+ # Shortcut function for hashPart with block-level boundaries.
+ #
+ return $this->hashPart($text, 'B');
+ }
+
+
+ protected $block_gamut = array(
+ #
+ # These are all the transformations that form block-level
+ # tags like paragraphs, headers, and list items.
+ #
+ "doHeaders" => 10,
+ "doHorizontalRules" => 20,
+
+ "doLists" => 40,
+ "doCodeBlocks" => 50,
+ "doBlockQuotes" => 60,
+ );
+
+ protected function runBlockGamut($text) {
+ #
+ # Run block gamut tranformations.
+ #
+ # 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
+ # list items and could have been indented. Indented blocks would have
+ # been seen as a code block in a previous pass of hashHTMLBlocks.
+ $text = $this->hashHTMLBlocks($text);
+
+ return $this->runBasicBlockGamut($text);
+ }
+
+ protected function runBasicBlockGamut($text) {
+ #
+ # Run block gamut tranformations, without hashing HTML blocks. This is
+ # useful when HTML blocks are known to be already hashed, like in the first
+ # whole-document pass.
+ #
+ foreach ($this->block_gamut as $method => $priority) {
+ $text = $this->$method($text);
+ }
+
+ # Finally form paragraph and restore hashed blocks.
+ $text = $this->formParagraphs($text);
+
+ return $text;
+ }
+
+
+ protected function doHorizontalRules($text) {
+ # Do Horizontal Rules:
+ return preg_replace(
+ '{
+ ^[ ]{0,3} # Leading space
+ ([-*_]) # $1: First marker
+ (?> # Repeated marker group
+ [ ]{0,2} # Zero, one, or two spaces.
+ \1 # Marker character
+ ){2,} # Group repeated at least twice
+ [ ]* # Tailing spaces
+ $ # End of line.
+ }mx',
+ "\n".$this->hashBlock("
empty_element_suffix")."\n",
+ $text);
+ }
+
+
+ protected $span_gamut = array(
+ #
+ # These are all the transformations that occur *within* block-level
+ # tags like paragraphs, headers, and list items.
+ #
+ # Process character escapes, code spans, and inline HTML
+ # in one shot.
+ "parseSpan" => -30,
+
+ # Process anchor and image tags. Images must come first,
+ # because ![foo][f] looks like an anchor.
+ "doImages" => 10,
+ "doAnchors" => 20,
+
+ # Make links out of things like ``
+ # Must come after doAnchors, because you can use < and >
+ # delimiters in inline links like [this]().
+ "doAutoLinks" => 30,
+ "encodeAmpsAndAngles" => 40,
+
+ "doItalicsAndBold" => 50,
+ "doHardBreaks" => 60,
+ );
+
+ protected function runSpanGamut($text) {
+ #
+ # Run span gamut tranformations.
+ #
+ foreach ($this->span_gamut as $method => $priority) {
+ $text = $this->$method($text);
+ }
+
+ return $text;
+ }
+
+
+ protected function doHardBreaks($text) {
+ # Do hard breaks:
+ return preg_replace_callback('/ {2,}\n/',
+ array(&$this, '_doHardBreaks_callback'), $text);
+ }
+ protected function _doHardBreaks_callback($matches) {
+ return $this->hashPart("
empty_element_suffix\n");
+ }
+
+
+ protected function doAnchors($text) {
+ #
+ # Turn Markdown link shortcuts into XHTML tags.
+ #
+ if ($this->in_anchor) return $text;
+ $this->in_anchor = true;
+
+ #
+ # First, handle reference-style links: [link text] [id]
+ #
+ $text = preg_replace_callback('{
+ ( # wrap whole match in $1
+ \[
+ ('.$this->nested_brackets_re.') # link text = $2
+ \]
+
+ [ ]? # one optional space
+ (?:\n[ ]*)? # one optional newline followed by spaces
+
+ \[
+ (.*?) # id = $3
+ \]
+ )
+ }xs',
+ array(&$this, '_doAnchors_reference_callback'), $text);
+
+ #
+ # Next, inline-style links: [link text](url "optional title")
+ #
+ $text = preg_replace_callback('{
+ ( # wrap whole match in $1
+ \[
+ ('.$this->nested_brackets_re.') # link text = $2
+ \]
+ \( # literal paren
+ [ \n]*
+ (?:
+ <(.+?)> # href = $3
+ |
+ ('.$this->nested_url_parenthesis_re.') # href = $4
+ )
+ [ \n]*
+ ( # $5
+ ([\'"]) # quote char = $6
+ (.*?) # Title = $7
+ \6 # matching quote
+ [ \n]* # ignore any spaces/tabs between closing quote and )
+ )? # title is optional
+ \)
+ )
+ }xs',
+ array(&$this, '_doAnchors_inline_callback'), $text);
+
+ #
+ # Last, handle reference-style shortcuts: [link text]
+ # These must come last in case you've also got [link text][1]
+ # or [link text](/foo)
+ #
+ $text = preg_replace_callback('{
+ ( # wrap whole match in $1
+ \[
+ ([^\[\]]+) # link text = $2; can\'t contain [ or ]
+ \]
+ )
+ }xs',
+ array(&$this, '_doAnchors_reference_callback'), $text);
+
+ $this->in_anchor = false;
+ return $text;
+ }
+ protected function _doAnchors_reference_callback($matches) {
+ $whole_match = $matches[1];
+ $link_text = $matches[2];
+ $link_id =& $matches[3];
+
+ if ($link_id == "") {
+ # for shortcut links like [this][] or [this].
+ $link_id = $link_text;
+ }
+
+ # lower-case and turn embedded newlines into spaces
+ $link_id = strtolower($link_id);
+ $link_id = preg_replace('{[ ]?\n}', ' ', $link_id);
+
+ if (isset($this->urls[$link_id])) {
+ $url = $this->urls[$link_id];
+ $url = $this->encodeAttribute($url);
+
+ $result = "titles[$link_id] ) ) {
+ $title = $this->titles[$link_id];
+ $title = $this->encodeAttribute($title);
+ $result .= " title=\"$title\"";
+ }
+
+ $link_text = $this->runSpanGamut($link_text);
+ $result .= ">$link_text";
+ $result = $this->hashPart($result);
+ }
+ else {
+ $result = $whole_match;
+ }
+ return $result;
+ }
+ protected function _doAnchors_inline_callback($matches) {
+ $whole_match = $matches[1];
+ $link_text = $this->runSpanGamut($matches[2]);
+ $url = $matches[3] == '' ? $matches[4] : $matches[3];
+ $title =& $matches[7];
+
+ $url = $this->encodeAttribute($url);
+
+ $result = "encodeAttribute($title);
+ $result .= " title=\"$title\"";
+ }
+
+ $link_text = $this->runSpanGamut($link_text);
+ $result .= ">$link_text";
+
+ return $this->hashPart($result);
+ }
+
+
+ protected function doImages($text) {
+ #
+ # Turn Markdown image shortcuts into
tags.
+ #
+ #
+ # First, handle reference-style labeled images: ![alt text][id]
+ #
+ $text = preg_replace_callback('{
+ ( # wrap whole match in $1
+ !\[
+ ('.$this->nested_brackets_re.') # alt text = $2
+ \]
+
+ [ ]? # one optional space
+ (?:\n[ ]*)? # one optional newline followed by spaces
+
+ \[
+ (.*?) # id = $3
+ \]
+
+ )
+ }xs',
+ array(&$this, '_doImages_reference_callback'), $text);
+
+ #
+ # Next, handle inline images: ![alt text](url "optional title")
+ # Don't forget: encode * and _
+ #
+ $text = preg_replace_callback('{
+ ( # wrap whole match in $1
+ !\[
+ ('.$this->nested_brackets_re.') # alt text = $2
+ \]
+ \s? # One optional whitespace character
+ \( # literal paren
+ [ \n]*
+ (?:
+ <(\S*)> # src url = $3
+ |
+ ('.$this->nested_url_parenthesis_re.') # src url = $4
+ )
+ [ \n]*
+ ( # $5
+ ([\'"]) # quote char = $6
+ (.*?) # title = $7
+ \6 # matching quote
+ [ \n]*
+ )? # title is optional
+ \)
+ )
+ }xs',
+ array(&$this, '_doImages_inline_callback'), $text);
+
+ return $text;
+ }
+ protected function _doImages_reference_callback($matches) {
+ $whole_match = $matches[1];
+ $alt_text = $matches[2];
+ $link_id = strtolower($matches[3]);
+
+ if ($link_id == "") {
+ $link_id = strtolower($alt_text); # for shortcut links like ![this][].
+ }
+
+ $alt_text = $this->encodeAttribute($alt_text);
+ if (isset($this->urls[$link_id])) {
+ $url = $this->encodeAttribute($this->urls[$link_id]);
+ $result = "
titles[$link_id])) {
+ $title = $this->titles[$link_id];
+ $title = $this->encodeAttribute($title);
+ $result .= " title=\"$title\"";
+ }
+ $result .= $this->empty_element_suffix;
+ $result = $this->hashPart($result);
+ }
+ else {
+ # If there's no such link ID, leave intact:
+ $result = $whole_match;
+ }
+
+ return $result;
+ }
+ protected function _doImages_inline_callback($matches) {
+ $whole_match = $matches[1];
+ $alt_text = $matches[2];
+ $url = $matches[3] == '' ? $matches[4] : $matches[3];
+ $title =& $matches[7];
+
+ $alt_text = $this->encodeAttribute($alt_text);
+ $url = $this->encodeAttribute($url);
+ $result = "
encodeAttribute($title);
+ $result .= " title=\"$title\""; # $title already quoted
+ }
+ $result .= $this->empty_element_suffix;
+
+ return $this->hashPart($result);
+ }
+
+
+ protected function doHeaders($text) {
+ # Setext-style headers:
+ # Header 1
+ # ========
+ #
+ # Header 2
+ # --------
+ #
+ $text = preg_replace_callback('{ ^(.+?)[ ]*\n(=+|-+)[ ]*\n+ }mx',
+ array(&$this, '_doHeaders_callback_setext'), $text);
+
+ # atx-style headers:
+ # # Header 1
+ # ## Header 2
+ # ## Header 2 with closing hashes ##
+ # ...
+ # ###### Header 6
+ #
+ $text = preg_replace_callback('{
+ ^(\#{1,6}) # $1 = string of #\'s
+ [ ]*
+ (.+?) # $2 = Header text
+ [ ]*
+ \#* # optional closing #\'s (not counted)
+ \n+
+ }xm',
+ array(&$this, '_doHeaders_callback_atx'), $text);
+
+ return $text;
+ }
+ protected function _doHeaders_callback_setext($matches) {
+ # Terrible hack to check we haven't found an empty list item.
+ if ($matches[2] == '-' && preg_match('{^-(?: |$)}', $matches[1]))
+ return $matches[0];
+
+ $level = $matches[2]{0} == '=' ? 1 : 2;
+ $block = "".$this->runSpanGamut($matches[1])."";
+ return "\n" . $this->hashBlock($block) . "\n\n";
+ }
+ protected function _doHeaders_callback_atx($matches) {
+ $level = strlen($matches[1]);
+ $block = "".$this->runSpanGamut($matches[2])."";
+ return "\n" . $this->hashBlock($block) . "\n\n";
+ }
+
+
+ protected function doLists($text) {
+ #
+ # Form HTML ordered (numbered) and unordered (bulleted) lists.
+ #
+ $less_than_tab = $this->tab_width - 1;
+
+ # Re-usable patterns to match list item bullets and number markers:
+ $marker_ul_re = '[*+-]';
+ $marker_ol_re = '\d+[\.]';
+ $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)";
+
+ $markers_relist = array(
+ $marker_ul_re => $marker_ol_re,
+ $marker_ol_re => $marker_ul_re,
+ );
+
+ foreach ($markers_relist as $marker_re => $other_marker_re) {
+ # Re-usable pattern to match any entirel ul or ol list:
+ $whole_list_re = '
+ ( # $1 = whole list
+ ( # $2
+ ([ ]{0,'.$less_than_tab.'}) # $3 = number of spaces
+ ('.$marker_re.') # $4 = first list item marker
+ [ ]+
+ )
+ (?s:.+?)
+ ( # $5
+ \z
+ |
+ \n{2,}
+ (?=\S)
+ (?! # Negative lookahead for another list item marker
+ [ ]*
+ '.$marker_re.'[ ]+
+ )
+ |
+ (?= # Lookahead for another kind of list
+ \n
+ \3 # Must have the same indentation
+ '.$other_marker_re.'[ ]+
+ )
+ )
+ )
+ '; // mx
+
+ # We use a different prefix before nested lists than top-level lists.
+ # See extended comment in _ProcessListItems().
+
+ if ($this->list_level) {
+ $text = preg_replace_callback('{
+ ^
+ '.$whole_list_re.'
+ }mx',
+ array(&$this, '_doLists_callback'), $text);
+ }
+ else {
+ $text = preg_replace_callback('{
+ (?:(?<=\n)\n|\A\n?) # Must eat the newline
+ '.$whole_list_re.'
+ }mx',
+ array(&$this, '_doLists_callback'), $text);
+ }
+ }
+
+ return $text;
+ }
+ protected function _doLists_callback($matches) {
+ # Re-usable patterns to match list item bullets and number markers:
+ $marker_ul_re = '[*+-]';
+ $marker_ol_re = '\d+[\.]';
+ $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)";
+
+ $list = $matches[1];
+ $list_type = preg_match("/$marker_ul_re/", $matches[4]) ? "ul" : "ol";
+
+ $marker_any_re = ( $list_type == "ul" ? $marker_ul_re : $marker_ol_re );
+
+ $list .= "\n";
+ $result = $this->processListItems($list, $marker_any_re);
+
+ $result = $this->hashBlock("<$list_type>\n" . $result . "$list_type>");
+ return "\n". $result ."\n\n";
+ }
+
+ protected $list_level = 0;
+
+ protected function processListItems($list_str, $marker_any_re) {
+ #
+ # Process the contents of a single ordered or unordered list, splitting it
+ # into individual list items.
+ #
+ # The $this->list_level global keeps track of when we're inside a list.
+ # Each time we enter a list, we increment it; when we leave a list,
+ # we decrement. If it's zero, we're not in a list anymore.
+ #
+ # We do this because when we're not inside a list, we want to treat
+ # something like this:
+ #
+ # I recommend upgrading to version
+ # 8. Oops, now this line is treated
+ # as a sub-list.
+ #
+ # As a single paragraph, despite the fact that the second line starts
+ # with a digit-period-space sequence.
+ #
+ # Whereas when we're inside a list (or sub-list), that line will be
+ # treated as the start of a sub-list. What a kludge, huh? This is
+ # an aspect of Markdown's syntax that's hard to parse perfectly
+ # without resorting to mind-reading. Perhaps the solution is to
+ # change the syntax rules such that sub-lists must start with a
+ # starting cardinal number; e.g. "1." or "a.".
+
+ $this->list_level++;
+
+ # trim trailing blank lines:
+ $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str);
+
+ $list_str = preg_replace_callback('{
+ (\n)? # leading line = $1
+ (^[ ]*) # leading whitespace = $2
+ ('.$marker_any_re.' # list marker and space = $3
+ (?:[ ]+|(?=\n)) # space only required if item is not empty
+ )
+ ((?s:.*?)) # list item text = $4
+ (?:(\n+(?=\n))|\n) # tailing blank line = $5
+ (?= \n* (\z | \2 ('.$marker_any_re.') (?:[ ]+|(?=\n))))
+ }xm',
+ array(&$this, '_processListItems_callback'), $list_str);
+
+ $this->list_level--;
+ return $list_str;
+ }
+ protected function _processListItems_callback($matches) {
+ $item = $matches[4];
+ $leading_line =& $matches[1];
+ $leading_space =& $matches[2];
+ $marker_space = $matches[3];
+ $tailing_blank_line =& $matches[5];
+
+ if ($leading_line || $tailing_blank_line ||
+ preg_match('/\n{2,}/', $item))
+ {
+ # Replace marker with the appropriate whitespace indentation
+ $item = $leading_space . str_repeat(' ', strlen($marker_space)) . $item;
+ $item = $this->runBlockGamut($this->outdent($item)."\n");
+ }
+ else {
+ # Recursion for sub-lists:
+ $item = $this->doLists($this->outdent($item));
+ $item = preg_replace('/\n+$/', '', $item);
+ $item = $this->runSpanGamut($item);
+ }
+
+ return "" . $item . "\n";
+ }
+
+
+ protected function doCodeBlocks($text) {
+ #
+ # Process Markdown `` blocks.
+ #
+ $text = preg_replace_callback('{
+ (?:\n\n|\A\n?)
+ ( # $1 = the code block -- one or more lines, starting with a space/tab
+ (?>
+ [ ]{'.$this->tab_width.'} # Lines must start with a tab or a tab-width of spaces
+ .*\n+
+ )+
+ )
+ ((?=^[ ]{0,'.$this->tab_width.'}\S)|\Z) # Lookahead for non-space at line-start, or end of doc
+ }xm',
+ array(&$this, '_doCodeBlocks_callback'), $text);
+
+ return $text;
+ }
+ protected function _doCodeBlocks_callback($matches) {
+ $codeblock = $matches[1];
+
+ $codeblock = $this->outdent($codeblock);
+ $codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
+
+ # trim leading newlines and trailing newlines
+ $codeblock = preg_replace('/\A\n+|\n+\z/', '', $codeblock);
+
+ $codeblock = "$codeblock\n
";
+ return "\n\n".$this->hashBlock($codeblock)."\n\n";
+ }
+
+
+ protected function makeCodeSpan($code) {
+ #
+ # Create a code span markup for $code. Called from handleSpanToken.
+ #
+ $code = htmlspecialchars(trim($code), ENT_NOQUOTES);
+ return $this->hashPart("$code
");
+ }
+
+
+ protected $em_relist = array(
+ '' => '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? '(?:(? '(?<=\S|^)(? '(?<=\S|^)(?em_relist as $em => $em_re) {
+ foreach ($this->strong_relist as $strong => $strong_re) {
+ # Construct list of allowed token expressions.
+ $token_relist = array();
+ if (isset($this->em_strong_relist["$em$strong"])) {
+ $token_relist[] = $this->em_strong_relist["$em$strong"];
+ }
+ $token_relist[] = $em_re;
+ $token_relist[] = $strong_re;
+
+ # Construct master expression from list.
+ $token_re = '{('. implode('|', $token_relist) .')}';
+ $this->em_strong_prepared_relist["$em$strong"] = $token_re;
+ }
+ }
+ }
+
+ protected function doItalicsAndBold($text) {
+ $token_stack = array('');
+ $text_stack = array('');
+ $em = '';
+ $strong = '';
+ $tree_char_em = false;
+
+ while (1) {
+ #
+ # Get prepared regular expression for seraching emphasis tokens
+ # in current context.
+ #
+ $token_re = $this->em_strong_prepared_relist["$em$strong"];
+
+ #
+ # Each loop iteration search for the next emphasis token.
+ # Each token is then passed to handleSpanToken.
+ #
+ $parts = preg_split($token_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE);
+ $text_stack[0] .= $parts[0];
+ $token =& $parts[1];
+ $text =& $parts[2];
+
+ if (empty($token)) {
+ # Reached end of text span: empty stack without emitting.
+ # any more emphasis.
+ while ($token_stack[0]) {
+ $text_stack[1] .= array_shift($token_stack);
+ $text_stack[0] .= array_shift($text_stack);
+ }
+ break;
+ }
+
+ $token_len = strlen($token);
+ if ($tree_char_em) {
+ # Reached closing marker while inside a three-char emphasis.
+ if ($token_len == 3) {
+ # Three-char closing marker, close em and strong.
+ array_shift($token_stack);
+ $span = array_shift($text_stack);
+ $span = $this->runSpanGamut($span);
+ $span = "$span";
+ $text_stack[0] .= $this->hashPart($span);
+ $em = '';
+ $strong = '';
+ } else {
+ # Other closing marker: close one em or strong and
+ # change current token state to match the other
+ $token_stack[0] = str_repeat($token{0}, 3-$token_len);
+ $tag = $token_len == 2 ? "strong" : "em";
+ $span = $text_stack[0];
+ $span = $this->runSpanGamut($span);
+ $span = "<$tag>$span$tag>";
+ $text_stack[0] = $this->hashPart($span);
+ $$tag = ''; # $$tag stands for $em or $strong
+ }
+ $tree_char_em = false;
+ } else if ($token_len == 3) {
+ if ($em) {
+ # Reached closing marker for both em and strong.
+ # Closing strong marker:
+ for ($i = 0; $i < 2; ++$i) {
+ $shifted_token = array_shift($token_stack);
+ $tag = strlen($shifted_token) == 2 ? "strong" : "em";
+ $span = array_shift($text_stack);
+ $span = $this->runSpanGamut($span);
+ $span = "<$tag>$span$tag>";
+ $text_stack[0] .= $this->hashPart($span);
+ $$tag = ''; # $$tag stands for $em or $strong
+ }
+ } else {
+ # Reached opening three-char emphasis marker. Push on token
+ # stack; will be handled by the special condition above.
+ $em = $token{0};
+ $strong = "$em$em";
+ array_unshift($token_stack, $token);
+ array_unshift($text_stack, '');
+ $tree_char_em = true;
+ }
+ } else if ($token_len == 2) {
+ if ($strong) {
+ # Unwind any dangling emphasis marker:
+ if (strlen($token_stack[0]) == 1) {
+ $text_stack[1] .= array_shift($token_stack);
+ $text_stack[0] .= array_shift($text_stack);
+ }
+ # Closing strong marker:
+ array_shift($token_stack);
+ $span = array_shift($text_stack);
+ $span = $this->runSpanGamut($span);
+ $span = "$span";
+ $text_stack[0] .= $this->hashPart($span);
+ $strong = '';
+ } else {
+ array_unshift($token_stack, $token);
+ array_unshift($text_stack, '');
+ $strong = $token;
+ }
+ } else {
+ # Here $token_len == 1
+ if ($em) {
+ if (strlen($token_stack[0]) == 1) {
+ # Closing emphasis marker:
+ array_shift($token_stack);
+ $span = array_shift($text_stack);
+ $span = $this->runSpanGamut($span);
+ $span = "$span";
+ $text_stack[0] .= $this->hashPart($span);
+ $em = '';
+ } else {
+ $text_stack[0] .= $token;
+ }
+ } else {
+ array_unshift($token_stack, $token);
+ array_unshift($text_stack, '');
+ $em = $token;
+ }
+ }
+ }
+ return $text_stack[0];
+ }
+
+
+ protected function doBlockQuotes($text) {
+ $text = preg_replace_callback('/
+ ( # Wrap whole match in $1
+ (?>
+ ^[ ]*>[ ]? # ">" at the start of a line
+ .+\n # rest of the first line
+ (.+\n)* # subsequent consecutive lines
+ \n* # blanks
+ )+
+ )
+ /xm',
+ array(&$this, '_doBlockQuotes_callback'), $text);
+
+ return $text;
+ }
+ protected function _doBlockQuotes_callback($matches) {
+ $bq = $matches[1];
+ # trim one level of quoting - trim whitespace-only lines
+ $bq = preg_replace('/^[ ]*>[ ]?|^[ ]+$/m', '', $bq);
+ $bq = $this->runBlockGamut($bq); # recurse
+
+ $bq = preg_replace('/^/m', " ", $bq);
+ # These leading spaces cause problem with content,
+ # so we need to fix that:
+ $bq = preg_replace_callback('{(\s*.+?
)}sx',
+ array(&$this, '_doBlockQuotes_callback2'), $bq);
+
+ return "\n". $this->hashBlock("\n$bq\n
")."\n\n";
+ }
+ protected function _doBlockQuotes_callback2($matches) {
+ $pre = $matches[1];
+ $pre = preg_replace('/^ /m', '', $pre);
+ return $pre;
+ }
+
+
+ protected function formParagraphs($text) {
+ #
+ # Params:
+ # $text - string to process with html tags
+ #
+ # Strip leading and trailing lines:
+ $text = preg_replace('/\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) {
+ if (!preg_match('/^B\x1A[0-9]+B$/', $value)) {
+ # Is a paragraph.
+ $value = $this->runSpanGamut($value);
+ $value = preg_replace('/^([ ]*)/', "
", $value);
+ $value .= "
";
+ $grafs[$key] = $this->unhash($value);
+ }
+ else {
+ # Is a block.
+ # Modify elements of @grafs in-place...
+ $graf = $value;
+ $block = $this->html_hashes[$graf];
+ $graf = $block;
+// if (preg_match('{
+// \A
+// ( # $1 = tag
+//
]*
+// \b
+// markdown\s*=\s* ([\'"]) # $2 = attr quote char
+// 1
+// \2
+// [^>]*
+// >
+// )
+// ( # $3 = contents
+// .*
+// )
+// (
) # $4 = closing tag
+// \z
+// }xs', $block, $matches))
+// {
+// list(, $div_open, , $div_content, $div_close) = $matches;
+//
+// # We can't call Markdown(), because that resets the hash;
+// # that initialization code should be pulled into its own sub, though.
+// $div_content = $this->hashHTMLBlocks($div_content);
+//
+// # Run document gamut methods on the content.
+// foreach ($this->document_gamut as $method => $priority) {
+// $div_content = $this->$method($div_content);
+// }
+//
+// $div_open = preg_replace(
+// '{\smarkdown\s*=\s*([\'"]).+?\1}', '', $div_open);
+//
+// $graf = $div_open . "\n" . $div_content . "\n" . $div_close;
+// }
+ $grafs[$key] = $graf;
+ }
+ }
+
+ return implode("\n\n", $grafs);
+ }
+
+
+ protected function encodeAttribute($text) {
+ #
+ # Encode text for a double-quoted HTML attribute. This function
+ # is *not* suitable for attributes enclosed in single quotes.
+ #
+ $text = $this->encodeAmpsAndAngles($text);
+ $text = str_replace('"', '"', $text);
+ return $text;
+ }
+
+
+ protected function encodeAmpsAndAngles($text) {
+ #
+ # Smart processing for ampersands and angle brackets that need to
+ # be encoded. Valid character entities are left alone unless the
+ # no-entities mode is set.
+ #
+ if ($this->no_entities) {
+ $text = str_replace('&', '&', $text);
+ } else {
+ # Ampersand-encoding based entirely on Nat Irons's Amputator
+ # MT plugin:
+ $text = preg_replace('/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/',
+ '&', $text);
+ }
+ # Encode remaining <'s
+ $text = str_replace('<', '<', $text);
+
+ return $text;
+ }
+
+
+ protected function doAutoLinks($text) {
+ $text = preg_replace_callback('{<((https?|ftp|dict):[^\'">\s]+)>}i',
+ array(&$this, '_doAutoLinks_url_callback'), $text);
+
+ # Email addresses:
+ $text = preg_replace_callback('{
+ <
+ (?:mailto:)?
+ (
+ (?:
+ [-!#$%&\'*+/=?^_`.{|}~\w\x80-\xFF]+
+ |
+ ".*?"
+ )
+ \@
+ (?:
+ [-a-z0-9\x80-\xFF]+(\.[-a-z0-9\x80-\xFF]+)*\.[a-z]+
+ |
+ \[[\d.a-fA-F:]+\] # IPv4 & IPv6
+ )
+ )
+ >
+ }xi',
+ array(&$this, '_doAutoLinks_email_callback'), $text);
+ $text = preg_replace_callback('{<(tel:([^\'">\s]+))>}i',array(&$this, '_doAutoLinks_tel_callback'), $text);
+
+ return $text;
+ }
+ protected function _doAutoLinks_tel_callback($matches) {
+ $url = $this->encodeAttribute($matches[1]);
+ $tel = $this->encodeAttribute($matches[2]);
+ $link = "$tel";
+ return $this->hashPart($link);
+ }
+ protected function _doAutoLinks_url_callback($matches) {
+ $url = $this->encodeAttribute($matches[1]);
+ $link = "$url";
+ return $this->hashPart($link);
+ }
+ protected function _doAutoLinks_email_callback($matches) {
+ $address = $matches[1];
+ $link = $this->encodeEmailAddress($address);
+ return $this->hashPart($link);
+ }
+
+
+ protected function encodeEmailAddress($addr) {
+ #
+ # Input: an email address, e.g. "foo@example.com"
+ #
+ # Output: the email address as a mailto link, with each character
+ # of the address encoded as either a decimal or hex entity, in
+ # the hopes of foiling most address harvesting spam bots. E.g.:
+ #
+ # foo@exampl
+ # e.com
+ #
+ # Based by a filter by Matthew Wickline, posted to BBEdit-Talk.
+ # With some optimizations by Milian Wolff.
+ #
+ $addr = "mailto:" . $addr;
+ $chars = preg_split('/(? $char) {
+ $ord = ord($char);
+ # Ignore non-ascii chars.
+ if ($ord < 128) {
+ $r = ($seed * (1 + $key)) % 100; # Pseudo-random function.
+ # roughly 10% raw, 45% hex, 45% dec
+ # '@' *must* be encoded. I insist.
+ if ($r > 90 && $char != '@') /* do nothing */;
+ else if ($r < 45) $chars[$key] = ''.dechex($ord).';';
+ else $chars[$key] = ''.$ord.';';
+ }
+ }
+
+ $addr = implode('', $chars);
+ $text = implode('', array_slice($chars, 7)); # text without `mailto:`
+ $addr = "$text";
+
+ return $addr;
+ }
+
+
+ protected function parseSpan($str) {
+ #
+ # Take the string $str and parse it into tokens, hashing embeded HTML,
+ # escaped characters and handling code spans.
+ #
+ $output = '';
+
+ $span_re = '{
+ (
+ \\\\'.$this->escape_chars_re.'
+ |
+ (?no_markup ? '' : '
+ |
+ # comment
+ |
+ <\?.*?\?> | <%.*?%> # processing instruction
+ |
+ <[!$]?[-a-zA-Z0-9:_]+ # regular tags
+ (?>
+ \s
+ (?>[^"\'>]+|"[^"]*"|\'[^\']*\')*
+ )?
+ >
+ |
+ <[-a-zA-Z0-9:_]+\s*/> # xml-style empty tag
+ |
+ [-a-zA-Z0-9:_]+\s*> # closing tag
+ ').'
+ )
+ }xs';
+
+ while (1) {
+ #
+ # Each loop iteration seach for either the next tag, the next
+ # openning code span marker, or the next escaped character.
+ # Each token is then passed to handleSpanToken.
+ #
+ $parts = preg_split($span_re, $str, 2, PREG_SPLIT_DELIM_CAPTURE);
+
+ # Create token from text preceding tag.
+ if ($parts[0] != "") {
+ $output .= $parts[0];
+ }
+
+ # Check if we reach the end.
+ if (isset($parts[1])) {
+ $output .= $this->handleSpanToken($parts[1], $parts[2]);
+ $str = $parts[2];
+ }
+ else {
+ break;
+ }
+ }
+
+ return $output;
+ }
+
+
+ protected function handleSpanToken($token, &$str) {
+ #
+ # Handle $token provided by parseSpan by determining its nature and
+ # returning the corresponding value that should replace it.
+ #
+ switch ($token{0}) {
+ case "\\":
+ return $this->hashPart("". ord($token{1}). ";");
+ case "`":
+ # Search for end marker in remaining text.
+ if (preg_match('/^(.*?[^`])'.preg_quote($token).'(?!`)(.*)$/sm',
+ $str, $matches))
+ {
+ $str = $matches[2];
+ $codespan = $this->makeCodeSpan($matches[1]);
+ return $this->hashPart($codespan);
+ }
+ return $token; // return as text since no ending marker found.
+ default:
+ return $this->hashPart($token);
+ }
+ }
+
+
+ protected function outdent($text) {
+ #
+ # Remove one level of line-leading tabs or spaces
+ #
+ return preg_replace('/^(\t|[ ]{1,'.$this->tab_width.'})/m', '', $text);
+ }
+
+
+ # String length function for detab. `_initDetab` will create a function to
+ # hanlde UTF-8 if the default function does not exist.
+ protected $utf8_strlen = 'mb_strlen';
+
+ protected function detab($text) {
+ #
+ # Replace tabs with the appropriate amount of space.
+ #
+ # For each line we separate the line in blocks delemited by
+ # tab characters. Then we reconstruct every line by adding the
+ # appropriate number of space between each blocks.
+
+ $text = preg_replace_callback('/^.*\t.*$/m',
+ array(&$this, '_detab_callback'), $text);
+
+ return $text;
+ }
+ protected function _detab_callback($matches) {
+ $line = $matches[0];
+ $strlen = $this->utf8_strlen; # strlen function for UTF-8.
+
+ # Split in blocks.
+ $blocks = explode("\t", $line);
+ # Add each blocks to the line.
+ $line = $blocks[0];
+ unset($blocks[0]); # Do not add first block twice.
+ foreach ($blocks as $block) {
+ # Calculate amount of space, insert spaces, insert block.
+ $amount = $this->tab_width -
+ $strlen($line, 'UTF-8') % $this->tab_width;
+ $line .= str_repeat(" ", $amount) . $block;
+ }
+ return $line;
+ }
+ protected function _initDetab() {
+ #
+ # Check for the availability of the function in the `utf8_strlen` property
+ # (initially `mb_strlen`). If the function is not available, create a
+ # function that will loosely count the number of UTF-8 characters with a
+ # regular expression.
+ #
+ if (function_exists($this->utf8_strlen)) return;
+ $this->utf8_strlen = create_function('$text', 'return preg_match_all(
+ "/[\\\\x00-\\\\xBF]|[\\\\xC0-\\\\xFF][\\\\x80-\\\\xBF]*/",
+ $text, $m);');
+ }
+
+
+ protected function unhash($text) {
+ #
+ # Swap back in all the tags hashed by _HashHTMLBlocks.
+ #
+ return preg_replace_callback('/(.)\x1A[0-9]+\1/',
+ array(&$this, '_unhash_callback'), $text);
+ }
+ protected function _unhash_callback($matches) {
+ return $this->html_hashes[$matches[0]];
+ }
+
+}
+
+
+#
+# Temporary Markdown Extra Parser Implementation Class
+#
+# NOTE: DON'T USE THIS CLASS
+# Currently the implementation of of Extra resides here in this temporary class.
+# This makes it easier to propagate the changes between the three different
+# packaging styles of PHP Markdown. When this issue is resolved, this
+# MarkdownExtra_TmpImpl class here will disappear and \Michelf\MarkdownExtra
+# will contain the code. So please use \Michelf\MarkdownExtra and ignore this
+# one.
+#
+
+abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown {
+
+ ### Configuration Variables ###
+
+ # Prefix for footnote ids.
+ public $fn_id_prefix = "";
+
+ # Optional title attribute for footnote links and backlinks.
+ public $fn_link_title = "";
+ public $fn_backlink_title = "";
+
+ # Optional class attribute for footnote links and backlinks.
+ public $fn_link_class = "footnote-ref";
+ public $fn_backlink_class = "footnote-backref";
+
+ # Class name for table cell alignment (%% replaced left/center/right)
+ # For instance: 'go-%%' becomes 'go-left' or 'go-right' or 'go-center'
+ # If empty, the align attribute is used instead of a class name.
+ public $table_align_class_tmpl = '';
+
+ # Optional class prefix for fenced code block.
+ public $code_class_prefix = "";
+ # Class attribute for code blocks goes on the `code` tag;
+ # setting this to true will put attributes on the `pre` tag instead.
+ public $code_attr_on_pre = false;
+
+ # Predefined abbreviations.
+ public $predef_abbr = array();
+
+
+ ### Parser Implementation ###
+
+ public function __construct() {
+ #
+ # Constructor function. Initialize the parser object.
+ #
+ # Add extra escapable characters before parent constructor
+ # initialize the table.
+ $this->escape_chars .= ':|';
+
+ # Insert extra document, block, and span transformations.
+ # Parent constructor will do the sorting.
+ $this->document_gamut += array(
+ "doFencedCodeBlocks" => 5,
+ "stripFootnotes" => 15,
+ "stripAbbreviations" => 25,
+ "appendFootnotes" => 50,
+ );
+ $this->block_gamut += array(
+ "doFencedCodeBlocks" => 5,
+ "doTables" => 15,
+ "doDefLists" => 45,
+ );
+ $this->span_gamut += array(
+ "doFootnotes" => 5,
+ "doAbbreviations" => 70,
+ );
+
+ parent::__construct();
+ }
+
+
+ # Extra variables used during extra transformations.
+ protected $footnotes = array();
+ protected $footnotes_ordered = array();
+ protected $footnotes_ref_count = array();
+ protected $footnotes_numbers = array();
+ protected $abbr_desciptions = array();
+ protected $abbr_word_re = '';
+
+ # Give the current footnote number.
+ protected $footnote_counter = 1;
+
+
+ protected function setup() {
+ #
+ # Setting up Extra-specific variables.
+ #
+ parent::setup();
+
+ $this->footnotes = array();
+ $this->footnotes_ordered = array();
+ $this->footnotes_ref_count = array();
+ $this->footnotes_numbers = array();
+ $this->abbr_desciptions = array();
+ $this->abbr_word_re = '';
+ $this->footnote_counter = 1;
+
+ foreach ($this->predef_abbr as $abbr_word => $abbr_desc) {
+ if ($this->abbr_word_re)
+ $this->abbr_word_re .= '|';
+ $this->abbr_word_re .= preg_quote($abbr_word);
+ $this->abbr_desciptions[$abbr_word] = trim($abbr_desc);
+ }
+ }
+
+ protected function teardown() {
+ #
+ # Clearing Extra-specific variables.
+ #
+ $this->footnotes = array();
+ $this->footnotes_ordered = array();
+ $this->footnotes_ref_count = array();
+ $this->footnotes_numbers = array();
+ $this->abbr_desciptions = array();
+ $this->abbr_word_re = '';
+
+ parent::teardown();
+ }
+
+
+ ### Extra Attribute Parser ###
+
+ # Expression to use to catch attributes (includes the braces)
+ protected $id_class_attr_catch_re = '\{((?:[ ]*[#.][-_:a-zA-Z0-9]+){1,})[ ]*\}';
+ # Expression to use when parsing in a context when no capture is desired
+ protected $id_class_attr_nocatch_re = '\{(?:[ ]*[#.][-_:a-zA-Z0-9]+){1,}[ ]*\}';
+
+ protected function doExtraAttributes($tag_name, $attr) {
+ #
+ # Parse attributes caught by the $this->id_class_attr_catch_re expression
+ # and return the HTML-formatted list of attributes.
+ #
+ # Currently supported attributes are .class and #id.
+ #
+ if (empty($attr)) return "";
+
+ # Split on components
+ preg_match_all('/[#.][-_:a-zA-Z0-9]+/', $attr, $matches);
+ $elements = $matches[0];
+
+ # handle classes and ids (only first id taken into account)
+ $classes = array();
+ $id = false;
+ foreach ($elements as $element) {
+ if ($element{0} == '.') {
+ $classes[] = substr($element, 1);
+ } else if ($element{0} == '#') {
+ if ($id === false) $id = substr($element, 1);
+ }
+ }
+
+ # compose attributes as string
+ $attr_str = "";
+ if (!empty($id)) {
+ $attr_str .= ' id="'.$id.'"';
+ }
+ if (!empty($classes)) {
+ $attr_str .= ' class="'.implode(" ", $classes).'"';
+ }
+ return $attr_str;
+ }
+
+
+ protected function stripLinkDefinitions($text) {
+ #
+ # Strips link definitions from text, stores the URLs and titles in
+ # hash references.
+ #
+ $less_than_tab = $this->tab_width - 1;
+
+ # Link defs are in the form: ^[id]: url "optional title"
+ $text = preg_replace_callback('{
+ ^[ ]{0,'.$less_than_tab.'}\[(.+)\][ ]?: # id = $1
+ [ ]*
+ \n? # maybe *one* newline
+ [ ]*
+ (?:
+ <(.+?)> # url = $2
+ |
+ (\S+?) # url = $3
+ )
+ [ ]*
+ \n? # maybe one newline
+ [ ]*
+ (?:
+ (?<=\s) # lookbehind for whitespace
+ ["(]
+ (.*?) # title = $4
+ [")]
+ [ ]*
+ )? # title is optional
+ (?:[ ]* '.$this->id_class_attr_catch_re.' )? # $5 = extra id & class attr
+ (?:\n+|\Z)
+ }xm',
+ array(&$this, '_stripLinkDefinitions_callback'),
+ $text);
+ return $text;
+ }
+ protected function _stripLinkDefinitions_callback($matches) {
+ $link_id = strtolower($matches[1]);
+ $url = $matches[2] == '' ? $matches[3] : $matches[2];
+ $this->urls[$link_id] = $url;
+ $this->titles[$link_id] =& $matches[4];
+ $this->ref_attr[$link_id] = $this->doExtraAttributes("", $dummy =& $matches[5]);
+ return ''; # String that will replace the block
+ }
+
+
+ ### HTML Block Parser ###
+
+ # Tags that are always treated as block tags:
+ protected $block_tags_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|form|fieldset|iframe|hr|legend|article|section|nav|aside|hgroup|header|footer|figcaption';
+
+ # Tags treated as block tags only if the opening tag is alone on its line:
+ protected $context_block_tags_re = 'script|noscript|ins|del|iframe|object|source|track|param|math|svg|canvas|audio|video';
+
+ # Tags where markdown="1" default to span mode:
+ protected $contain_span_tags_re = 'p|h[1-6]|li|dd|dt|td|th|legend|address';
+
+ # Tags which must not have their contents modified, no matter where
+ # they appear:
+ protected $clean_tags_re = 'script|math|svg';
+
+ # Tags that do not need to be closed.
+ protected $auto_close_tags_re = 'hr|img|param|source|track';
+
+
+ protected 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 within 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!
+ #
+ if ($this->no_markup) return $text;
+
+ #
+ # Call the HTML-in-Markdown hasher.
+ #
+ list($text, ) = $this->_hashHTMLBlocks_inMarkdown($text);
+
+ return $text;
+ }
+ protected function _hashHTMLBlocks_inMarkdown($text, $indent = 0,
+ $enclosing_tag_re = '', $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_re 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_before_re = '/(?:^\n?|\n\n)*$/';
+ $newline_after_re =
+ '{
+ ^ # Start of text following the tag.
+ (?>[ ]*)? # Optional comment.
+ [ ]*\n # Must be followed by newline.
+ }xs';
+
+ # Regex to match any tag.
+ $block_tag_re =
+ '{
+ ( # $2: Capture whole tag.
+ ? # Any opening or closing tag.
+ (?> # Tag name.
+ '.$this->block_tags_re.' |
+ '.$this->context_block_tags_re.' |
+ '.$this->clean_tags_re.' |
+ (?!\s)'.$enclosing_tag_re.'
+ )
+ (?:
+ (?=[\s"\'/a-zA-Z0-9]) # Allowed characters after tag name.
+ (?>
+ ".*?" | # Double quotes (can contain `>`)
+ \'.*?\' | # Single quotes (can contain `>`)
+ .+? # Anything but quotes and `>`.
+ )*?
+ )?
+ > # End of tag.
+ |
+ # HTML Comment
+ |
+ <\?.*?\?> | <%.*?%> # Processing instruction
+ |
+ # CData Block
+ '. ( !$span ? ' # If not in span.
+ |
+ # Indented code block
+ (?: ^[ ]*\n | ^ | \n[ ]*\n )
+ [ ]{'.($indent+4).'}[^\n]* \n
+ (?>
+ (?: [ ]{'.($indent+4).'}[^\n]* | [ ]* ) \n
+ )*
+ |
+ # Fenced code block marker
+ (?<= ^ | \n )
+ [ ]{0,'.($indent+3).'}(?:~{3,}|`{3,})
+ [ ]*
+ (?:
+ \.?[-_:a-zA-Z0-9]+ # standalone class name
+ |
+ '.$this->id_class_attr_nocatch_re.' # extra attributes
+ )?
+ [ ]*
+ (?= \n )
+ ' : '' ). ' # End (if not is span).
+ |
+ # Code span marker
+ # Note, this regex needs to go after backtick fenced
+ # code blocks but it should also be kept outside of the
+ # "if not in span" condition adding backticks to the parser
+ `+
+ )
+ }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_re, $text, 2,
+ PREG_SPLIT_DELIM_CAPTURE);
+
+ # If in Markdown span mode, add a empty-string span-level hash
+ # after each newline to prevent triggering any block element.
+ if ($span) {
+ $void = $this->hashPart("", ':');
+ $newline = "$void\n";
+ $parts[0] = $void . str_replace("\n", $newline, $parts[0]) . $void;
+ }
+
+ $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.
+ $tag_re = preg_quote($tag); # For use in a regular expression.
+
+ #
+ # Check for: Fenced code block marker.
+ # Note: need to recheck the whole tag to disambiguate backtick
+ # fences from code spans
+ #
+ if (preg_match('{^\n?([ ]{0,'.($indent+3).'})(~{3,}|`{3,})[ ]*(?:\.?[-_:a-zA-Z0-9]+|'.$this->id_class_attr_nocatch_re.')?[ ]*\n?$}', $tag, $capture)) {
+ # Fenced code block marker: find matching end marker.
+ $fence_indent = strlen($capture[1]); # use captured indent in re
+ $fence_re = $capture[2]; # use captured fence in re
+ if (preg_match('{^(?>.*\n)*?[ ]{'.($fence_indent).'}'.$fence_re.'[ ]*(?:\n|$)}', $text,
+ $matches))
+ {
+ # End marker found: pass text unchanged until marker.
+ $parsed .= $tag . $matches[0];
+ $text = substr($text, strlen($matches[0]));
+ }
+ else {
+ # No end marker: just skip it.
+ $parsed .= $tag;
+ }
+ }
+ #
+ # Check for: Indented code block.
+ #
+ else if ($tag{0} == "\n" || $tag{0} == " ") {
+ # Indented code block: pass it unchanged, will be handled
+ # later.
+ $parsed .= $tag;
+ }
+ #
+ # Check for: Code span marker
+ # Note: need to check this after backtick fenced code blocks
+ #
+ else if ($tag{0} == "`") {
+ # Find corresponding end marker.
+ $tag_re = preg_quote($tag);
+ if (preg_match('{^(?>.+?|\n(?!\n))*?(?block_tags_re.')\b}', $tag) ||
+ ( preg_match('{^<(?:'.$this->context_block_tags_re.')\b}', $tag) &&
+ preg_match($newline_before_re, $parsed) &&
+ preg_match($newline_after_re, $text) )
+ )
+ {
+ # Need to parse tag and following text using the HTML parser.
+ list($block_text, $text) =
+ $this->_hashHTMLBlocks_inHTML($tag . $text, "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_re.')\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, "hashClean", false);
+
+ $parsed .= $block_text;
+ }
+ #
+ # Check for: Tag with same name as enclosing tag.
+ #
+ else if ($enclosing_tag_re !== '' &&
+ # Same name as enclosing tag.
+ preg_match('{^?(?:'.$enclosing_tag_re.')\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);
+ }
+ protected 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_re = '
+ {
+ \s* # Eat whitespace before the `markdown` attribute
+ markdown
+ \s*=\s*
+ (?>
+ (["\']) # $1: quote delimiter
+ (.*?) # $2: attribute value
+ \1 # matching delimiter
+ |
+ ([^\s>]*) # $3: unquoted attribute value
+ )
+ () # $4: make $3 always defined (avoid warnings)
+ }xs';
+
+ # Regex to match any tag.
+ $tag_re = '{
+ ( # $2: Capture whole tag.
+ ? # Any opening or closing tag.
+ [\w:$]+ # Tag name.
+ (?:
+ (?=[\s"\'/a-zA-Z0-9]) # Allowed characters after tag name.
+ (?>
+ ".*?" | # 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.
+ # (This pattern makes $base_tag_name_re safe without quoting.)
+ #
+ if (preg_match('/^<([\w:$]*)\b/', $text, $matches))
+ $base_tag_name_re = $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_re, $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_re.')\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_re.'\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_re, $tag, $attr_m) &&
+ preg_match('/^1|block|span$/', $attr_m[2] . $attr_m[3]))
+ {
+ # Remove `markdown` attribute from opening tag.
+ $tag = preg_replace($markdown_attr_re, '', $tag);
+
+ # Check if text inside this tag must be parsed in span mode.
+ $this->mode = $attr_m[2] . $attr_m[3];
+ $span_mode = $this->mode == 'span' || $this->mode != 'block' &&
+ preg_match('{^<(?:'.$this->contain_span_tags_re.')\b}', $tag);
+
+ # Calculate indent before tag.
+ if (preg_match('/(?:^|\n)( *?)(?! ).*?$/', $block_text, $matches)) {
+ $strlen = $this->utf8_strlen;
+ $indent = $strlen($matches[1], 'UTF-8');
+ } else {
+ $indent = 0;
+ }
+
+ # End preceding block with this tag.
+ $block_text .= $tag;
+ $parsed .= $this->$hash_method($block_text);
+
+ # Get enclosing tag name for the ParseMarkdown function.
+ # (This pattern makes $tag_name_re safe without quoting.)
+ preg_match('/^<([\w:$]*)\b/', $tag, $matches);
+ $tag_name_re = $matches[1];
+
+ # Parse the content using the HTML-in-Markdown parser.
+ list ($block_text, $text)
+ = $this->_hashHTMLBlocks_inMarkdown($text, $indent,
+ $tag_name_re, $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 with 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);
+ }
+
+
+ protected function hashClean($text) {
+ #
+ # Called whenever a tag must be hashed when a function inserts a "clean" tag
+ # in $text, it passes through this function and is automaticaly escaped,
+ # blocking invalid nested overlap.
+ #
+ return $this->hashPart($text, 'C');
+ }
+
+
+ protected function doAnchors($text) {
+ #
+ # Turn Markdown link shortcuts into XHTML
tags.
+ #
+ if ($this->in_anchor) return $text;
+ $this->in_anchor = true;
+
+ #
+ # First, handle reference-style links: [link text] [id]
+ #
+ $text = preg_replace_callback('{
+ ( # wrap whole match in $1
+ \[
+ ('.$this->nested_brackets_re.') # link text = $2
+ \]
+
+ [ ]? # one optional space
+ (?:\n[ ]*)? # one optional newline followed by spaces
+
+ \[
+ (.*?) # id = $3
+ \]
+ )
+ }xs',
+ array(&$this, '_doAnchors_reference_callback'), $text);
+
+ #
+ # Next, inline-style links: [link text](url "optional title")
+ #
+ $text = preg_replace_callback('{
+ ( # wrap whole match in $1
+ \[
+ ('.$this->nested_brackets_re.') # link text = $2
+ \]
+ \( # literal paren
+ [ \n]*
+ (?:
+ <(.+?)> # href = $3
+ |
+ ('.$this->nested_url_parenthesis_re.') # href = $4
+ )
+ [ \n]*
+ ( # $5
+ ([\'"]) # quote char = $6
+ (.*?) # Title = $7
+ \6 # matching quote
+ [ \n]* # ignore any spaces/tabs between closing quote and )
+ )? # title is optional
+ \)
+ (?:[ ]? '.$this->id_class_attr_catch_re.' )? # $8 = id/class attributes
+ )
+ }xs',
+ array(&$this, '_doAnchors_inline_callback'), $text);
+
+ #
+ # Last, handle reference-style shortcuts: [link text]
+ # These must come last in case you've also got [link text][1]
+ # or [link text](/foo)
+ #
+ $text = preg_replace_callback('{
+ ( # wrap whole match in $1
+ \[
+ ([^\[\]]+) # link text = $2; can\'t contain [ or ]
+ \]
+ )
+ }xs',
+ array(&$this, '_doAnchors_reference_callback'), $text);
+
+ $this->in_anchor = false;
+ return $text;
+ }
+ protected function _doAnchors_reference_callback($matches) {
+ $whole_match = $matches[1];
+ $link_text = $matches[2];
+ $link_id =& $matches[3];
+
+ if ($link_id == "") {
+ # for shortcut links like [this][] or [this].
+ $link_id = $link_text;
+ }
+
+ # lower-case and turn embedded newlines into spaces
+ $link_id = strtolower($link_id);
+ $link_id = preg_replace('{[ ]?\n}', ' ', $link_id);
+
+ if (isset($this->urls[$link_id])) {
+ $url = $this->urls[$link_id];
+ $url = $this->encodeAttribute($url);
+
+ $result = "titles[$link_id] ) ) {
+ $title = $this->titles[$link_id];
+ $title = $this->encodeAttribute($title);
+ $result .= " title=\"$title\"";
+ }
+ if (isset($this->ref_attr[$link_id]))
+ $result .= $this->ref_attr[$link_id];
+
+ $link_text = $this->runSpanGamut($link_text);
+ $result .= ">$link_text";
+ $result = $this->hashPart($result);
+ }
+ else {
+ $result = $whole_match;
+ }
+ return $result;
+ }
+ protected function _doAnchors_inline_callback($matches) {
+ $whole_match = $matches[1];
+ $link_text = $this->runSpanGamut($matches[2]);
+ $url = $matches[3] == '' ? $matches[4] : $matches[3];
+ $title =& $matches[7];
+ $attr = $this->doExtraAttributes("a", $dummy =& $matches[8]);
+
+
+ $url = $this->encodeAttribute($url);
+
+ $result = "
encodeAttribute($title);
+ $result .= " title=\"$title\"";
+ }
+ $result .= $attr;
+
+ $link_text = $this->runSpanGamut($link_text);
+ $result .= ">$link_text";
+
+ return $this->hashPart($result);
+ }
+
+
+ protected function doImages($text) {
+ #
+ # Turn Markdown image shortcuts into
![]()
tags.
+ #
+ #
+ # First, handle reference-style labeled images: ![alt text][id]
+ #
+ $text = preg_replace_callback('{
+ ( # wrap whole match in $1
+ !\[
+ ('.$this->nested_brackets_re.') # alt text = $2
+ \]
+
+ [ ]? # one optional space
+ (?:\n[ ]*)? # one optional newline followed by spaces
+
+ \[
+ (.*?) # id = $3
+ \]
+
+ )
+ }xs',
+ array(&$this, '_doImages_reference_callback'), $text);
+
+ #
+ # Next, handle inline images: ![alt text](url "optional title")
+ # Don't forget: encode * and _
+ #
+ $text = preg_replace_callback('{
+ ( # wrap whole match in $1
+ !\[
+ ('.$this->nested_brackets_re.') # alt text = $2
+ \]
+ \s? # One optional whitespace character
+ \( # literal paren
+ [ \n]*
+ (?:
+ <(\S*)> # src url = $3
+ |
+ ('.$this->nested_url_parenthesis_re.') # src url = $4
+ )
+ [ \n]*
+ ( # $5
+ ([\'"]) # quote char = $6
+ (.*?) # title = $7
+ \6 # matching quote
+ [ \n]*
+ )? # title is optional
+ \)
+ (?:[ ]? '.$this->id_class_attr_catch_re.' )? # $8 = id/class attributes
+ )
+ }xs',
+ array(&$this, '_doImages_inline_callback'), $text);
+
+ return $text;
+ }
+ protected function _doImages_reference_callback($matches) {
+ $whole_match = $matches[1];
+ $alt_text = $matches[2];
+ $link_id = strtolower($matches[3]);
+
+ if ($link_id == "") {
+ $link_id = strtolower($alt_text); # for shortcut links like ![this][].
+ }
+
+ $alt_text = $this->encodeAttribute($alt_text);
+ if (isset($this->urls[$link_id])) {
+ $url = $this->encodeAttribute($this->urls[$link_id]);
+ $result = "
![\"$alt_text\"";](\"$url\")
titles[$link_id])) {
+ $title = $this->titles[$link_id];
+ $title = $this->encodeAttribute($title);
+ $result .= " title=\"$title\"";
+ }
+ if (isset($this->ref_attr[$link_id]))
+ $result .= $this->ref_attr[$link_id];
+ $result .= $this->empty_element_suffix;
+ $result = $this->hashPart($result);
+ }
+ else {
+ # If there's no such link ID, leave intact:
+ $result = $whole_match;
+ }
+
+ return $result;
+ }
+ protected function _doImages_inline_callback($matches) {
+ $whole_match = $matches[1];
+ $alt_text = $matches[2];
+ $url = $matches[3] == '' ? $matches[4] : $matches[3];
+ $title =& $matches[7];
+ $attr = $this->doExtraAttributes("img", $dummy =& $matches[8]);
+
+ $alt_text = $this->encodeAttribute($alt_text);
+ $url = $this->encodeAttribute($url);
+ $result = "
![\"$alt_text\"";](\"$url\")
encodeAttribute($title);
+ $result .= " title=\"$title\""; # $title already quoted
+ }
+ $result .= $attr;
+ $result .= $this->empty_element_suffix;
+
+ return $this->hashPart($result);
+ }
+
+
+ protected function doHeaders($text) {
+ #
+ # Redefined to add id and class attribute support.
+ #
+ # Setext-style headers:
+ # Header 1 {#header1}
+ # ========
+ #
+ # Header 2 {#header2 .class1 .class2}
+ # --------
+ #
+ $text = preg_replace_callback(
+ '{
+ (^.+?) # $1: Header text
+ (?:[ ]+ '.$this->id_class_attr_catch_re.' )? # $3 = id/class attributes
+ [ ]*\n(=+|-+)[ ]*\n+ # $3: Header footer
+ }mx',
+ array(&$this, '_doHeaders_callback_setext'), $text);
+
+ # atx-style headers:
+ # # Header 1 {#header1}
+ # ## Header 2 {#header2}
+ # ## Header 2 with closing hashes ## {#header3.class1.class2}
+ # ...
+ # ###### Header 6 {.class2}
+ #
+ $text = preg_replace_callback('{
+ ^(\#{1,6}) # $1 = string of #\'s
+ [ ]*
+ (.+?) # $2 = Header text
+ [ ]*
+ \#* # optional closing #\'s (not counted)
+ (?:[ ]+ '.$this->id_class_attr_catch_re.' )? # $3 = id/class attributes
+ [ ]*
+ \n+
+ }xm',
+ array(&$this, '_doHeaders_callback_atx'), $text);
+
+ return $text;
+ }
+ protected function _doHeaders_callback_setext($matches) {
+ if ($matches[3] == '-' && preg_match('{^- }', $matches[1]))
+ return $matches[0];
+ $level = $matches[3]{0} == '=' ? 1 : 2;
+ $attr = $this->doExtraAttributes("h$level", $dummy =& $matches[2]);
+ $block = "
".$this->runSpanGamut($matches[1])."";
+ return "\n" . $this->hashBlock($block) . "\n\n";
+ }
+ protected function _doHeaders_callback_atx($matches) {
+ $level = strlen($matches[1]);
+ $attr = $this->doExtraAttributes("h$level", $dummy =& $matches[3]);
+ $block = "
".$this->runSpanGamut($matches[2])."";
+ return "\n" . $this->hashBlock($block) . "\n\n";
+ }
+
+
+ protected 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;
+ }
+ protected 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));
+ }
+ protected function _doTable_makeAlignAttr($alignname)
+ {
+ if (empty($this->table_align_class_tmpl))
+ return " align=\"$alignname\"";
+
+ $classname = str_replace('%%', $alignname, $this->table_align_class_tmpl);
+ return " class=\"$classname\"";
+ }
+ protected 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] = $this->_doTable_makeAlignAttr('right');
+ else if (preg_match('/^ *:-+: *$/', $s))
+ $attr[$n] = $this->_doTable_makeAlignAttr('center');
+ else if (preg_match('/^ *:-+ *$/', $s))
+ $attr[$n] = $this->_doTable_makeAlignAttr('left');
+ else
+ $attr[$n] = '';
+ }
+
+ # Parsing span elements, including code spans, character escapes,
+ # and inline HTML tags, so that pipes inside those gets ignored.
+ $head = $this->parseSpan($head);
+ $headers = preg_split('/ *[|] */', $head);
+ $col_count = count($headers);
+ $attr = array_pad($attr, $col_count, '');
+
+ # 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) {
+ # Parsing span elements, including code spans, character escapes,
+ # and inline HTML tags, so that pipes inside those gets ignored.
+ $row = $this->parseSpan($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";
+ }
+
+
+ protected 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_re = '(?>
+ ( # $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('{
+ (?>\A\n?|(?<=\n\n))
+ '.$whole_list_re.'
+ }mx',
+ array(&$this, '_doDefLists_callback'), $text);
+
+ return $text;
+ }
+ protected 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";
+ }
+
+
+ protected 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('{
+ (?>\A\n?|\n\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
+ ( # marker space = $2
+ [ ]{0,'.$less_than_tab.'} # whitespace before colon
+ \:[ ]+ # definition mark (colon)
+ )
+ ((?s:.+?)) # definition text = $3
+ (?= \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;
+ }
+ protected 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";
+ }
+ protected function _processDefListItems_callback_dd($matches) {
+ $leading_line = $matches[1];
+ $marker_space = $matches[2];
+ $def = $matches[3];
+
+ if ($leading_line || preg_match('/\n{2,}/', $def)) {
+ # Replace marker with the appropriate whitespace indentation
+ $def = str_repeat(' ', strlen($marker_space)) . $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";
+ }
+
+
+ protected function doFencedCodeBlocks($text) {
+ #
+ # Adding the fenced code block syntax to regular Markdown:
+ #
+ # ~~~
+ # Code block
+ # ~~~
+ #
+ $less_than_tab = $this->tab_width;
+
+ $text = preg_replace_callback('{
+ (?:\n|\A)
+ # 1: Opening marker
+ (
+ (?:~{3,}|`{3,}) # 3 or more tildes/backticks.
+ )
+ [ ]*
+ (?:
+ \.?([-_:a-zA-Z0-9]+) # 2: standalone class name
+ |
+ '.$this->id_class_attr_catch_re.' # 3: Extra attributes
+ )?
+ [ ]* \n # Whitespace and newline following marker.
+
+ # 4: Content
+ (
+ (?>
+ (?!\1 [ ]* \n) # Not a closing marker.
+ .*\n+
+ )+
+ )
+
+ # Closing marker.
+ \1 [ ]* (?= \n )
+ }xm',
+ array(&$this, '_doFencedCodeBlocks_callback'), $text);
+
+ return $text;
+ }
+ protected function _doFencedCodeBlocks_callback($matches) {
+ $classname =& $matches[2];
+ $attrs =& $matches[3];
+ $codeblock = $matches[4];
+ $codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
+ $codeblock = preg_replace_callback('/^\n+/',
+ array(&$this, '_doFencedCodeBlocks_newlines'), $codeblock);
+
+ if ($classname != "") {
+ if ($classname{0} == '.')
+ $classname = substr($classname, 1);
+ $attr_str = ' class="'.$this->code_class_prefix.$classname.'"';
+ } else {
+ $attr_str = $this->doExtraAttributes($this->code_attr_on_pre ? "pre" : "code", $attrs);
+ }
+ $pre_attr_str = $this->code_attr_on_pre ? $attr_str : '';
+ $code_attr_str = $this->code_attr_on_pre ? '' : $attr_str;
+ $codeblock = "
$codeblock
";
+
+ return "\n\n".$this->hashBlock($codeblock)."\n\n";
+ }
+ protected function _doFencedCodeBlocks_newlines($matches) {
+ return str_repeat("
empty_element_suffix",
+ strlen($matches[0]));
+ }
+
+
+ #
+ # Redefining emphasis markers so that emphasis by underscore does not
+ # work in the middle of a word.
+ #
+ protected $em_relist = array(
+ '' => '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? tags
+ #
+ # Strip leading and trailing lines:
+ $text = preg_replace('/\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.
+ # Clean tag hashes & block tag hashes are left alone.
+ $is_p = !preg_match('/^B\x1A[0-9]+B|^C\x1A[0-9]+C$/', $value);
+
+ 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->unhash($text);
+
+ return $text;
+ }
+
+
+ ### Footnotes
+
+ protected function stripFootnotes($text) {
+ #
+ # Strips link definitions from text, stores the URLs and titles in
+ # hash references.
+ #
+ $less_than_tab = $this->tab_width - 1;
+
+ # Link defs are in the form: [^id]: url "optional title"
+ $text = preg_replace_callback('{
+ ^[ ]{0,'.$less_than_tab.'}\[\^(.+?)\][ ]?: # note_id = $1
+ [ ]*
+ \n? # maybe *one* newline
+ ( # text = $2 (no blank lines allowed)
+ (?:
+ .+ # actual text
+ |
+ \n # newlines but
+ (?!\[\^.+?\]:\s)# negative lookahead for footnote marker.
+ (?!\n+[ ]{0,3}\S)# ensure line is not blank and followed
+ # by non-indented content
+ )*
+ )
+ }xm',
+ array(&$this, '_stripFootnotes_callback'),
+ $text);
+ return $text;
+ }
+ protected function _stripFootnotes_callback($matches) {
+ $note_id = $this->fn_id_prefix . $matches[1];
+ $this->footnotes[$note_id] = $this->outdent($matches[2]);
+ return ''; # String that will replace the block
+ }
+
+
+ protected function doFootnotes($text) {
+ #
+ # Replace footnote references in $text [^id] with a special text-token
+ # which will be replaced by the actual footnote marker in appendFootnotes.
+ #
+ if (!$this->in_anchor) {
+ $text = preg_replace('{\[\^(.+?)\]}', "F\x1Afn:\\1\x1A:", $text);
+ }
+ return $text;
+ }
+
+
+ protected function appendFootnotes($text) {
+ #
+ # Append footnote list to text.
+ #
+ $text = preg_replace_callback('{F\x1Afn:(.*?)\x1A:}',
+ array(&$this, '_appendFootnotes_callback'), $text);
+
+ if (!empty($this->footnotes_ordered)) {
+ $text .= "\n\n";
+ $text .= "";
+ }
+ return $text;
+ }
+ protected function _appendFootnotes_callback($matches) {
+ $node_id = $this->fn_id_prefix . $matches[1];
+
+ # Create footnote marker only if it has a corresponding footnote *and*
+ # the footnote hasn't been used by another marker.
+ if (isset($this->footnotes[$node_id])) {
+ $num =& $this->footnotes_numbers[$node_id];
+ if (!isset($num)) {
+ # Transfer footnote content to the ordered list and give it its
+ # number
+ $this->footnotes_ordered[$node_id] = $this->footnotes[$node_id];
+ $this->footnotes_ref_count[$node_id] = 1;
+ $num = $this->footnote_counter++;
+ $ref_count_mark = '';
+ } else {
+ $ref_count_mark = $this->footnotes_ref_count[$node_id] += 1;
+ }
+
+ $attr = "";
+ if ($this->fn_link_class != "") {
+ $class = $this->fn_link_class;
+ $class = $this->encodeAttribute($class);
+ $attr .= " class=\"$class\"";
+ }
+ if ($this->fn_link_title != "") {
+ $title = $this->fn_link_title;
+ $title = $this->encodeAttribute($title);
+ $attr .= " title=\"$title\"";
+ }
+
+ $attr = str_replace("%%", $num, $attr);
+ $node_id = $this->encodeAttribute($node_id);
+
+ return
+ "
".
+ "$num".
+ "";
+ }
+
+ return "[^".$matches[1]."]";
+ }
+
+
+ ### Abbreviations ###
+
+ protected function stripAbbreviations($text) {
+ #
+ # Strips abbreviations from text, stores titles in hash references.
+ #
+ $less_than_tab = $this->tab_width - 1;
+
+ # Link defs are in the form: [id]*: url "optional title"
+ $text = preg_replace_callback('{
+ ^[ ]{0,'.$less_than_tab.'}\*\[(.+?)\][ ]?: # abbr_id = $1
+ (.*) # text = $2 (no blank lines allowed)
+ }xm',
+ array(&$this, '_stripAbbreviations_callback'),
+ $text);
+ return $text;
+ }
+ protected function _stripAbbreviations_callback($matches) {
+ $abbr_word = $matches[1];
+ $abbr_desc = $matches[2];
+ if ($this->abbr_word_re)
+ $this->abbr_word_re .= '|';
+ $this->abbr_word_re .= preg_quote($abbr_word);
+ $this->abbr_desciptions[$abbr_word] = trim($abbr_desc);
+ return ''; # String that will replace the block
+ }
+
+
+ protected function doAbbreviations($text) {
+ #
+ # Find defined abbreviations in text and wrap them in
elements.
+ #
+ if ($this->abbr_word_re) {
+ // cannot use the /x modifier because abbr_word_re may
+ // contain significant spaces:
+ $text = preg_replace_callback('{'.
+ '(?abbr_word_re.')'.
+ '(?![\w\x1A])'.
+ '}',
+ array(&$this, '_doAbbreviations_callback'), $text);
+ }
+ return $text;
+ }
+ protected function _doAbbreviations_callback($matches) {
+ $abbr = $matches[0];
+ if (isset($this->abbr_desciptions[$abbr])) {
+ $desc = $this->abbr_desciptions[$abbr];
+ if (empty($desc)) {
+ return $this->hashPart("$abbr");
+ } else {
+ $desc = $this->encodeAttribute($desc);
+ return $this->hashPart("$abbr");
+ }
+ } else {
+ return $matches[0];
+ }
+ }
+
+}
diff --git a/serendipity_event_markdown/lib/Michelf/MarkdownExtra.inc.php b/serendipity_event_markdown/lib/Michelf/MarkdownExtra.inc.php
new file mode 100644
index 00000000..e11b1ef9
--- /dev/null
+++ b/serendipity_event_markdown/lib/Michelf/MarkdownExtra.inc.php
@@ -0,0 +1,11 @@
+
+#
+# Original Markdown
+# Copyright (c) 2004-2006 John Gruber
+#
+#
+namespace Michelf;
+
+
+# Just force Michelf/Markdown.php to load. This is needed to load
+# the temporary implementation class. See below for details.
+\Michelf\Markdown::MARKDOWNLIB_VERSION;
+
+#
+# Markdown Extra Parser Class
+#
+# Note: Currently the implementation resides in the temporary class
+# \Michelf\MarkdownExtra_TmpImpl (in the same file as \Michelf\Markdown).
+# This makes it easier to propagate the changes between the three different
+# packaging styles of PHP Markdown. Once this issue is resolved, the
+# _MarkdownExtra_TmpImpl will disappear and this one will contain the code.
+#
+
+class MarkdownExtra extends \Michelf\_MarkdownExtra_TmpImpl {
+
+ ### Parser Implementation ###
+
+ # Temporarily, the implemenation is in the _MarkdownExtra_TmpImpl class.
+ # See note above.
+
+}
+
diff --git a/serendipity_event_markdown/lib/Michelf/MarkdownInterface.inc.php b/serendipity_event_markdown/lib/Michelf/MarkdownInterface.inc.php
new file mode 100644
index 00000000..a023ed4e
--- /dev/null
+++ b/serendipity_event_markdown/lib/Michelf/MarkdownInterface.inc.php
@@ -0,0 +1,9 @@
+
+#
+# Original Markdown
+# Copyright (c) 2004-2006 John Gruber
+#
+#
+namespace Michelf;
+
+
+#
+# Markdown Parser Interface
+#
+
+interface MarkdownInterface {
+
+ #
+ # Initialize the parser and return the result of its transform method.
+ # This will work fine for derived classes too.
+ #
+ public static function defaultTransform($text);
+
+ #
+ # Main function. Performs some preprocessing on the input text
+ # and pass it through the document gamut.
+ #
+ public function transform($text);
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/serendipity_event_markdown/lib/Michelf/SmartyPants.php b/serendipity_event_markdown/lib/Michelf/SmartyPants.php
new file mode 100644
index 00000000..ae8933d6
--- /dev/null
+++ b/serendipity_event_markdown/lib/Michelf/SmartyPants.php
@@ -0,0 +1,1031 @@
+
+#
+# Original SmartyPants
+# Copyright (c) 2003-2004 John Gruber
+#
+#
+namespace Michelf;
+
+
+### Pre-Configured SmartyPants Modes ###
+
+# SmartyPants does nothing at all
+const SMARTYPANTS_ATTR_DO_NOTHING = 0;
+# "--" for em-dashes; no en-dash support
+const SMARTYPANTS_ATTR_EM_DASH = 1;
+# "---" for em-dashes; "--" for en-dashes
+const SMARTYPANTS_ATTR_LONG_EM_DASH_SHORT_EN = 2;
+# "--" for em-dashes; "---" for en-dashes
+const SMARTYPANTS_ATTR_SHORT_EM_DASH_LONG_EN = 3;
+
+# Default is SMARTYPANTS_ATTR_EM_DASH
+const SMARTYPANTS_ATTR_DEFAULT = SMARTYPANTS_ATTR_EM_DASH;
+
+
+#
+# SmartyPants Parser Class
+#
+
+class SmartyPants {
+
+ ### Version ###
+
+ const SMARTYPANTSLIB_VERSION = "1.6.0-beta1";
+
+
+ ### Standard Function Interface ###
+
+ public static function defaultTransform($text, $attr = SMARTYPANTS_ATTR_DEFAULT) {
+ #
+ # Initialize the parser and return the result of its transform method.
+ # This will work fine for derived classes too.
+ #
+ # Take parser class on which this function was called.
+ $parser_class = \get_called_class();
+
+ # try to take parser from the static parser list
+ static $parser_list;
+ $parser =& $parser_list[$parser_class][$attr];
+
+ # create the parser if not already set
+ if (!$parser)
+ $parser = new $parser_class($attr);
+
+ # Transform text using parser.
+ return $parser->transform($text);
+ }
+
+
+ ### Configuration Variables ###
+
+ # Partial regex for matching tags to skip
+ public $tags_to_skip = 'pre|code|kbd|script|style|math';
+
+ # Options to specify which transformations to make:
+ protected $do_nothing = 0; # disable all transforms
+ protected $do_quotes = 0;
+ protected $do_backticks = 0; # 1 => double only, 2 => double & single
+ protected $do_dashes = 0; # 1, 2, or 3 for the three modes described above
+ protected $do_ellipses = 0;
+ protected $do_stupefy = 0;
+ protected $convert_quot = 0; # should we translate " entities into normal quotes?
+
+
+ ### Parser Implementation ###
+
+ public function __construct($attr = SMARTYPANTS_ATTR_DEFAULT) {
+ #
+ # Initialize a parser with certain attributes.
+ #
+ # Parser attributes:
+ # 0 : do nothing
+ # 1 : set all
+ # 2 : set all, using old school en- and em- dash shortcuts
+ # 3 : set all, using inverted old school en and em- dash shortcuts
+ #
+ # q : quotes
+ # b : backtick quotes (``double'' only)
+ # B : backtick quotes (``double'' and `single')
+ # d : dashes
+ # D : old school dashes
+ # i : inverted old school dashes
+ # e : ellipses
+ # w : convert " entities to " for Dreamweaver users
+ #
+ if ($attr == "0") {
+ $this->do_nothing = 1;
+ }
+ else if ($attr == "1") {
+ # Do everything, turn all options on.
+ $this->do_quotes = 1;
+ $this->do_backticks = 1;
+ $this->do_dashes = 1;
+ $this->do_ellipses = 1;
+ }
+ else if ($attr == "2") {
+ # Do everything, turn all options on, use old school dash shorthand.
+ $this->do_quotes = 1;
+ $this->do_backticks = 1;
+ $this->do_dashes = 2;
+ $this->do_ellipses = 1;
+ }
+ else if ($attr == "3") {
+ # Do everything, turn all options on, use inverted old school dash shorthand.
+ $this->do_quotes = 1;
+ $this->do_backticks = 1;
+ $this->do_dashes = 3;
+ $this->do_ellipses = 1;
+ }
+ else if ($attr == "-1") {
+ # Special "stupefy" mode.
+ $this->do_stupefy = 1;
+ }
+ else {
+ $chars = preg_split('//', $attr);
+ foreach ($chars as $c){
+ if ($c == "q") { $this->do_quotes = 1; }
+ else if ($c == "b") { $this->do_backticks = 1; }
+ else if ($c == "B") { $this->do_backticks = 2; }
+ else if ($c == "d") { $this->do_dashes = 1; }
+ else if ($c == "D") { $this->do_dashes = 2; }
+ else if ($c == "i") { $this->do_dashes = 3; }
+ else if ($c == "e") { $this->do_ellipses = 1; }
+ else if ($c == "w") { $this->convert_quot = 1; }
+ else {
+ # Unknown attribute option, ignore.
+ }
+ }
+ }
+ }
+
+ public function transform($text) {
+
+ if ($this->do_nothing) {
+ return $text;
+ }
+
+ $tokens = $this->tokenizeHTML($text);
+ $result = '';
+ $in_pre = 0; # Keep track of when we're inside or tags.
+
+ $prev_token_last_char = ""; # This is a cheat, used to get some context
+ # for one-character tokens that consist of
+ # just a quote char. What we do is remember
+ # the last character of the previous text
+ # token, to use as context to curl single-
+ # character quote tokens correctly.
+
+ foreach ($tokens as $cur_token) {
+ if ($cur_token[0] == "tag") {
+ # Don't mess with quotes inside tags.
+ $result .= $cur_token[1];
+ if (preg_match('@<(/?)(?:'.$this->tags_to_skip.')[\s>]@', $cur_token[1], $matches)) {
+ $in_pre = isset($matches[1]) && $matches[1] == '/' ? 0 : 1;
+ }
+ } else {
+ $t = $cur_token[1];
+ $last_char = substr($t, -1); # Remember last char of this token before processing.
+ if (! $in_pre) {
+ $t = $this->educate($t, $prev_token_last_char);
+ }
+ $prev_token_last_char = $last_char;
+ $result .= $t;
+ }
+ }
+
+ return $result;
+ }
+
+
+ protected function educate($t, $prev_token_last_char) {
+ $t = $this->processEscapes($t);
+
+ if ($this->convert_quot) {
+ $t = preg_replace('/"/', '"', $t);
+ }
+
+ if ($this->do_dashes) {
+ if ($this->do_dashes == 1) $t = $this->educateDashes($t);
+ if ($this->do_dashes == 2) $t = $this->educateDashesOldSchool($t);
+ if ($this->do_dashes == 3) $t = $this->educateDashesOldSchoolInverted($t);
+ }
+
+ if ($this->do_ellipses) $t = $this->educateEllipses($t);
+
+ # Note: backticks need to be processed before quotes.
+ if ($this->do_backticks) {
+ $t = $this->educateBackticks($t);
+ if ($this->do_backticks == 2) $t = $this->educateSingleBackticks($t);
+ }
+
+ if ($this->do_quotes) {
+ if ($t == "'") {
+ # Special case: single-character ' token
+ if (preg_match('/\S/', $prev_token_last_char)) {
+ $t = "’";
+ }
+ else {
+ $t = "‘";
+ }
+ }
+ else if ($t == '"') {
+ # Special case: single-character " token
+ if (preg_match('/\S/', $prev_token_last_char)) {
+ $t = "”";
+ }
+ else {
+ $t = "“";
+ }
+ }
+ else {
+ # Normal case:
+ $t = $this->educateQuotes($t);
+ }
+ }
+
+ if ($this->do_stupefy) $t = $this->stupefyEntities($t);
+
+ return $t;
+ }
+
+
+ protected function educateQuotes($_) {
+ #
+ # Parameter: String.
+ #
+ # Returns: The string, with "educated" curly quote HTML entities.
+ #
+ # Example input: "Isn't this fun?"
+ # Example output: “Isn’t this fun?”
+ #
+ # Make our own "punctuation" character class, because the POSIX-style
+ # [:PUNCT:] is only available in Perl 5.6 or later:
+ $punct_class = "[!\"#\\$\\%'()*+,-.\\/:;<=>?\\@\\[\\\\\]\\^_`{|}~]";
+
+ # Special case if the very first character is a quote
+ # followed by punctuation at a non-word-break. Close the quotes by brute force:
+ $_ = preg_replace(
+ array("/^'(?=$punct_class\\B)/", "/^\"(?=$punct_class\\B)/"),
+ array('’', '”'), $_);
+
+
+ # Special case for double sets of quotes, e.g.:
+ # He said, "'Quoted' words in a larger quote."
+ $_ = preg_replace(
+ array("/\"'(?=\w)/", "/'\"(?=\w)/"),
+ array('“‘', '‘“'), $_);
+
+ # Special case for decade abbreviations (the '80s):
+ $_ = preg_replace("/'(?=\\d{2}s)/", '’', $_);
+
+ $close_class = '[^\ \t\r\n\[\{\(\-]';
+ $dec_dashes = '&\#8211;|&\#8212;';
+
+ # Get most opening single quotes:
+ $_ = preg_replace("{
+ (
+ \\s | # a whitespace char, or
+ | # a non-breaking space entity, or
+ -- | # dashes, or
+ &[mn]dash; | # named dash entities
+ $dec_dashes | # or decimal entities
+ &\\#x201[34]; # or hex
+ )
+ ' # the quote
+ (?=\\w) # followed by a word character
+ }x", '\1‘', $_);
+ # Single closing quotes:
+ $_ = preg_replace("{
+ ($close_class)?
+ '
+ (?(1)| # If $1 captured, then do nothing;
+ (?=\\s | s\\b) # otherwise, positive lookahead for a whitespace
+ ) # char or an 's' at a word ending position. This
+ # is a special case to handle something like:
+ # \"Custer's Last Stand.\"
+ }xi", '\1’', $_);
+
+ # Any remaining single quotes should be opening ones:
+ $_ = str_replace("'", '‘', $_);
+
+
+ # Get most opening double quotes:
+ $_ = preg_replace("{
+ (
+ \\s | # a whitespace char, or
+ | # a non-breaking space entity, or
+ -- | # dashes, or
+ &[mn]dash; | # named dash entities
+ $dec_dashes | # or decimal entities
+ &\\#x201[34]; # or hex
+ )
+ \" # the quote
+ (?=\\w) # followed by a word character
+ }x", '\1“', $_);
+
+ # Double closing quotes:
+ $_ = preg_replace("{
+ ($close_class)?
+ \"
+ (?(1)|(?=\\s)) # If $1 captured, then do nothing;
+ # if not, then make sure the next char is whitespace.
+ }x", '\1”', $_);
+
+ # Any remaining quotes should be opening ones.
+ $_ = str_replace('"', '“', $_);
+
+ return $_;
+ }
+
+
+ protected function educateBackticks($_) {
+ #
+ # Parameter: String.
+ # Returns: The string, with ``backticks'' -style double quotes
+ # translated into HTML curly quote entities.
+ #
+ # Example input: ``Isn't this fun?''
+ # Example output: “Isn't this fun?”
+ #
+
+ $_ = str_replace(array("``", "''",),
+ array('“', '”'), $_);
+ return $_;
+ }
+
+
+ protected function educateSingleBackticks($_) {
+ #
+ # Parameter: String.
+ # Returns: The string, with `backticks' -style single quotes
+ # translated into HTML curly quote entities.
+ #
+ # Example input: `Isn't this fun?'
+ # Example output: ‘Isn’t this fun?’
+ #
+
+ $_ = str_replace(array("`", "'",),
+ array('‘', '’'), $_);
+ return $_;
+ }
+
+
+ protected function educateDashes($_) {
+ #
+ # Parameter: String.
+ #
+ # Returns: The string, with each instance of "--" translated to
+ # an em-dash HTML entity.
+ #
+
+ $_ = str_replace('--', '—', $_);
+ return $_;
+ }
+
+
+ protected function educateDashesOldSchool($_) {
+ #
+ # Parameter: String.
+ #
+ # Returns: The string, with each instance of "--" translated to
+ # an en-dash HTML entity, and each "---" translated to
+ # an em-dash HTML entity.
+ #
+
+ # em en
+ $_ = str_replace(array("---", "--",),
+ array('—', '–'), $_);
+ return $_;
+ }
+
+
+ protected function educateDashesOldSchoolInverted($_) {
+ #
+ # Parameter: String.
+ #
+ # Returns: The string, with each instance of "--" translated to
+ # an em-dash HTML entity, and each "---" translated to
+ # an en-dash HTML entity. Two reasons why: First, unlike the
+ # en- and em-dash syntax supported by
+ # EducateDashesOldSchool(), it's compatible with existing
+ # entries written before SmartyPants 1.1, back when "--" was
+ # only used for em-dashes. Second, em-dashes are more
+ # common than en-dashes, and so it sort of makes sense that
+ # the shortcut should be shorter to type. (Thanks to Aaron
+ # Swartz for the idea.)
+ #
+
+ # en em
+ $_ = str_replace(array("---", "--",),
+ array('–', '—'), $_);
+ return $_;
+ }
+
+
+ protected function educateEllipses($_) {
+ #
+ # Parameter: String.
+ # Returns: The string, with each instance of "..." translated to
+ # an ellipsis HTML entity. Also converts the case where
+ # there are spaces between the dots.
+ #
+ # Example input: Huh...?
+ # Example output: Huh…?
+ #
+
+ $_ = str_replace(array("...", ". . .",), '…', $_);
+ return $_;
+ }
+
+
+ protected function stupefyEntities($_) {
+ #
+ # Parameter: String.
+ # Returns: The string, with each SmartyPants HTML entity translated to
+ # its ASCII counterpart.
+ #
+ # Example input: “Hello — world.”
+ # Example output: "Hello -- world."
+ #
+
+ # en-dash em-dash
+ $_ = str_replace(array('–', '—'),
+ array('-', '--'), $_);
+
+ # single quote open close
+ $_ = str_replace(array('‘', '’'), "'", $_);
+
+ # double quote open close
+ $_ = str_replace(array('“', '”'), '"', $_);
+
+ $_ = str_replace('…', '...', $_); # ellipsis
+
+ return $_;
+ }
+
+
+ protected function processEscapes($_) {
+ #
+ # Parameter: String.
+ # Returns: The string, with after processing the following backslash
+ # escape sequences. This is useful if you want to force a "dumb"
+ # quote or other character to appear.
+ #
+ # Escape Value
+ # ------ -----
+ # \\ \
+ # \" "
+ # \' '
+ # \. .
+ # \- -
+ # \` `
+ #
+ $_ = str_replace(
+ array('\\\\', '\"', "\'", '\.', '\-', '\`'),
+ array('\', '"', ''', '.', '-', '`'), $_);
+
+ return $_;
+ }
+
+
+ protected function tokenizeHTML($str) {
+ #
+ # Parameter: String containing HTML markup.
+ # Returns: An array of the tokens comprising the input
+ # string. Each token is either a tag (possibly with nested,
+ # tags contained therein, such as , or a
+ # run of text between tags. Each element of the array is a
+ # two-element array; the first is either 'tag' or 'text';
+ # the second is the actual value.
+ #
+ #
+ # Regular expression derived from the _tokenize() subroutine in
+ # Brad Choate's MTRegex plugin.
+ #
+ #
+ $index = 0;
+ $tokens = array();
+
+ $match = '(?s:)|'. # comment
+ '(?s:<\?.*?\?>)|'. # processing instruction
+ # regular tags
+ '(?:<[/!$]?[-a-zA-Z0-9:]+\b(?>[^"\'>]+|"[^"]*"|\'[^\']*\')*>)';
+
+ $parts = preg_split("{($match)}", $str, -1, PREG_SPLIT_DELIM_CAPTURE);
+
+ foreach ($parts as $part) {
+ if (++$index % 2 && $part != '')
+ $tokens[] = array('text', $part);
+ else
+ $tokens[] = array('tag', $part);
+ }
+ return $tokens;
+ }
+
+}
+
+
+#
+# SmartyPants Typographer Parser Class
+#
+class _SmartyPantsTypographer_TmpImpl extends \Michelf\SmartyPants {
+
+ ### Configuration Variables ###
+
+ # Options to specify which transformations to make:
+ public $do_comma_quotes = 0;
+ public $do_guillemets = 0;
+ public $do_space_emdash = 0;
+ public $do_space_endash = 0;
+ public $do_space_colon = 0;
+ public $do_space_semicolon = 0;
+ public $do_space_marks = 0;
+ public $do_space_frenchquote = 0;
+ public $do_space_thousand = 0;
+ public $do_space_unit = 0;
+
+ # Smart quote characters:
+ # Opening and closing smart double-quotes.
+ public $smart_doublequote_open = '“';
+ public $smart_doublequote_close = '”';
+ public $smart_singlequote_open = '‘';
+ public $smart_singlequote_close = '’'; # Also apostrophe.
+
+ # Space characters for different places:
+ # Space around em-dashes. "He_—_or she_—_should change that."
+ public $space_emdash = " ";
+ # Space around en-dashes. "He_–_or she_–_should change that."
+ public $space_endash = " ";
+ # Space before a colon. "He said_: here it is."
+ public $space_colon = " ";
+ # Space before a semicolon. "That's what I said_; that's what he said."
+ public $space_semicolon = " ";
+ # Space before a question mark and an exclamation mark: "¡_Holà_! What_?"
+ public $space_marks = " ";
+ # Space inside french quotes. "Voici la «_chose_» qui m'a attaqué."
+ public $space_frenchquote = " ";
+ # Space as thousand separator. "On compte 10_000 maisons sur cette liste."
+ public $space_thousand = " ";
+ # Space before a unit abreviation. "This 12_kg of matter costs 10_$."
+ public $space_unit = " ";
+
+ # Expression of a space (breakable or not):
+ public $space = '(?: | | |*160;|*[aA]0;)';
+
+
+ ### Parser Implementation ###
+
+ public function __construct($attr = SMARTYPANTS_ATTR_DEFAULT) {
+ #
+ # Initialize a SmartyPantsTypographer_Parser with certain attributes.
+ #
+ # Parser attributes:
+ # 0 : do nothing
+ # 1 : set all, except dash spacing
+ # 2 : set all, except dash spacing, using old school en- and em- dash shortcuts
+ # 3 : set all, except dash spacing, using inverted old school en and em- dash shortcuts
+ #
+ # Punctuation:
+ # q -> quotes
+ # b -> backtick quotes (``double'' only)
+ # B -> backtick quotes (``double'' and `single')
+ # c -> comma quotes (,,double`` only)
+ # g -> guillemets (<> only)
+ # d -> dashes
+ # D -> old school dashes
+ # i -> inverted old school dashes
+ # e -> ellipses
+ # w -> convert " entities to " for Dreamweaver users
+ #
+ # Spacing:
+ # : -> colon spacing +-
+ # ; -> semicolon spacing +-
+ # m -> question and exclamation marks spacing +-
+ # h -> em-dash spacing +-
+ # H -> en-dash spacing +-
+ # f -> french quote spacing +-
+ # t -> thousand separator spacing -
+ # u -> unit spacing +-
+ # (you can add a plus sign after some of these options denoted by + to
+ # add the space when it is not already present, or you can add a minus
+ # sign to completly remove any space present)
+ #
+ # Initialize inherited SmartyPants parser.
+ parent::__construct($attr);
+
+ if ($attr == "1" || $attr == "2" || $attr == "3") {
+ # Do everything, turn all options on.
+ $this->do_comma_quotes = 1;
+ $this->do_guillemets = 1;
+ $this->do_space_emdash = 1;
+ $this->do_space_endash = 1;
+ $this->do_space_colon = 1;
+ $this->do_space_semicolon = 1;
+ $this->do_space_marks = 1;
+ $this->do_space_frenchquote = 1;
+ $this->do_space_thousand = 1;
+ $this->do_space_unit = 1;
+ }
+ else if ($attr == "-1") {
+ # Special "stupefy" mode.
+ $this->do_stupefy = 1;
+ }
+ else {
+ $chars = preg_split('//', $attr);
+ foreach ($chars as $c){
+ if ($c == "c") { $current =& $this->do_comma_quotes; }
+ else if ($c == "g") { $current =& $this->do_guillemets; }
+ else if ($c == ":") { $current =& $this->do_space_colon; }
+ else if ($c == ";") { $current =& $this->do_space_semicolon; }
+ else if ($c == "m") { $current =& $this->do_space_marks; }
+ else if ($c == "h") { $current =& $this->do_space_emdash; }
+ else if ($c == "H") { $current =& $this->do_space_endash; }
+ else if ($c == "f") { $current =& $this->do_space_frenchquote; }
+ else if ($c == "t") { $current =& $this->do_space_thousand; }
+ else if ($c == "u") { $current =& $this->do_space_unit; }
+ else if ($c == "+") {
+ $current = 2;
+ unset($current);
+ }
+ else if ($c == "-") {
+ $current = -1;
+ unset($current);
+ }
+ else {
+ # Unknown attribute option, ignore.
+ }
+ $current = 1;
+ }
+ }
+ }
+
+
+ function educate($t, $prev_token_last_char) {
+ $t = parent::educate($t, $prev_token_last_char);
+
+ if ($this->do_comma_quotes) $t = $this->educateCommaQuotes($t);
+ if ($this->do_guillemets) $t = $this->educateGuillemets($t);
+
+ if ($this->do_space_emdash) $t = $this->spaceEmDash($t);
+ if ($this->do_space_endash) $t = $this->spaceEnDash($t);
+ if ($this->do_space_colon) $t = $this->spaceColon($t);
+ if ($this->do_space_semicolon) $t = $this->spaceSemicolon($t);
+ if ($this->do_space_marks) $t = $this->spaceMarks($t);
+ if ($this->do_space_frenchquote) $t = $this->spaceFrenchQuotes($t);
+ if ($this->do_space_thousand) $t = $this->spaceThousandSeparator($t);
+ if ($this->do_space_unit) $t = $this->spaceUnit($t);
+
+ return $t;
+ }
+
+
+ protected function educateQuotes($_) {
+ #
+ # Parameter: String.
+ #
+ # Returns: The string, with "educated" curly quote HTML entities.
+ #
+ # Example input: "Isn't this fun?"
+ # Example output: “Isn’t this fun?”
+ #
+ $dq_open = $this->smart_doublequote_open;
+ $dq_close = $this->smart_doublequote_close;
+ $sq_open = $this->smart_singlequote_open;
+ $sq_close = $this->smart_singlequote_close;
+
+ # Make our own "punctuation" character class, because the POSIX-style
+ # [:PUNCT:] is only available in Perl 5.6 or later:
+ $punct_class = "[!\"#\\$\\%'()*+,-.\\/:;<=>?\\@\\[\\\\\]\\^_`{|}~]";
+
+ # Special case if the very first character is a quote
+ # followed by punctuation at a non-word-break. Close the quotes by brute force:
+ $_ = preg_replace(
+ array("/^'(?=$punct_class\\B)/", "/^\"(?=$punct_class\\B)/"),
+ array($sq_close, $dq_close), $_);
+
+ # Special case for double sets of quotes, e.g.:
+ # He said, "'Quoted' words in a larger quote."
+ $_ = preg_replace(
+ array("/\"'(?=\w)/", "/'\"(?=\w)/"),
+ array($dq_open.$sq_open, $sq_open.$dq_open), $_);
+
+ # Special case for decade abbreviations (the '80s):
+ $_ = preg_replace("/'(?=\\d{2}s)/", $sq_close, $_);
+
+ $close_class = '[^\ \t\r\n\[\{\(\-]';
+ $dec_dashes = '&\#8211;|&\#8212;';
+
+ # Get most opening single quotes:
+ $_ = preg_replace("{
+ (
+ \\s | # a whitespace char, or
+ | # a non-breaking space entity, or
+ -- | # dashes, or
+ &[mn]dash; | # named dash entities
+ $dec_dashes | # or decimal entities
+ &\\#x201[34]; # or hex
+ )
+ ' # the quote
+ (?=\\w) # followed by a word character
+ }x", '\1'.$sq_open, $_);
+ # Single closing quotes:
+ $_ = preg_replace("{
+ ($close_class)?
+ '
+ (?(1)| # If $1 captured, then do nothing;
+ (?=\\s | s\\b) # otherwise, positive lookahead for a whitespace
+ ) # char or an 's' at a word ending position. This
+ # is a special case to handle something like:
+ # \"Custer's Last Stand.\"
+ }xi", '\1'.$sq_close, $_);
+
+ # Any remaining single quotes should be opening ones:
+ $_ = str_replace("'", $sq_open, $_);
+
+
+ # Get most opening double quotes:
+ $_ = preg_replace("{
+ (
+ \\s | # a whitespace char, or
+ | # a non-breaking space entity, or
+ -- | # dashes, or
+ &[mn]dash; | # named dash entities
+ $dec_dashes | # or decimal entities
+ &\\#x201[34]; # or hex
+ )
+ \" # the quote
+ (?=\\w) # followed by a word character
+ }x", '\1'.$dq_open, $_);
+
+ # Double closing quotes:
+ $_ = preg_replace("{
+ ($close_class)?
+ \"
+ (?(1)|(?=\\s)) # If $1 captured, then do nothing;
+ # if not, then make sure the next char is whitespace.
+ }x", '\1'.$dq_close, $_);
+
+ # Any remaining quotes should be opening ones.
+ $_ = str_replace('"', $dq_open, $_);
+
+ return $_;
+ }
+
+
+ protected function educateCommaQuotes($_) {
+ #
+ # Parameter: String.
+ # Returns: The string, with ,,comma,, -style double quotes
+ # translated into HTML curly quote entities.
+ #
+ # Example input: ,,Isn't this fun?,,
+ # Example output: „Isn't this fun?„
+ #
+ # Note: this is meant to be used alongside with backtick quotes; there is
+ # no language that use only lower quotations alone mark like in the example.
+ #
+ $_ = str_replace(",,", '„', $_);
+ return $_;
+ }
+
+
+ protected function educateGuillemets($_) {
+ #
+ # Parameter: String.
+ # Returns: The string, with << guillemets >> -style quotes
+ # translated into HTML guillemets entities.
+ #
+ # Example input: << Isn't this fun? >>
+ # Example output: „ Isn't this fun? „
+ #
+ $_ = preg_replace("/(?:<|<){2}/", '«', $_);
+ $_ = preg_replace("/(?:>|>){2}/", '»', $_);
+ return $_;
+ }
+
+
+ protected function spaceFrenchQuotes($_) {
+ #
+ # Parameters: String, replacement character, and forcing flag.
+ # Returns: The string, with appropriates spaces replaced
+ # inside french-style quotes, only french quotes.
+ #
+ # Example input: Quotes in « French », »German« and »Finnish» style.
+ # Example output: Quotes in «_French_», »German« and »Finnish» style.
+ #
+ $opt = ( $this->do_space_frenchquote == 2 ? '?' : '' );
+ $chr = ( $this->do_space_frenchquote != -1 ? $this->space_frenchquote : '' );
+
+ # Characters allowed immediatly outside quotes.
+ $outside_char = $this->space . '|\s|[.,:;!?\[\](){}|@*~=+-]|¡|¿';
+
+ $_ = preg_replace(
+ "/(^|$outside_char)(«|«|›|‹)$this->space$opt/",
+ "\\1\\2$chr", $_);
+ $_ = preg_replace(
+ "/$this->space$opt(»|»|‹|›)($outside_char|$)/",
+ "$chr\\1\\2", $_);
+ return $_;
+ }
+
+
+ protected function spaceColon($_) {
+ #
+ # Parameters: String, replacement character, and forcing flag.
+ # Returns: The string, with appropriates spaces replaced
+ # before colons.
+ #
+ # Example input: Ingredients : fun.
+ # Example output: Ingredients_: fun.
+ #
+ $opt = ( $this->do_space_colon == 2 ? '?' : '' );
+ $chr = ( $this->do_space_colon != -1 ? $this->space_colon : '' );
+
+ $_ = preg_replace("/$this->space$opt(:)(\\s|$)/m",
+ "$chr\\1\\2", $_);
+ return $_;
+ }
+
+
+ protected function spaceSemicolon($_) {
+ #
+ # Parameters: String, replacement character, and forcing flag.
+ # Returns: The string, with appropriates spaces replaced
+ # before semicolons.
+ #
+ # Example input: There he goes ; there she goes.
+ # Example output: There he goes_; there she goes.
+ #
+ $opt = ( $this->do_space_semicolon == 2 ? '?' : '' );
+ $chr = ( $this->do_space_semicolon != -1 ? $this->space_semicolon : '' );
+
+ $_ = preg_replace("/$this->space(;)(?=\\s|$)/m",
+ " \\1", $_);
+ $_ = preg_replace("/((?:^|\\s)(?>[^&;\\s]+|?[a-zA-Z0-9]+;)*)".
+ " $opt(;)(?=\\s|$)/m",
+ "\\1$chr\\2", $_);
+ return $_;
+ }
+
+
+ protected function spaceMarks($_) {
+ #
+ # Parameters: String, replacement character, and forcing flag.
+ # Returns: The string, with appropriates spaces replaced
+ # around question and exclamation marks.
+ #
+ # Example input: ¡ Holà ! What ?
+ # Example output: ¡_Holà_! What_?
+ #
+ $opt = ( $this->do_space_marks == 2 ? '?' : '' );
+ $chr = ( $this->do_space_marks != -1 ? $this->space_marks : '' );
+
+ // Regular marks.
+ $_ = preg_replace("/$this->space$opt([?!]+)/", "$chr\\1", $_);
+
+ // Inverted marks.
+ $imarks = "(?:¡|¡|¡|[Aa]1;|¿|¿|¿|[Bb][Ff];)";
+ $_ = preg_replace("/($imarks+)$this->space$opt/", "\\1$chr", $_);
+
+ return $_;
+ }
+
+
+ protected function spaceEmDash($_) {
+ #
+ # Parameters: String, two replacement characters separated by a hyphen (`-`),
+ # and forcing flag.
+ #
+ # Returns: The string, with appropriates spaces replaced
+ # around dashes.
+ #
+ # Example input: Then — without any plan — the fun happend.
+ # Example output: Then_—_without any plan_—_the fun happend.
+ #
+ $opt = ( $this->do_space_emdash == 2 ? '?' : '' );
+ $chr = ( $this->do_space_emdash != -1 ? $this->space_emdash : '' );
+ $_ = preg_replace("/$this->space$opt(—|—)$this->space$opt/",
+ "$chr\\1$chr", $_);
+ return $_;
+ }
+
+
+ protected function spaceEnDash($_) {
+ #
+ # Parameters: String, two replacement characters separated by a hyphen (`-`),
+ # and forcing flag.
+ #
+ # Returns: The string, with appropriates spaces replaced
+ # around dashes.
+ #
+ # Example input: Then — without any plan — the fun happend.
+ # Example output: Then_—_without any plan_—_the fun happend.
+ #
+ $opt = ( $this->do_space_endash == 2 ? '?' : '' );
+ $chr = ( $this->do_space_endash != -1 ? $this->space_endash : '' );
+ $_ = preg_replace("/$this->space$opt(–|–)$this->space$opt/",
+ "$chr\\1$chr", $_);
+ return $_;
+ }
+
+
+ protected function spaceThousandSeparator($_) {
+ #
+ # Parameters: String, replacement character, and forcing flag.
+ # Returns: The string, with appropriates spaces replaced
+ # inside numbers (thousand separator in french).
+ #
+ # Example input: Il y a 10 000 insectes amusants dans ton jardin.
+ # Example output: Il y a 10_000 insectes amusants dans ton jardin.
+ #
+ $chr = ( $this->do_space_thousand != -1 ? $this->space_thousand : '' );
+ $_ = preg_replace('/([0-9]) ([0-9])/', "\\1$chr\\2", $_);
+ return $_;
+ }
+
+
+ protected $units = '
+ ### Metric units (with prefixes)
+ (?:
+ p |
+ µ | µ | &\#0*181; | &\#[xX]0*[Bb]5; |
+ [mcdhkMGT]
+ )?
+ (?:
+ [mgstAKNJWCVFSTHBL]|mol|cd|rad|Hz|Pa|Wb|lm|lx|Bq|Gy|Sv|kat|
+ Ω | Ohm | Ω | &\#0*937; | &\#[xX]0*3[Aa]9;
+ )|
+ ### Computers units (KB, Kb, TB, Kbps)
+ [kKMGT]?(?:[oBb]|[oBb]ps|flops)|
+ ### Money
+ ¢ | ¢ | &\#0*162; | &\#[xX]0*[Aa]2; |
+ M?(?:
+ £ | £ | &\#0*163; | &\#[xX]0*[Aa]3; |
+ ¥ | ¥ | &\#0*165; | &\#[xX]0*[Aa]5; |
+ € | € | &\#0*8364; | &\#[xX]0*20[Aa][Cc]; |
+ $
+ )|
+ ### Other units
+ (?: ° | ° | &\#0*176; | &\#[xX]0*[Bb]0; ) [CF]? |
+ %|pt|pi|M?px|em|en|gal|lb|[NSEOW]|[NS][EOW]|ha|mbar
+ '; //x
+
+ protected function spaceUnit($_) {
+ #
+ # Parameters: String, replacement character, and forcing flag.
+ # Returns: The string, with appropriates spaces replaced
+ # before unit symbols.
+ #
+ # Example input: Get 3 mol of fun for 3 $.
+ # Example output: Get 3_mol of fun for 3_$.
+ #
+ $opt = ( $this->do_space_unit == 2 ? '?' : '' );
+ $chr = ( $this->do_space_unit != -1 ? $this->space_unit : '' );
+
+ $_ = preg_replace('/
+ (?:([0-9])[ ]'.$opt.') # Number followed by space.
+ ('.$this->units.') # Unit.
+ (?![a-zA-Z0-9]) # Negative lookahead for other unit characters.
+ /x',
+ "\\1$chr\\2", $_);
+
+ return $_;
+ }
+
+
+ protected function spaceAbbr($_) {
+ #
+ # Parameters: String, replacement character, and forcing flag.
+ # Returns: The string, with appropriates spaces replaced
+ # around abbreviations.
+ #
+ # Example input: Fun i.e. something pleasant.
+ # Example output: Fun i.e._something pleasant.
+ #
+ $opt = ( $this->do_space_abbr == 2 ? '?' : '' );
+
+ $_ = preg_replace("/(^|\s)($this->abbr_after) $opt/m",
+ "\\1\\2$this->space_abbr", $_);
+ $_ = preg_replace("/( )$opt($this->abbr_sp_before)(?![a-zA-Z'])/m",
+ "\\1$this->space_abbr\\2", $_);
+ return $_;
+ }
+
+
+ protected function stupefyEntities($_) {
+ #
+ # Adding angle quotes and lower quotes to SmartyPants's stupefy mode.
+ #
+ $_ = parent::stupefyEntities($_);
+
+ $_ = str_replace(array('„', '«', '»'), '"', $_);
+
+ return $_;
+ }
+
+
+ protected function processEscapes($_) {
+ #
+ # Adding a few more escapes to SmartyPants's escapes:
+ #
+ # Escape Value
+ # ------ -----
+ # \, ,
+ # \< <
+ # \> >
+ #
+ $_ = parent::processEscapes($_);
+
+ $_ = str_replace(
+ array('\,', '\<', '\>', '\<', '\>'),
+ array(',', '<', '>', '<', '>'), $_);
+
+ return $_;
+ }
+}
diff --git a/serendipity_event_markdown/lib/Michelf/SmartyPantsTypographer.php b/serendipity_event_markdown/lib/Michelf/SmartyPantsTypographer.php
new file mode 100644
index 00000000..04b26ba2
--- /dev/null
+++ b/serendipity_event_markdown/lib/Michelf/SmartyPantsTypographer.php
@@ -0,0 +1,40 @@
+
+#
+# Original SmartyPants
+# Copyright (c) 2003-2004 John Gruber
+#
+#
+namespace Michelf;
+
+
+# Just force Michelf/SmartyPants.php to load. This is needed to load
+# the temporary implementation class. See below for details.
+\Michelf\SmartyPants::SMARTYPANTSLIB_VERSION;
+
+
+#
+# SmartyPants Typographer Parser Class
+#
+# Note: Currently the implementation resides in the temporary class
+# \Michelf\_SmartyPantsTypographer_TmpImpl (in the same file as
+# \Michelf\SmartyPants). This makes it easier to propagate the changes between
+# the three different packaging styles of PHP SmartyPants. Once this issue is
+# resolved, the _SmartyPantsTypographer_TmpImpl class will disappear and this
+# one will contain the code.
+#
+use \Michelf\SmartyPants;
+
+class SmartyPantsTypographer extends \Michelf\_SmartyPantsTypographer_TmpImpl {
+
+ ### Parser Implementation ###
+
+ # Temporarily, the implemenation is in the _SmartyPantsTypographer_TmpImpl
+ # class. See note above.
+
+}
diff --git a/serendipity_event_markdown/lib/Readme.md b/serendipity_event_markdown/lib/Readme.md
new file mode 100644
index 00000000..6430c47b
--- /dev/null
+++ b/serendipity_event_markdown/lib/Readme.md
@@ -0,0 +1,271 @@
+PHP Markdown
+============
+
+PHP Markdown Lib 1.4.0 - 29 Nov 2013
+
+by Michel Fortin
+
+
+based on Markdown by John Gruber
+
+
+
+Introduction
+------------
+
+This is a library package that includes the PHP Markdown parser and its
+sibling PHP Markdown Extra with additional features.
+
+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 actually two things: a plain text markup syntax, and a
+software tool, originally 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.
+
+* [Full documentation of the Markdown syntax]()
+ - Daring Fireball (John Gruber)
+* [Markdown Extra syntax additions]()
+ - Michel Fortin
+
+
+Requirement
+-----------
+
+This library package requires PHP 5.3 or later.
+
+Note: The older plugin/library hybrid package for PHP Markdown and
+PHP Markdown Extra is still maintained and will work with PHP 4.0.5 and later.
+
+Before PHP 5.3.7, pcre.backtrack_limit defaults to 100 000, which is too small
+in many situations. You might need to set it to higher values. Later PHP
+releases defaults to 1 000 000, which is usually fine.
+
+
+Usage
+-----
+
+This library package is meant to be used with class autoloading. For autoloading
+to work, your project needs have setup a PSR-0-compatible autoloader. See the
+included Readme.php file for a minimal autoloader setup. (If you cannot use
+autoloading, see below.)
+
+With class autoloading in place, putting the 'Michelf' folder in your
+include path should be enough for this to work:
+
+ use \Michelf\Markdown;
+ $my_html = Markdown::defaultTransform($my_text);
+
+Markdown Extra syntax is also available the same way:
+
+ use \Michelf\MarkdownExtra;
+ $my_html = MarkdownExtra::defaultTransform($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 `transform`
+function call. This is an example with [PHP SmartyPants][psp]:
+
+ use \Michelf\Markdown, \Michelf\SmartyPants;
+ $my_html = Markdown::defaultTransform($my_text);
+ $my_html = SmartyPants::defaultTransform($my_html);
+
+All these examples are using the static `defaultTransform` static function
+found inside the parser class. If you want to customize the parser
+configuration, you can also instantiate it directly and change some
+configuration variables:
+
+ use \Michelf\MarkdownExtra;
+ $parser = new MarkdownExtra;
+ $parser->fn_id_prefix = "post22-";
+ $my_html = $parser->transform($my_text);
+
+To learn more, see the full list of [configuration variables].
+
+ [configuration variables]: http://michelf.ca/projects/php-markdown/configuration/
+
+
+### Usage without an autoloader
+
+If you cannot use class autoloading, you can still use `include` or `require`
+to access the parser. To load the `\Michelf\Markdown` parser, do it this way:
+
+ require_once 'Michelf/Markdown.inc.php';
+
+Or, if you need the `\Michelf\MarkdownExtra` parser:
+
+ require_once 'Michelf/MarkdownExtra.inc.php';
+
+While the plain `.php` files depend on autoloading to work correctly, using the
+`.inc.php` files instead will eagerly load the dependencies that would be
+loaded on demand if you were using autoloading.
+
+
+Public API and Versioning Policy
+---------------------------------
+
+Version numbers are of the form *major*.*minor*.*patch*.
+
+The public API of PHP Markdown consist of the two parser classes `Markdown`
+and `MarkdownExtra`, their constructors, the `transform` and `defaultTransform`
+functions and their configuration variables. The public API is stable for
+a given major version number. It might get additions when the minor version
+number increments.
+
+**Protected members are not considered public API.** This is unconventional
+and deserves an explanation. Incrementing the major version number every time
+the underlying implementation of something changes is going to give
+nonessential version numbers for the vast majority of people who just use the
+parser. Protected members are meant to create parser subclasses that behave in
+different ways. Very few people create parser subclasses. I don't want to
+discourage it by making everything private, but at the same time I can't
+guarantee any stable hook between versions if you use protected members.
+
+**Syntax changes** will increment the minor number for new features, and the
+patch number for small corrections. A *new feature* is something that needs a
+change in the syntax documentation. Note that since PHP Markdown Lib includes
+two parsers, a syntax change for either of them will increment the minor
+number. Also note that there is nothing perfectly backward-compatible with the
+Markdown syntax: all inputs are always valid, so new features always replace
+something that was previously legal, although generally nonsensical to do.
+
+
+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.
+
+If you have a problem where Markdown gives you an empty result, first check
+that the backtrack limit is not too low by running `php --info | grep pcre`.
+See Installation and Requirement above for details.
+
+
+Development and Testing
+-----------------------
+
+Pull requests for fixing bugs are welcome. Proposed new features are
+going meticulously reviewed -- taking into account backward compatibility,
+potential side effects, and future extensibility -- before deciding on
+acceptance or rejection.
+
+If you make a pull request that includes changes to the parser please add
+tests for what is being changed to [MDTest][] and make a pull request there
+too.
+
+ [MDTest]: https://github.com/michelf/mdtest/
+
+
+Version History
+---------------
+
+PHP Markdown Lib 1.4.0 (29 Nov 2013)
+
+* Added support for the `tel:` URL scheme in automatic links.
+
+
+
+ It gets converted to this (note the `tel:` prefix becomes invisible):
+
+ +1-111-111-1111
+
+* Added backtick fenced code blocks to MarkdownExtra, originally from
+ Github-Flavored Markdown.
+
+* Added an interface called MarkdownInterface implemented by both
+ the Markdown and MarkdownExtra parsers. You can use the interface if
+ you want to create a mockup parser object for unit testing.
+
+* For those of you who cannot use class autoloading, you can now
+ include `Michelf/Markdown.inc.php` or `Michelf/MarkdownExtra.inc.php` (note
+ the `.inc.php` extension) to automatically include other files required
+ by the parser.
+
+
+PHP Markdown Lib 1.3 (11 Apr 2013)
+
+This is the first release of PHP Markdown Lib. This package requires PHP
+version 5.3 or later and is designed to work with PSR-0 autoloading and,
+optionally with Composer. Here is a list of the changes since
+PHP Markdown Extra 1.2.6:
+
+* Plugin interface for WordPress and other systems is no longer present in
+ the Lib package. The classic package is still available if you need it:
+
+
+* Added `public` and `protected` protection attributes, plus a section about
+ what is "public API" and what isn't in the Readme file.
+
+* Changed HTML output for footnotes: now instead of adding `rel` and `rev`
+ attributes, footnotes links have the class name `footnote-ref` and
+ backlinks `footnote-backref`.
+
+* Fixed some regular expressions to make PCRE not shout warnings about POSIX
+ collation classes (dependent on your version of PCRE).
+
+* Added optional class and id attributes to images and links using the same
+ syntax as for headers:
+
+ [link](url){#id .class}
+ ![img](url){#id .class}
+
+ It work too for reference-style links and images. In this case you need
+ to put those attributes at the reference definition:
+
+ [link][linkref] or [linkref]
+ ![img][linkref]
+
+ [linkref]: url "optional title" {#id .class}
+
+* Fixed a PHP notice message triggered when some table column separator
+ markers are missing on the separator line below column headers.
+
+* Fixed a small mistake that could cause the parser to retain an invalid
+ state related to parsing links across multiple runs. This was never
+ observed (that I know of), but it's still worth fixing.
+
+
+Copyright and License
+---------------------
+
+PHP Markdown Lib
+Copyright (c) 2004-2013 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/serendipity_event_markdown/lib/Readme.php b/serendipity_event_markdown/lib/Readme.php
new file mode 100644
index 00000000..d007b119
--- /dev/null
+++ b/serendipity_event_markdown/lib/Readme.php
@@ -0,0 +1,31 @@
+
+
+
+
+ PHP Markdown Lib - Readme
+
+
+
+
+
diff --git a/serendipity_event_markdown/lib/composer.json b/serendipity_event_markdown/lib/composer.json
new file mode 100644
index 00000000..45abc677
--- /dev/null
+++ b/serendipity_event_markdown/lib/composer.json
@@ -0,0 +1,31 @@
+{
+ "name": "michelf/php-markdown",
+ "type": "library",
+ "description": "PHP Markdown",
+ "homepage": "http://michelf.ca/projects/php-markdown/",
+ "keywords": ["markdown"],
+ "license": "BSD-3-Clause",
+ "authors": [
+ {
+ "name": "Michel Fortin",
+ "email": "michel.fortin@michelf.ca",
+ "homepage": "http://michelf.ca/",
+ "role": "Developer"
+ },
+ {
+ "name": "John Gruber",
+ "homepage": "http://daringfireball.net/"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "autoload": {
+ "psr-0": { "Michelf": "" }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-lib": "1.4.x-dev"
+ }
+ }
+}
diff --git a/serendipity_event_markdown/markdown.php b/serendipity_event_markdown/markdown.php
index 88447b75..31e5991a 100644
--- a/serendipity_event_markdown/markdown.php
+++ b/serendipity_event_markdown/markdown.php
@@ -2,17 +2,17 @@
#
# Markdown - A text-to-HTML conversion tool for web writers
#
-# PHP Markdown
-# Copyright (c) 2004-2012 Michel Fortin
-#
+# PHP Markdown
+# Copyright (c) 2004-2013 Michel Fortin
+#
#
-# Original Markdown
+# Original Markdown
# Copyright (c) 2004-2006 John Gruber
#
#
-define( 'MARKDOWN_VERSION', "1.0.1o" ); # Sun 8 Jan 2012
+define( 'MARKDOWN_VERSION', "1.0.2" ); # 29 Nov 2013
#
@@ -60,16 +60,16 @@ function Markdown($text) {
/*
Plugin Name: Markdown
-Plugin URI: http://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.1o
+Plugin URI: http://michelf.ca/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.2
Author: Michel Fortin
-Author URI: http://michelf.com/
+Author URI: http://michelf.ca/
*/
if (isset($wp_version)) {
# More details about how it works here:
- #
+ #
# Post content and excerpts
# - Remove WordPress paragraph generator.
@@ -147,7 +147,7 @@ function identify_modifier_markdown() {
'authors' => 'Michel Fortin and John Gruber',
'licence' => 'BSD-like',
'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...'
+ '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...'
);
}
@@ -190,17 +190,7 @@ if (strcasecmp(substr(__FILE__, -16), "classTextile.php") == 0) {
class Markdown_Parser {
- # Regex to match balanced [brackets].
- # Needed to insert a maximum bracked depth while converting to PHP.
- var $nested_brackets_depth = 6;
- var $nested_brackets_re;
-
- var $nested_url_parenthesis_depth = 4;
- var $nested_url_parenthesis_re;
-
- # Table of hash values for escaped characters:
- var $escape_chars = '\`*_{}[]()>#+-.!';
- var $escape_chars_re;
+ ### Configuration Variables ###
# Change to ">" for HTML output.
var $empty_element_suffix = MARKDOWN_EMPTY_ELEMENT_SUFFIX;
@@ -215,6 +205,21 @@ class Markdown_Parser {
var $predef_titles = array();
+ ### Parser Implementation ###
+
+ # Regex to match balanced [brackets].
+ # Needed to insert a maximum bracked depth while converting to PHP.
+ var $nested_brackets_depth = 6;
+ var $nested_brackets_re;
+
+ var $nested_url_parenthesis_depth = 4;
+ var $nested_url_parenthesis_re;
+
+ # Table of hash values for escaped characters:
+ var $escape_chars = '\`*_{}[]()>#+-.!';
+ var $escape_chars_re;
+
+
function Markdown_Parser() {
#
# Constructor function. Initialize appropriate member variables.
@@ -258,7 +263,7 @@ class Markdown_Parser {
$this->titles = $this->predef_titles;
$this->html_hashes = array();
- $in_anchor = false;
+ $this->in_anchor = false;
}
function teardown() {
@@ -382,7 +387,9 @@ class Markdown_Parser {
#
$block_tags_a_re = 'ins|del';
$block_tags_b_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|'.
- 'script|noscript|form|fieldset|iframe|math';
+ 'script|noscript|form|fieldset|iframe|math|svg|'.
+ 'article|section|nav|aside|hgroup|header|footer|'.
+ 'figure';
# Regular expression for the content of a block tag.
$nested_tags_level = 4;
@@ -907,7 +914,7 @@ class Markdown_Parser {
if ($matches[2] == '-' && preg_match('{^-(?: |$)}', $matches[1]))
return $matches[0];
- $level = $matches[2][0] == '=' ? 1 : 2;
+ $level = $matches[2]{0} == '=' ? 1 : 2;
$block = "".$this->runSpanGamut($matches[1])."";
return "\n" . $this->hashBlock($block) . "\n\n";
}
@@ -1203,7 +1210,7 @@ class Markdown_Parser {
} else {
# Other closing marker: close one em or strong and
# change current token state to match the other
- $token_stack[0] = str_repeat($token[0], 3-$token_len);
+ $token_stack[0] = str_repeat($token{0}, 3-$token_len);
$tag = $token_len == 2 ? "strong" : "em";
$span = $text_stack[0];
$span = $this->runSpanGamut($span);
@@ -1228,7 +1235,7 @@ class Markdown_Parser {
} else {
# Reached opening three-char emphasis marker. Push on token
# stack; will be handled by the special condition above.
- $em = $token[0];
+ $em = $token{0};
$strong = "$em$em";
array_unshift($token_stack, $token);
array_unshift($text_stack, '');
@@ -1440,9 +1447,16 @@ class Markdown_Parser {
>
}xi',
array(&$this, '_doAutoLinks_email_callback'), $text);
+ $text = preg_replace_callback('{<(tel:([^\'">\s]+))>}i',array(&$this, '_doAutoLinks_tel_callback'), $text);
return $text;
}
+ function _doAutoLinks_tel_callback($matches) {
+ $url = $this->encodeAttribute($matches[1]);
+ $tel = $this->encodeAttribute($matches[2]);
+ $link = "$tel";
+ return $this->hashPart($link);
+ }
function _doAutoLinks_url_callback($matches) {
$url = $this->encodeAttribute($matches[1]);
$link = "$url";
@@ -1515,12 +1529,16 @@ class Markdown_Parser {
|
<\?.*?\?> | <%.*?%> # processing instruction
|
- <[/!$]?[-a-zA-Z0-9:_]+ # regular tags
+ <[!$]?[-a-zA-Z0-9:_]+ # regular tags
(?>
\s
(?>[^"\'>]+|"[^"]*"|\'[^\']*\')*
)?
>
+ |
+ <[-a-zA-Z0-9:_]+\s*/> # xml-style empty tag
+ |
+ [-a-zA-Z0-9:_]+\s*> # closing tag
').'
)
}xs';
@@ -1557,9 +1575,9 @@ class Markdown_Parser {
# Handle $token provided by parseSpan by determining its nature and
# returning the corresponding value that should replace it.
#
- switch ($token[0]) {
+ switch ($token{0}) {
case "\\":
- return $this->hashPart("". ord($token[1]). ";");
+ return $this->hashPart("". ord($token{1}). ";");
case "`":
# Search for end marker in remaining text.
if (preg_match('/^(.*?[^`])'.preg_quote($token).'(?!`)(.*)$/sm',
@@ -1658,7 +1676,7 @@ Perl by John Gruber.
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
-is most similar to that of plain text email, and supports features such
+is mostly similar to that of plain text email, and supports features such
as headers, *emphasis*, code blocks, blockquotes, and links.
Markdown's syntax is designed not as a generic markup language, but
@@ -1676,7 +1694,7 @@ 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 Markdown actually produced.
@@ -1691,12 +1709,12 @@ See the readme file for detailed release notes for this version.
Copyright and License
---------------------
-PHP Markdown
-Copyright (c) 2004-2009 Michel Fortin
-
+PHP Markdown
+Copyright (c) 2004-2013 Michel Fortin
+
All rights reserved.
-Based on Markdown
+Based on Markdown
Copyright (c) 2003-2006 John Gruber
All rights reserved.
@@ -1729,4 +1747,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
+?>
diff --git a/serendipity_event_markdown/markdown_extra.php b/serendipity_event_markdown/markdown_extra.php
index 8760aa91..0edba0f3 100644
--- a/serendipity_event_markdown/markdown_extra.php
+++ b/serendipity_event_markdown/markdown_extra.php
@@ -2,18 +2,18 @@
#
# Markdown Extra - A text-to-HTML conversion tool for web writers
#
-# PHP Markdown & Extra
-# Copyright (c) 2004-2012 Michel Fortin
-#
+# PHP Markdown & Extra
+# Copyright (c) 2004-2013 Michel Fortin
+#
#
-# Original Markdown
+# Original Markdown
# Copyright (c) 2004-2006 John Gruber
#
#
-define( 'MARKDOWN_VERSION', "1.0.1o" ); # Sun 8 Jan 2012
-define( 'MARKDOWNEXTRA_VERSION', "1.2.5" ); # Sun 8 Jan 2012
+define( 'MARKDOWN_VERSION', "1.0.2" ); # 29 Nov 2013
+define( 'MARKDOWNEXTRA_VERSION', "1.2.8" ); # 29 Nov 2013
#
@@ -34,6 +34,13 @@ define( 'MARKDOWNEXTRA_VERSION', "1.2.5" ); # Sun 8 Jan 2012
@define( 'MARKDOWN_FN_LINK_CLASS', "" );
@define( 'MARKDOWN_FN_BACKLINK_CLASS', "" );
+# Optional class prefix for fenced code block.
+@define( 'MARKDOWN_CODE_CLASS_PREFIX', "" );
+
+# Class attribute for code blocks goes on the `code` tag;
+# setting this to true will put attributes on the `pre` tag instead.
+@define( 'MARKDOWN_CODE_ATTR_ON_PRE', false );
+
#
# WordPress settings:
@@ -69,16 +76,17 @@ function Markdown($text) {
/*
Plugin Name: Markdown Extra
-Plugin URI: http://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.2.5
+Plugin Name: Markdown
+Plugin URI: http://michelf.ca/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.2.8
Author: Michel Fortin
-Author URI: http://michelf.com/
+Author URI: http://michelf.ca/
*/
if (isset($wp_version)) {
# More details about how it works here:
- #
+ #
# Post content and excerpts
# - Remove WordPress paragraph generator.
@@ -171,7 +179,7 @@ function identify_modifier_markdown() {
'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...',
+ '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...',
);
}
@@ -214,17 +222,7 @@ if (strcasecmp(substr(__FILE__, -16), "classTextile.php") == 0) {
class Markdown_Parser {
- # Regex to match balanced [brackets].
- # Needed to insert a maximum bracked depth while converting to PHP.
- var $nested_brackets_depth = 6;
- var $nested_brackets_re;
-
- var $nested_url_parenthesis_depth = 4;
- var $nested_url_parenthesis_re;
-
- # Table of hash values for escaped characters:
- var $escape_chars = '\`*_{}[]()>#+-.!';
- var $escape_chars_re;
+ ### Configuration Variables ###
# Change to ">" for HTML output.
var $empty_element_suffix = MARKDOWN_EMPTY_ELEMENT_SUFFIX;
@@ -239,6 +237,21 @@ class Markdown_Parser {
var $predef_titles = array();
+ ### Parser Implementation ###
+
+ # Regex to match balanced [brackets].
+ # Needed to insert a maximum bracked depth while converting to PHP.
+ var $nested_brackets_depth = 6;
+ var $nested_brackets_re;
+
+ var $nested_url_parenthesis_depth = 4;
+ var $nested_url_parenthesis_re;
+
+ # Table of hash values for escaped characters:
+ var $escape_chars = '\`*_{}[]()>#+-.!';
+ var $escape_chars_re;
+
+
function Markdown_Parser() {
#
# Constructor function. Initialize appropriate member variables.
@@ -282,7 +295,7 @@ class Markdown_Parser {
$this->titles = $this->predef_titles;
$this->html_hashes = array();
- $in_anchor = false;
+ $this->in_anchor = false;
}
function teardown() {
@@ -406,7 +419,9 @@ class Markdown_Parser {
#
$block_tags_a_re = 'ins|del';
$block_tags_b_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|'.
- 'script|noscript|form|fieldset|iframe|math';
+ 'script|noscript|form|fieldset|iframe|math|svg|'.
+ 'article|section|nav|aside|hgroup|header|footer|'.
+ 'figure';
# Regular expression for the content of a block tag.
$nested_tags_level = 4;
@@ -931,7 +946,7 @@ class Markdown_Parser {
if ($matches[2] == '-' && preg_match('{^-(?: |$)}', $matches[1]))
return $matches[0];
- $level = $matches[2][0] == '=' ? 1 : 2;
+ $level = $matches[2]{0} == '=' ? 1 : 2;
$block = "".$this->runSpanGamut($matches[1])."";
return "\n" . $this->hashBlock($block) . "\n\n";
}
@@ -1227,7 +1242,7 @@ class Markdown_Parser {
} else {
# Other closing marker: close one em or strong and
# change current token state to match the other
- $token_stack[0] = str_repeat($token[0], 3-$token_len);
+ $token_stack[0] = str_repeat($token{0}, 3-$token_len);
$tag = $token_len == 2 ? "strong" : "em";
$span = $text_stack[0];
$span = $this->runSpanGamut($span);
@@ -1252,7 +1267,7 @@ class Markdown_Parser {
} else {
# Reached opening three-char emphasis marker. Push on token
# stack; will be handled by the special condition above.
- $em = $token[0];
+ $em = $token{0};
$strong = "$em$em";
array_unshift($token_stack, $token);
array_unshift($text_stack, '');
@@ -1464,9 +1479,16 @@ class Markdown_Parser {
>
}xi',
array(&$this, '_doAutoLinks_email_callback'), $text);
+ $text = preg_replace_callback('{<(tel:([^\'">\s]+))>}i',array(&$this, '_doAutoLinks_tel_callback'), $text);
return $text;
}
+ function _doAutoLinks_tel_callback($matches) {
+ $url = $this->encodeAttribute($matches[1]);
+ $tel = $this->encodeAttribute($matches[2]);
+ $link = "$tel";
+ return $this->hashPart($link);
+ }
function _doAutoLinks_url_callback($matches) {
$url = $this->encodeAttribute($matches[1]);
$link = "$url";
@@ -1539,12 +1561,16 @@ class Markdown_Parser {
|
<\?.*?\?> | <%.*?%> # processing instruction
|
- <[/!$]?[-a-zA-Z0-9:_]+ # regular tags
+ <[!$]?[-a-zA-Z0-9:_]+ # regular tags
(?>
\s
(?>[^"\'>]+|"[^"]*"|\'[^\']*\')*
)?
>
+ |
+ <[-a-zA-Z0-9:_]+\s*/> # xml-style empty tag
+ |
+ [-a-zA-Z0-9:_]+\s*> # closing tag
').'
)
}xs';
@@ -1581,9 +1607,9 @@ class Markdown_Parser {
# Handle $token provided by parseSpan by determining its nature and
# returning the corresponding value that should replace it.
#
- switch ($token[0]) {
+ switch ($token{0}) {
case "\\":
- return $this->hashPart("". ord($token[1]). ";");
+ return $this->hashPart("". ord($token{1}). ";");
case "`":
# Search for end marker in remaining text.
if (preg_match('/^(.*?[^`])'.preg_quote($token).'(?!`)(.*)$/sm',
@@ -1676,6 +1702,8 @@ class Markdown_Parser {
class MarkdownExtra_Parser extends Markdown_Parser {
+ ### Configuration Variables ###
+
# Prefix for footnote ids.
var $fn_id_prefix = "";
@@ -1686,11 +1714,19 @@ class MarkdownExtra_Parser extends Markdown_Parser {
# Optional class attribute for footnote links and backlinks.
var $fn_link_class = MARKDOWN_FN_LINK_CLASS;
var $fn_backlink_class = MARKDOWN_FN_BACKLINK_CLASS;
+
+ # Optional class prefix for fenced code block.
+ var $code_class_prefix = MARKDOWN_CODE_CLASS_PREFIX;
+ # Class attribute for code blocks goes on the `code` tag;
+ # setting this to true will put attributes on the `pre` tag instead.
+ var $code_attr_on_pre = MARKDOWN_CODE_ATTR_ON_PRE;
# Predefined abbreviations.
var $predef_abbr = array();
+ ### Parser Implementation ###
+
function MarkdownExtra_Parser() {
#
# Constructor function. Initialize the parser object.
@@ -1724,6 +1760,8 @@ class MarkdownExtra_Parser extends Markdown_Parser {
# Extra variables used during extra transformations.
var $footnotes = array();
var $footnotes_ordered = array();
+ var $footnotes_ref_count = array();
+ var $footnotes_numbers = array();
var $abbr_desciptions = array();
var $abbr_word_re = '';
@@ -1739,6 +1777,8 @@ class MarkdownExtra_Parser extends Markdown_Parser {
$this->footnotes = array();
$this->footnotes_ordered = array();
+ $this->footnotes_ref_count = array();
+ $this->footnotes_numbers = array();
$this->abbr_desciptions = array();
$this->abbr_word_re = '';
$this->footnote_counter = 1;
@@ -1757,6 +1797,8 @@ class MarkdownExtra_Parser extends Markdown_Parser {
#
$this->footnotes = array();
$this->footnotes_ordered = array();
+ $this->footnotes_ref_count = array();
+ $this->footnotes_numbers = array();
$this->abbr_desciptions = array();
$this->abbr_word_re = '';
@@ -1764,23 +1806,111 @@ class MarkdownExtra_Parser extends Markdown_Parser {
}
+ ### Extra Attribute Parser ###
+
+ # Expression to use to catch attributes (includes the braces)
+ var $id_class_attr_catch_re = '\{((?:[ ]*[#.][-_:a-zA-Z0-9]+){1,})[ ]*\}';
+ # Expression to use when parsing in a context when no capture is desired
+ var $id_class_attr_nocatch_re = '\{(?:[ ]*[#.][-_:a-zA-Z0-9]+){1,}[ ]*\}';
+
+ function doExtraAttributes($tag_name, $attr) {
+ #
+ # Parse attributes caught by the $this->id_class_attr_catch_re expression
+ # and return the HTML-formatted list of attributes.
+ #
+ # Currently supported attributes are .class and #id.
+ #
+ if (empty($attr)) return "";
+
+ # Split on components
+ preg_match_all('/[#.][-_:a-zA-Z0-9]+/', $attr, $matches);
+ $elements = $matches[0];
+
+ # handle classes and ids (only first id taken into account)
+ $classes = array();
+ $id = false;
+ foreach ($elements as $element) {
+ if ($element{0} == '.') {
+ $classes[] = substr($element, 1);
+ } else if ($element{0} == '#') {
+ if ($id === false) $id = substr($element, 1);
+ }
+ }
+
+ # compose attributes as string
+ $attr_str = "";
+ if (!empty($id)) {
+ $attr_str .= ' id="'.$id.'"';
+ }
+ if (!empty($classes)) {
+ $attr_str .= ' class="'.implode(" ", $classes).'"';
+ }
+ return $attr_str;
+ }
+
+
+ function stripLinkDefinitions($text) {
+ #
+ # Strips link definitions from text, stores the URLs and titles in
+ # hash references.
+ #
+ $less_than_tab = $this->tab_width - 1;
+
+ # Link defs are in the form: ^[id]: url "optional title"
+ $text = preg_replace_callback('{
+ ^[ ]{0,'.$less_than_tab.'}\[(.+)\][ ]?: # id = $1
+ [ ]*
+ \n? # maybe *one* newline
+ [ ]*
+ (?:
+ <(.+?)> # url = $2
+ |
+ (\S+?) # url = $3
+ )
+ [ ]*
+ \n? # maybe one newline
+ [ ]*
+ (?:
+ (?<=\s) # lookbehind for whitespace
+ ["(]
+ (.*?) # title = $4
+ [")]
+ [ ]*
+ )? # title is optional
+ (?:[ ]* '.$this->id_class_attr_catch_re.' )? # $5 = extra id & class attr
+ (?:\n+|\Z)
+ }xm',
+ array(&$this, '_stripLinkDefinitions_callback'),
+ $text);
+ return $text;
+ }
+ function _stripLinkDefinitions_callback($matches) {
+ $link_id = strtolower($matches[1]);
+ $url = $matches[2] == '' ? $matches[3] : $matches[2];
+ $this->urls[$link_id] = $url;
+ $this->titles[$link_id] =& $matches[4];
+ $this->ref_attr[$link_id] = $this->doExtraAttributes("", $dummy =& $matches[5]);
+ return ''; # String that will replace the block
+ }
+
+
### HTML Block Parser ###
# Tags that are always treated as block tags:
- var $block_tags_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|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_re = 'script|noscript|math|ins|del';
+ var $block_tags_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|form|fieldset|iframe|hr|legend|article|section|nav|aside|hgroup|header|footer|figcaption';
+
+ # Tags treated as block tags only if the opening tag is alone on its line:
+ var $context_block_tags_re = 'script|noscript|ins|del|iframe|object|source|track|param|math|svg|canvas|audio|video';
# Tags where markdown="1" default to span mode:
var $contain_span_tags_re = 'p|h[1-6]|li|dd|dt|td|th|legend|address';
# Tags which must not have their contents modified, no matter where
# they appear:
- var $clean_tags_re = 'script|math';
+ var $clean_tags_re = 'script|math|svg';
# Tags that do not need to be closed.
- var $auto_close_tags_re = 'hr|img';
+ var $auto_close_tags_re = 'hr|img|param|source|track';
function hashHTMLBlocks($text) {
@@ -1795,10 +1925,12 @@ class MarkdownExtra_Parser extends Markdown_Parser {
#
# 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
+ # attribute is found within 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!
#
+ if ($this->no_markup) return $text;
+
#
# Call the HTML-in-Markdown hasher.
#
@@ -1848,7 +1980,7 @@ class MarkdownExtra_Parser extends Markdown_Parser {
# Regex to match any tag.
$block_tag_re =
'{
- ( # $2: Capture hole tag.
+ ( # $2: Capture whole tag.
? # Any opening or closing tag.
(?> # Tag name.
'.$this->block_tags_re.' |
@@ -1871,9 +2003,6 @@ class MarkdownExtra_Parser extends Markdown_Parser {
<\?.*?\?> | <%.*?%> # Processing instruction
|
# CData Block
- |
- # Code span marker
- `+
'. ( !$span ? ' # If not in span.
|
# Indented code block
@@ -1884,9 +2013,23 @@ class MarkdownExtra_Parser extends Markdown_Parser {
)*
|
# Fenced code block marker
- (?> ^ | \n )
- [ ]{0,'.($indent).'}~~~+[ ]*\n
+ (?<= ^ | \n )
+ [ ]{0,'.($indent+3).'}(?:~{3,}|`{3,})
+ [ ]*
+ (?:
+ \.?[-_:a-zA-Z0-9]+ # standalone class name
+ |
+ '.$this->id_class_attr_nocatch_re.' # extra attributes
+ )?
+ [ ]*
+ (?= \n )
' : '' ). ' # End (if not is span).
+ |
+ # Code span marker
+ # Note, this regex needs to go after backtick fenced
+ # code blocks but it should also be kept outside of the
+ # "if not in span" condition adding backticks to the parser
+ `+
)
}xs';
@@ -1928,31 +2071,16 @@ class MarkdownExtra_Parser extends Markdown_Parser {
$text = $parts[2]; # Remaining text after current tag.
$tag_re = preg_quote($tag); # For use in a regular expression.
- #
- # Check for: Code span marker
- #
- if ($tag[0] == "`") {
- # Find corresponding end marker.
- $tag_re = preg_quote($tag);
- if (preg_match('{^(?>.+?|\n(?!\n))*?(?id_class_attr_nocatch_re.')?[ ]*\n?$}', $tag, $capture)) {
# Fenced code block marker: find matching end marker.
- $tag_re = preg_quote(trim($tag));
- if (preg_match('{^(?>.*\n)+?[ ]{0,'.($indent).'}'.$tag_re.'[ ]*\n}', $text,
+ $fence_indent = strlen($capture[1]); # use captured indent in re
+ $fence_re = $capture[2]; # use captured fence in re
+ if (preg_match('{^(?>.*\n)*?[ ]{'.($fence_indent).'}'.$fence_re.'[ ]*(?:\n|$)}', $text,
$matches))
{
# End marker found: pass text unchanged until marker.
@@ -1967,12 +2095,31 @@ class MarkdownExtra_Parser extends Markdown_Parser {
#
# Check for: Indented code block.
#
- else if ($tag[0] == "\n" || $tag[0] == " ") {
+ else if ($tag{0} == "\n" || $tag{0} == " ") {
# Indented code block: pass it unchanged, will be handled
# later.
$parsed .= $tag;
}
#
+ # Check for: Code span marker
+ # Note: need to check this after backtick fenced code blocks
+ #
+ else if ($tag{0} == "`") {
+ # Find corresponding end marker.
+ $tag_re = preg_quote($tag);
+ if (preg_match('{^(?>.+?|\n(?!\n))*?(?clean_tags_re.')\b}', $tag) ||
- $tag[1] == '!' || $tag[1] == '?')
+ $tag{1} == '!' || $tag{1} == '?')
{
# Need to parse tag and following text using the HTML parser.
# (don't check for markdown attribute)
@@ -2014,8 +2161,8 @@ class MarkdownExtra_Parser extends Markdown_Parser {
#
# Increase/decrease nested tag count.
#
- if ($tag[1] == '/') $depth--;
- else if ($tag[strlen($tag)-2] != '/') $depth++;
+ if ($tag{1} == '/') $depth--;
+ else if ($tag{strlen($tag)-2} != '/') $depth++;
if ($depth < 0) {
#
@@ -2066,7 +2213,7 @@ class MarkdownExtra_Parser extends Markdown_Parser {
# Regex to match any tag.
$tag_re = '{
- ( # $2: Capture hole tag.
+ ( # $2: Capture whole tag.
? # Any opening or closing tag.
[\w:$]+ # Tag name.
(?:
@@ -2119,7 +2266,7 @@ class MarkdownExtra_Parser extends Markdown_Parser {
# first character as filtered to prevent an infinite loop in the
# parent function.
#
- return array($original_text[0], substr($original_text, 1));
+ return array($original_text{0}, substr($original_text, 1));
}
$block_text .= $parts[0]; # Text before current tag.
@@ -2131,7 +2278,7 @@ class MarkdownExtra_Parser extends Markdown_Parser {
# Comments and Processing Instructions.
#
if (preg_match('{^?(?:'.$this->auto_close_tags_re.')\b}', $tag) ||
- $tag[1] == '!' || $tag[1] == '?')
+ $tag{1} == '!' || $tag{1} == '?')
{
# Just add the tag to the block as if it was text.
$block_text .= $tag;
@@ -2142,8 +2289,8 @@ class MarkdownExtra_Parser extends Markdown_Parser {
# the tag's name match base tag's.
#
if (preg_match('{^?'.$base_tag_name_re.'\b}', $tag)) {
- if ($tag[1] == '/') $depth--;
- else if ($tag[strlen($tag)-2] != '/') $depth++;
+ if ($tag{1} == '/') $depth--;
+ else if ($tag{strlen($tag)-2} != '/') $depth++;
}
#
@@ -2193,7 +2340,7 @@ class MarkdownExtra_Parser extends Markdown_Parser {
if (!$span_mode) $parsed .= "\n\n$block_text\n\n";
else $parsed .= "$block_text";
- # Start over a new block.
+ # Start over with a new block.
$block_text = "";
}
else $block_text .= $tag;
@@ -2212,29 +2359,267 @@ class MarkdownExtra_Parser extends Markdown_Parser {
function hashClean($text) {
#
- # Called whenever a tag must be hashed when a function insert a "clean" tag
- # in $text, it pass through this function and is automaticaly escaped,
+ # Called whenever a tag must be hashed when a function inserts a "clean" tag
+ # in $text, it passes through this function and is automaticaly escaped,
# blocking invalid nested overlap.
#
return $this->hashPart($text, 'C');
}
+ function doAnchors($text) {
+ #
+ # Turn Markdown link shortcuts into XHTML tags.
+ #
+ if ($this->in_anchor) return $text;
+ $this->in_anchor = true;
+
+ #
+ # First, handle reference-style links: [link text] [id]
+ #
+ $text = preg_replace_callback('{
+ ( # wrap whole match in $1
+ \[
+ ('.$this->nested_brackets_re.') # link text = $2
+ \]
+
+ [ ]? # one optional space
+ (?:\n[ ]*)? # one optional newline followed by spaces
+
+ \[
+ (.*?) # id = $3
+ \]
+ )
+ }xs',
+ array(&$this, '_doAnchors_reference_callback'), $text);
+
+ #
+ # Next, inline-style links: [link text](url "optional title")
+ #
+ $text = preg_replace_callback('{
+ ( # wrap whole match in $1
+ \[
+ ('.$this->nested_brackets_re.') # link text = $2
+ \]
+ \( # literal paren
+ [ \n]*
+ (?:
+ <(.+?)> # href = $3
+ |
+ ('.$this->nested_url_parenthesis_re.') # href = $4
+ )
+ [ \n]*
+ ( # $5
+ ([\'"]) # quote char = $6
+ (.*?) # Title = $7
+ \6 # matching quote
+ [ \n]* # ignore any spaces/tabs between closing quote and )
+ )? # title is optional
+ \)
+ (?:[ ]? '.$this->id_class_attr_catch_re.' )? # $8 = id/class attributes
+ )
+ }xs',
+ array(&$this, '_doAnchors_inline_callback'), $text);
+
+ #
+ # Last, handle reference-style shortcuts: [link text]
+ # These must come last in case you've also got [link text][1]
+ # or [link text](/foo)
+ #
+ $text = preg_replace_callback('{
+ ( # wrap whole match in $1
+ \[
+ ([^\[\]]+) # link text = $2; can\'t contain [ or ]
+ \]
+ )
+ }xs',
+ array(&$this, '_doAnchors_reference_callback'), $text);
+
+ $this->in_anchor = false;
+ return $text;
+ }
+ function _doAnchors_reference_callback($matches) {
+ $whole_match = $matches[1];
+ $link_text = $matches[2];
+ $link_id =& $matches[3];
+
+ if ($link_id == "") {
+ # for shortcut links like [this][] or [this].
+ $link_id = $link_text;
+ }
+
+ # lower-case and turn embedded newlines into spaces
+ $link_id = strtolower($link_id);
+ $link_id = preg_replace('{[ ]?\n}', ' ', $link_id);
+
+ if (isset($this->urls[$link_id])) {
+ $url = $this->urls[$link_id];
+ $url = $this->encodeAttribute($url);
+
+ $result = "titles[$link_id] ) ) {
+ $title = $this->titles[$link_id];
+ $title = $this->encodeAttribute($title);
+ $result .= " title=\"$title\"";
+ }
+ if (isset($this->ref_attr[$link_id]))
+ $result .= $this->ref_attr[$link_id];
+
+ $link_text = $this->runSpanGamut($link_text);
+ $result .= ">$link_text";
+ $result = $this->hashPart($result);
+ }
+ else {
+ $result = $whole_match;
+ }
+ return $result;
+ }
+ function _doAnchors_inline_callback($matches) {
+ $whole_match = $matches[1];
+ $link_text = $this->runSpanGamut($matches[2]);
+ $url = $matches[3] == '' ? $matches[4] : $matches[3];
+ $title =& $matches[7];
+ $attr = $this->doExtraAttributes("a", $dummy =& $matches[8]);
+
+
+ $url = $this->encodeAttribute($url);
+
+ $result = "encodeAttribute($title);
+ $result .= " title=\"$title\"";
+ }
+ $result .= $attr;
+
+ $link_text = $this->runSpanGamut($link_text);
+ $result .= ">$link_text";
+
+ return $this->hashPart($result);
+ }
+
+
+ function doImages($text) {
+ #
+ # Turn Markdown image shortcuts into
tags.
+ #
+ #
+ # First, handle reference-style labeled images: ![alt text][id]
+ #
+ $text = preg_replace_callback('{
+ ( # wrap whole match in $1
+ !\[
+ ('.$this->nested_brackets_re.') # alt text = $2
+ \]
+
+ [ ]? # one optional space
+ (?:\n[ ]*)? # one optional newline followed by spaces
+
+ \[
+ (.*?) # id = $3
+ \]
+
+ )
+ }xs',
+ array(&$this, '_doImages_reference_callback'), $text);
+
+ #
+ # Next, handle inline images: ![alt text](url "optional title")
+ # Don't forget: encode * and _
+ #
+ $text = preg_replace_callback('{
+ ( # wrap whole match in $1
+ !\[
+ ('.$this->nested_brackets_re.') # alt text = $2
+ \]
+ \s? # One optional whitespace character
+ \( # literal paren
+ [ \n]*
+ (?:
+ <(\S*)> # src url = $3
+ |
+ ('.$this->nested_url_parenthesis_re.') # src url = $4
+ )
+ [ \n]*
+ ( # $5
+ ([\'"]) # quote char = $6
+ (.*?) # title = $7
+ \6 # matching quote
+ [ \n]*
+ )? # title is optional
+ \)
+ (?:[ ]? '.$this->id_class_attr_catch_re.' )? # $8 = id/class attributes
+ )
+ }xs',
+ array(&$this, '_doImages_inline_callback'), $text);
+
+ return $text;
+ }
+ function _doImages_reference_callback($matches) {
+ $whole_match = $matches[1];
+ $alt_text = $matches[2];
+ $link_id = strtolower($matches[3]);
+
+ if ($link_id == "") {
+ $link_id = strtolower($alt_text); # for shortcut links like ![this][].
+ }
+
+ $alt_text = $this->encodeAttribute($alt_text);
+ if (isset($this->urls[$link_id])) {
+ $url = $this->encodeAttribute($this->urls[$link_id]);
+ $result = "
titles[$link_id])) {
+ $title = $this->titles[$link_id];
+ $title = $this->encodeAttribute($title);
+ $result .= " title=\"$title\"";
+ }
+ if (isset($this->ref_attr[$link_id]))
+ $result .= $this->ref_attr[$link_id];
+ $result .= $this->empty_element_suffix;
+ $result = $this->hashPart($result);
+ }
+ else {
+ # If there's no such link ID, leave intact:
+ $result = $whole_match;
+ }
+
+ return $result;
+ }
+ function _doImages_inline_callback($matches) {
+ $whole_match = $matches[1];
+ $alt_text = $matches[2];
+ $url = $matches[3] == '' ? $matches[4] : $matches[3];
+ $title =& $matches[7];
+ $attr = $this->doExtraAttributes("img", $dummy =& $matches[8]);
+
+ $alt_text = $this->encodeAttribute($alt_text);
+ $url = $this->encodeAttribute($url);
+ $result = "
encodeAttribute($title);
+ $result .= " title=\"$title\""; # $title already quoted
+ }
+ $result .= $attr;
+ $result .= $this->empty_element_suffix;
+
+ return $this->hashPart($result);
+ }
+
+
function doHeaders($text) {
#
- # Redefined to add id attribute support.
+ # Redefined to add id and class attribute support.
#
# Setext-style headers:
# Header 1 {#header1}
# ========
#
- # Header 2 {#header2}
+ # Header 2 {#header2 .class1 .class2}
# --------
#
$text = preg_replace_callback(
'{
(^.+?) # $1: Header text
- (?:[ ]+\{\#([-_:a-zA-Z0-9]+)\})? # $2: Id attribute
+ (?:[ ]+ '.$this->id_class_attr_catch_re.' )? # $3 = id/class attributes
[ ]*\n(=+|-+)[ ]*\n+ # $3: Header footer
}mx',
array(&$this, '_doHeaders_callback_setext'), $text);
@@ -2242,9 +2627,9 @@ class MarkdownExtra_Parser extends Markdown_Parser {
# atx-style headers:
# # Header 1 {#header1}
# ## Header 2 {#header2}
- # ## Header 2 with closing hashes ## {#header3}
+ # ## Header 2 with closing hashes ## {#header3.class1.class2}
# ...
- # ###### Header 6 {#header2}
+ # ###### Header 6 {.class2}
#
$text = preg_replace_callback('{
^(\#{1,6}) # $1 = string of #\'s
@@ -2252,7 +2637,7 @@ class MarkdownExtra_Parser extends Markdown_Parser {
(.+?) # $2 = Header text
[ ]*
\#* # optional closing #\'s (not counted)
- (?:[ ]+\{\#([-_:a-zA-Z0-9]+)\})? # id attribute
+ (?:[ ]+ '.$this->id_class_attr_catch_re.' )? # $3 = id/class attributes
[ ]*
\n+
}xm',
@@ -2260,21 +2645,17 @@ class MarkdownExtra_Parser extends Markdown_Parser {
return $text;
}
- function _doHeaders_attr($attr) {
- if (empty($attr)) return "";
- return " id=\"$attr\"";
- }
function _doHeaders_callback_setext($matches) {
if ($matches[3] == '-' && preg_match('{^- }', $matches[1]))
return $matches[0];
- $level = $matches[3][0] == '=' ? 1 : 2;
- $attr = $this->_doHeaders_attr($id =& $matches[2]);
+ $level = $matches[3]{0} == '=' ? 1 : 2;
+ $attr = $this->doExtraAttributes("h$level", $dummy =& $matches[2]);
$block = "".$this->runSpanGamut($matches[1])."";
return "\n" . $this->hashBlock($block) . "\n\n";
}
function _doHeaders_callback_atx($matches) {
$level = strlen($matches[1]);
- $attr = $this->_doHeaders_attr($id =& $matches[3]);
+ $attr = $this->doExtraAttributes("h$level", $dummy =& $matches[3]);
$block = "".$this->runSpanGamut($matches[2])."";
return "\n" . $this->hashBlock($block) . "\n\n";
}
@@ -2375,6 +2756,7 @@ class MarkdownExtra_Parser extends Markdown_Parser {
$head = $this->parseSpan($head);
$headers = preg_split('/ *[|] */', $head);
$col_count = count($headers);
+ $attr = array_pad($attr, $col_count, '');
# Write column headers.
$text = "\n";
@@ -2479,7 +2861,7 @@ class MarkdownExtra_Parser extends Markdown_Parser {
(?>\A\n?|\n\n+) # leading line
( # definition terms = $1
[ ]{0,'.$less_than_tab.'} # leading whitespace
- (?![:][ ]|[ ]) # negative lookahead for a definition
+ (?!\:[ ]|[ ]) # negative lookahead for a definition
# mark (colon) or more whitespace.
(?> \S.* \n)+? # actual term (not whitespace).
)
@@ -2493,12 +2875,12 @@ class MarkdownExtra_Parser extends Markdown_Parser {
\n(\n+)? # leading line = $1
( # marker space = $2
[ ]{0,'.$less_than_tab.'} # whitespace before colon
- [:][ ]+ # definition mark (colon)
+ \:[ ]+ # definition mark (colon)
)
((?s:.+?)) # definition text = $3
(?= \n+ # stop at next definition mark,
(?: # next term or end of text
- [ ]{0,'.$less_than_tab.'} [:][ ] |
+ [ ]{0,'.$less_than_tab.'} \:[ ] |
| \z
)
)
@@ -2550,11 +2932,17 @@ class MarkdownExtra_Parser extends Markdown_Parser {
(?:\n|\A)
# 1: Opening marker
(
- ~{3,} # Marker: three tilde or more.
+ (?:~{3,}|`{3,}) # 3 or more tildes/backticks.
)
+ [ ]*
+ (?:
+ \.?([-_:a-zA-Z0-9]+) # 2: standalone class name
+ |
+ '.$this->id_class_attr_catch_re.' # 3: Extra attributes
+ )?
[ ]* \n # Whitespace and newline following marker.
- # 2: Content
+ # 4: Content
(
(?>
(?!\1 [ ]* \n) # Not a closing marker.
@@ -2563,18 +2951,31 @@ class MarkdownExtra_Parser extends Markdown_Parser {
)
# Closing marker.
- \1 [ ]* \n
+ \1 [ ]* (?= \n )
}xm',
array(&$this, '_doFencedCodeBlocks_callback'), $text);
return $text;
}
function _doFencedCodeBlocks_callback($matches) {
- $codeblock = $matches[2];
+ $classname =& $matches[2];
+ $attrs =& $matches[3];
+ $codeblock = $matches[4];
$codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
$codeblock = preg_replace_callback('/^\n+/',
array(&$this, '_doFencedCodeBlocks_newlines'), $codeblock);
- $codeblock = "$codeblock
";
+
+ if ($classname != "") {
+ if ($classname{0} == '.')
+ $classname = substr($classname, 1);
+ $attr_str = ' class="'.$this->code_class_prefix.$classname.'"';
+ } else {
+ $attr_str = $this->doExtraAttributes($this->code_attr_on_pre ? "pre" : "code", $attrs);
+ }
+ $pre_attr_str = $this->code_attr_on_pre ? $attr_str : '';
+ $code_attr_str = $this->code_attr_on_pre ? '' : $attr_str;
+ $codeblock = "$codeblock
";
+
return "\n\n".$this->hashBlock($codeblock)."\n\n";
}
function _doFencedCodeBlocks_newlines($matches) {
@@ -2718,6 +3119,9 @@ class MarkdownExtra_Parser extends Markdown_Parser {
$footnote = reset($this->footnotes_ordered);
$note_id = key($this->footnotes_ordered);
unset($this->footnotes_ordered[$note_id]);
+ $ref_count = $this->footnotes_ref_count[$note_id];
+ unset($this->footnotes_ref_count[$note_id]);
+ unset($this->footnotes[$note_id]);
$footnote .= "\n"; # Need to append newline before parsing.
$footnote = $this->runBlockGamut("$footnote\n");
@@ -2726,9 +3130,13 @@ class MarkdownExtra_Parser extends Markdown_Parser {
$attr = str_replace("%%", ++$num, $attr);
$note_id = $this->encodeAttribute($note_id);
-
- # Add backlink to last paragraph; create new paragraph if needed.
+
+ # Prepare backlink, multiple backlinks if multiple references
$backlink = "↩";
+ for ($ref_num = 2; $ref_num <= $ref_count; ++$ref_num) {
+ $backlink .= " ↩";
+ }
+ # Add backlink to last paragraph; create new paragraph if needed.
if (preg_match('{$}', $footnote)) {
$footnote = substr($footnote, 0, -4) . " $backlink";
} else {
@@ -2751,11 +3159,18 @@ class MarkdownExtra_Parser extends Markdown_Parser {
# Create footnote marker only if it has a corresponding footnote *and*
# the footnote hasn't been used by another marker.
if (isset($this->footnotes[$node_id])) {
- # Transfert footnote content to the ordered list.
- $this->footnotes_ordered[$node_id] = $this->footnotes[$node_id];
- unset($this->footnotes[$node_id]);
+ $num =& $this->footnotes_numbers[$node_id];
+ if (!isset($num)) {
+ # Transfer footnote content to the ordered list and give it its
+ # number
+ $this->footnotes_ordered[$node_id] = $this->footnotes[$node_id];
+ $this->footnotes_ref_count[$node_id] = 1;
+ $num = $this->footnote_counter++;
+ $ref_count_mark = '';
+ } else {
+ $ref_count_mark = $this->footnotes_ref_count[$node_id] += 1;
+ }
- $num = $this->footnote_counter++;
$attr = " rel=\"footnote\"";
if ($this->fn_link_class != "") {
$class = $this->fn_link_class;
@@ -2772,7 +3187,7 @@ class MarkdownExtra_Parser extends Markdown_Parser {
$node_id = $this->encodeAttribute($node_id);
return
- "".
+ "".
"$num".
"";
}
@@ -2858,7 +3273,7 @@ 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
-is most similar to that of plain text email, and supports features such
+is mostly similar to that of plain text email, and supports features such
as headers, *emphasis*, code blocks, blockquotes, and links.
Markdown's syntax is designed not as a generic markup language, but
@@ -2876,7 +3291,7 @@ 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 Markdown actually produced.
@@ -2891,9 +3306,9 @@ See the readme file for detailed release notes for this version.
Copyright and License
---------------------
-PHP Markdown & Extra
-Copyright (c) 2004-2009 Michel Fortin
-
+PHP Markdown & Extra
+Copyright (c) 2004-2013 Michel Fortin
+
All rights reserved.
Based on Markdown
@@ -2929,4 +3344,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
+?>
diff --git a/serendipity_event_markdown/serendipity_event_markdown.php b/serendipity_event_markdown/serendipity_event_markdown.php
index 3413c583..801e2c21 100644
--- a/serendipity_event_markdown/serendipity_event_markdown.php
+++ b/serendipity_event_markdown/serendipity_event_markdown.php
@@ -1,5 +1,6 @@
add('requirements', array(
'serendipity' => '0.7',
'smarty' => '2.6.7',
- 'php' => '4.1.0'
+ 'php' => '5.3.0'
));
- $propbag->add('version', '1.18');
+ $propbag->add('version', '1.21');
$propbag->add('cachable_events', array('frontend_display' => true));
$propbag->add('event_hooks', array('frontend_display' => true, 'frontend_comment' => true));
$propbag->add('groups', array('MARKUP'));
@@ -59,6 +60,8 @@ class serendipity_event_markdown extends serendipity_event
$conf_array[] = $element['name'];
}
$conf_array[] = 'MARKDOWN_EXTRA';
+ $conf_array[] = 'MARKDOWN_VERSION';
+ $conf_array[] = 'MARKDOWN_SMARTYPANTS';
$propbag->add('configuration', $conf_array);
}
@@ -80,24 +83,48 @@ class serendipity_event_markdown extends serendipity_event
function introspect_config_item($name, &$propbag)
{
switch($name) {
- case 'ENTRY_BODY':
- case 'EXTENDED_BODY':
- case 'COMMENT':
- case 'HTML_NUGGET':
- $propbag->add('type', 'boolean');
- $propbag->add('name', constant($name));
- $propbag->add('description', sprintf(APPLY_MARKUP_TO, constant($name)));
- $propbag->add('default', 'true');
- return true;
- break;
+ case 'ENTRY_BODY':
+ case 'EXTENDED_BODY':
+ case 'COMMENT':
+ case 'HTML_NUGGET':
+ $propbag->add('type', 'boolean');
+ $propbag->add('name', constant($name));
+ $propbag->add('description', sprintf(APPLY_MARKUP_TO, constant($name)));
+ $propbag->add('default', 'true');
+ return true;
+ break;
- case 'MARKDOWN_EXTRA':
- $propbag->add('type', 'boolean');
- $propbag->add('name', PLUGIN_EVENT_MARKDOWN_EXTRA_NAME);
- $propbag->add('description', PLUGIN_EVENT_MARKDOWN_EXTRA_DESC);
- $propbag->add('default', false);
- return true;
- break;
+ case 'MARKDOWN_EXTRA':
+ $propbag->add('type', 'boolean');
+ $propbag->add('name', PLUGIN_EVENT_MARKDOWN_EXTRA_NAME);
+ $propbag->add('description', PLUGIN_EVENT_MARKDOWN_EXTRA_DESC);
+ $propbag->add('default', false);
+ return true;
+ break;
+
+ case 'MARKDOWN_SMARTYPANTS':
+ $propbag->add('type', 'radio');
+ $propbag->add('name', PLUGIN_EVENT_MARKDOWN_SMARTYPANTS_NAME);
+ $propbag->add('description', PLUGIN_EVENT_MARKDOWN_SMARTYPANTS_DESC);
+ $propbag->add('radio', array(
+ 'value' => array(1, 2, 0),
+ 'desc' => array(YES, PLUGIN_EVENT_MARKDOWN_SMARTYPANTS_EXTENDED, PLUGIN_EVENT_MARKDOWN_SMARTYPANTS_NEVER)
+ ));
+ $propbag->add('default', 0);
+ return true;
+ break;
+
+ case 'MARKDOWN_VERSION':
+ $propbag->add('type', 'radio');
+ $propbag->add('name', PLUGIN_EVENT_MARKDOWN_VERSION);
+ $propbag->add('description', PLUGIN_EVENT_MARKDOWN_VERSION_BLABLAH);
+ $propbag->add('radio', array(
+ 'value' => array(1, 2),
+ 'desc' => array('classic', 'lib'),
+ ));
+ $propbag->add('default', 2);
+ return true;
+ break;
}
}
@@ -105,10 +132,31 @@ class serendipity_event_markdown extends serendipity_event
function event_hook($event, &$bag, &$eventData, $addData = null) {
global $serendipity;
- if($this->get_config('MARKDOWN_EXTRA', false)) {
- include_once dirname(__FILE__) . '/markdown_extra.php';
- } else {
- include_once dirname(__FILE__) . '/markdown.php';
+ $mdsp = $this->get_config('MARKDOWN_SMARTYPANTS');
+ $mdv = $this->get_config('MARKDOWN_VERSION');
+
+ switch($mdv) {
+ case 2:
+ if ($this->get_config('MARKDOWN_EXTRA', false)) {
+ require_once dirname(__FILE__) . '/lib/Michelf/MarkdownExtra.inc.php';
+ } else {
+ require_once dirname(__FILE__) . '/lib/Michelf/Markdown.inc.php';
+ }
+ if ($mdsp > 0) {
+ require_once dirname(__FILE__) . '/lib/Michelf/SmartyPants.php';
+ }
+ if ($mdsp == 2) {
+ require_once dirname(__FILE__) . '/lib/Michelf/SmartyPantsTypographer.php';
+ }
+ break;
+
+ case 1:
+ if (serendipity_db_bool($this->get_config('MARKDOWN_EXTRA', false))) {
+ include_once dirname(__FILE__) . '/markdown_extra.php';
+ } else {
+ include_once dirname(__FILE__) . '/markdown.php';
+ }
+ break;
}
$hooks = &$bag->get('event_hooks');
@@ -122,10 +170,16 @@ class serendipity_event_markdown extends serendipity_event
!$eventData['properties']['ep_disable_markup_' . $this->instance] &&
!isset($serendipity['POST']['properties']['disable_markup_' . $this->instance])) {
$element = $temp['element'];
- $eventData[$element] = str_replace('javascript:', '', Markdown($eventData[$element]));
+ if ($mdv == 2) {
+ $eventData[$element] = str_replace('javascript:', '', Markdown::defaultTransform($eventData[$element]));
+ if ($mdsp == 1) $eventData[$element] = SmartyPants::defaultTransform($eventData[$element]);
+ if ($mdsp == 2) $eventData[$element] = SmartyPantsTypographer::defaultTransform($eventData[$element]);
+ } else {
+ $eventData[$element] = str_replace('javascript:', '', Markdown($eventData[$element]));
+ }
}
}
- $this->setPlaintextBody($eventData);
+ $this->setPlaintextBody($eventData, $mdv, $mdsp);
return true;
break;
@@ -149,16 +203,18 @@ class serendipity_event_markdown extends serendipity_event
* @see http://board.s9y.org/viewtopic.php?f=11&t=18351 Discussion of this feature in the S9y forum.
*
* @param array $eventData
+ * @param int $version Markdown Classic or Lib default 2
+ * @param int $pants SmartyPants option default 0
*/
- function setPlaintextBody(array $eventData)
+ function setPlaintextBody(array $eventData, $version=2, $pants=0)
{
if (isset($GLOBALS['entry'][0]['plaintext_body'])) {
- $html = Markdown($GLOBALS['entry'][0]['plaintext_body']);
- $GLOBALS['entry'][0]['plaintext_body'] = trim(strip_tags(str_replace('javascript:', '', $html)));
+ $html = ($version == 2) ? Markdown::defaultTransform($GLOBALS['entry'][0]['plaintext_body']) : Markdown($GLOBALS['entry'][0]['plaintext_body']);
} else {
- $html = Markdown(html_entity_decode($eventData['body']));
- $GLOBALS['entry'][0]['plaintext_body'] = trim(strip_tags(str_replace('javascript:', '', $html)));
+ $html = ($version == 2) ? Markdown::defaultTransform(html_entity_decode($eventData['body'])) : Markdown(html_entity_decode($eventData['body']));
}
+ if ($pants > 0) $html = ($pants == 2) ? SmartyPantsTypographer::defaultTransform($html) : SmartyPants::defaultTransform($html);
+ $GLOBALS['entry'][0]['plaintext_body'] = trim(strip_tags(str_replace('javascript:', '', $html)));
}
/* disabled, probably used in later versions