fmsystem-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Fmsystem-commits] [8258] API: add wiki parser


From: Sigurd Nes
Subject: [Fmsystem-commits] [8258] API: add wiki parser
Date: Fri, 09 Dec 2011 09:09:11 +0000

Revision: 8258
          http://svn.sv.gnu.org/viewvc/?view=rev&root=fmsystem&revision=8258
Author:   sigurdne
Date:     2011-12-09 09:09:11 +0000 (Fri, 09 Dec 2011)
Log Message:
-----------
API: add wiki parser

Added Paths:
-----------
    trunk/phpgwapi/inc/class.wiki2html.inc.php
    trunk/phpgwapi/inc/wiki2html/
    trunk/phpgwapi/inc/wiki2html/markdown/
    trunk/phpgwapi/inc/wiki2html/markdown/License.text
    trunk/phpgwapi/inc/wiki2html/markdown/PHP Markdown Readme.text
    trunk/phpgwapi/inc/wiki2html/markdown/markdown.php
    trunk/phpgwapi/inc/wiki2html/textile/
    trunk/phpgwapi/inc/wiki2html/textile/Textile.php

Added: trunk/phpgwapi/inc/class.wiki2html.inc.php
===================================================================
--- trunk/phpgwapi/inc/class.wiki2html.inc.php                          (rev 0)
+++ trunk/phpgwapi/inc/class.wiki2html.inc.php  2011-12-09 09:09:11 UTC (rev 
8258)
@@ -0,0 +1,72 @@
+<?php
+       /**
+        * phpGroupWare
+        *
+        * @author Sigurd Nes <address@hidden>
+        * @copyright Copyright (C) 2011 Free Software Foundation, Inc. 
http://www.fsf.org/
+        * @license http://www.fsf.org/licenses/gpl.html GNU General Public 
License
+        * @package phpgroupware
+        * @subpackage phpgwapi
+        * @category utilities
+        * @version $Id: class.wiki2html.inc.php 2588 2009-04-14 11:00:02Z 
sigurd $
+        */
+
+       /*
+          This program is free software: you can redistribute it and/or modify
+          it under the terms of the GNU General Public License as published by
+          the Free Software Foundation, either version 2 of the License, or
+          (at your option) any later version.
+
+          This program is distributed in the hope that it will be useful,
+          but WITHOUT ANY WARRANTY; without even the implied warranty of
+          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+          GNU General Public License for more details.
+
+          You should have received a copy of the GNU General Public License
+          along with this program.  If not, see <http://www.gnu.org/licenses/>.
+        */
+
+       /**
+       * Document me!
+       *
+       * @package phpgwapi
+       * @subpackage utilities
+       */
+
+       /**
+       * Include the Textile class
+       * @see wiki2html
+       */
+
+       
+       class phpgwapi_wiki2html
+       {
+               protected $syntax = 'textile';
+
+               public function __construct()
+               {
+
+               }
+
+               public function set_syntax($syntax)
+               {
+                       $this->syntax = $syntax;
+               }
+
+               public function process($content, $lite = '', $encode = '', 
$noimage = '', $strict = '', $rel = '')
+               {
+                       // Convert the raw content to wiki content
+                       switch ($this->syntax)
+                       {
+                               case 'markdown':
+                                       require_once PHPGW_API_INC . 
'/wiki2html/markdown/markdown.php';
+                               $html = Markdown($content);
+                                       break;
+                               default:
+                                       require_once PHPGW_API_INC . 
'/wiki2html/textile/Textile.php';
+                                       $textile = new Textile();
+                                       $html = $textile->TextileThis($content);
+                       }
+                       return $html;
+               }
+       }

Added: trunk/phpgwapi/inc/wiki2html/markdown/License.text
===================================================================
--- trunk/phpgwapi/inc/wiki2html/markdown/License.text                          
(rev 0)
+++ trunk/phpgwapi/inc/wiki2html/markdown/License.text  2011-12-09 09:09:11 UTC 
(rev 8258)
@@ -0,0 +1,36 @@
+PHP Markdown
+Copyright (c) 2004-2009 Michel Fortin  
+<http://michelf.com/>  
+All rights reserved.
+
+Based on Markdown  
+Copyright (c) 2003-2006 John Gruber   
+<http://daringfireball.net/>   
+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.


Property changes on: trunk/phpgwapi/inc/wiki2html/markdown/License.text
___________________________________________________________________
Added: svn:executable
   + *

Added: trunk/phpgwapi/inc/wiki2html/markdown/PHP Markdown Readme.text
===================================================================
--- trunk/phpgwapi/inc/wiki2html/markdown/PHP Markdown Readme.text              
                (rev 0)
