diff --git a/serendipity_event_markdown/ChangeLog b/serendipity_event_markdown/ChangeLog index 07aa5f0f..b64cb14d 100644 --- a/serendipity_event_markdown/ChangeLog +++ b/serendipity_event_markdown/ChangeLog @@ -1,3 +1,17 @@ +Version 1.21: +============= + +- update to PHP Markdown 1.0.2 + read http://michelf.ca/projects/php-markdown/ +- update to PHP Markdown Extra 1.2.8 +- include new lib version 1.4.0 +- added optional SmartyPants library, of August, 2013 dev. + This is a library package that includes the PHP SmartyPants and its + sibling PHP SmartyPants Typographer with additional features. + SmartyPants is a free web typography prettifyier tool for web writers. + It easily translates plain ASCII punctuation characters into "smart" + typographic punctuation HTML entities. +- extend to PHP >= 5.3 only (while lib using namespaces) Version 1.1.5: diff --git a/serendipity_event_markdown/License.text b/serendipity_event_markdown/License.text new file mode 100644 index 00000000..48a19cf0 --- /dev/null +++ b/serendipity_event_markdown/License.text @@ -0,0 +1,36 @@ +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/PHP Markdown Extra Readme.text b/serendipity_event_markdown/PHP Markdown Extra Readme.text new file mode 100644 index 00000000..bb0b0f12 --- /dev/null +++ b/serendipity_event_markdown/PHP Markdown Extra Readme.text @@ -0,0 +1,895 @@ +PHP Markdown Extra +================== + +Version 1.2.8 - 29 Nov 2013 + +by Michel Fortin + + +based on Markdown by John Gruber + + + +Introduction +------------ + +This is a special version of PHP Markdown with extra features. See + for details. + +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/ + + +### 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. + + +### 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 -- or +replace it with PHP Markdown Extra -- 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/` + +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/ + + +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 +--------------- + +Extra 1.2.8: + +* Added backtick fenced code blocks, originally from Github-flavored Markdown. + + +1.0.2 + +* 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 + + +Extra 1.2.7 (11 Apr 2013): + +* 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. + + +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. + + +Extra 1.2.6 (13 Jan 2013): + +* Headers can now have a class attribute. You can add a class inside the + extra attribute block which can optionally be put after a header: + + ### Header ### {#id .class1 .class2} + + Spaces between components in the brace is optional. + +* Fenced code blocks can also have a class and an id attribute. If you only + need to apply a class (typically to indicate the language of a code + snippet), you can write it like this: + + ~~~ html + bold + ~~~ + + or like this: + + ~~~ .html + bold + ~~~ + + There is a new configuration option `MARKDOWN_CODE_CLASS_PREFIX` you can + use if you need to append a prefix to the class name. + + You might also opt to use an extra attribute block just like for headers: + + ~~~ {.html #id .codeclass} + bold + ~~~ + + Note that class names added this way are not affected by the + MARKDOWN_CODE_CLASS_PREFIX. + + A code block creates a `pre` HTML element containing a `code` element. + The `code` HTML element is the one that receives the attribute. If for + some reason you need attributes to be applied to the enclosing `pre` + element instead, you can set the MARKDOWN_CODE_ATTR_ON_PRE configuration + variable to true. + +* Fixed an issue were consecutive fenced code blocks containing HTML-like + code would confuse the parser. + +* Multiple references to the same footnote are now allowed. + +* Fixed an issue where no_markup mode was ineffective. + + +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. + + +Extra 1.2.5 (8 Jan 2012): + +* Fixed an issue preventing fenced code blocks indented inside lists items + and elsewhere from being interpreted correctly. + +* Fixed an issue where HTML tags inside fenced code blocks were sometime + not encoded with entities. + + +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. + + +Extra 1.2.4 (10 Oct 2009): + +* Fixed a problem where unterminated tags in indented code blocks could + prevent proper escaping of characaters in the code block. + + +Extra 1.2.3 (31 Dec 2008): + +* In WordPress pages featuring more than one post, footnote id prefixes are + now automatically applied with the current post ID to avoid clashes + between footnotes belonging to different posts. + +* Fix for a bug introduced in Extra 1.2 where block-level HTML tags where + not detected correctly, thus the addition of erroneous `

