[Top][All Lists]
[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 `<`.
+
+* 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('"', '"', $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('&', '&', $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+);)/',
+ '&',
$text);;
+ }
+ # Encode remaining <'s
+ $text = str_replace('<', '<', $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="mailto:foo
+ #
@example.co
+ # m">foo@exampl
+ # e.com</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', '‘');
address@hidden('txt_quote_single_close', '’');
address@hidden('txt_quote_double_open', '“');
address@hidden('txt_quote_double_close', '”');
address@hidden('txt_apostrophe', '’');
address@hidden('txt_prime', '′');
address@hidden('txt_prime_double', '″');
address@hidden('txt_ellipsis', '…');
address@hidden('txt_emdash', '—');
address@hidden('txt_endash', '–');
address@hidden('txt_dimension', '×');
address@hidden('txt_trademark', '™');
address@hidden('txt_registered', '®');
address@hidden('txt_copyright', '©');
address@hidden('txt_half', '½');
address@hidden('txt_quarter', '¼');
address@hidden('txt_threequarters', '¾');
address@hidden('txt_degrees', '°');
address@hidden('txt_plusminus', '±');
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%", "&", $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(">", "<", "&"),
+ 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', '&', $text);
+ }
+
+// -------------------------------------------------------------
+ function encode_lt_gt($text)
+ {
+ return strtr($text, array('<' => '<', '>' => '>'));
+ }
+
+// -------------------------------------------------------------
+ function encode_html($str, $quotes=1)
+ {
+ $a = array(
+ '&' => '&',
+ '<' => '<',
+ '>' => '>',
+ );
+ if ($quotes) $a = $a + array(
+ "'" => ''', // numeric, as in htmlspecialchars
+ '"' => '"',
+ );
+
+ 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
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Fmsystem-commits] [8258] API: add wiki parser,
Sigurd Nes <=