+++ trunk/phpgwapi/inc/wiki2html/markdown/PHP Markdown Readme.text      
2011-12-09 09:09:11 UTC (rev 8258)
@@ -0,0 +1,770 @@
+PHP Markdown
+============
+
+Version 1.0.1m - Sat 21 Jun 2008
+
+by Michel Fortin
+<http://michelf.com/>
+
+based on work by John Gruber  
+<http://daringfireball.net/>
+
+
+Introduction
+------------
+
+Markdown is a text-to-HTML conversion tool for web writers. Markdown
+allows you to write using an easy-to-read, easy-to-write plain text
+format, then convert it to structurally valid XHTML (or HTML).
+
+"Markdown" is two things: a plain text markup syntax, and a software 
+tool, written in Perl, that converts the plain text markup to HTML. 
+PHP Markdown is a port to PHP of the original Markdown program by 
+John Gruber.
+
+PHP Markdown can work as a plug-in for WordPress and bBlog, as a 
+modifier for the Smarty templating engine, or as a remplacement for
+textile formatting in any software that support textile.
+
+Full documentation of Markdown's syntax is available on John's 
+Markdown page: <http://daringfireball.net/projects/markdown/>
+
+
+Installation and Requirement
+----------------------------
+
+PHP Markdown requires PHP version 4.0.5 or later.
+
+
+### WordPress ###
+
+PHP Markdown works with [WordPress][wp], version 1.2 or later.
+
+ [wp]: http://wordpress.org/
+
+1.  To use PHP Markdown with WordPress, place the "makrdown.php" file 
+    in the "plugins" folder. This folder is located inside 
+    "wp-content" at the root of your site:
+
+        (site home)/wp-content/plugins/
+
+2.  Activate the plugin with the administrative interface of 
+    WordPress. In the "Plugins" section you will now find Markdown. 
+    To activate the plugin, click on the "Activate" button on the 
+    same line than Markdown. Your entries will now be formatted by 
+    PHP Markdown.
+
+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 formated 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.
+
+
+### bBlog ###
+
+PHP Markdown also works with [bBlog][bb].
+
+ [bb]: http://www.bblog.com/
+
+To use PHP Markdown with bBlog, rename "markdown.php" to 
+"modifier.markdown.php" and place the file in the "bBlog_plugins" 
+folder. This folder is located inside the "bblog" directory of 
+your site, like this:
+
+        (site home)/bblog/bBlog_plugins/modifier.markdown.php
+
+Select "Markdown" as the "Entry Modifier" when you post a new 
+entry. This setting will only apply to the entry you are editing.
+
+
+### Replacing Textile 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 *Texitle 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.com/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.:
+
+    <br />
+
+Markdown can be configured to produce HTML-style tags; e.g.:
+
+    <br>
+
+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:
+<address@hidden>
+
+Please include with your report: (1) the example input; (2) the output you
+expected; (3) the output PHP Markdown actually produced.
+
+
+Version History
+---------------
+
+1.0.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+mailbox/address@hidden>
+               <!#$%&'*+-/=?^_`.{|address@hidden>
+               <"address@hidden"@example.com>
+               <"Fred Bloggs"@example.com>
+               <address@hidden>
+
+*      Now accepting spaces in URLs for inline and reference-style links. Such 
+       URLs need to be surrounded by angle brakets. For instance:
+       
+               [link text](<http://url/with space> "optional title")
+
+               [link text][ref]
+               [ref]: <http://url/with space> "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 `<HTTP://EXAMPLE.COM/>`.
+
+*      Fixed a problem where `<address@hidden>` 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:
+
+               <p>The MD5 value for "+" is "+".</p>
+
+*      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 `&lt;`.
+
+*      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:
+       
+               <span attr='`ticks`'>bar</span>
+               <span attr='``double ticks``'>bar</span>
+               `<test a="` content of attribute `">`
+
+*      Changed the parsing of HTML comments to match simply from `<!--` to 
`-->` 
+       instead using of the more complicated SGML-style rule with paired `--`.
+       This is how most browsers parse comments and how XML defines them too.
+
+*      `<address>` 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 `<p>` 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:
+       <http://michelf.com/weblog/2005/wordpress-text-flow-vs-markdown/>
+
+
+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](<http://example.com/>)
+
+       Fixed by moving `_DoAutoLinks()` after `_DoAnchors()` in
+       `_RunSpanGamut()`.
+
+*      Fixed bug where auto-links were being processed within code spans:
+
+               like this: `<http://example.com/>`
+
+       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 `<p>` tag.
+
+*      `_HashHTMLBlocks()` now tolerates trailing spaces and tabs following
+       HTML comments and `<hr/>` tags.
+
+*      Changed special case pattern for hashing `<hr>` 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:
+
+               <mailto:address@hidden>
+               <address@hidden>
+
+*      Fixed annoying bug where nested lists would wind up with
+       spurious (and invalid) `<p>` 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
+
+               <strong><em>this</em></strong>
+
+       Instead of
+
+               <strong><em>this</strong></em>
+
+       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:
+
+               <p>This is \"before\"  [tab] and "after" a tab.</p>
+
+*      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-2009 Michel Fortin  
+<http://michelf.com/>  
+All rights reserved.
+
+Based on Markdown  
+Copyright (c) 2003-2006 John Gruber   
+<http://daringfireball.net/>   
+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.


Property changes on: trunk/phpgwapi/inc/wiki2html/markdown/PHP Markdown 
Readme.text
___________________________________________________________________
Added: svn:executable
   + *

Added: trunk/phpgwapi/inc/wiki2html/markdown/markdown.php
===================================================================
--- trunk/phpgwapi/inc/wiki2html/markdown/markdown.php                          
(rev 0)
+++ trunk/phpgwapi/inc/wiki2html/markdown/markdown.php  2011-12-09 09:09:11 UTC 
(rev 8258)
@@ -0,0 +1,1732 @@
+<?php
+#
+# Markdown  -  A text-to-HTML conversion tool for web writers
+#
+# PHP Markdown
+# Copyright (c) 2004-2009 Michel Fortin  
+# <http://michelf.com/projects/php-markdown/>
+#
+# Original Markdown
+# Copyright (c) 2004-2006 John Gruber  
+# <http://daringfireball.net/projects/markdown/>
+#
+
+
+define( 'MARKDOWN_VERSION',  "1.0.1n" ); # Sat 10 Oct 2009
+
+
+#
+# Global default settings:
+#
+
+# Change to ">" for HTML output
address@hidden( 'MARKDOWN_EMPTY_ELEMENT_SUFFIX',  " />");
+
+# Define the width of a tab for code blocks.
address@hidden( 'MARKDOWN_TAB_WIDTH',     4 );
+
+
+#
+# WordPress settings:
+#
+
+# Change to false to remove Markdown from posts and/or comments.
address@hidden( 'MARKDOWN_WP_POSTS',      true );
address@hidden( 'MARKDOWN_WP_COMMENTS',   true );
+
+
+
+### Standard Function Interface ###
+
address@hidden( 'MARKDOWN_PARSER_CLASS',  'Markdown_Parser' );
+
+function Markdown($text) {
+#
+# Initialize the parser and return the result of its transform method.
+#
+       # Setup static parser variable.
+       static $parser;
+       if (!isset($parser)) {
+               $parser_class = MARKDOWN_PARSER_CLASS;
+               $parser = new $parser_class;
+       }
+
+       # Transform text using parser.
+       return $parser->transform($text);
+}
+
+
+### WordPress Plugin Interface ###
+
+/*
+Plugin Name: Markdown
+Plugin URI: http://michelf.com/projects/php-markdown/
+Description: <a 
href="http://daringfireball.net/projects/markdown/syntax";>Markdown syntax</a> 
allows you to write using an easy-to-read, easy-to-write plain text format. 
Based on the original Perl version by <a href="http://daringfireball.net/";>John 
Gruber</a>. <a href="http://michelf.com/projects/php-markdown/";>More...</a>
+Version: 1.0.1n
+Author: Michel Fortin
+Author URI: http://michelf.com/
+*/
+
+if (isset($wp_version)) {
+       # More details about how it works here:
+       # <http://michelf.com/weblog/2005/wordpress-text-flow-vs-markdown/>
+       
+       # Post content and excerpts
+       # - Remove WordPress paragraph generator.
+       # - Run Markdown on excerpt, then remove all tags.
+       # - Add paragraph tag around the excerpt, but remove it for the excerpt 
rss.
+       if (MARKDOWN_WP_POSTS) {
+               remove_filter('the_content',     'wpautop');
+        remove_filter('the_content_rss', 'wpautop');
+               remove_filter('the_excerpt',     'wpautop');
+               add_filter('the_content',     'Markdown', 6);
+        add_filter('the_content_rss', 'Markdown', 6);
+               add_filter('get_the_excerpt', 'Markdown', 6);
+               add_filter('get_the_excerpt', 'trim', 7);
+               add_filter('the_excerpt',     'mdwp_add_p');
+               add_filter('the_excerpt_rss', 'mdwp_strip_p');
+               
+               remove_filter('content_save_pre',  'balanceTags', 50);
+               remove_filter('excerpt_save_pre',  'balanceTags', 50);
+               add_filter('the_content',         'balanceTags', 50);
+               add_filter('get_the_excerpt', 'balanceTags', 9);
+       }
+       
+       # Comments
+       # - Remove WordPress paragraph generator.
+       # - Remove WordPress auto-link generator.
+       # - Scramble important tags before passing them to the kses filter.
+       # - Run Markdown on excerpt then remove paragraph tags.
+       if (MARKDOWN_WP_COMMENTS) {
+               remove_filter('comment_text', 'wpautop', 30);
+               remove_filter('comment_text', 'make_clickable');
+               add_filter('pre_comment_content', 'Markdown', 6);
+               add_filter('pre_comment_content', 'mdwp_hide_tags', 8);
+               add_filter('pre_comment_content', 'mdwp_show_tags', 12);
+               add_filter('get_comment_text',    'Markdown', 6);
+               add_filter('get_comment_excerpt', 'Markdown', 6);
+               add_filter('get_comment_excerpt', 'mdwp_strip_p', 7);
+       
+               global $mdwp_hidden_tags, $mdwp_placeholders;
+               $mdwp_hidden_tags = explode(' ',
+                       '<p> </p> <pre> </pre> <ol> </ol> <ul> </ul> <li> 
</li>');
+               $mdwp_placeholders = explode(' ', str_rot13(
+                       'pEj07ZbbBZ U1kqgh4w4p pre2zmeN6K QTi31t9pre ol0MP1jzJR 
'.
+                       'ML5IjmbRol ulANi1NsGY J7zRLJqPul liA8ctl16T 
K9nhooUHli'));
+       }
+       
+       function mdwp_add_p($text) {
+               if (!preg_match('{^$|^<(p|ul|ol|dl|pre|blockquote)>}i', $text)) 
{
+                       $text = '<p>'.$text.'</p>';
+                       $text = preg_replace('{\n{2,}}', "</p>\n\n<p>", $text);
+               }
+               return $text;
+       }
+       
+       function mdwp_strip_p($t) { return preg_replace('{</?p>}i', '', $t); }
+
+       function mdwp_hide_tags($text) {
+               global $mdwp_hidden_tags, $mdwp_placeholders;
+               return str_replace($mdwp_hidden_tags, $mdwp_placeholders, 
$text);
+       }
+       function mdwp_show_tags($text) {
+               global $mdwp_hidden_tags, $mdwp_placeholders;
+               return str_replace($mdwp_placeholders, $mdwp_hidden_tags, 
$text);
+       }
+}
+
+
+### bBlog Plugin Info ###
+
+function identify_modifier_markdown() {
+       return array(
+               'name'                  => 'markdown',
+               'type'                  => 'modifier',
+               'nicename'              => 'Markdown',
+               'description'   => 'A text-to-HTML conversion tool for web 
writers',
+               'authors'               => 'Michel Fortin and John Gruber',
+               'licence'               => 'BSD-like',
+               'version'               => MARKDOWN_VERSION,
+               'help'                  => '<a 
href="http://daringfireball.net/projects/markdown/syntax";>Markdown syntax</a> 
allows you to write using an easy-to-read, easy-to-write plain text format. 
Based on the original Perl version by <a href="http://daringfireball.net/";>John 
Gruber</a>. <a href="http://michelf.com/projects/php-markdown/";>More...</a>'
+       );
+}
+
+
+### Smarty Modifier Interface ###
+
+function smarty_modifier_markdown($text) {
+       return Markdown($text);
+}
+
+
+### Textile Compatibility Mode ###
+
+# Rename this file to "classTextile.php" and it can replace Textile everywhere.
+
+if (strcasecmp(substr(__FILE__, -16), "classTextile.php") == 0) {
+       # Try to include PHP SmartyPants. Should be in the same directory.
+       @include_once 'smartypants.php';
+       # Fake Textile class. It calls Markdown instead.
+       class Textile {
+               function TextileThis($text, $lite='', $encode='') {
+                       if ($lite == '' && $encode == '')    $text = 
Markdown($text);
+                       if (function_exists('SmartyPants'))  $text = 
SmartyPants($text);
+                       return $text;
+               }
+               # Fake restricted version: restrictions are not supported for 
now.
+               function TextileRestricted($text, $lite='', $noimage='') {
+                       return $this->TextileThis($text, $lite);
+               }
+               # Workaround to ensure compatibility with TextPattern 4.0.3.
+               function blockLite($text) { return $text; }
+       }
+}
+
+
+
+#
+# Markdown Parser Class
+#
+
+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;
+
+       # Change to ">" for HTML output.
+       var $empty_element_suffix = MARKDOWN_EMPTY_ELEMENT_SUFFIX;
+       var $tab_width = MARKDOWN_TAB_WIDTH;
+       
+       # Change to `true` to disallow markup or entities.
+       var $no_markup = false;
+       var $no_entities = false;
+       
+       # Predefined urls and titles for reference links and images.
+       var $predef_urls = array();
+       var $predef_titles = array();
+
+
+       function Markdown_Parser() {
+       #
+       # 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.
+       var $urls = array();
+       var $titles = array();
+       var $html_hashes = array();
+       
+       # Status flag to avoid invalid nesting.
+       var $in_anchor = false;
+       
+       
+       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();
+               
+               $in_anchor = false;
+       }
+       
+       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();
+       }
+
+
+       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";
+       }
+       
+       var $document_gamut = array(
+               # Strip link definitions, store in hashes.
+               "stripLinkDefinitions" => 20,
+               
+               "runBasicBlockGamut"   => 30,
+               );
+
+
+       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;
+       }
+       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
+       }
+
+
+       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 <p>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';
+
+               # Regular expression for the content of a block tag.
+               $nested_tags_level = 4;
+               $attr = '
+                       (?>                             # optional tag 
attributes
+                         \s                    # starts with whitespace
+                         (?>
+                               [^>"/]+         # text outside quotes
+                         |
+                               /+(?!>)         # slash not followed by ">"
+                         |
+                               "[^"]*"         # text inside double quotes 
(tolerate ">")
+                         |
+                               \'[^\']*\'      # text inside single quotes 
(tolerate ">")
+                         )*
+                       )?      
+                       ';
+               $content =
+                       str_repeat('
+                               (?>
+                                 [^<]+                 # content without tag
+                               |
+                                 <\2                   # nested opening tag
+                                       '.$attr.'       # attributes
+                                       (?>
+                                         />
+                                       |
+                                         >', $nested_tags_level).      # end 
of opening tag
+                                         '.*?'.                                
        # last level nested tag content
+                       str_repeat('
+                                         </\2\s*>      # closing nested tag
+                                       )
+                                 |                             
+                                       <(?!/\2\s*>     # other tags with a 
different name
+                                 )
+                               )*',
+                               $nested_tags_level);
+               $content2 = str_replace('\2', '\3', $content);
+
+               # First, look for nested blocks, e.g.:
+               #       <div>
+               #               <div>
+               #               tags for inner block must be indented.
+               #               </div>
+               #       </div>
+               #
+               # 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 `<div>` and stop at the first 
`</div>`.
+               $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<tag>` to `</tag>\n`, handling nested 
tags 
+                         # in between.
+                                       
+                                               [ ]{0,'.$less_than_tab.'}
+                                               <('.$block_tags_b_re.')# start 
tag = $2
+                                               '.$attr.'>                      
# attributes followed by > and \n
+                                               '.$content.'            # 
content, support nesting
+                                               </\2>                           
# the matching end tag
+                                               [ ]*                            
# trailing spaces/tabs
+                                               (?=\n+|\Z)      # followed by a 
newline or end of document
+
+                       | # Special version for tags of group a.
+
+                                               [ ]{0,'.$less_than_tab.'}
+                                               <('.$block_tags_a_re.')# start 
tag = $3
+                                               '.$attr.'>[ ]*\n        # 
attributes followed by >
+                                               '.$content2.'           # 
content, support nesting
+                                               </\3>                           
# the matching end tag
+                                               [ ]*                            
# trailing spaces/tabs
+                                               (?=\n+|\Z)      # followed by a 
newline or end of document
+                                       
+                       | # Special case just for <hr />. It was easier to make 
a special 
+                         # case than to make the other regex more complicated.
+                       
+                                               [ ]{0,'.$less_than_tab.'}
+                                               <(hr)                           
# start tag = $2
+                                               '.$attr.'                       
# attributes
+                                               /?>                             
        # the matching end tag
+                                               [ ]*
+                                               (?=\n{2,}|\Z)           # 
followed by a blank line or end of document
+                       
+                       | # Special case for standalone HTML comments:
+                       
+                                       [ ]{0,'.$less_than_tab.'}
+                                       (?s:
+                                               <!-- .*? -->
+                                       )
+                                       [ ]*
+                                       (?=\n{2,}|\Z)           # followed by a 
blank line or end of document
+                       
+                       | # PHP and ASP-style processor instructions (<? and <%)
+                       
+                                       [ ]{0,'.$less_than_tab.'}
+                                       (?s:
+                                               <([?%])                 # $2
+                                               .*?
+                                               \2>
+                                       )
+                                       [ ]*
+                                       (?=\n{2,}|\Z)           # followed by a 
blank line or end of document
+                                       
+                       )
+                       )}Sxmi',
+                       array(&$this, '_hashHTMLBlocks_callback'),
+                       $text);
+
+               return $text;
+       }
+       function _hashHTMLBlocks_callback($matches) {
+               $text = $matches[1];
+               $key  = $this->hashBlock($text);
+               return "\n\n$key\n\n";
+       }
+       
+       
+       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.
+       }
+
+
+       function hashBlock($text) {
+       #
+       # Shortcut function for hashPart with block-level boundaries.
+       #
+               return $this->hashPart($text, 'B');
+       }
+
+
+       var $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,
+               );
+
+       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);
+       }
+       
+       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;
+       }
+       
+       
+       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("<hr$this->empty_element_suffix")."\n", 
+                       $text);
+       }
+
+
+       var $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 `<http://example.com/>`
+               # Must come after doAnchors, because you can use < and >
+               # delimiters in inline links like [this](<url>).
+               "doAutoLinks"         =>  30,
+               "encodeAmpsAndAngles" =>  40,
+
+               "doItalicsAndBold"    =>  50,
+               "doHardBreaks"        =>  60,
+               );
+
+       function runSpanGamut($text) {
+       #
+       # Run span gamut tranformations.
+       #
+               foreach ($this->span_gamut as $method => $priority) {
+                       $text = $this->$method($text);
+               }
+
+               return $text;
+       }
+       
+       
+       function doHardBreaks($text) {
+               # Do hard breaks:
+               return preg_replace_callback('/ {2,}\n/', 
+                       array(&$this, '_doHardBreaks_callback'), $text);
+       }
+       function _doHardBreaks_callback($matches) {
+               return $this->hashPart("<br$this->empty_element_suffix\n");
+       }
+
+
+       function doAnchors($text) {
+       #
+       # Turn Markdown link shortcuts into XHTML <a> 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;
+       }
+       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 = "<a href=\"$url\"";
+                       if ( isset( $this->titles[$link_id] ) ) {
+                               $title = $this->titles[$link_id];
+                               $title = $this->encodeAttribute($title);
+                               $result .=  " title=\"$title\"";
+                       }
+               
+                       $link_text = $this->runSpanGamut($link_text);
+                       $result .= ">$link_text</a>";
+                       $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];
+
+               $url = $this->encodeAttribute($url);
+
+               $result = "<a href=\"$url\"";
+               if (isset($title)) {
+                       $title = $this->encodeAttribute($title);
+                       $result .=  " title=\"$title\"";
+               }
+               
+               $link_text = $this->runSpanGamut($link_text);
+               $result .= ">$link_text</a>";
+
+               return $this->hashPart($result);
+       }
+
+
+       function doImages($text) {
+       #
+       # Turn Markdown image shortcuts into <img> 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;
+       }
+       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 = "<img src=\"$url\" alt=\"$alt_text\"";
+                       if (isset($this->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;
+       }
+       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 = "<img src=\"$url\" alt=\"$alt_text\"";
+               if (isset($title)) {
+                       $title = $this->encodeAttribute($title);
+                       $result .=  " title=\"$title\""; # $title already quoted
+               }
+               $result .= $this->empty_element_suffix;
+
+               return $this->hashPart($result);
+       }
+
+
+       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;
+       }
+       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 = 
"<h$level>".$this->runSpanGamut($matches[1])."</h$level>";
+               return "\n" . $this->hashBlock($block) . "\n\n";
+       }
+       function _doHeaders_callback_atx($matches) {
+               $level = strlen($matches[1]);
+               $block = 
"<h$level>".$this->runSpanGamut($matches[2])."</h$level>";
+               return "\n" . $this->hashBlock($block) . "\n\n";
+       }
+
+
+       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;
+       }
+       function _doLists_callback($matches) {
+               # Re-usable patterns to match list item bullets and number 
markers:
+               $marker_ul_re  = '[*+-]';
+               $marker_ol_re  = '\d+[.]';
+               $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)";
+               
+               $list = $matches[1];
+               $list_type = preg_match("/$marker_ul_re/", $matches[4]) ? "ul" 
: "ol";
+               
+               $marker_any_re = ( $list_type == "ul" ? $marker_ul_re : 
$marker_ol_re );
+               
+               $list .= "\n";
+               $result = $this->processListItems($list, $marker_any_re);
+               
+               $result = $this->hashBlock("<$list_type>\n" . $result . 
"</$list_type>");
+               return "\n". $result ."\n\n";
+       }
+
+       var $list_level = 0;
+
+       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;
+       }
+       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 "<li>" . $item . "</li>\n";
+       }
+
+
+       function doCodeBlocks($text) {
+       #
+       #       Process Markdown `<pre><code>` 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;
+       }
+       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 = "<pre><code>$codeblock\n</code></pre>";
+               return "\n\n".$this->hashBlock($codeblock)."\n\n";
+       }
+
+
+       function makeCodeSpan($code) {
+       #
+       # Create a code span markup for $code. Called from handleSpanToken.
+       #
+               $code = htmlspecialchars(trim($code), ENT_NOQUOTES);
+               return $this->hashPart("<code>$code</code>");
+       }
+
+
+       var $em_relist = array(
+               ''  => '(?:(?<!\*)\*(?!\*)|(?<!_)_(?!_))(?=\S|$)(?![.,:;]\s)',
+               '*' => '(?<=\S|^)(?<!\*)\*(?!\*)',
+               '_' => '(?<=\S|^)(?<!_)_(?!_)',
+               );
+       var $strong_relist = array(
+               ''   => 
'(?:(?<!\*)\*\*(?!\*)|(?<!_)__(?!_))(?=\S|$)(?![.,:;]\s)',
+               '**' => '(?<=\S|^)(?<!\*)\*\*(?!\*)',
+               '__' => '(?<=\S|^)(?<!_)__(?!_)',
+               );
+       var $em_strong_relist = array(
+               ''    => 
'(?:(?<!\*)\*\*\*(?!\*)|(?<!_)___(?!_))(?=\S|$)(?![.,:;]\s)',
+               '***' => '(?<=\S|^)(?<!\*)\*\*\*(?!\*)',
+               '___' => '(?<=\S|^)(?<!_)___(?!_)',
+               );
+       var $em_strong_prepared_relist;
+       
+       function prepareItalicsAndBold() {
+       #
+       # Prepare regular expressions for searching emphasis tokens in any
+       # context.
+       #
+               foreach ($this->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;
+                       }
+               }
+       }
+       
+       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 = 
"<strong><em>$span</em></strong>";
+                                       $text_stack[0] .= 
$this->hashPart($span);
+                                       $em = '';
+                                       $strong = '';
+                               } else {
+                                       # Other closing marker: close one em or 
strong and
+                                       # change current token state to match 
the other
+                                       $token_stack[0] = str_repeat($token{0}, 
3-$token_len);
+                                       $tag = $token_len == 2 ? "strong" : 
"em";
+                                       $span = $text_stack[0];
+                                       $span = $this->runSpanGamut($span);
+                                       $span = "<$tag>$span</$tag>";
+                                       $text_stack[0] = $this->hashPart($span);
+                                       $$tag = ''; # $$tag stands for $em or 
$strong
+                               }
+                               $tree_char_em = false;
+                       } else if ($token_len == 3) {
+                               if ($em) {
+                                       # Reached closing marker for both em 
and strong.
+                                       # Closing strong marker:
+                                       for ($i = 0; $i < 2; ++$i) {
+                                               $shifted_token = 
array_shift($token_stack);
+                                               $tag = strlen($shifted_token) 
== 2 ? "strong" : "em";
+                                               $span = 
array_shift($text_stack);
+                                               $span = 
$this->runSpanGamut($span);
+                                               $span = "<$tag>$span</$tag>";
+                                               $text_stack[0] .= 
$this->hashPart($span);
+                                               $$tag = ''; # $$tag stands for 
$em or $strong
+                                       }
+                               } else {
+                                       # Reached opening three-char emphasis 
marker. Push on token 
+                                       # stack; will be handled by the special 
condition above.
+                                       $em = $token{0};
+                                       $strong = "$em$em";
+                                       array_unshift($token_stack, $token);
+                                       array_unshift($text_stack, '');
+                                       $tree_char_em = true;
+                               }
+                       } else if ($token_len == 2) {
+                               if ($strong) {
+                                       # Unwind any dangling emphasis marker:
+                                       if (strlen($token_stack[0]) == 1) {
+                                               $text_stack[1] .= 
array_shift($token_stack);
+                                               $text_stack[0] .= 
array_shift($text_stack);
+                                       }
+                                       # Closing strong marker:
+                                       array_shift($token_stack);
+                                       $span = array_shift($text_stack);
+                                       $span = $this->runSpanGamut($span);
+                                       $span = "<strong>$span</strong>";
+                                       $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 = "<em>$span</em>";
+                                               $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];
+       }
+
+
+       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;
+       }
+       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 <pre> content, 
+               # so we need to fix that:
+               $bq = preg_replace_callback('{(\s*<pre>.+?</pre>)}sx', 
+                       array(&$this, '_doBlockQuotes_callback2'), $bq);
+
+               return "\n". 
$this->hashBlock("<blockquote>\n$bq\n</blockquote>")."\n\n";
+       }
+       function _doBlockQuotes_callback2($matches) {
+               $pre = $matches[1];
+               $pre = preg_replace('/^  /m', '', $pre);
+               return $pre;
+       }
+
+
+       function formParagraphs($text) {
+       #
+       #       Params:
+       #               $text - string to process with html <p> 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 <p> 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('/^([ ]*)/', "<p>", 
$value);
+                               $value .= "</p>";
+                               $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 = <div> tag
+//                                       <div  \s+
+//                                       [^>]*
+//                                       \b
+//                                       markdown\s*=\s*  ([\'"])      #       
$2 = attr quote char
+//                                       1
+//                                       \2
+//                                       [^>]*
+//                                       >
+//                                     )
+//                                     (                                       
                # $3 = contents