` tags and + interpretation of their content as Markdown-formatted instead of + HTML-formatted. + + +Extra 1.2.2 (21 Jun 2008): + +* Fixed a problem where abbreviation definitions, footnote + definitions and link references were stripped inside + fenced code blocks. + +* Fixed a bug where characters such as `"` in abbreviation + definitions weren't properly encoded to HTML entities. + +* Fixed a bug where double quotes `"` were not correctly encoded + as HTML entities when used inside a footnote reference id. + + +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. + + +Extra 1.2.1 (27 May 2008): + +* Fixed a problem where Markdown headers and horizontal rules were + transformed into their HTML equivalent inside fenced code blocks. + + +Extra 1.2 (11 May 2008): + +* Added fenced code block syntax which don't require indentation + and can start and end with blank lines. A fenced code block + starts with a line of consecutive tilde (~) and ends on the + next line with the same number of consecutive tilde. Here's an + example: + + ~~~~~~~~~~~~ + Hello World! + ~~~~~~~~~~~~ + +* Rewrote parts of the HTML block parser to better accomodate + fenced code blocks. + +* Footnotes may now be referenced from within another footnote. + +* Added programatically-settable parser property `predef_attr` for + predefined attribute definitions. + +* Fixed an issue where an indented code block preceded by a blank + line containing some other whitespace would confuse the HTML + block parser into creating an HTML block when it should have + been code. + + +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. + + +Extra 1.1.7 (26 Sep 2007): + +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. + + +Extra 1.1.6 (4 Sep 2007): + +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. + + +Extra 1.1.5 (31 Aug 2007): + +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. + + +Extra 1.1.4 (3 Aug 2007): + +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. + + +Extra 1.1.3 (3 Jul 2007): + +* Fixed a performance problem when parsing some invalid HTML as an HTML + block which was resulting in too much recusion and a segmentation fault + for long documents. + +* The markdown="" attribute now accepts unquoted values. + +* Fixed an issue where underscore-emphasis didn't work when applied on the + first or the last word of an element having the markdown="1" or + markdown="span" attribute set unless there was some surrounding whitespace. + This didn't work: + +

_Hello_ _world_

+ + Now it does produce emphasis as expected. + +* Fixed an issue preventing footnotes from working when the parser's + footnote id prefix variable (fn_id_prefix) is not empty. + +* Fixed a performance problem where the regular expression for strong + emphasis introduced in version 1.1 could sometime be long to process, + give slightly wrong results, and in some circumstances could remove + entirely the content for a whole paragraph. + +* Fixed an issue were abbreviations tags could be incorrectly added + inside URLs and title of links. + +* Placing footnote markers inside a link, resulting in two nested links, is + no longer allowed. + + +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: \-- + + +Extra 1.1.2 (7 Feb 2007) + +* Fixed an issue where headers preceded too closely by a paragraph + (with no blank line separating them) where put inside the paragraph. + +* Added the missing TextileRestricted method that was added to regular + PHP Markdown since 1.0.1d but which I forgot to add to Extra. + + +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. + + +Extra 1.1.1 (28 Dec 2006) + +* Fixed a problem where whitespace at the end of the line of an atx-style + header would cause tailing `#` to appear as part of the header's content. + This was caused by a small error in the regex that handles the definition + for the id attribute in PHP Markdown Extra. + +* Fixed a problem where empty abbreviations definitions would eat the + following line as its definition. + +* Fixed an issue with calling the Markdown parser repetitivly with text + containing footnotes. The footnote hashes were not reinitialized properly. + + +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. + + +Extra 1.1 (1 Dec 2006) + +* Added a syntax for footnotes. + +* Added an experimental syntax to define abbreviations. + + +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 + + +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(' + # 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 + # 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 + # 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 ( + ) + [ ]* + (?=\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 = "\"$alt_text\"";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 = "\"$alt_text\"";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 . ""); + 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"; + $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"; + $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] = '&#x'.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 + | + # 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. + # 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('{^= 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. + + ".*?" | # 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('{^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('{^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\"";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\"";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 .= "
    \n"; + $text .= "empty_element_suffix ."\n"; + $text .= "
      \n\n"; + + $attr = ""; + if ($this->fn_backlink_class != "") { + $class = $this->fn_backlink_class; + $class = $this->encodeAttribute($class); + $attr .= " class=\"$class\""; + } + if ($this->fn_backlink_title != "") { + $title = $this->fn_backlink_title; + $title = $this->encodeAttribute($title); + $attr .= " title=\"$title\""; + } + $num = 0; + + while (!empty($this->footnotes_ordered)) { + $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"); + $footnote = preg_replace_callback('{F\x1Afn:(.*?)\x1A:}', + array(&$this, '_appendFootnotes_callback'), $footnote); + + $attr = str_replace("%%", ++$num, $attr); + $note_id = $this->encodeAttribute($note_id); + + # 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 { + $footnote .= "\n\n

      $backlink

      "; + } + + $text .= "
    1. \n"; + $text .= $footnote . "\n"; + $text .= "
    2. \n\n"; + } + + $text .= "
    \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 = "(?:¡|¡|¡|&#x[Aa]1;|¿|¿|¿|&#x[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 + | + # 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 + | + # 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. # 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. 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('{^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 = "\"$alt_text\"";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 = "\"$alt_text\"";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