+//                                     .*
+//                                     )
+//                                     (</div>)                                
        # $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);
+       }
+
+
+       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('"', '&quot;', $text);
+               return $text;
+       }
+       
+       
+       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('&', '&amp;', $text);
+               } else {
+                       # Ampersand-encoding based entirely on Nat Irons's 
Amputator
+                       # MT plugin: <http://bumppo.net/projects/amputator/>
+                       $text = 
preg_replace('/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/', 
+                                                               '&amp;', 
$text);;
+               }
+               # Encode remaining <'s
+               $text = str_replace('<', '&lt;', $text);
+
+               return $text;
+       }
+
+
+       function doAutoLinks($text) {
+               $text = 
preg_replace_callback('{<((https?|ftp|dict):[^\'">\s]+)>}i', 
+                       array(&$this, '_doAutoLinks_url_callback'), $text);
+
+               # Email addresses: <address@hidden>
+               $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);
+
+               return $text;
+       }
+       function _doAutoLinks_url_callback($matches) {
+               $url = $this->encodeAttribute($matches[1]);
+               $link = "<a href=\"$url\">$url</a>";
+               return $this->hashPart($link);
+       }
+       function _doAutoLinks_email_callback($matches) {
+               $address = $matches[1];
+               $link = $this->encodeEmailAddress($address);
+               return $this->hashPart($link);
+       }
+
+
+       function encodeEmailAddress($addr) {
+       #
+       #       Input: an email address, e.g. "address@hidden"
+       #
+       #       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.:
+       #
+       #         <p><a 
href="&#109;&#x61;&#105;&#x6c;&#116;&#x6f;&#58;&#x66;o&#111;
+       #        
&#x40;&#101;&#x78;&#97;&#x6d;&#112;&#x6c;&#101;&#46;&#x63;&#111;
+       #        &#x6d;">&#x66;o&#111;&#x40;&#101;&#x78;&#97;&#x6d;&#112;&#x6c;
+       #        &#101;&#46;&#x63;&#111;&#x6d;</a></p>
+       #
+       #       Based by a filter by Matthew Wickline, posted to BBEdit-Talk.
+       #   With some optimizations by Milian Wolff.
+       #
+               $addr = "mailto:"; . $addr;
+               $chars = preg_split('/(?<!^)(?!$)/', $addr);
+               $seed = (int)abs(crc32($addr) / strlen($addr)); # Deterministic 
seed.
+               
+               foreach ($chars as $key => $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 = "<a href=\"$addr\">$text</a>";
+
+               return $addr;
+       }
+
+
+       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.'
+                               |
+                                       (?<![`\\\\])
+                                       `+                                      
        # code span marker
+                       '.( $this->no_markup ? '' : '
+                               |
+                                       <!--    .*?     -->             # 
comment
+                               |
+                                       <\?.*?\?> | <%.*?%>             # 
processing instruction
+                               |
+                                       <[/!$]?[-a-zA-Z0-9:_]+  # regular tags
+                                       (?>
+                                               \s
+                                               (?>[^"\'>]+|"[^"]*"|\'[^\']*\')*
+                                       )?
+                                       >
+                       ').'
+                               )
+                               }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;
+       }
+       
+       
+       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);
+               }
+       }
+
+
+       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.
+       var $utf8_strlen = 'mb_strlen';
+       
+       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;
+       }
+       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;
+       }
+       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);');
+       }
+
+
+       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);
+       }
+       function _unhash_callback($matches) {
+               return $this->html_hashes[$matches[0]];
+       }
+
+}
+
+/*
+
+PHP Markdown
+============
+
+Description
+-----------
+
+This is a PHP translation of the original Markdown formatter written in
+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
+as headers, *emphasis*, code blocks, blockquotes, and links.
+
+Markdown's syntax is designed not as a generic markup language, but
+specifically to serve as a front-end to (X)HTML. You can use span-level
+HTML tags anywhere in a Markdown document, and you can use block level
+HTML tags (like <div> and <table> as well).
+
+For more information about Markdown's syntax, see:
+
+<http://daringfireball.net/projects/markdown/>
+
+
+Bugs
+----
+
+To file bug reports please send email to:
+
+<address@hidden>
+
+Please include with your report: (1) the example input; (2) the output you
+expected; (3) the output Markdown actually produced.
+
+
+Version History
+--------------- 
+
+See the readme file for detailed release notes for this version.
+
+
+Copyright and License
+---------------------
+
+PHP Markdown
+Copyright (c) 2004-2009 Michel Fortin  
+<http://michelf.com/>  
+All rights reserved.
+
+Based on Markdown
+Copyright (c) 2003-2006 John Gruber   
+<http://daringfireball.net/>   
+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.
+
+*/
+?>
\ No newline at end of file


Property changes on: trunk/phpgwapi/inc/wiki2html/markdown/markdown.php
___________________________________________________________________
Added: svn:executable
   + *

Added: trunk/phpgwapi/inc/wiki2html/textile/Textile.php
===================================================================
--- trunk/phpgwapi/inc/wiki2html/textile/Textile.php                            
(rev 0)
+++ trunk/phpgwapi/inc/wiki2html/textile/Textile.php    2011-12-09 09:09:11 UTC 
(rev 8258)
@@ -0,0 +1,1714 @@
+<?php
+
+/**
+ * Example: get XHTML from a given Textile-markup string ($string)
+ *
+ *               $textile = new Textile;
+ *               echo $textile->TextileThis($string);
+ *
+ */
+
+/*
+
+_____________
+T E X T I L E
+
+A Humane Web Text Generator
+
+Version 2.2
+
+Copyright (c) 2003-2004, Dean Allen <address@hidden>
+All rights reserved.
+
+Thanks to Carlo Zottmann <address@hidden> for refactoring
+Textile's procedural code into a class framework
+
+Additions and fixes Copyright (c) 2006 Alex Shiels http://thresholdstate.com/
+
+_____________
+L I C E N S E
+
+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 Textile 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.
+
+_________
+U S A G E
+
+Block modifier syntax:
+
+       Header: h(1-6).
+       Paragraphs beginning with 'hn. ' (where n is 1-6) are wrapped in header 
tags.
+       Example: h1. Header... -> <h1>Header...</h1>
+
+       Paragraph: p. (also applied by default)
+       Example: p. Text -> <p>Text</p>
+
+       Blockquote: bq.
+       Example: bq. Block quotation... -> <blockquote>Block 
quotation...</blockquote>
+
+       Blockquote with citation: bq.:http://citation.url
+       Example: bq.:http://textism.com/ Text...
+       ->      <blockquote cite="http://textism.com";>Text...</blockquote>
+
+       Footnote: fn(1-100).
+       Example: fn1. Footnote... -> <p id="fn1">Footnote...</p>
+
+       Numeric list: #, ##
+       Consecutive paragraphs beginning with # are wrapped in ordered list 
tags.
+       Example: <ol><li>ordered list</li></ol>
+
+       Bulleted list: *, **
+       Consecutive paragraphs beginning with * are wrapped in unordered list 
tags.
+       Example: <ul><li>unordered list</li></ul>
+
+       Definition list:
+               Terms ;, ;;
+               Definitions :, ::
+       Consecutive paragraphs beginning with ; or : are wrapped in definition 
list tags.
+       Example: <dl><dt>term</dt><dd>definition</dd></dl>
+
+Phrase modifier syntax:
+
+                  _emphasis_   ->       <em>emphasis</em>
+                  __italic__   ->       <i>italic</i>
+                        *strong*       ->       <strong>strong</strong>
+                        **bold**       ->       <b>bold</b>
+                ??citation??   ->       <cite>citation</cite>
+          -deleted text-       ->       <del>deleted</del>
+         +inserted text+       ->       <ins>inserted</ins>
+               ^superscript^   ->       <sup>superscript</sup>
+                 ~subscript~   ->       <sub>subscript</sub>
+                          @code@       ->       <code>computer code</code>
+                 %(bob)span%   ->       <span class="bob">span</span>
+
+               ==notextile==   ->       leave text alone (do not format)
+
+          "linktext":url       ->       <a href="url">linktext</a>
+ "linktext(title)":url ->       <a href="url" title="title">linktext</a>
+            "$":url  ->  <a href="url">url</a>
+     "$(title)":url  ->  <a href="url" title="title">url</a>
+
+                  !imageurl!   ->       <img src="imageurl" />
+       !imageurl(alt text)!    ->       <img src="imageurl" alt="alt text" />
+       !imageurl!:linkurl      ->       <a href="linkurl"><img src="imageurl" 
/></a>
+
+ABC(Always Be Closing) ->       <acronym title="Always Be 
Closing">ABC</acronym>
+
+
+Linked Notes:
+============
+
+       Allows the generation of an automated list of notes with links.
+
+       Linked notes are composed of three parts, a set of named _definitions_, 
a set of
+       _references_ to those definitions and one or more _placeholders_ 
indicating where
+       the consolidated list of notes is to be placed in your document.
+
+       Definitions.
+       -----------
+
+       Each note definition must occur in its own paragraph and should look 
like this...
+
+       note#mynotelabel. Your definition text here.
+
+       You are free to use whatever label you wish after the # as long as it 
is made up
+       of letters, numbers, colon(:) or dash(-).
+
+       References.
+       ----------
+
+       Each note reference is marked in your text like this[#mynotelabel] and
+       it will be replaced with a superscript reference that links into the 
list of
+       note definitions.
+
+       List Placeholder(s).
+       -------------------
+
+       The note list can go anywhere in your document. You have to indicate 
where
+       like this...
+
+       notelist.
+
+       notelist can take attributes (class#id) like this: notelist(class#id).
+
+       By default, the note list will show each definition in the order that 
they
+       are referenced in the text by the _references_. It will show each 
definition with
+       a full list of backlinks to each reference. If you do not want this, 
you can choose
+       to override the backlinks like this...
+
+       notelist(class#id)!.    Produces a list with no backlinks.
+       notelist(class#id)^.    Produces a list with only the first backlink.
+
+       Should you wish to have a specific definition display backlinks 
differently to this
+       then you can override the backlink method by appending a link override 
to the
+       _definition_ you wish to customise.
+
+       note#label.    Uses the citelist's setting for backlinks.
+       note#label!.   Causes that definition to have no backlinks.
+       note#label^.   Causes that definition to have one backlink (to the 
first ref.)
+       note#label*.   Causes that definition to have all backlinks.
+
+       Any unreferenced notes will be left out of the list unless you 
explicitly state
+       you want them by adding a '+'. Like this...
+
+       notelist(class#id)!+. Giving a list of all notes without any backlinks.
+
+       You can mix and match the list backlink control and unreferenced links 
controls
+       but the backlink control (if any) must go first. Like so: notelist^+. , 
not
+       like this: notelist+^.
+
+       Example...
+               Scientists say[#lavader] the moon is small.
+
+               note#other. An unreferenced note.
+
+               note#lavader(myliclass). "Proof":url of a small moon.
+
+               notelist(myclass#myid)+.
+
+               Would output (the actual IDs used would be randomised)...
+
+               <p>Scientists say<sup><a href="#def_id_1" 
id="ref_id_1a">1</sup> the moon is small.</p>
+
+               <ol class="myclass" id="myid">
+                       <li class="myliclass"><a 
href="#ref_id_1a"><sup>a</sup></a><span id="def_id_1"> </span><a 
href="url">Proof</a> of a small moon.</li>
+                       <li>An unreferenced note.</li>
+               </ol>
+
+               The 'a b c' backlink characters can be altered too.
+               For example if you wanted the notes to have numeric backlinks 
starting from 1:
+
+               notelist:1.
+
+Table syntax:
+
+       Simple tables:
+
+               |a|simple|table|row|
+               |And|Another|table|row|
+               |With an||empty|cell|
+
+               |=. My table caption goes here
+               |_. A|_. table|_. header|_.row|
+               |A|simple|table|row|
+
+       Tables with attributes:
+
+               table{border:1px solid black}. My table summary here
+               {background:#ddd;color:red}. |{}| | | |
+
+       To specify thead / tfoot / tbody groups, add one of these on its own 
line
+       above the row(s) you wish to wrap (you may specify attributes before 
the dot):
+
+               |^.     # thead
+               |-.     # tbody
+               |~.     # tfoot
+
+       Column groups:
+
+               |:\3. 100
+
+               Becomes:
+                       <colgroup span="3" width="100"></colgroup>
+
+               You can omit either of the \N or width values. You may also
+               add cells after the colgroup definition to specify 
span/width/attributes:
+
+               |:\5. 50 |(firstcol). |\2. 250||300|
+
+               Becomes:
+                       <colgroup span="5" width="50">
+                               <col class="firstcol" />
+                               <col span="2" width="250" />
+                               <col />
+                               <col width="300" />
+                       </colgroup>
+
+Applying Attributes:
+
+       Most anywhere Textile code is used, attributes such as arbitrary css 
style,
+       css classes, and ids can be applied. The syntax is fairly consistent.
+
+       The following characters quickly alter the alignment of block elements:
+
+               <  ->  left align        ex. p<. left-aligned para
+               >  ->  right align               h3>. right-aligned header 3
+               =  ->  centred                   h4=. centred header 4
+               <> ->  justified                 p<>. justified paragraph
+
+       These will change vertical alignment in table cells:
+
+               ^  ->  top                 ex. |^. top-aligned table cell|
+               -  ->  middle              |-. middle aligned|
+               ~  ->  bottom              |~. bottom aligned cell|
+
+       Plain (parentheses) inserted between block syntax and the closing 
dot-space
+       indicate classes and ids:
+
+               p(hector). paragraph -> <p class="hector">paragraph</p>
+
+               p(#fluid). paragraph -> <p id="fluid">paragraph</p>
+
+               (classes and ids can be combined)
+               p(hector#fluid). paragraph -> <p class="hector" 
id="fluid">paragraph</p>
+
+       Curly {brackets} insert arbitrary css style
+
+               p{line-height:18px}. paragraph -> <p 
style="line-height:18px">paragraph</p>
+
+               h3{color:red}. header 3 -> <h3 style="color:red">header 3</h3>
+
+       Square [brackets] insert language attributes
+
+               p[no]. paragraph -> <p lang="no">paragraph</p>
+
+               %[fr]phrase% -> <span lang="fr">phrase</span>
+
+       Usually Textile block element syntax requires a dot and space before 
the block
+       begins, but since lists don't, they can be styled just using braces
+
+               #{color:blue} one  ->  <ol style="color:blue">
+               # big                                   <li>one</li>
+               # list                                  <li>big</li>
+                                                               <li>list</li>
+                                                          </ol>
+
+       Using the span tag to style a phrase
+
+               It goes like this, %{color:red}the fourth the fifth%
+                         -> It goes like this, <span style="color:red">the 
fourth the fifth</span>
+
+*/
+
+// define these before including this file to override the standard glyphs
address@hidden('txt_quote_single_open',  '&#8216;');
address@hidden('txt_quote_single_close', '&#8217;');
address@hidden('txt_quote_double_open',  '&#8220;');
address@hidden('txt_quote_double_close', '&#8221;');
address@hidden('txt_apostrophe',         '&#8217;');
address@hidden('txt_prime',              '&#8242;');
address@hidden('txt_prime_double',       '&#8243;');
address@hidden('txt_ellipsis',           '&#8230;');
address@hidden('txt_emdash',             '&#8212;');
address@hidden('txt_endash',             '&#8211;');
address@hidden('txt_dimension',          '&#215;');
address@hidden('txt_trademark',          '&#8482;');
address@hidden('txt_registered',         '&#174;');
address@hidden('txt_copyright',          '&#169;');
address@hidden('txt_half',               '&#189;');
address@hidden('txt_quarter',            '&#188;');
address@hidden('txt_threequarters',      '&#190;');
address@hidden('txt_degrees',            '&#176;');
address@hidden('txt_plusminus',          '&#177;');
address@hidden('txt_has_unicode',        @preg_match('/\pL/u', 'a')); // Detect 
if Unicode is compiled into PCRE
+
+class Textile
+{
+       var $hlgn;
+       var $vlgn;
+       var $clas;
+       var $lnge;
+       var $styl;
+       var $cspn;
+       var $rspn;
+       var $a;
+       var $s;
+       var $c;
+       var $pnct;
+       var $rel;
+       var $fn;
+
+       var $shelf = array();
+       var $restricted = false;
+       var $noimage = false;
+       var $lite = false;
+       var $url_schemes = array();
+       var $glyph = array();
+       var $hu = '';
+       var $max_span_depth = 5;
+
+       var $ver = '2.2.0';
+       var $rev = '$Rev$';
+
+       var $doc_root;
+
+// -------------------------------------------------------------
+       function Textile()
+       {
+               $this->hlgn = "(?:\<(?!>)|(?<!<)\>|\<\>|\=|[()]+(?! ))";
+               $this->vlgn = "[\-^~]";
+               $this->clas = "(?:\([^)\n]+\))";        # Don't allow 
classes/ids/languages/styles to span across newlines
+               $this->lnge = "(?:\[[^]\n]+\])";
+               $this->styl = "(?:\{[^}\n]+\})";
+               $this->cspn = "(?:\\\\\d+)";
+               $this->rspn = "(?:\/\d+)";
+               $this->a  = "(?:{$this->hlgn}|{$this->vlgn})*";
+               $this->s  = "(?:{$this->cspn}|{$this->rspn})*";
+               $this->c  = 
"(?:{$this->clas}|{$this->styl}|{$this->lnge}|{$this->hlgn})*";
+               $this->lc = "(?:{$this->clas}|{$this->styl}|{$this->lnge})*";
+
+               $this->pnct  = 
'[\!"#\$%&\'()\*\+,\-\./:;<=>address@hidden|}\~]';
+               $this->urlch = '[\w"$\-_.+!*\'(),";\/?:@=&%#{}|\\^~\[\]`]';
+               $pnc = '[[:punct:]]';
+
+               $this->url_schemes = array('http','https','ftp','mailto');
+
+               $this->btag = array('bq', 'bc', 'notextile', 'pre', 'h[1-6]', 
'fn\d+', 'p', '###' );
+
+               if (txt_has_unicode) {
+                       $this->regex_snippets = array(
+                               'acr' => '\p{Lu}\p{Nd}',
+                               'abr' => '\p{Lu}',
+                               'nab' => '\p{Ll}',
+                               'wrd' => '(?:\p{L}|\p{M}|\p{N}|\p{Pc})',
+                               'mod' => 'u', # Make sure to mark the unicode 
patterns as such, Some servers seem to need this.
+                       );
+               } else {
+                       $this->regex_snippets = array(
+                               'acr' => 'A-Z0-9',
+                               'abr' => 'A-Z',
+                               'nab' => 'a-z',
+                               'wrd' => '\w',
+                               'mod' => '',
+                       );
+               }
+               extract( $this->regex_snippets );
+
+               $this->glyph_search = array(
+                       '/('.$wrd.')\'('.$wrd.')/'.$mod,        // I'm an 
apostrophe
+                       '/(\s)\'(\d+'.$wrd.'?)\b(?![.]?['.$wrd.']*?\')/'.$mod,  
// back in '88/the '90s but not in his '90s', '1', '1.' '10m' or '5.png'
+                       '/(\S)\'(?=\s|'.$pnc.'|<|$)/',          // single 
closing
+                       '/\'/',                                 // single 
opening
+                       '/(\S)\"(?=\s|'.$pnc.'|<|$)/',          // double 
closing
+                       '/"/',                                  // double 
opening
+                       
'/\b(['.$abr.']['.$acr.']{2,})\b(?:[(]([^)]*)[)])/'.$mod,  // 3+ uppercase 
acronym
+                       
//'/(?<=\s|^|[>(;-])(['.$abr.']{3,})(['.$nab.']*)(?=\s|'.$pnc.'|<|$)(?=[^">]*?(<|$))/'.$mod,
  // 3+ uppercase
+                       '/([^.]?)\.{3}/',                       // ellipsis
+                       '/(\s?)--(\s?)/',                       // em dash
+                       '/\s-(?:\s|$)/',                        // en dash
+                       '/(\d+)( ?)x( ?)(?=\d+)/',              // dimension 
sign
+                       '/(\b ?|\s|^)[([]TM[])]/i',             // trademark
+                       '/(\b ?|\s|^)[([]R[])]/i',              // registered
+                       '/(\b ?|\s|^)[([]C[])]/i',              // copyright
+                       '/[([]1\/4[])]/',                       // 1/4
+                       '/[([]1\/2[])]/',                       // 1/2
+                       '/[([]3\/4[])]/',                       // 3/4
+                       '/[([]o[])]/',                          // degrees -- 
that's a small 'oh'
+                       '/[([]\+\/-[])]/',                      // plus minus
+               );
+
+               $this->glyph_replace = array(
+                       '$1'.txt_apostrophe.'$2',              // I'm an 
apostrophe
+                       '$1'.txt_apostrophe.'$2',              // back in '88
+                       '$1'.txt_quote_single_close,           // single closing
+                       txt_quote_single_open,                 // single opening
+                       '$1'.txt_quote_double_close,           // double closing
+                       txt_quote_double_open,                 // double opening
+                       '<acronym title="$2">$1</acronym>',     // 3+ uppercase 
acronym
+                       //'<span class="caps">glyph:$1</span>$2', // 3+ 
uppercase
+                       '$1'.txt_ellipsis,                     // ellipsis
+                       '$1'.txt_emdash.'$2',                  // em dash
+                       ' '.txt_endash.' ',                    // en dash
+                       '$1$2'.txt_dimension.'$3',             // dimension sign
+                       '$1'.txt_trademark,                    // trademark
+                       '$1'.txt_registered,                   // registered
+                       '$1'.txt_copyright,                    // copyright
+                       txt_quarter,                           // 1/4
+                       txt_half,                              // 1/2
+                       txt_threequarters,                     // 3/4
+                       txt_degrees,                           // degrees
+                       txt_plusminus,                         // plus minus
+               );
+
+               if (defined('hu'))
+                       $this->hu = hu;
+
+               if (defined('DIRECTORY_SEPARATOR'))
+                       $this->ds = constant('DIRECTORY_SEPARATOR');
+               else
+                       $this->ds = '/';
+
+               $this->doc_root = @$_SERVER['DOCUMENT_ROOT'];
+               if (!$this->doc_root)
+                       $this->doc_root = @$_SERVER['PATH_TRANSLATED']; // IIS
+
+               $this->doc_root = rtrim($this->doc_root, $this->ds).$this->ds;
+       }
+
+// -------------------------------------------------------------
+
+       function TextileThis($text, $lite = '', $encode = '', $noimage = '', 
$strict = '', $rel = '')
+       {
+               $this->span_depth = 0;
+               $this->tag_index = 1;
+               $this->notes = $this->unreferencedNotes = $this->notelist_cache 
= array();
+               $this->note_index = 1;
+               $this->rel = ($rel) ? ' rel="'.$rel.'"' : '';
+
+               $this->lite = $lite;
+               $this->noimage = $noimage;
+
+               if ($encode)
+               {
+                       $text = $this->incomingEntities($text);
+                       $text = str_replace("x%x%", "&amp;", $text);
+                       return $text;
+               } else {
+                       if(!$strict) {
+                               $text = $this->cleanWhiteSpace($text);
+                       }
+
+                       if(!$lite) {
+                               $text = $this->block($text);
+                               $text = $this->placeNoteLists($text);
+                       }
+
+                       $text = $this->retrieve($text);
+                       $text = $this->replaceGlyphs($text);
+                       $text = $this->retrieveTags($text);
+                       $text = $this->retrieveURLs($text);
+                       $this->span_depth = 0;
+
+                       // just to be tidy
+                       $text = str_replace("<br />", "<br />\n", $text);
+
+                       return $text;
+               }
+       }
+
+// -------------------------------------------------------------
+
+       function TextileRestricted($text, $lite = 1, $noimage = 1, $rel = 
'nofollow')
+       {
+               $this->restricted = true;
+               $this->lite = $lite;
+               $this->noimage = $noimage;
+
+               $this->span_depth = 0;
+               $this->tag_index = 1;
+               $this->notes = $this->unreferencedNotes = $this->notelist_cache 
= array();
+               $this->note_index = 1;
+
+               $this->rel = ($rel) ? ' rel="'.$rel.'"' : '';
+
+               // escape any raw html
+               $text = $this->encode_html($text, 0);
+
+               $text = $this->cleanWhiteSpace($text);
+
+               if($lite) {
+                       $text = $this->blockLite($text);
+               } else {
+                       $text = $this->block($text);
+                       $text = $this->placeNoteLists($text);
+               }
+
+               $text = $this->retrieve($text);
+               $text = $this->replaceGlyphs($text);
+               $text = $this->retrieveTags($text);
+               $text = $this->retrieveURLs($text);
+               $this->span_depth = 0;
+
+               // just to be tidy
+               $text = str_replace("<br />", "<br />\n", $text);
+
+               return $text;
+       }
+
+// -------------------------------------------------------------
+       function pba($in, $element = "", $include_id = 1) // "parse block 
attributes"
+       {
+               $style = '';
+               $class = '';
+               $lang = '';
+               $colspan = '';
+               $rowspan = '';
+               $span = '';
+               $width = '';
+               $id = '';
+               $atts = '';
+
+               if (!empty($in)) {
+                       $matched = $in;
+                       if ($element == 'td') {
+                               if (preg_match("/\\\\(\d+)/", $matched, $csp)) 
$colspan = $csp[1];
+                               if (preg_match("/\/(\d+)/", $matched, $rsp)) 
$rowspan = $rsp[1];
+                       }
+
+                       if ($element == 'td' or $element == 'tr') {
+                               if (preg_match("/($this->vlgn)/", $matched, 
$vert))
+                                       $style[] = "vertical-align:" . 
$this->vAlign($vert[1]);
+                       }
+
+                       if (preg_match("/\{([^}]*)\}/", $matched, $sty)) {
+                               $style[] = rtrim($sty[1], ';');
+                               $matched = str_replace($sty[0], '', $matched);
+                       }
+
+                       if (preg_match("/\[([^]]+)\]/U", $matched, $lng)) {
+                               $lang = $lng[1];
+                               $matched = str_replace($lng[0], '', $matched);
+                       }
+
+                       if (preg_match("/\(([^()]+)\)/U", $matched, $cls)) {
+                               $class = $cls[1];
+                               $matched = str_replace($cls[0], '', $matched);
+                       }
+
+                       if (preg_match("/([(]+)/", $matched, $pl)) {
+                               $style[] = "padding-left:" . strlen($pl[1]) . 
"em";
+                               $matched = str_replace($pl[0], '', $matched);
+                       }
+
+                       if (preg_match("/([)]+)/", $matched, $pr)) {
+                               $style[] = "padding-right:" . strlen($pr[1]) . 
"em";
+                               $matched = str_replace($pr[0], '', $matched);
+                       }
+
+                       if (preg_match("/($this->hlgn)/", $matched, $horiz))
+                               $style[] = "text-align:" . 
$this->hAlign($horiz[1]);
+
+                       if (preg_match("/^(.*)#(.*)$/", $class, $ids)) {
+                               $id = $ids[2];
+                               $class = $ids[1];
+                       }
+
+                       if ($element == 'col') {
+                               if (preg_match("/(?:\\\\(\d+))?\s*(\d+)?/", 
$matched, $csp)) {
+                                       $span = isset($csp[1]) ? $csp[1] : '';
+                                       $width = isset($csp[2]) ? $csp[2] : '';
+                               }
+                       }
+
+                       if ($this->restricted)
+                               return ($lang)    ? ' lang="'   . $lang . 
'"':'';
+
+                       $o = '';
+                       if( $style ) {
+                               foreach($style as $s) {
+                                       $parts = split(';', $s);
+                                       foreach( $parts as $p ) {
+                                               $p = trim($p, '; ');
+                                               if( !empty( $p ) )
+                                                       $o .= $p.'; ';
+                                       }
+                               }
+                               $style = trim( strtr($o, 
array("\n"=>'',';;'=>';')) );
+                       }
+
+                       return join('',array(
+                               ($style)   ? ' style="'   . $style    .'"':'',
+                               ($class)   ? ' class="'   . $class    .'"':'',
+                               ($lang)    ? ' lang="'    . $lang     .'"':'',
+                               ($id and $include_id) ? ' id="' . $id .'"':'',
+                               ($colspan) ? ' colspan="' . $colspan  .'"':'',
+                               ($rowspan) ? ' rowspan="' . $rowspan  .'"':'',
+                               ($span)    ? ' span="'    . $span     .'"':'',
+                               ($width)   ? ' width="'   . $width    .'"':'',
+                       ));
+               }
+               return '';
+       }
+
+// -------------------------------------------------------------
+       function hasRawText($text)
+       {
+               // checks whether the text has text not already enclosed by a 
block tag
+               $r = 
trim(preg_replace('@<(p|blockquote|div|form|table|ul|ol|dl|pre|h\d)[^>]*?>.*</\1>@s',
 '', trim($text)));
+               $r = trim(preg_replace('@<(hr|br)[^>]*?/>@', '', $r));
+               return '' != $r;
+       }
+
+// -------------------------------------------------------------
+       function table($text)
+       {
+               $text = $text . "\n\n";
+               return 
preg_replace_callback("/^(?:table(_?{$this->s}{$this->a}{$this->c})\.(.*)?\n)?^({$this->a}{$this->c}\.?
 ?\|.*\|)[\s]*\n\n/smU",
+                        array(&$this, "fTable"), $text);
+       }
+
+// -------------------------------------------------------------
+       function fTable($matches)
+       {
+               $tatts = $this->pba($matches[1], 'table');
+
+               $sum = trim($matches[2]) ? ' 
summary="'.htmlspecialchars(trim($matches[2])).'"' : '';
+               $cap = '';
+               $colgrp = $last_rgrp = '';
+               foreach(preg_split("/\|\s*?$/m", $matches[3], -1, 
PREG_SPLIT_NO_EMPTY) as $row) {
+            // Caption
+            /* TextileWiki fix
+                       if (preg_match("/^\|\=($this->s$this->a$this->c)\. 
([^\|\n]*)(.*)/s", ltrim($row), $cmtch)) {
+                               $capts = $this->pba($cmtch[1]);
+                               $cap = 
"\t<caption".$capts.">".trim($cmtch[2])."</caption>\n";
+                               $row = $cmtch[3];
+            }
+            */
+
+                       // Colgroup
+                       if (preg_match("/^\|:($this->s$this->a$this->c\. 
.*)/m", ltrim($row), $gmtch)) {
+                               $idx=0;
+                               foreach (explode('|', str_replace('.', '', 
$gmtch[1])) as $col) {
+                                       $gatts = $this->pba(trim($col), 'col');
+                                       $colgrp .= "\t<col".(($idx==0) ? 
"group".$gatts.">" : $gatts." />")."\n";
+                                       $idx++;
+                               }
+                               $colgrp .= "\t</colgroup>\n";
+                               continue;
+                       }
+
+                       
preg_match("/(:?^\|($this->vlgn)($this->s$this->a$this->c)\.\s*$\n)?^(.*)/sm", 
ltrim($row), $grpmatch);
+
+                       // Row group
+                       $rgrp = isset($grpmatch[2]) ? (($grpmatch[2] == '^') ? 
'head' : ( ($grpmatch[2] == '~') ? 'foot' : (($grpmatch[2] == '-') ? 'body' : 
'' ) ) ) : '';
+                       $rgrpatts = isset($grpmatch[3]) ? 
$this->pba($grpmatch[3]) : '';
+                       $row = $grpmatch[4];
+
+                       if (preg_match("/^($this->a$this->c\. )(.*)/m", 
ltrim($row), $rmtch)) {
+                               $ratts = $this->pba($rmtch[1], 'tr');
+                               $row = $rmtch[2];
+                       } else $ratts = '';
+
+                       $cells = array();
+                       $cellctr = 0;
+                       foreach(explode("|", $row) as $cell) {
+                               $ctyp = "d";
+                               if (preg_match("/^_/", $cell)) $ctyp = "h";
+                               if (preg_match("/^(_?$this->s$this->a$this->c\. 
)(.*)/", $cell, $cmtch)) {
+                                       $catts = $this->pba($cmtch[1], 'td');
+                                       $cell = $cmtch[2];
+                               } else $catts = '';
+
+                               $cell = $this->graf($cell);
+
+                               if ($cellctr>0) // Ignore first 'cell': it 
precedes the opening pipe
+                                       $cells[] = $this->doTagBr("t$ctyp", 
"\t\t\t<t$ctyp$catts>$cell</t$ctyp>");
+
+                               $cellctr++;
+                       }
+                       $grp = (($rgrp && $last_rgrp) ? 
"\t</t".$last_rgrp.">\n" : '') . (($rgrp) ? "\t<t".$rgrp.$rgrpatts.">\n" : '');
+                       $last_rgrp = ($rgrp) ? $rgrp : $last_rgrp;
+                       $rows[] = $grp."\t\t<tr$ratts>\n" . join("\n", $cells) 
. ($cells ? "\n" : "") . "\t\t</tr>";
+                       unset($cells, $catts);
+               }
+
+               return "\t<table{$tatts}{$sum}>\n" .$cap. $colgrp. join("\n", 
$rows) . "\n".(($last_rgrp) ? "\t</t".$last_rgrp.">\n" : '')."\t</table>\n\n";
+       }
+
+// -------------------------------------------------------------
+       function lists($text)
+       {
+               return preg_replace_callback("/^([#*;:]+$this->lc[ 
.].*)$(?![^#*;:])/smU", array(&$this, "fList"), $text);
+       }
+
+// -------------------------------------------------------------
+       function fList($m)
+       {
+               $text = preg_split('/\n(?=[*#;:])/m', $m[0]);
+               $pt = '';
+               foreach($text as $nr => $line) {
+                       $nextline = isset($text[$nr+1]) ? $text[$nr+1] : false;
+                       if (preg_match("/^([#*;:]+)($this->lc)[ .](.*)$/s", 
$line, $m)) {
+                               list(, $tl, $atts, $content) = $m;
+                               $content = trim($content);
+                               $nl = '';
+                               $ltype = $this->lT($tl);
+                               $litem = (strpos($tl, ';') !== false) ? 'dt' : 
((strpos($tl, ':') !== false) ? 'dd' : 'li');
+                               $showitem = (strlen($content) > 0);
+
+                               if (preg_match("/^([#*;:]+)($this->lc)[ .].*/", 
$nextline, $nm))
+                                       $nl = $nm[1];
+
+                               if ((strpos($pt, ';') !== false) && 
(strpos($tl, ':') !== false)) {
+                                       $lists[$tl] = 2; // We're already in a 
<dl> so flag not to start another
+                               }
+
+                               $atts = $this->pba($atts);
+                               if (!isset($lists[$tl])) {
+                                       $lists[$tl] = 1;
+                                       $line = "\t<" . $ltype . "l$atts>" . 
(($showitem) ? "\n\t\t<$litem>" . $content : '');
+                               } else {
+                                       $line = ($showitem) ? 
"\t\t<$litem$atts>" . $content : '';
+                               }
+
+                               if((strlen($nl) <= strlen($tl))) $line .= 
(($showitem) ? "</$litem>" : '');
+                               foreach(array_reverse($lists) as $k => $v) {
+                                       if(strlen($k) > strlen($nl)) {
+                                               $line .= ($v==2) ? '' : 
"\n\t</" . $this->lT($k) . "l>";
+                                               if((strlen($k) > 1) && ($v != 
2))
+                                                       $line .= 
"</".$litem.">";
+                                               unset($lists[$k]);
+                                       }
+                               }
+                               $pt = $tl; // Remember the current Textile tag
+                       }
+                       else {
+                               $line .= "\n";
+                       }
+                       $out[] = $line;
+               }
+               return $this->doTagBr($litem, join("\n", $out));
+       }
+
+// -------------------------------------------------------------
+       function lT($in)
+       {
+               return preg_match("/^#+/", $in) ? 'o' : ((preg_match("/^\*+/", 
$in)) ? 'u' : 'd');
+       }
+
+// -------------------------------------------------------------
+       function doTagBr($tag, $in)
+       {
+               return 
preg_replace_callback('@<('.preg_quote($tag).')([^>]*?)>(.*)(</\1>)@s', 
array(&$this, 'fBr'), $in);
+       }
+
+// -------------------------------------------------------------
+       function doPBr($in)
+       {
+               return preg_replace_callback('@<(p)([^>]*?)>(.*)(</\1>)@s', 
array(&$this, 'fPBr'), $in);
+       }
+
+// -------------------------------------------------------------
+       function fPBr($m)
+       {
+               # Less restrictive version of fBr() ... used only in paragraphs 
where the next
+               # row may start with a smiley or perhaps something like '#8 
bolt...' or '*** stars...'
+               $content = preg_replace("@(.+)(?<!<br>|<br />)\n(?![\s|])@", 
'$1<br />', $m[3]);
+               return '<'.$m[1].$m[2].'>'.$content.$m[4];
+       }
+
+// -------------------------------------------------------------
+       function fBr($m)
+       {
+               $content = preg_replace("@(.+)(?<!<br>|<br 
/>)\n(?![#*;:\s|])@", '$1<br />', $m[3]);
+               return '<'.$m[1].$m[2].'>'.$content.$m[4];
+       }
+
+// -------------------------------------------------------------
+       function block($text)
+       {
+               $find = $this->btag;
+               $tre = join('|', $find);
+
+               $text = explode("\n\n", $text);
+
+               $tag = 'p';
+               $atts = $cite = $graf = $ext = '';
+               $eat = false;
+
+               $out = array();
+
+               foreach($text as $line) {
+                       $anon = 0;
+                       if 
(preg_match("/^($tre)($this->a$this->c)\.(\.?)(?::(\S+))? (.*)$/s", $line, $m)) 
{
+                               // last block was extended, so close it
+                               if ($ext)
+                                       $out[count($out)-1] .= $c1;
+                               // new block
+                               list(,$tag,$atts,$ext,$cite,$graf) = $m;
+                               list($o1, $o2, $content, $c2, $c1, $eat) = 
$this->fBlock(array(0,$tag,$atts,$ext,$cite,$graf));
+
+                               // leave off c1 if this block is extended, 
we'll close it at the start of the next block
+                               if ($ext)
+                                       $line = $o1.$o2.$content.$c2;
+                               else
+                                       $line = $o1.$o2.$content.$c2.$c1;
+                       }
+                       else {
+                               // anonymous block
+                               $anon = 1;
+                               if ($ext or !preg_match('/^ /', $line)) {
+                                       list($o1, $o2, $content, $c2, $c1, 
$eat) = $this->fBlock(array(0,$tag,$atts,$ext,$cite,$line));
+                                       // skip $o1/$c1 because this is part of 
a continuing extended block
+                                       if ($tag == 'p' and 
!$this->hasRawText($content)) {
+                                               $line = $content;
+                                       }
+                                       else {
+                                               $line = $o2.$content.$c2;
+                                       }
+                               }
+                               else {
+                                       $line = $this->graf($line);
+                               }
+                       }
+
+                       $line = $this->doPBr($line);
+                       $line = preg_replace('/<br>/', '<br />', $line);
+
+                       if ($ext and $anon)
+                               $out[count($out)-1] .= "\n".$line;
+                       elseif(!$eat)
+                               $out[] = $line;
+
+                       if (!$ext) {
+                               $tag = 'p';
+                               $atts = '';
+                               $cite = '';
+                               $graf = '';
+                               $eat = false;
+                       }
+               }
+               if ($ext) $out[count($out)-1] .= $c1;
+               return join("\n\n", $out);
+       }
+
+// -------------------------------------------------------------
+       function fBlock($m)
+       {
+               extract($this->regex_snippets);
+               list(, $tag, $att, $ext, $cite, $content) = $m;
+               $atts = $this->pba($att);
+
+               $o1 = $o2 = $c2 = $c1 = '';
+               $eat = false;
+
+               if( $tag === 'p' ) {
+                       # Is this an anonymous block with a note definition?
+                       $notedef = preg_replace_callback("/
+                                       ^note\#               #  start of note 
def marker
+                                       ([$wrd:-]+)           # !label
+                                       ([*!^]?)              # !link
+                                       ({$this->c})          # !att
+                                       \.[\s]+               #  end of def 
marker
+                                       (.*)$                 # !content
+                               /x$mod", array(&$this, "fParseNoteDefs"), 
$content);
+                       if( empty($notedef) ) # It will be empty if the regex 
matched and ate it.
+                               return array($o1, $o2, $notedef, $c2, $c1, 
true);
+                       }
+
+               if (preg_match("/fn(\d+)/", $tag, $fns)) {
+                       $tag = 'p';
+                       $fnid = empty($this->fn[$fns[1]]) ? $fns[1] : 
$this->fn[$fns[1]];
+
+                       # If there is an author-specified ID goes on the 
wrapper & the auto-id gets pushed to the <sup>
+                       $supp_id = '';
+                       if (strpos($atts, ' id=') === false)
+                               $atts .= ' id="fn' . $fnid . '"';
+                       else
+                               $supp_id = ' id="fn' . $fnid . '"';
+
+                       if (strpos($atts, 'class=') === false)
+                               $atts .= ' class="footnote"';
+
+                       $backlink = (strpos($att, '^') === false) ? $fns[1] : 
'<a href="#fnrev' . $fnid . '">'.$fns[1].'</a>';
+                       $sup = "<sup$supp_id>$backlink</sup>";
+
+                       $content = $sup . ' ' . $content;
+               }
+
+               if ($tag == "bq") {
+                       $cite = $this->shelveURL($cite);
+                       $cite = ($cite != '') ? ' cite="' . $cite . '"' : '';
+                       $o1 = "\t<blockquote$cite$atts>\n";
+                       $o2 = "\t\t<p".$this->pba($att, '', 0).">";
+                       $c2 = "</p>";
+                       $c1 = "\n\t</blockquote>";
+               }
+               elseif ($tag == 'bc') {
+                       $o1 = "<pre$atts>";
+                       $o2 = "<code".$this->pba($att, '', 0).">";
+                       $c2 = "</code>";
+                       $c1 = "</pre>";
+                       $content = 
$this->shelve($this->r_encode_html(rtrim($content, "\n")."\n"));
+               }
+               elseif ($tag == 'notextile') {
+                       $content = $this->shelve($content);
+                       $o1 = $o2 = '';
+                       $c1 = $c2 = '';
+               }
+               elseif ($tag == 'pre') {
+                       $content = 
$this->shelve($this->r_encode_html(rtrim($content, "\n")."\n"));
+                       $o1 = "<pre$atts>";
+                       $o2 = $c2 = '';
+                       $c1 = "</pre>";
+               }
+               elseif ($tag == '###') {
+                       $eat = true;
+               }
+               else {
+                       $o2 = "\t<$tag$atts>";
+                       $c2 = "</$tag>";
+               }
+
+               $content = (!$eat) ? $this->graf($content) : '';
+
+               return array($o1, $o2, $content, $c2, $c1, $eat);
+       }
+
+// -------------------------------------------------------------
+       function graf($text)
+       {
+               // handle normal paragraph text
+               if (!$this->lite) {
+                       $text = $this->noTextile($text);
+                       $text = $this->code($text);
+               }
+
+               $text = $this->getRefs($text);
+               $text = $this->links($text);
+               if (!$this->noimage)
+                       $text = $this->image($text);
+
+               if (!$this->lite) {
+                       $text = $this->table($text);
+                       $text = $this->lists($text);
+               }
+
+               $text = $this->span($text);
+               $text = $this->footnoteRef($text);
+               $text = $this->noteRef($text);
+               $text = $this->glyphs($text);
+               return rtrim($text, "\n");
+       }
+
+// -------------------------------------------------------------
+       function span($text)
+       {
+               $qtags = 
array('\*\*','\*','\?\?','-','__','_','%','\+','~','\^');
+               $pnct = ".,\"'?!;:";
+               $this->span_depth++;
+
+               if( $this->span_depth <= $this->max_span_depth )
+               {
+                       foreach($qtags as $f)
+                       {
+                               $text = preg_replace_callback("/
+                                       (^|(?<=[\s>$pnct\(])|[{[])            # 
pre
+                                       ($f)(?!$f)                            # 
tag
+                                       ({$this->c})                          # 
atts
+                                       (?::(\S+))?                           # 
cite
+                                       ([^\s$f]+|\S.*?[^\s$f\n])             # 
content
+                                       ([$pnct]*)                            # 
end
+                                       $f
+                                       ($|[\]}]|(?=[[:punct:]]{1,2}|\s|\)))  # 
tail
+                               /x", array(&$this, "fSpan"), $text);
+                       }
+               }
+               $this->span_depth--;
+               return $text;
+       }
+
+// -------------------------------------------------------------
+       function fSpan($m)
+       {
+               $qtags = array(
+                       '*'  => 'strong',
+                       '**' => 'b',
+                       '??' => 'cite',
+                       '_'  => 'em',
+                       '__' => 'i',
+                       '-'  => 'del',
+                       '%'  => 'span',
+                       '+'  => 'ins',
+                       '~'  => 'sub',
+                       '^'  => 'sup',
+               );
+
+               list(, $pre, $tag, $atts, $cite, $content, $end, $tail) = $m;
+
+               $tag = $qtags[$tag];
+               $atts = $this->pba($atts);
+               $atts .= ($cite != '') ? 'cite="' . $cite . '"' : '';
+
+               $content = $this->span($content);
+
+               $opentag = '<'.$tag.$atts.'>';
+               $closetag = '</'.$tag.'>';
+               $tags = $this->storeTags($opentag, $closetag);
+               $out = "{$tags['open']}{$content}{$end}{$tags['close']}";
+
+               if (($pre and !$tail) or ($tail and !$pre))
+                       $out = $pre.$out.$tail;
+
+               return $out;
+       }
+
+// -------------------------------------------------------------
+       function storeTags($opentag,$closetag='')
+       {
+               $key = ($this->tag_index++);
+
+               $key = str_pad( (string)$key, 10, '0', STR_PAD_LEFT ); # $key 
must be of fixed length to allow proper matching in retrieveTags
+               $this->tagCache[$key] = array('open'=>$opentag, 
'close'=>$closetag);
+               $tags = array(
+                       'open'  => "textileopentag{$key} ",
+                       'close' => " textileclosetag{$key}",
+               );
+               return $tags;
+       }
+
+// -------------------------------------------------------------
+       function retrieveTags($text)
+       {
+               $text = preg_replace_callback('/textileopentag([\d]{10}) /' , 
array(&$this, 'fRetrieveOpenTags'),  $text);
+               $text = preg_replace_callback('/ textileclosetag([\d]{10})/', 
array(&$this, 'fRetrieveCloseTags'), $text);
+               return $text;
+       }
+
+// -------------------------------------------------------------
+       function fRetrieveOpenTags($m)
+       {
+               list(, $key ) = $m;
+               return $this->tagCache[$key]['open'];
+       }
+
+// -------------------------------------------------------------
+       function fRetrieveCloseTags($m)
+       {
+               list(, $key ) = $m;
+               return $this->tagCache[$key]['close'];
+       }
+
+// -------------------------------------------------------------
+       function placeNoteLists($text)
+       {
+               extract($this->regex_snippets);
+
+               # Sequence all referenced definitions...
+               if( !empty($this->notes) ) {
+                       $o = array();
+                       foreach( $this->notes as $label=>$info ) {
+                               $i = @$info['seq'];
+                               if( !empty($i) ) {
+                                       $info['seq'] = $label;
+                                       $o[$i] = $info;
+                               } else {
+                                       $this->unreferencedNotes[] = $info;     
# unreferenced definitions go here for possible future use.
+                               }
+                       }
+                       if( !empty($o) ) ksort($o);
+                       $this->notes = $o;
+               }
+
+               # Replace list markers...
+               $text = 
preg_replace_callback("@<p>notelist({$this->c})(?:\:($wrd))?([\^!]?)(\+?)\.[\s]*</p>@U$mod",
 array(&$this, "fNoteLists"), $text );
+
+               return $text;
+       }
+
+// -------------------------------------------------------------
+       function fParseNoteDefs($m)
+       {
+               list(, $label, $link, $att, $content) = $m;
+
+               # Assign an id if the note reference parse hasn't found the 
label yet.
+               $id = @$this->notes[$label]['id'];
+               if( !$id )
+                       $this->notes[$label]['id'] = uniqid(rand());
+
+               if( empty($this->notes[$label]['def']) ) # Ignores subsequent 
defs using the same label
+               {
+                       $this->notes[$label]['def'] = array(
+                               'atts'    => $this->pba($att),
+                               'content' => $this->graf($content),
+                               'link'    => $link,
+                       );
+               }
+               return '';
+       }
+
+// -------------------------------------------------------------
+       function noteRef($text)
+       {
+               $text = preg_replace_callback("/
+                       \[                   #  start
+                       ({$this->c})         # !atts
+                       \#
+                       ([^\]!]+?)           # !label
+                       ([!]?)               # !nolink
+                       \]
+               /Ux", array(&$this, "fParseNoteRefs"), $text);
+               return $text;
+       }
+
+// -------------------------------------------------------------
+       function fParseNoteRefs($m)
+       {
+               #   By the time this function is called, all the defs will have 
been processed
+               # into the notes array. So now we can resolve the link numbers 
in the order
+               # we process the refs...
+
+               list(, $atts, $label, $nolink) = $m;
+               $atts = $this->pba($atts);
+               $nolink = ($nolink === '!');
+
+               # Assign a sequence number to this reference if there isn't one 
already...
+               $num = @$this->notes[$label]['seq'];
+               if( !$num )
+                       $num = $this->notes[$label]['seq'] = 
($this->note_index++);
+
+               # Make our anchor point & stash it for possible use in 
backlinks when the
+               # note list is generated later...
+               $this->notes[$label]['refids'][] = $refid = uniqid(rand());
+
+               # If we are referencing a note that hasn't had the definition 
parsed yet, then assign it an ID...
+               $id = @$this->notes[$label]['id'];
+               if( !$id )
+                       $id = $this->notes[$label]['id'] = uniqid(rand());
+
+               # Build the link (if any)...
+               $_ = '<span id="noteref'.$refid.'">'.$num.'</span>';
+               if( !$nolink )
+                       $_ = '<a href="#note'.$id.'">'.$_.'</a>';
+
+               # Build the reference...
+               $_ = '<sup'.$atts.'>'.$_.'</sup>';
+
+               return $_;
+       }
+
+// -------------------------------------------------------------
+       function fNoteLists($m)
+       {
+               list(, $att, $start_char, $g_links, $extras) = $m;
+               if( !$start_char ) $start_char = 'a';
+               $index = $g_links.$extras.$start_char;
+
+               if( empty($this->notelist_cache[$index]) ) { # If not in cache, 
build the entry...
+                       $o = array();
+
+                       if( !empty($this->notes)) {
+                               foreach($this->notes as $seq=>$info) {
+                                       $links = $this->makeBackrefLink($info, 
$g_links, $start_char );
+                                       if( !empty($info['def'])) {
+                                               $id = $info['id'];
+                                               extract($info['def']);
+                                               $o[] = 
"\t".'<li'.$atts.'>'.$links.'<span id="note'.$id.'"> </span>'.$content.'</li>';
+                                       } else {
+                                               $o[] = 
"\t".'<li'.$atts.'>'.$links.' Undefined Note [#'.$info['seq'].'].</li>';
+                                       }
+                               }
+                       }
+                       if( '+' == $extras && !empty($this->unreferencedNotes) 
) {
+                               foreach($this->unreferencedNotes as 
$seq=>$info) {
+                                       if( !empty($info['def'])) {
+                                               extract($info['def']);
+                                               $o[] = 
"\t".'<li'.$atts.'>'.$content.'</li>';
+                                       }
+                               }
+                       }
+
+                       $this->notelist_cache[$index] = join("\n",$o);
+               }
+
+               $_ = ($this->notelist_cache[$index]) ? 
$this->notelist_cache[$index] : '';
+
+               if( !empty($_) ) {
+                       $list_atts = $this->pba($att);
+                       $_ = "<ol$list_atts>\n$_\n</ol>";
+               }
+
+               return $_;
+       }
+
+// -------------------------------------------------------------
+       function makeBackrefLink( &$info, $g_links, $i )
+       {
+               $atts = $content = $id = $link = '';
+               @extract( $info['def'] );
+               $backlink_type = ($link) ? $link : $g_links;
+
+               $i_ = strtr( $this->encode_high($i) , array('&'=>'', ';'=>'', 
'#'=>''));
+               $decode = (strlen($i) !== strlen($i_));
+
+               if( $backlink_type === '!' )
+                       return '';
+               elseif( $backlink_type === '^' )
+                       return '<a 
href="#noteref'.$info['refids'][0].'"><sup>'.$i.'</sup></a>';
+               else {
+                       $_ = array();
+                       foreach( $info['refids'] as $id ) {
+                               $_[] = '<a href="#noteref'.$id.'"><sup>'. ( 
($decode) ? $this->decode_high('&#'.$i_.';') : $i_ ) .'</sup></a>';
+                               $i_++;
+                       }
+                       $_ = join( ' ', $_ );
+                       return $_;
+               }
+
+               return '';
+       }
+
+// -------------------------------------------------------------
+       function links($text)
+       {
+               return preg_replace_callback('/
+                       (^|(?<=[\s>.\(])|[{[]) # $pre
+                       "                      # start
+                       (' . $this->c . ')     # $atts
+                       ([^"]+?)               # $text
+                       (?:\(([^)]+?)\)(?="))? # $title
+                       ":
+                       ('.$this->urlch.'+?)   # $url
+                       (\/)?                  # $slash
+                       ([^\w\/;]*?)           # $post
+                       ([\]}]|(?=\s|$|\)))
+               /x', array(&$this, "fLink"), $text);
+       }
+
+// -------------------------------------------------------------
+       function fLink($m)
+       {
+               list(, $pre, $atts, $text, $title, $url, $slash, $post, $tail) 
= $m;
+
+               if( '$' === $text ) $text = $url;
+
+               $atts = $this->pba($atts);
+               $atts .= ($title != '') ? ' title="' . 
$this->encode_html($title) . '"' : '';
+
+               if (!$this->noimage)
+                       $text = $this->image($text);
+
+               $text = $this->span($text);
+               $text = $this->glyphs($text);
+               $url = $this->shelveURL($url.$slash);
+
+               $opentag = '<a href="' . $url . '"' . $atts . $this->rel . '>';
+               $closetag = '</a>';
+               $tags = $this->storeTags($opentag, $closetag);
+               $out = $tags['open'].trim($text).$tags['close'];
+
+               if (($pre and !$tail) or ($tail and !$pre))
+               {
+                       $out = $pre.$out.$post.$tail;
+                       $post = '';
+               }
+
+               return $this->shelve($out).$post;
+       }
+
+// -------------------------------------------------------------
+       function getRefs($text)
+       {
+               return 
preg_replace_callback("/^\[(.+)\]((?:http:\/\/|\/)\S+)(?=\s|$)/Um",
+                       array(&$this, "refs"), $text);
+       }
+
+// -------------------------------------------------------------
+       function refs($m)
+       {
+               list(, $flag, $url) = $m;
+               $this->urlrefs[$flag] = $url;
+               return '';
+       }
+
+// -------------------------------------------------------------
+       function shelveURL($text)
+       {
+               if (!$text) return '';
+               $ref = md5($text);
+               $this->urlshelf[$ref] = $text;
+               return 'urlref:'.$ref;
+       }
+
+// -------------------------------------------------------------
+       function retrieveURLs($text)
+       {
+               return preg_replace_callback('/urlref:(\w{32})/',
+                       array(&$this, "retrieveURL"), $text);
+       }
+
+// -------------------------------------------------------------
+       function retrieveURL($m)
+       {
+               $ref = $m[1];
+               if (!isset($this->urlshelf[$ref]))
+                       return $ref;
+               $url = $this->urlshelf[$ref];
+               if (isset($this->urlrefs[$url]))
+                       $url = $this->urlrefs[$url];
+               return $this->r_encode_html($this->relURL($url));
+       }
+
+// -------------------------------------------------------------
+       function relURL($url)
+       {
+               $parts = @parse_url(urldecode($url));
+               if ((empty($parts['scheme']) or @$parts['scheme'] == 'http') and
+                        empty($parts['host']) and
+                        preg_match('/^\w/', @$parts['path']))
+                       $url = $this->hu.$url;
+               if ($this->restricted and !empty($parts['scheme']) and
+                               !in_array($parts['scheme'], $this->url_schemes))
+                       return '#';
+               return $url;
+       }
+
+// -------------------------------------------------------------
+       function isRelURL($url)
+       {
+               $parts = @parse_url($url);
+               return (empty($parts['scheme']) and empty($parts['host']));
+       }
+
+// -------------------------------------------------------------
+       function image($text)
+       {
+               return preg_replace_callback("/
+                       (?:[[{])?                  # pre
+                       \!                                 # opening !
+                       (\<|\=|\>)?        # optional alignment atts
+                       ($this->c)                 # optional style,class atts
+                       (?:\. )?                   # optional dot-space
+                       ([^\s(!]+)                 # presume this is the src
+                       \s?                        # optional space
+                       (?:\(([^\)]+)\))?  # optional title
+                       \!                                 # closing
+                       (?::(\S+))?        # optional href
+                       (?:[\]}]|(?=\s|$|\))) # lookahead: space or end of 
string
+               /x", array(&$this, "fImage"), $text);
+       }
+
+// -------------------------------------------------------------
+       function fImage($m)
+       {
+               list(, $algn, $atts, $url) = $m;
+               $url = htmlspecialchars($url);
+               $atts  = $this->pba($atts);
+               $atts .= ($algn != '')  ? ' align="' . $this->iAlign($algn) . 
'"' : '';
+               if (isset($m[4])) {
+                       $m[4] = htmlspecialchars($m[4]);
+                       $atts .= ' title="' . $m[4] . '" alt="'  . $m[4] . '"';
+               } else {
+                       $atts .= ' alt=""';
+               }
+
+               $size = false;
+               if ($this->isRelUrl($url))
+                       $size = 
@getimagesize(realpath($this->doc_root.ltrim($url, $this->ds)));
+               if ($size) $atts .= " $size[3]";
+
+               $href = (isset($m[5])) ? $this->shelveURL($m[5]) : '';
+               $url = $this->shelveURL($url);
+
+               $out = array(
+                       ($href) ? '<a href="' . $href . '">' : '',
+                       '<img src="' . $url . '"' . $atts . ' />',
+                       ($href) ? '</a>' : ''
+               );
+
+               return $this->shelve(join('',$out));
+       }
+
+// -------------------------------------------------------------
+       function code($text)
+       {
+               $text = $this->doSpecial($text, '<code>', '</code>', 'fCode');
+               $text = $this->doSpecial($text, '@', '@', 'fCode');
+               $text = $this->doSpecial($text, '<pre>', '</pre>', 'fPre');
+               return $text;
+       }
+
+// -------------------------------------------------------------
+       function fCode($m)
+       {
+               @list(, $before, $text, $after) = $m;
+               return 
$before.$this->shelve('<code>'.$this->r_encode_html($text).'</code>').$after;
+       }
+
+// -------------------------------------------------------------
+       function fPre($m)
+       {
+               @list(, $before, $text, $after) = $m;
+               return 
$before.'<pre>'.$this->shelve($this->r_encode_html($text)).'</pre>'.$after;
+       }
+
+// -------------------------------------------------------------
+       function shelve($val)
+       {
+               $i = uniqid(rand());
+               $this->shelf[$i] = $val;
+               return $i;
+       }
+
+// -------------------------------------------------------------
+       function retrieve($text)
+       {
+               if (is_array($this->shelf))
+                       do {
+                               $old = $text;
+                               $text = strtr($text, $this->shelf);
+                        } while ($text != $old);
+
+               return $text;
+       }
+
+// -------------------------------------------------------------
+// NOTE: deprecated
+       function incomingEntities($text)
+       {
+               return preg_replace("/&(?![#a-z0-9]+;)/i", "x%x%", $text);
+       }
+
+// -------------------------------------------------------------
+// NOTE: deprecated
+       function encodeEntities($text)
+       {
+               return (function_exists('mb_encode_numericentity'))
+               ?        $this->encode_high($text)
+               :        htmlentities($text, ENT_NOQUOTES, "utf-8");
+       }
+
+// -------------------------------------------------------------
+// NOTE: deprecated
+       function fixEntities($text)
+       {
+               /*      de-entify any remaining angle brackets or ampersands */
+               return str_replace(array("&gt;", "&lt;", "&amp;"),
+                       array(">", "<", "&"), $text);
+       }
+
+// -------------------------------------------------------------
+       function cleanWhiteSpace($text)
+       {
+               $out = preg_replace("/^\xEF\xBB\xBF|\x1A/", '', $text); # Byte 
order mark (if present)
+               $out = preg_replace("/\r\n?/", "\n", $out); # DOS and MAC line 
endings to *NIX style endings
+               $out = preg_replace("/^[ \t]*\n/m", "\n", $out);        # lines 
containing only whitespace
+               $out = preg_replace("/\n{3,}/", "\n\n", $out);  # 3 or more 
line ends
+               $out = preg_replace("/^\n*/", "", $out);                # 
leading blank lines
+               return $out;
+       }
+
+// -------------------------------------------------------------
+       function doSpecial($text, $start, $end, $method='fSpecial')
+       {
+               return 
preg_replace_callback('/(^|\s|[[({>])'.preg_quote($start, 
'/').'(.*?)'.preg_quote($end, '/').'(\s|$|[\])}])?/ms',
+                       array(&$this, $method), $text);
+       }
+
+// -------------------------------------------------------------
+       function fSpecial($m)
+       {
+               // A special block like notextile or code
+               @list(, $before, $text, $after) = $m;
+               return $before.$this->shelve($this->encode_html($text)).$after;
+       }
+
+// -------------------------------------------------------------
+       function noTextile($text)
+       {
+                $text = $this->doSpecial($text, '<notextile>', '</notextile>', 
'fTextile');
+                return $this->doSpecial($text, '==', '==', 'fTextile');
+
+       }
+
+// -------------------------------------------------------------
+       function fTextile($m)
+       {
+               @list(, $before, $notextile, $after) = $m;
+               #$notextile = str_replace(array_keys($modifiers), 
array_values($modifiers), $notextile);
+               return $before.$this->shelve($notextile).$after;
+       }
+
+// -------------------------------------------------------------
+       function footnoteRef($text)
+       {
+               return preg_replace('/(?<=\S)\[([0-9]+)([\!]?)\](\s)?/Ue',
+                       '$this->footnoteID(\'\1\',\'\2\',\'\3\')', $text);
+       }
+
+// -------------------------------------------------------------
+       function footnoteID($id, $nolink, $t)
+       {
+               $backref = '';
+               if (empty($this->fn[$id])) {
+                       $this->fn[$id] = $a = uniqid(rand());
+                       $backref = 'id="fnrev'.$a.'" ';
+               }
+
+               $fnid = $this->fn[$id];
+
+               $footref = ( '!' == $nolink ) ? $id : '<a 
href="#fn'.$fnid.'">'.$id.'</a>';
+               $footref = '<sup 
'.$backref.'class="footnote">'.$footref.'</sup>';
+
+               return $footref;
+       }
+
+// -------------------------------------------------------------
+       function glyphs($text)
+       {
+               // fix: hackish -- adds a space if final char of text is a 
double quote.
+               $text = preg_replace('/"\z/', "\" ", $text);
+
+               $text = preg_split("@(<[\w/!?].*>)@Us", $text, -1, 
PREG_SPLIT_DELIM_CAPTURE);
+               $i = 0;
+               foreach($text as $line) {
+                       // text tag text tag text ...
+                       if (++$i % 2) {
+                               // raw < > & chars are already entity encoded 
in restricted mode
+                               if (!$this->restricted) {
+                                       $line = $this->encode_raw_amp($line);
+                                       $line = $this->encode_lt_gt($line);
+                               }
+                               $line = preg_replace($this->glyph_search, 
$this->glyph_replace, $line);
+                       }
+                       $glyph_out[] = $line;
+               }
+               return join('', $glyph_out);
+       }
+
+// -------------------------------------------------------------
+       function replaceGlyphs($text)
+       {
+               return preg_replace('/glyph:([^<]+)/','$1',$text);
+       }
+
+// -------------------------------------------------------------
+       function iAlign($in)
+       {
+               $vals = array(
+                       '<' => 'left',
+                       '=' => 'center',
+                       '>' => 'right');
+               return (isset($vals[$in])) ? $vals[$in] : '';
+       }
+
+// -------------------------------------------------------------
+       function hAlign($in)
+       {
+               $vals = array(
+                       '<'  => 'left',
+                       '='  => 'center',
+                       '>'  => 'right',
+                       '<>' => 'justify');
+               return (isset($vals[$in])) ? $vals[$in] : '';
+       }
+
+// -------------------------------------------------------------
+       function vAlign($in)
+       {
+               $vals = array(
+                       '^' => 'top',
+                       '-' => 'middle',
+                       '~' => 'bottom');
+               return (isset($vals[$in])) ? $vals[$in] : '';
+       }
+
+// -------------------------------------------------------------
+// NOTE: used in notelists
+       function encode_high($text, $charset = "UTF-8")
+       {
+               return mb_encode_numericentity($text, $this->cmap(), $charset);
+       }
+
+// -------------------------------------------------------------
+// NOTE: used in notelists
+       function decode_high($text, $charset = "UTF-8")
+       {
+               return mb_decode_numericentity($text, $this->cmap(), $charset);
+       }
+
+// -------------------------------------------------------------
+// NOTE: deprecated
+       function cmap()
+       {
+               $f = 0xffff;
+               $cmap = array(
+                       0x0080, 0xffff, 0, $f);
+               return $cmap;
+       }
+
+// -------------------------------------------------------------
+       function encode_raw_amp($text)
+        {
+               return preg_replace('/&(?!#?[a-z0-9]+;)/i', '&amp;', $text);
+       }
+
+// -------------------------------------------------------------
+       function encode_lt_gt($text)
+        {
+               return strtr($text, array('<' => '&lt;', '>' => '&gt;'));
+       }
+
+// -------------------------------------------------------------
+       function encode_html($str, $quotes=1)
+       {
+               $a = array(
+                       '&' => '&amp;',
+                       '<' => '&lt;',
+                       '>' => '&gt;',
+               );
+               if ($quotes) $a = $a + array(
+                       "'" => '&#39;', // numeric, as in htmlspecialchars
+                       '"' => '&quot;',
+               );
+
+               return strtr($str, $a);
+       }
+
+// -------------------------------------------------------------
+       function r_encode_html($str, $quotes=1)
+       {
+               // in restricted mode, input has already been escaped
+               if ($this->restricted)
+                       return $str;
+               return $this->encode_html($str, $quotes);
+       }
+
+// -------------------------------------------------------------
+       function textile_popup_help($name, $helpvar, $windowW, $windowH)
+       {
+               return ' <a target="_blank" 
href="http://www.textpattern.com/help/?item=' . $helpvar . '" 
onclick="window.open(this.href, \'popupwindow\', \'width=' . $windowW . 
',height=' . $windowH . ',scrollbars,resizable\'); return false;">' . $name . 
'</a><br />';
+
+               return $out;
+       }
+
+// -------------------------------------------------------------
+// NOTE: deprecated
+       function txtgps($thing)
+       {
+               if (isset($_POST[$thing])) {
+                       if (get_magic_quotes_gpc()) {
+                               return stripslashes($_POST[$thing]);
+                       }
+                       else {
+                               return $_POST[$thing];
+                       }
+               }
+               else {
+                       return '';
+               }
+       }
+
+// -------------------------------------------------------------
+// NOTE: deprecated
+       function dump()
+       {
+               static $bool = array( 0=>'false', 1=>'true' );
+               foreach (func_get_args() as $a)
+                       echo "\n<pre>",(is_array($a)) ? print_r($a) : 
((is_bool($a)) ? $bool[(int)$a] : $a), "</pre>\n";
+               return $this;
+       }
+
+// -------------------------------------------------------------
+
+       function blockLite($text)
+       {
+               $this->btag = array('bq', 'p');
+               return $this->block($text."\n\n");
+       }
+
+
+} // end class




reply via email to

[Prev in Thread] Current Thread [Next in Thread]