4090 lines
142 KiB
PHP
4090 lines
142 KiB
PHP
<?php
|
|
// @(#) $Id: Textile.php,v 1.13 2005/03/21 15:26:55 jhriggs Exp $
|
|
|
|
/* 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, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
function Textile($text) {
|
|
$textile = new Textile;
|
|
return $textile->process($text);
|
|
}
|
|
|
|
function smarty_modifier_textile($text) {
|
|
return Textile($text);
|
|
}
|
|
|
|
/**
|
|
* The Textile class serves as a wrapper for all Textile
|
|
* functionality. It is not inherently necessary that Textile be a
|
|
* class; however, this is as close as one can get to a namespace in
|
|
* PHP. Wrapping the functionality in a class prevents name
|
|
* collisions and dirtying of the global namespace. The Textile class
|
|
* uses no global variables and will not have any side-effects on
|
|
* other code.
|
|
*
|
|
* @brief Class wrapper for the Textile functionality.
|
|
*/
|
|
class Textile {
|
|
/**
|
|
* The @c array containing all of the Textile options for this
|
|
* object.
|
|
*
|
|
* @private
|
|
*/
|
|
var $options = array();
|
|
|
|
/**
|
|
* The @c string containing the regular expression pattern for a
|
|
* URL. This variable is initialized by @c _create_re() which is
|
|
* called in the contructor.
|
|
*
|
|
* @private
|
|
*/
|
|
var $urlre;
|
|
|
|
/**
|
|
* The @c string containing the regular expression pattern for
|
|
* punctuation characters. This variable is initialized by
|
|
* @c _create_re() which is called in the contructor.
|
|
*
|
|
* @private
|
|
*/
|
|
var $punct;
|
|
|
|
/**
|
|
* The @c string containing the regular expression pattern for the
|
|
* valid vertical alignment codes. This variable is initialized by
|
|
* @c _create_re() which is called in the contructor.
|
|
*
|
|
* @private
|
|
*/
|
|
var $valignre;
|
|
|
|
/**
|
|
* The @c string containing the regular expression pattern for the
|
|
* valid table alignment codes. This variable is initialized by
|
|
* @c _create_re() which is called in the contructor.
|
|
*
|
|
* @private
|
|
*/
|
|
var $tblalignre;
|
|
|
|
/**
|
|
* The @c string containing the regular expression pattern for the
|
|
* valid horizontal alignment codes. This variable is initialized by
|
|
* @c _create_re() which is called in the contructor.
|
|
*
|
|
* @private
|
|
*/
|
|
var $halignre;
|
|
|
|
/**
|
|
* The @c string containing the regular expression pattern for the
|
|
* valid alignment codes. This variable is initialized by
|
|
* @c _create_re() which is called in the contructor.
|
|
*
|
|
* @private
|
|
*/
|
|
var $alignre;
|
|
|
|
/**
|
|
* The @c string containing the regular expression pattern for the
|
|
* valid image alignment codes. This variable is initialized by
|
|
* @c _create_re() which is called in the contructor.
|
|
*
|
|
* @private
|
|
*/
|
|
var $imgalignre;
|
|
|
|
/**
|
|
* The @c string containing the regular expression pattern for a
|
|
* class, ID, and/or padding specification. This variable is
|
|
* initialized by @c _create_re() which is called in the contructor.
|
|
*
|
|
* @private
|
|
*/
|
|
var $clstypadre;
|
|
|
|
/**
|
|
* The @c string containing the regular expression pattern for a
|
|
* class and/or ID specification. This variable is initialized by
|
|
* @c _create_re() which is called in the contructor.
|
|
*
|
|
* @private
|
|
*/
|
|
var $clstyre;
|
|
|
|
/**
|
|
* The @c string containing the regular expression pattern for a
|
|
* class, ID, and/or filter specification. This variable is
|
|
* initialized by @c _create_re() which is called in the contructor.
|
|
*
|
|
* @private
|
|
*/
|
|
var $clstyfiltre;
|
|
|
|
/**
|
|
* The @c string containing the regular expression pattern for a
|
|
* code block. This variable is initialized by @c _create_re() which
|
|
* is called in the contructor.
|
|
*
|
|
* @private
|
|
*/
|
|
var $codere;
|
|
|
|
/**
|
|
* The @c string containing the regular expression pattern for all
|
|
* block tags. This variable is initialized by @c _create_re() which
|
|
* is called in the contructor.
|
|
*
|
|
* @private
|
|
*/
|
|
var $blocktags;
|
|
|
|
/**
|
|
* The @c array containing the list of lookup links.
|
|
*
|
|
* @private
|
|
*/
|
|
var $links = array();
|
|
|
|
/**
|
|
* The @c array containing <code>array</code>s of replacement blocks
|
|
* of text that are temporary removed from the input text to avoid
|
|
* processing. Different functions use this replacement
|
|
* functionality, and each shifts its own replacement array into
|
|
* position 0 and removes it when finished. This avoids having
|
|
* several replacement variables and/or functions clobbering
|
|
* eachothers' replacement blocks.
|
|
*
|
|
* @private
|
|
*/
|
|
var $repl = array();
|
|
|
|
/**
|
|
* The @c array containing temporary <code>string</code>s used in
|
|
* replacement callbacks. *JHR*
|
|
*
|
|
* @private
|
|
*/
|
|
var $tmp = array();
|
|
|
|
/**
|
|
* Instantiates a new Textile object. Optional options
|
|
* can be passed to initialize the object. Attributes for the
|
|
* options key are the same as the get/set method names
|
|
* documented here.
|
|
*
|
|
* @param $options The @c array specifying the options to use for
|
|
* this object.
|
|
*
|
|
* @public
|
|
*/
|
|
function Textile($options = array()) {
|
|
$this->options = $options;
|
|
$this->options['filters'] = ($this->options['filters'] ? $this->options['filters'] : array());
|
|
$this->options['charset'] = ($this->options['charset'] ? $this->options['charset'] : 'iso-8859-1');
|
|
$this->options['char_encoding'] = (isset($this->options['char_encoding']) ? $this->options['char_encoding'] : 1);
|
|
$this->options['do_quotes'] = (isset($this->options['do_quotes']) ? $this->options['do_quotes'] : 1);
|
|
$this->options['trim_spaces'] = (isset($this->options['trim_spaces']) ? $this->options['trim_spaces'] : 0);
|
|
$this->options['smarty_mode'] = (isset($this->options['smarty_mode']) ? $this->options['smarty_mode'] : 1);
|
|
$this->options['preserve_spaces'] = (isset($this->options['preserve_spaces']) ? $this->options['preserve_spaaces'] : 0);
|
|
$this->options['head_offset'] = (isset($this->options['head_offset']) ? $this->options['head_offset'] : 0);
|
|
|
|
if (is_array($this->options['css'])) {
|
|
$this->css($this->options['css']);
|
|
}
|
|
$this->options['macros'] = ($this->options['macros'] ? $this->options['macros'] : $this->default_macros());
|
|
if (isset($this->options['flavor'])) {
|
|
$this->flavor($this->options['flavor']);
|
|
} else {
|
|
$this->flavor('xhtml1/css');
|
|
}
|
|
$this->_create_re();
|
|
} // function Textile
|
|
|
|
// getter/setter methods...
|
|
|
|
/**
|
|
* Used to set Textile attributes. Attribute names are the same
|
|
* as the get/set method names documented here.
|
|
*
|
|
* @param $opt A @c string specifying the name of the option to
|
|
* change or an @c array specifying options and values.
|
|
* @param $value The value for the provided option name.
|
|
*
|
|
* @public
|
|
*/
|
|
function set($opt, $value = NULL) {
|
|
if (is_array($opt)) {
|
|
foreach ($opt as $opt => $value) {
|
|
$this->set($opt, $value);
|
|
}
|
|
} else {
|
|
// the following options have special set methods
|
|
// that activate upon setting:
|
|
if ($opt == 'charset') {
|
|
$this->charset($value);
|
|
} elseif ($opt == 'css') {
|
|
$this->css($value);
|
|
} elseif ($opt == 'flavor') {
|
|
$this->flavor($value);
|
|
} else {
|
|
$this->options[$opt] = $value;
|
|
}
|
|
}
|
|
} // function set
|
|
|
|
/**
|
|
* Used to get Textile attributes. Attribute names are the same
|
|
* as the get/set method names documented here.
|
|
*
|
|
* @param $opt A @c string specifying the name of the option to get.
|
|
*
|
|
* @return The value for the provided option.
|
|
*
|
|
* @public
|
|
*/
|
|
function get($opt) {
|
|
return $this->options[$opt];
|
|
} // function get
|
|
|
|
/**
|
|
* Gets or sets the "disable html" control, which allows you to
|
|
* prevent HTML tags from being used within the text processed.
|
|
* Any HTML tags encountered will be removed if disable html is
|
|
* enabled. Default behavior is to allow HTML.
|
|
*
|
|
* @param $disable_html If provided, a @c bool indicating whether or
|
|
* not this object should disable HTML.
|
|
*
|
|
* @return A true value if this object disables HTML; a false value
|
|
* otherwise.
|
|
*
|
|
* @public
|
|
*/
|
|
function disable_html($disable_html = NULL) {
|
|
if ($disable_html != NULL) {
|
|
$this->options['disable_html'] = $disable_html;
|
|
}
|
|
return ($this->options['disable_html'] ? $this->options['disable_html'] : 0);
|
|
} // function disable_html
|
|
|
|
/**
|
|
* Gets or sets the relative heading offset, which allows you to
|
|
* change the heading level used within the text processed. For
|
|
* example, if the heading offset is '2' and the text contains an
|
|
* 'h1' block, an \<h3\> block will be output.
|
|
*
|
|
* @param $head_offset If provided, an @c integer specifying the
|
|
* heading offset for this object.
|
|
*
|
|
* @return An @c integer containing the heading offset for this
|
|
* object.
|
|
*
|
|
* @public
|
|
*/
|
|
function head_offset($head_offset = NULL) {
|
|
if ($head_offset != NULL) {
|
|
$this->options['head_offset'] = $head_offset;
|
|
}
|
|
return ($this->options['head_offset'] ? $this->options['head_offset'] : 0);
|
|
} // function head_offset
|
|
|
|
/**
|
|
* Assigns the HTML flavor of output from Textile. Currently
|
|
* these are the valid choices: html, xhtml (behaves like "xhtml1"),
|
|
* xhtml1, xhtml2. Default flavor is "xhtml1".
|
|
*
|
|
* Note that the xhtml2 flavor support is experimental and incomplete
|
|
* (and will remain that way until the XHTML 2.0 draft becomes a
|
|
* proper recommendation).
|
|
*
|
|
* @param $flavor If provided, a @c string specifying the flavor to
|
|
* be used for this object.
|
|
*
|
|
* @return A @c string containing the flavor for this object.
|
|
*
|
|
* @public
|
|
*/
|
|
function flavor($flavor = NULL) {
|
|
if ($flavor != NULL) {
|
|
$this->options['flavor'] = $flavor;
|
|
if (preg_match('/^xhtml(\d)?(\D|$)/', $flavor, $matches)) {
|
|
if ($matches[1] == '2') {
|
|
$this->options['_line_open'] = '<l>';
|
|
$this->options['_line_close'] = '</l>';
|
|
$this->options['_blockcode_open'] = '<blockcode>';
|
|
$this->options['_blockcode_close'] = '</blockcode>';
|
|
$this->options['css_mode'] = 1;
|
|
} else {
|
|
// xhtml 1.x
|
|
$this->options['_line_open'] = '';
|
|
$this->options['_line_close'] = '<br />';
|
|
$this->options['_blockcode_open'] = '<pre><code>';
|
|
$this->options['_blockcode_close'] = '</code></pre>';
|
|
$this->options['css_mode'] = 1;
|
|
}
|
|
} elseif (preg_match('/^html/', $flavor)) {
|
|
$this->options['_line_open'] = '';
|
|
$this->options['_line_close'] = '<br>';
|
|
$this->options['_blockcode_open'] = '<pre><code>';
|
|
$this->options['_blockcode_close'] = '</code></pre>';
|
|
$this->options['css_mode'] = preg_match('/\/css/', $flavor);
|
|
}
|
|
if ($this->options['css_mode'] && !isset($this->options['css'])) { $this->_css_defaults(); }
|
|
}
|
|
return $this->options['flavor'];
|
|
} // function flavor
|
|
|
|
/**
|
|
* Gets or sets the css support for Textile. If css is enabled,
|
|
* Textile will emit CSS rules. You may pass a 1 or 0 to enable
|
|
* or disable CSS behavior altogether. If you pass an associative array,
|
|
* you may assign the CSS class names that are used by
|
|
* Textile. The following key names for such an array are
|
|
* recognized:
|
|
*
|
|
* <ul>
|
|
* <li><b>class_align_right</b>
|
|
*
|
|
* defaults to 'right'</li>
|
|
*
|
|
* <li><b>class_align_left</b>
|
|
*
|
|
* defaults to 'left'</li>
|
|
*
|
|
* <li><b>class_align_center</b>
|
|
*
|
|
* defaults to 'center'</li>
|
|
*
|
|
* <li><b>class_align_top</b>
|
|
*
|
|
* defaults to 'top'</li>
|
|
*
|
|
* <li><b>class_align_bottom</b>
|
|
*
|
|
* defaults to 'bottom'</li>
|
|
*
|
|
* <li><b>class_align_middle</b>
|
|
*
|
|
* defaults to 'middle'</li>
|
|
*
|
|
* <li><b>class_align_justify</b>
|
|
*
|
|
* defaults to 'justify'</li>
|
|
*
|
|
* <li><b>class_caps</b>
|
|
*
|
|
* defaults to 'caps'</li>
|
|
*
|
|
* <li><b>class_footnote</b>
|
|
*
|
|
* defaults to 'footnote'</li>
|
|
*
|
|
* <li><b>id_footnote_prefix</b>
|
|
*
|
|
* defaults to 'fn'</li>
|
|
*
|
|
* </ul>
|
|
*
|
|
* @param $css If provided, either a @c bool indicating whether or
|
|
* not this object should use css or an associative @c array
|
|
* specifying class names to use.
|
|
*
|
|
* @return Either an associative @c array containing class names
|
|
* used by this object, or a true or false value indicating
|
|
* whether or not this object uses css.
|
|
*
|
|
* @public
|
|
*/
|
|
function css($css = NULL) {
|
|
if ($css != NULL) {
|
|
if (is_array($css)) {
|
|
$this->options['css'] = $css;
|
|
$this->options['css_mode'] = 1;
|
|
} else {
|
|
$this->options['css_mode'] = $css;
|
|
if ($this->options['css_mode'] && !isset($this->options['css'])) { $this->_css_defaults(); }
|
|
}
|
|
}
|
|
return ($this->options['css_mode'] ? $this->options['css'] : 0);
|
|
} // function css
|
|
|
|
/**
|
|
* Gets or sets the character set targetted for publication.
|
|
* At this time, Textile only changes its behavior
|
|
* if the 'utf-8' character set is assigned.
|
|
*
|
|
* Specifically, if utf-8 is requested, any special characters
|
|
* created by Textile will be output as native utf-8 characters
|
|
* rather than HTML entities.
|
|
*
|
|
* @param $charset If provided, a @c string specifying the
|
|
* characater set to be used for this object.
|
|
*
|
|
* @return A @c string containing the character set for this object.
|
|
*
|
|
* @public
|
|
*/
|
|
function charset($charset = NULL) {
|
|
if ($charset != NULL) {
|
|
$this->options['charset'] = $charset;
|
|
if (preg_match('/^utf-?8$/i', $this->options['charset'])) {
|
|
$this->char_encoding(0);
|
|
} else {
|
|
$this->char_encoding(1);
|
|
}
|
|
}
|
|
return $this->options['charset'];
|
|
} // function charset
|
|
|
|
/**
|
|
* Gets or sets the physical file path to root of document files.
|
|
* This path is utilized when images are referenced and size
|
|
* calculations are needed (the getimagesize() function is used to read
|
|
* the image dimensions).
|
|
*
|
|
* @param $docroot If provided, a @c string specifying the document
|
|
* root to use for this object.
|
|
*
|
|
* @return A @c string containing the docroot for this object.
|
|
*
|
|
* @public
|
|
*/
|
|
function docroot($docroot = NULL) {
|
|
if ($docroot != NULL) {
|
|
$this->options['docroot'] = $docroot;
|
|
}
|
|
return $this->options['docroot'];
|
|
} // function docroot
|
|
|
|
/**
|
|
* Gets or sets the 'trim spaces' control flag. If enabled, this
|
|
* will clear any lines that have only spaces on them (the newline
|
|
* itself will remain).
|
|
*
|
|
* @param $trim_spaces If provided, a @c bool indicating whether or
|
|
* not this object should trim spaces.
|
|
*
|
|
* @return A true value if this object trims spaces; a false value
|
|
* otherwise.
|
|
*
|
|
* @public
|
|
*/
|
|
function trim_spaces($trim_spaces = NULL) {
|
|
if ($trim_spaces != NULL) {
|
|
$this->options['trim_spaces'] = $trim_spaces;
|
|
}
|
|
return $this->options['trim_spaces'];
|
|
} // function trim_spaces
|
|
|
|
/**
|
|
* Gets or sets a parameter that is passed to filters.
|
|
*
|
|
* @param $filter_param If provided, a parameter that this object
|
|
* should pass to filters.
|
|
*
|
|
* @return The parameter this object passes to filters.
|
|
*
|
|
* @public
|
|
*/
|
|
function filter_param($filter_param = NULL) {
|
|
if ($filter_param != NULL) {
|
|
$this->options['filter_param'] = $filter_param;
|
|
}
|
|
return $this->options['filter_param'];
|
|
} // function filter_param
|
|
|
|
/**
|
|
* Gets or sets the 'preserve spaces' control flag. If enabled, this
|
|
* will replace any double spaces within the paragraph data with the
|
|
* \&#8195; HTML entity (wide space). The default is 0. Spaces will
|
|
* pass through to the browser unchanged and render as a single space.
|
|
* Note that this setting has no effect on spaces within \<pre\>,
|
|
* \<code\> blocks or \<script\> sections.
|
|
*
|
|
* @param $preserve_spaces If provided, a @c bool indicating whether
|
|
* or not this object should preserve spaces.
|
|
*
|
|
* @return A true value if this object preserves spaces; a false
|
|
* value otherwise.
|
|
*
|
|
* @public
|
|
*/
|
|
function preserve_spaces($preserve_spaces = NULL) {
|
|
if ($preserve_spaces != NULL) {
|
|
$this->options['preserve_spaces'] = $preserve_spaces;
|
|
}
|
|
return $this->options['preserve_spaces'];
|
|
} // function preserve_spaces
|
|
|
|
/**
|
|
* Gets or sets a list of filters to make available for
|
|
* Textile to use. Returns a hash reference of the currently
|
|
* assigned filters.
|
|
*
|
|
* @param $filters If provided, an @c array of filters to be used
|
|
* for this object.
|
|
*
|
|
* @return An @c array containing the filters for this object.
|
|
*
|
|
* @public
|
|
*/
|
|
function filters($filters = NULL) {
|
|
if ($filters != NULL) {
|
|
$this->options['filters'] = $filters;
|
|
}
|
|
return $this->options['filters'];
|
|
} // function filters
|
|
|
|
/**
|
|
* Gets or sets the character encoding logical flag. If character
|
|
* encoding is enabled, the htmlentities function is used to
|
|
* encode special characters. If character encoding is disabled,
|
|
* only \<, \>, " and & are encoded to HTML entities.
|
|
*
|
|
* @param $char_encoding If provided, a @c bool indicating whether
|
|
* or not this object should encode special characters.
|
|
*
|
|
* @return A true value if this object encodes special characters; a
|
|
* false value otherwise.
|
|
*
|
|
* @public
|
|
*/
|
|
function char_encoding($char_encoding = NULL) {
|
|
if ($char_encoding != NULL) {
|
|
$this->options['char_encoding'] = $char_encoding;
|
|
}
|
|
return $this->options['char_encoding'];
|
|
} // function char_encoding
|
|
|
|
/**
|
|
* Gets or sets the "smart quoting" control flag. Returns the
|
|
* current setting.
|
|
*
|
|
* @param $do_quotes If provided, a @c bool indicating whether or
|
|
* not this object should use smart quoting.
|
|
*
|
|
* @return A true value if this object uses smart quoting; a false
|
|
* value otherwise.
|
|
*
|
|
* @public
|
|
*/
|
|
function handle_quotes($do_quotes = NULL) {
|
|
if ($do_quotes != NULL) {
|
|
$this->options['do_quotes'] = $do_quotes;
|
|
}
|
|
return $this->options['do_quotes'];
|
|
} // function handle_quotes
|
|
|
|
// end of getter/setter methods
|
|
|
|
/**
|
|
* Creates the class variable regular expression patterns used by
|
|
* Textile. They are not initialized in the declaration, because
|
|
* some rely on the others, requiring a @c $this reference.
|
|
*
|
|
* PHP does not have the Perl qr operator to quote or precompile
|
|
* patterns, so to avoid escaping and matching problems, all
|
|
* patterns must use the same delimiter; this implementation uses
|
|
* {}. Every use of these patterns within this class has been
|
|
* changed to use these delimiters. *JHR*
|
|
*
|
|
* @private
|
|
*/
|
|
function _create_re() {
|
|
// a URL discovery regex. This is from Mastering Regex from O'Reilly.
|
|
// Some modifications by Brad Choate <brad at bradchoate dot com>
|
|
$this->urlre = '(?:
|
|
# Must start out right...
|
|
(?=[a-zA-Z0-9./#])
|
|
# Match the leading part (proto://hostname, or just hostname)
|
|
(?:
|
|
# ftp://, http://, or https:// leading part
|
|
(?:ftp|https?|telnet|nntp)://(?:\w+(?::\w+)?@)?[-\w]+(?:\.\w[-\w]*)+
|
|
|
|
|
(?:mailto:)?[-\+\w]+@[-\w]+(?:\.\w[-\w]*)+
|
|
|
|
|
# or, try to find a hostname with our more specific sub-expression
|
|
(?i: [a-z0-9] (?:[-a-z0-9]*[a-z0-9])? \. )+ # sub domains
|
|
# Now ending .com, etc. For these, require lowercase
|
|
(?-i: com\b
|
|
| edu\b
|
|
| biz\b
|
|
| gov\b
|
|
| in(?:t|fo)\b # .int or .info
|
|
| mil\b
|
|
| net\b
|
|
| org\b
|
|
| museum\b
|
|
| aero\b
|
|
| coop\b
|
|
| name\b
|
|
| pro\b
|
|
| [a-z][a-z]\b # two-letter country codes
|
|
)
|
|
)?
|
|
|
|
# Allow an optional port number
|
|
(?: : \d+ )?
|
|
|
|
# The rest of the URL is optional, and begins with / . . .
|
|
(?:
|
|
/?
|
|
# The rest are heuristics for what seems to work well
|
|
[^.!,?;:"\'<>()\[\]{}\s\x7F-\xFF]*
|
|
(?:
|
|
[.!,?;:]+ [^.!,?;:"\'<>()\[\]{}\s\x7F-\xFF]+ #\'"
|
|
)*
|
|
)?
|
|
)';
|
|
|
|
$this->punct = '[\!"\#\$%&\'()\*\+,\-\./:;<=>\?@\[\\\\\]\^_`{\|}\~]';
|
|
$this->valignre = '[\-^~]';
|
|
$this->tblalignre = '[<>=]';
|
|
$this->halignre = '(?:<>|[<>=])';
|
|
$this->alignre = '(?:(?:' . $this->valignre . '|<>' . $this->valignre . '?|' . $this->valignre . '?<>|' . $this->valignre . '?' . $this->halignre . '?|' . $this->halignre . '?' . $this->valignre . '?)(?!\w))';
|
|
$this->imgalignre = '(?:(?:[<>]|' . $this->valignre . '){1,2})';
|
|
|
|
$this->clstypadre = '(?:
|
|
(?:\([A-Za-z0-9_\- \#]+\))
|
|
|
|
|
(?:{
|
|
(?: \( [^)]+ \) | [^\}] )+
|
|
})
|
|
|
|
|
(?:\(+? (?![A-Za-z0-9_\-\#]) )
|
|
|
|
|
(?:\)+?)
|
|
|
|
|
(?: \[ [a-zA-Z\-]+? \] )
|
|
)';
|
|
|
|
$this->clstyre = '(?:
|
|
(?:\([A-Za-z0-9_\- \#]+\))
|
|
|
|
|
(?:{
|
|
[A-Za-z0-9_\-](?: \( [^)]+ \) | [^\}] )+
|
|
})
|
|
|
|
|
(?: \[ [a-zA-Z\-]+? \] )
|
|
)';
|
|
|
|
$this->clstyfiltre = '(?:
|
|
(?:\([A-Za-z0-9_\- \#]+\))
|
|
|
|
|
(?:{
|
|
[A-Za-z0-9_\-](?: \( [^)]+ \) | [^\}] )+
|
|
})
|
|
|
|
|
(?:\|[^\|]+\|)
|
|
|
|
|
(?:\(+?(?![A-Za-z0-9_\-\#]))
|
|
|
|
|
(?:\)+)
|
|
|
|
|
(?: \[ [a-zA-Z]+? \] )
|
|
)';
|
|
|
|
$this->codere = '(?:
|
|
(?:
|
|
[\[{]
|
|
@ # opening
|
|
(?:\[([A-Za-z0-9]+)\])? # $1: language id
|
|
(.+?) # $2: code
|
|
@ # closing
|
|
[\]}]
|
|
)
|
|
|
|
|
(?:
|
|
(?:^|(?<=[\s\(]))
|
|
@ # opening
|
|
(?:\[([A-Za-z0-9]+)\])? # $3: language id
|
|
([^\s].+?[^\s]) # $4: code itself
|
|
@ # closing
|
|
(?:$|(?=' . $this->punct . '{1,2}|\s))
|
|
)
|
|
)';
|
|
|
|
$this->blocktags = '
|
|
<
|
|
(( /? ( h[1-6]
|
|
| p
|
|
| pre
|
|
| div
|
|
| table
|
|
| t[rdh]
|
|
| [ou]l
|
|
| li
|
|
| block(?:quote|code)
|
|
| form
|
|
| input
|
|
| select
|
|
| option
|
|
| textarea
|
|
)
|
|
[ >]
|
|
)
|
|
| !--
|
|
)
|
|
';
|
|
} // function _create_re
|
|
|
|
/**
|
|
* Transforms the provided text using Textile markup rules.
|
|
*
|
|
* @param $str The @c string specifying the text to process.
|
|
*
|
|
* @return A @c string containing the processed (X)HTML.
|
|
*
|
|
* @public
|
|
*/
|
|
function process($str) {
|
|
/*
|
|
* Function names in PHP are case insensitive, so function
|
|
* textile() cannot be redefined. Thus, this PHP implementation
|
|
* will only use process().
|
|
*
|
|
* return $this->textile($str);
|
|
* } // function process
|
|
*
|
|
* function textile($str) {
|
|
*/
|
|
|
|
// quick translator for abbreviated block names
|
|
// to their tag
|
|
$macros = array('bq' => 'blockquote');
|
|
|
|
// an array to hold any portions of the text to be preserved
|
|
// without further processing by Textile
|
|
array_unshift($this->repl, array());
|
|
|
|
// strip out extra newline characters. we're only matching for \n herein
|
|
//$str = preg_replace('!(?:\r?\n|\r)!', "\n", $str);
|
|
$str = preg_replace('!(?:\015?\012|\015)!', "\n", $str);
|
|
|
|
// optionally remove trailing spaces
|
|
if ($this->options['trim_spaces']) { $str = preg_replace('/ +$/m', '', $str); }
|
|
|
|
// preserve contents of the '==', 'pre', 'blockcode' sections
|
|
$str = preg_replace_callback('{(^|\n\n)==(.+?)==($|\n\n)}s',
|
|
$this->_cb('"$m[1]\n\n" . $me->_repl($me->repl[0], $me->format_block(array("text" => $m[2]))) . "\n\n$m[3]"'), $str);
|
|
|
|
if (!$this->disable_html()) {
|
|
// preserve style, script tag contents
|
|
$str = preg_replace_callback('!(<(style|script)(?:>| .+?>).*?</\2>)!s', $this->_cb('$me->_repl($me->repl[0], $m[1])'), $str);
|
|
|
|
// preserve HTML comments
|
|
$str = preg_replace_callback('|(<!--.+?-->)|s', $this->_cb('$me->_repl($me->repl[0], $m[1])'), $str);
|
|
|
|
// preserve pre block contents, encode contents by default
|
|
$pre_start = count($this->repl[0]);
|
|
$str = preg_replace_callback('{(<pre(?: [^>]*)?>)(.+?)(</pre>)}s',
|
|
$this->_cb('"\n\n" . $me->_repl($me->repl[0], $m[1] . $me->encode_html($m[2], 1) . $m[3]) . "\n\n"'), $str);
|
|
// fix code tags within pre blocks we just saved.
|
|
for ($i = $pre_start; $i < count($this->repl[0]); $i++) {
|
|
$this->repl[0][$i] = preg_replace('|<(/?)code(.*?)>|s', '<$1code$2>', $this->repl[0][$i]);
|
|
}
|
|
|
|
// preserve code blocks by default, encode contents
|
|
$str = preg_replace_callback('{(<code(?: [^>]+)?>)(.+?)(</code>)}s',
|
|
$this->_cb('$me->_repl($me->repl[0], $m[1] . $me->encode_html($m[2], 1) . $m[3])'), $str);
|
|
|
|
// encode blockcode tag (an XHTML 2 tag) and encode it's
|
|
// content by default
|
|
$str = preg_replace_callback('{(<blockcode(?: [^>]+)?>)(.+?)(</blockcode>)}s',
|
|
$this->_cb('"\n\n" . $me->_repl($me->repl[0], $m[1] . $me->encode_html($m[2], 1) . $m[3]) . "\n\n"'), $str);
|
|
|
|
// preserve PHPish, ASPish code
|
|
$str = preg_replace_callback('!(<([\?%]).*?(\2)>)!s', $this->_cb('$me->_repl($me->repl[0], $m[1])'), $str);
|
|
}
|
|
|
|
// pass through and remove links that follow this format
|
|
// [id_without_spaces (optional title text)]url
|
|
// lines like this are stripped from the content, and can be
|
|
// referred to using the "link text":id_without_spaces syntax
|
|
//$links = array();
|
|
$str = preg_replace_callback('{(?:\n|^) [ ]* \[ ([^ ]+?) [ ]*? (?:\( (.+?) \) )? \] ((?:(?:ftp|https?|telnet|nntp)://|/)[^ ]+?) [ ]* (\n|$)}mx',
|
|
$this->_cb('($me->links[$m[1]] = array("url" => $m[3], "title" => $m[2])) ? $m[4] : $m[4]'), $str);
|
|
//$this->links = $links;
|
|
|
|
// eliminate starting/ending blank lines
|
|
$str = preg_replace('/^\n+/s', '', $str, 1);
|
|
$str = preg_replace('/\n+$/s', '', $str, 1);
|
|
|
|
// split up text into paragraph blocks, capturing newlines too
|
|
$para = preg_split('/(\n{2,})/', $str, -1, PREG_SPLIT_DELIM_CAPTURE);
|
|
unset($block, $bqlang, $filter, $class, $sticky, $lines,
|
|
$style, $stickybuff, $lang, $clear);
|
|
|
|
$out = '';
|
|
|
|
foreach ($para as $para) {
|
|
if (preg_match('/^\n+$/s', $para)) {
|
|
if ($sticky && $stickybuff) {
|
|
$stickybuff .= $para;
|
|
} else {
|
|
$out .= $para;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if ($sticky) {
|
|
$sticky++;
|
|
} else {
|
|
unset($block);
|
|
unset($class);
|
|
$style = '';
|
|
unset($lang);
|
|
}
|
|
|
|
unset($id, $cite, $align, $padleft, $padright, $lines, $buffer);
|
|
if (preg_match('{^(h[1-6]|p|bq|bc|fn\d+)
|
|
((?:' . $this->clstyfiltre . '*|' . $this->halignre . ')*)
|
|
(\.\.?)
|
|
(?::(\d+|' . $this->urlre . '))?\ (.*)$}sx', $para, $matches)) {
|
|
if ($sticky) {
|
|
if ($block == 'bc') {
|
|
// close our blockcode section
|
|
$out = preg_replace('/\n\n$/', '', $out, 1);
|
|
$out .= $this->options['_blockcode_close'] . "\n\n";
|
|
} elseif ($block == 'bq') {
|
|
$out = preg_replace('/\n\n$/', '', $out, 1);
|
|
$out .= '</blockquote>' . "\n\n";
|
|
} elseif ($block == 'table') {
|
|
$table_out = $this->format_table(array('text' => $stickybuff));
|
|
if (!$table_out) { $table_out = ''; }
|
|
$out .= $table_out;
|
|
unset($stickybuff);
|
|
} elseif ($block == 'dl') {
|
|
$dl_out = $this->format_deflist(array('text' => $stickybuff));
|
|
if (!$dl_out) { $dl_out = ''; }
|
|
$out .= $dl_out;
|
|
unset($stickybuff);
|
|
}
|
|
$sticky = 0;
|
|
}
|
|
// block macros: h[1-6](class)., bq(class)., bc(class)., p(class).
|
|
//warn "paragraph: [[$para]]\n\tblock: $1\n\tparams: $2\n\tcite: $4";
|
|
$block = $matches[1];
|
|
$params = $matches[2];
|
|
$cite = $matches[4];
|
|
if ($matches[3] == '..') {
|
|
$sticky = 1;
|
|
} else {
|
|
$sticky = 0;
|
|
unset($class);
|
|
unset($bqlang);
|
|
unset($lang);
|
|
$style = '';
|
|
unset($filter);
|
|
}
|
|
if (preg_match('/^h([1-6])$/', $block, $matches2)) {
|
|
if ($this->options['head_offset']) {
|
|
$block = 'h' . ($matches2[1] + $this->options['head_offset']);
|
|
}
|
|
}
|
|
if (preg_match('{(' . $this->halignre . '+)}', $params, $matches2)) {
|
|
$align = $matches2[1];
|
|
$params = preg_replace('{' . $this->halignre . '+}', '', $params, 1);
|
|
}
|
|
if ($params) {
|
|
if (preg_match('/\|(.+)\|/', $params, $matches2)) {
|
|
$filter = $matches2[1];
|
|
$params = preg_replace('/\|.+?\|/', '', $params, 1);
|
|
}
|
|
if (preg_match('/{([^}]+)}/', $params, $matches2)) {
|
|
$style = $matches2[1];
|
|
$style = preg_replace('/\n/', ' ', $style);
|
|
$params = preg_replace('/{[^}]+}/', '', $params);
|
|
}
|
|
if (preg_match('/\(([A-Za-z0-9_\-\ ]+?)(?:\#(.+?))?\)/', $params, $matches2) ||
|
|
preg_match('/\(([A-Za-z0-9_\-\ ]+?)?(?:\#(.+?))\)/', $params, $matches2)) {
|
|
if ($matches2[1] || $matches2[2]) {
|
|
$class = $matches2[1];
|
|
$id = $matches2[2];
|
|
if ($class) {
|
|
$params = preg_replace('/\([A-Za-z0-9_\-\ ]+?(#.*?)?\)/', '', $params);
|
|
} elseif ($id) {
|
|
$params = preg_replace('/\(#.+?\)/', '', $params);
|
|
}
|
|
}
|
|
}
|
|
if (preg_match('/(\(+)/', $params, $matches2)) {
|
|
$padleft = strlen($matches2[1]);
|
|
$params = preg_replace('/\(+/', '', $params, 1);
|
|
}
|
|
if (preg_match('/(\)+)/', $params, $matches2)) {
|
|
$padright = strlen($matches2[1]);
|
|
$params = preg_replace('/\)+/', '', $params, 1);
|
|
}
|
|
if (preg_match('/\[(.+?)\]/', $params, $matches2)) {
|
|
$lang = $matches2[1];
|
|
if ($block == 'bc') {
|
|
$bqlang = $lang;
|
|
unset($lang);
|
|
}
|
|
$params = preg_replace('/\[.+?\]/', '', $params, 1);
|
|
}
|
|
}
|
|
// warn "settings:\n\tblock: $block\n\tpadleft: $padleft\n\tpadright: $padright\n\tclass: $class\n\tstyle: $style\n\tid: $id\n\tfilter: $filter\n\talign: $align\n\tlang: $lang\n\tsticky: $sticky";
|
|
$para = $matches[5];
|
|
} elseif (preg_match('|^<textile#(\d+)>$|', $para, $matches)) {
|
|
$buffer = $this->repl[0][$matches[1] - 1];
|
|
} elseif (preg_match('/^clear([<>]+)?\.$/', $para, $matches)) {
|
|
if ($matches[1] == '<') {
|
|
$clear = 'left';
|
|
} elseif ($matches[1] == '>') {
|
|
$clear = 'right';
|
|
} else {
|
|
$clear = 'both';
|
|
}
|
|
continue;
|
|
} elseif ($sticky && $stickybuff &&
|
|
($block == 'table' || $block == 'dl')) {
|
|
$stickybuff .= $para;
|
|
continue;
|
|
} elseif (preg_match('{^(?:' . $this->halignre . '|' . $this->clstypadre . '*)*
|
|
[\*\#]
|
|
(?:' . $this->halignre . '|' . $this->clstypadre . '*)*
|
|
\ }x', $para)) {
|
|
// '*', '#' prefix means a list
|
|
$buffer = $this->format_list(array('text' => $para));
|
|
} elseif (preg_match('{^(?:table(?:' . $this->tblalignre . '|' . $this->clstypadre . '*)*
|
|
(\.\.?)\s+)?
|
|
(?:_|' . $this->alignre . '|' . $this->clstypadre . '*)*\|}x', $para, $matches)) {
|
|
// handle wiki-style tables
|
|
if ($matches[1] && ($matches[1] == '..')) {
|
|
$block = 'table';
|
|
$stickybuff = $para;
|
|
$sticky = 1;
|
|
continue;
|
|
} else {
|
|
$buffer = $this->format_table(array('text' => $para));
|
|
}
|
|
} elseif (preg_match('{^(?:dl(?:' . $this->clstyre . ')*(\.\.?)\s+)}x', $para, $matches)) {
|
|
// handle definition lists
|
|
if ($matches[1] && ($matches[1] == '..')) {
|
|
$block = 'dl';
|
|
$stickybuff = $para;
|
|
$sticky = 1;
|
|
continue;
|
|
} else {
|
|
$buffer = $this->format_deflist(array('text' => $para));
|
|
}
|
|
}
|
|
if ($buffer) {
|
|
$out .= $buffer;
|
|
continue;
|
|
}
|
|
$lines = preg_split('/\n/', $para);
|
|
if ((count($lines) == 1) && ($lines[0] == '')) {
|
|
continue;
|
|
}
|
|
|
|
$block = ($block ? $block : 'p');
|
|
|
|
$buffer = '';
|
|
$pre = '';
|
|
$post = '';
|
|
|
|
if ($block == 'bc') {
|
|
if ($sticky <= 1) {
|
|
$pre .= $this->options['_blockcode_open'];
|
|
$pre = preg_replace('/>$/s', '', $pre, 1);
|
|
if ($bqlang) { $pre .= " language=\"$bqlang\""; }
|
|
if ($align) {
|
|
$alignment = $this->_halign($align);
|
|
if ($this->options['css_mode']) {
|
|
if (($padleft || $padright) &&
|
|
(($alignment == 'left') || ($alignment == 'right'))) {
|
|
$style .= ';float:' . $alignment;
|
|
} else {
|
|
$style .= ';text-align:' . $alignment;
|
|
}
|
|
$class .= ' ' . ($this->options['css']["class_align_$alignment"] ? $this->options['css']["class_align_$alignment"] : $alignment);
|
|
} else {
|
|
if ($alignment) { $pre .= " align=\"$alignment\""; }
|
|
}
|
|
}
|
|
if ($padleft) { $style .= ";padding-left:${padleft}em"; }
|
|
if ($padright) { $style .= ";padding-right:${padright}em"; }
|
|
if ($clear) { $style .= ";clear:${clear}"; }
|
|
if ($class) { $class = preg_replace('/^ /', '', $class, 1); }
|
|
if ($class) { $pre .= " class=\"$class\""; }
|
|
if ($id) { $pre .= " id=\"$id\""; }
|
|
if ($style) { $style = preg_replace('/^;/', '', $style, 1); }
|
|
if ($style) { $pre .= " style=\"$style\""; }
|
|
if ($lang) { $pre .= " lang=\"$lang\""; }
|
|
$pre .= '>';
|
|
unset($lang);
|
|
unset($bqlang);
|
|
unset($clear);
|
|
}
|
|
$para = preg_replace_callback('{(?:^|(?<=[\s>])|([{[]))
|
|
==(.+?)==
|
|
(?:$|([\]}])|(?=' . $this->punct . '{1,2}|\s))}sx',
|
|
$this->_cb('$me->_repl($me->repl[0], $me->format_block(array("text" => $m[2], "inline" => 1, "pre" => $m[1], "post" => $m[3])))'), $para);
|
|
$buffer .= $this->encode_html_basic($para, 1);
|
|
$buffer = preg_replace('/<textile#(\d+)>/', '<textile#$1>', $buffer);
|
|
if ($sticky == 0) {
|
|
$post .= $this->options['_blockcode_close'];
|
|
}
|
|
$out .= $pre . $buffer . $post;
|
|
continue;
|
|
} elseif ($block == 'bq') {
|
|
if ($sticky <= 1) {
|
|
$pre .= '<blockquote';
|
|
if ($align) {
|
|
$alignment = $this->_halign($align);
|
|
if ($this->options['css_mode']) {
|
|
if (($padleft || $padright) &&
|
|
(($alignment == 'left') || ($alignment == 'right'))) {
|
|
$style .= ';float:' . $alignment;
|
|
} else {
|
|
$style .= ';text-align:' . $alignment;
|
|
}
|
|
$class .= ' ' . ($this->options['css']["class_align_$alignment"] ? $this->options['css']["class_align_$alignment"] : $alignment);
|
|
} else {
|
|
if ($alignment) { $pre .= " align=\"$alignment\""; }
|
|
}
|
|
}
|
|
if ($padleft) { $style .= ";padding-left:${padleft}em"; }
|
|
if ($padright) { $style .= ";padding-right:${padright}em"; }
|
|
if ($clear) { $style .= ";clear:${clear}"; }
|
|
if ($class) { $class = preg_replace('/^ /', '', $class, 1); }
|
|
if ($class) { $pre .= " class=\"$class\""; }
|
|
if ($id) { $pre .= " id=\"$id\""; }
|
|
if ($style) { $style = preg_replace('/^;/', '', $style, 1); }
|
|
if ($style) { $pre .= " style=\"$style\""; }
|
|
if ($lang) { $pre .= " lang=\"$lang\""; }
|
|
if ($cite) { $pre .= ' cite="' . $this->format_url(array('url' => $cite)) . '"'; }
|
|
$pre .= '>';
|
|
unset($clear);
|
|
}
|
|
$pre .= '<p>';
|
|
} elseif (preg_match('/fn(\d+)/', $block, $matches)) {
|
|
$fnum = $matches[1];
|
|
$pre .= '<p';
|
|
if ($this->options['css']['class_footnote']) { $class .= ' ' . $this->options['css']['class_footnote']; }
|
|
if ($align) {
|
|
$alignment = $this->_halign($align);
|
|
if ($this->options['css_mode']) {
|
|
if (($padleft || $padright) &&
|
|
(($alignment == 'left') || ($alignment == 'right'))) {
|
|
$style .= ';float:' . $alignment;
|
|
} else {
|
|
$style .= ';text-align:' . $alignment;
|
|
}
|
|
$class .= ($this->options['css']["class_align_$alignment"] ? $this->options['css']["class_align_$alignment"] : $alignment);
|
|
} else {
|
|
$pre .= " align=\"$alignment\"";
|
|
}
|
|
}
|
|
if ($padleft) { $style .= ";padding-left:${padleft}em"; }
|
|
if ($padright) { $style .= ";padding-right:${padright}em"; }
|
|
if ($clear) { $style .= ";clear:${clear}"; }
|
|
if ($class) { $class = preg_replace('/^ /', '', $class, 1); }
|
|
if ($class) { $pre .= " class=\"$class\""; }
|
|
$pre .= ' id="' . ($this->options['css']['id_footnote_prefix'] ? $this->options['css']['id_footnote_prefix'] : 'fn') . $fnum . '"';
|
|
if ($style) { $style = preg_replace('/^;/', '', $style, 1); }
|
|
if ($style) { $pre .= " style=\"$style\""; }
|
|
if ($lang) { $pre .= " lang=\"$lang\""; }
|
|
$pre .= '>';
|
|
$pre .= '<sup>' . $fnum . '</sup> ';
|
|
// we can close like a regular paragraph tag now
|
|
$block = 'p';
|
|
unset($clear);
|
|
} else {
|
|
$pre .= '<' . ($macros[$block] ? $macros[$block] : $block);
|
|
if ($align) {
|
|
$alignment = $this->_halign($align);
|
|
if ($this->options['css_mode']) {
|
|
if (($padleft || $padright) &&
|
|
(($alignment == 'left') || ($alignment == 'right'))) {
|
|
$style .= ';float:' . $alignment;
|
|
} else {
|
|
$style .= ';text-align:' . $alignment;
|
|
}
|
|
$class .= ' ' . ($this->options['css']["class_align_$alignment"] ? $this->options['css']["class_align_$alignment"] : $alignment);
|
|
} else {
|
|
$pre .= " align=\"$alignment\"";
|
|
}
|
|
}
|
|
if ($padleft) { $style .= ";padding-left:${padleft}em"; }
|
|
if ($padright) { $style .= ";padding-right:${padright}em"; }
|
|
if ($clear) { $style .= ";clear:${clear}"; }
|
|
if ($class) { $class = preg_replace('/^ /', '', $class, 1); }
|
|
if ($class) { $pre .= " class=\"$class\""; }
|
|
if ($id) { $pre .= " id=\"$id\""; }
|
|
if ($style) { $style = preg_replace('/^;/', '', $style, 1); }
|
|
if ($style) { $pre .= " style=\"$style\""; }
|
|
if ($lang) { $pre .= " lang=\"$lang\""; }
|
|
if ($cite && ($block == 'bq')) { $pre .= ' cite="' . $this->format_url(array('url' => $cite)) . '"'; }
|
|
$pre .= '>';
|
|
unset($clear);
|
|
}
|
|
|
|
$buffer = $this->format_paragraph(array('text' => $para));
|
|
|
|
if ($block == 'bq') {
|
|
if (!preg_match('/<p[ >]/', $buffer)) { $post .= '</p>'; }
|
|
if ($sticky == 0) {
|
|
$post .= '</blockquote>';
|
|
}
|
|
} else {
|
|
$post .= '</' . $block . '>';
|
|
}
|
|
|
|
if (preg_match('{' . $this->blocktags . '}x', $buffer)) {
|
|
$buffer = preg_replace('/^\n\n/s', '', $buffer, 1);
|
|
$out .= $buffer;
|
|
} else {
|
|
if ($filter) { $buffer = $this->format_block(array('text' => "|$filter|" . $buffer, 'inline' => 1)); }
|
|
$out .= $pre . $buffer . $post;
|
|
}
|
|
}
|
|
|
|
if ($sticky) {
|
|
if ($block == 'bc') {
|
|
// close our blockcode section
|
|
$out .= $this->options['_blockcode_close']; // . "\n\n";
|
|
} elseif ($block == 'bq') {
|
|
$out .= '</blockquote>'; // . "\n\n";
|
|
} elseif (($block == 'table') && $stickybuff) {
|
|
$table_out = $this->format_table(array('text' => $stickybuff));
|
|
if ($table_out) { $out .= $table_out; }
|
|
} elseif (($block == 'dl') && $stickybuff) {
|
|
$dl_out = $this->format_deflist(array('text' => $stickybuff));
|
|
if ($dl_out) { $out .= $dl_out; }
|
|
}
|
|
}
|
|
|
|
// cleanup-- restore preserved blocks
|
|
for ($i = count($this->repl[0]); $i > 0; $i--) {
|
|
$out = preg_replace('!(?:<|<)textile#' . $i . '(?:>|>)!', str_replace('$', '\\$', $this->repl[0][$i - 1]), $out, 1);
|
|
}
|
|
array_shift($this->repl);
|
|
|
|
// scan for br, hr tags that are not closed and close them
|
|
// only for xhtml! just the common ones -- don't fret over input
|
|
// and the like.
|
|
if (preg_match('/^xhtml/i', $this->flavor())) {
|
|
$out = preg_replace('/(<(?:img|br|hr)[^>]*?(?<!\/))>/', '$1 />', $out);
|
|
}
|
|
|
|
return $out;
|
|
} // function process
|
|
|
|
/**
|
|
* Processes a single paragraph. The following attributes are
|
|
* allowed:
|
|
*
|
|
* <ul>
|
|
*
|
|
* <li><b>text</b>
|
|
*
|
|
* The text to be processed.</li>
|
|
*
|
|
* </ul>
|
|
*
|
|
* @param $args An @c array specifying the attributes for formatting
|
|
* the paragraph.
|
|
*
|
|
* @return A @c string containing the formatted paragraph.
|
|
*
|
|
* @private
|
|
*/
|
|
function format_paragraph($args) {
|
|
$buffer = (isset($args['text']) ? $args['text'] : '');
|
|
|
|
array_unshift($this->repl, array());
|
|
$buffer = preg_replace_callback('{(?:^|(?<=[\s>])|([{[]))
|
|
==(.+?)==
|
|
(?:$|([\]}])|(?=' . $this->punct . '{1,2}|\s))}sx',
|
|
$this->_cb('$me->_repl($me->repl[0], $me->format_block(array("text" => $m[2], "inline" => 1, "pre" => $m[1], "post" => $m[3])))'), $buffer);
|
|
|
|
unset($tokens);
|
|
if (preg_match('/</', $buffer) && (!$this->disable_html())) { // optimization -- no point in tokenizing if we
|
|
// have no tags to tokenize
|
|
$tokens = $this->_tokenize($buffer);
|
|
} else {
|
|
$tokens = array(array('text', $buffer));
|
|
}
|
|
$result = '';
|
|
foreach ($tokens as $token) {
|
|
$text = $token[1];
|
|
if ($token[0] == 'tag') {
|
|
$text = preg_replace('/&(?!amp;)/', '&', $text);
|
|
$result .= $text;
|
|
} else {
|
|
$text = $this->format_inline(array('text' => $text));
|
|
$result .= $text;
|
|
}
|
|
}
|
|
|
|
// now, add line breaks for lines that contain plaintext
|
|
$lines = preg_split('/\n/', $result);
|
|
$result = '';
|
|
$needs_closing = 0;
|
|
foreach ($lines as $line) {
|
|
if (!preg_match('{(' . $this->blocktags . ')}x', $line)
|
|
&& ((preg_match('/^[^<]/', $line) || preg_match('/>[^<]/', $line))
|
|
|| !preg_match('/<img /', $line))) {
|
|
if ($this->options['_line_open']) {
|
|
if ($result != '') { $result .= "\n"; }
|
|
$result .= $this->options['_line_open'] . $line . $this->options['_line_close'];
|
|
} else {
|
|
if ($needs_closing) {
|
|
$result .= $this->options['_line_close'] . "\n";
|
|
} else {
|
|
$needs_closing = 1;
|
|
if ($result != '') { $result .= "\n"; }
|
|
}
|
|
$result .= $line;
|
|
}
|
|
} else {
|
|
if ($needs_closing) {
|
|
$result .= $this->options['_line_close'] . "\n";
|
|
} else {
|
|
if ($result != '') { $result .= "\n"; }
|
|
}
|
|
$result .= $line;
|
|
$needs_closing = 0;
|
|
}
|
|
}
|
|
|
|
// at this point, we will restore the \001's to \n's (reversing
|
|
// the step taken in _tokenize).
|
|
//$result = preg_replace('/\r/', "\n", $result);
|
|
$result = preg_replace('/\001/', "\n", $result);
|
|
|
|
for ($i = count($this->repl[0]); $i > 0; $i--) {
|
|
$result = preg_replace("|<textile#$i>|", str_replace('$', '\\$', $this->repl[0][$i - 1]), $result, 1);
|
|
}
|
|
array_shift($this->repl);
|
|
|
|
// quotalize
|
|
if ($this->options['do_quotes']) {
|
|
$result = $this->process_quotes($result);
|
|
}
|
|
|
|
return $result;
|
|
} // function format_paragraph
|
|
|
|
/**
|
|
* Processes an inline string (plaintext) for Textile syntax.
|
|
* The following attributes are allowed:
|
|
*
|
|
* <ul>
|
|
*
|
|
* <li><b>text</b>
|
|
*
|
|
* The text to be processed.</li>
|
|
*
|
|
* </ul>
|
|
*
|
|
* @param $args An @c array specifying the attributes for formatting
|
|
* the inline string.
|
|
*
|
|
* @return A @c string containing the formatted inline string.
|
|
*
|
|
* @private
|
|
*/
|
|
function format_inline($args) {
|
|
$qtags = array(array('**', 'b', '(?<!\*)\*\*(?!\*)', '\*'),
|
|
array('__', 'i', '(?<!_)__(?!_)', '_'),
|
|
array('??', 'cite', '\?\?(?!\?)', '\?'),
|
|
array('*', 'strong', '(?<!\*)\*(?!\*)', '\*'),
|
|
array('_', 'em', '(?<!_)_(?!_)', '_'),
|
|
array('-', 'del', '(?<!\-)\-(?!\-)', '-'),
|
|
array('+', 'ins', '(?<!\+)\+(?!\+)', '\+'),
|
|
array('++', 'big', '(?<!\+)\+\+(?!\+)', '\+\+'),
|
|
array('--', 'small', '(?<!\-)\-\-(?!\-)', '\-\-'),
|
|
array('~', 'sub', '(?<!\~)\~(?![\\\\/~])', '\~'));
|
|
$text = (isset($args['text']) ? $args['text'] : '');
|
|
|
|
array_unshift($this->repl, array());
|
|
|
|
$text = preg_replace_callback('{' . $this->codere . '}mx', $this->_cb('$me->_repl($me->repl[0], $me->format_code(array("text" => $m[2] . $m[4], "lang" => $m[1] . $m[3])))'), $text);
|
|
|
|
// images must be processed before encoding the text since they might
|
|
// have the <, > alignment specifiers...
|
|
|
|
// !blah (alt)! -> image
|
|
$text = preg_replace_callback('{(?:^|(?<=[\s>])|([{[])) # $1: open brace/bracket
|
|
! # opening
|
|
(' . $this->imgalignre . '?) # $2: optional alignment
|
|
(' . $this->clstypadre . '*) # $3: optional CSS class/id
|
|
(' . $this->imgalignre . '?) # $4: optional alignment
|
|
(?:\s*) # space between alignment/css stuff
|
|
([^\s\(!]+) # $5: filename
|
|
(\s*[^\(!]*(?:\([^\)]+\))?[^!]*) # $6: extras (alt text)
|
|
! # closing
|
|
(?::(\d+|' . $this->urlre . '))? # $7: optional URL
|
|
(?:$|([\]}])|(?=' . $this->punct . '{1,2}|\s)) # $8: closing brace/bracket
|
|
}mx', $this->_cb('$me->_repl($me->repl[0], $me->format_image(array("pre" => $m[1], "src" => $m[5], "align" => ($m[2] ? $m[2] : $m[4]), "extra" => $m[6], "url" => $m[7], "clsty" => $m[3], "post" => $m[8])))'), $text);
|
|
|
|
$text = preg_replace_callback('{(?:^|(?<=[\s>])|([{[])) # $1: open brace/bracket
|
|
% # opening
|
|
(' . $this->halignre . '?) # $2: optional alignment
|
|
(' . $this->clstyre . '*) # $3: optional CSS class/id
|
|
(' . $this->halignre . '?) # $4: optional alignment
|
|
(?:\s*) # spacing
|
|
([^%]+?) # $5: text
|
|
% # closing
|
|
(?::(\d+|' . $this->urlre . '))? # $6: optional URL
|
|
(?:$|([]}])|(?=' . $this->punct . '{1,2}|\s)) # $7: closing brace/bracket
|
|
}mx', $this->_cb('$me->_repl($me->repl[0], $me->format_span(array("pre" => $m[1], "text" => $m[5], "align" => ($m[2] ? $m[2] : $m[4]), "cite" => $m[6], "clsty" => $m[3], "post" => $m[7])))'), $text);
|
|
|
|
$text = $this->encode_html($text);
|
|
$text = preg_replace('!<textile#(\d+)>!', '<textile#$1>', $text);
|
|
$text = preg_replace('!&quot;!', '"', $text);
|
|
$text = preg_replace('!&(([a-z]+|#\d+);)!', '&$1', $text);
|
|
$text = preg_replace('!"!', '"', $text);
|
|
|
|
// These create markup with entities. Do first and 'save' result for later:
|
|
// "text":url -> hyperlink
|
|
// links with brackets surrounding
|
|
$parenre = '\( (?: [^()] )* \)';
|
|
$text = preg_replace_callback('{(
|
|
[{[]
|
|
(?:
|
|
(?:" # quote character
|
|
(' . $this->clstyre . '*)? # $2: optional CSS class/id
|
|
([^"]+?) # $3: link text
|
|
(?:\( ( (?:[^()]|' . $parenre . ')*) \))? # $4: optional link title
|
|
" # closing quote
|
|
)
|
|
|
|
|
(?:\' # open single quote
|
|
(' . $this->clstyre . '*)? # $5: optional CSS class/id
|
|
([^\']+?) # $6: link text
|
|
(?:\( ( (?:[^()]|' . $parenre . ')*) \))? # $7: optional link title
|
|
\' # closing quote
|
|
)
|
|
)
|
|
:(.+?) # $8: URL suffix
|
|
[\]}]
|
|
)
|
|
}mx', $this->_cb('$me->_repl($me->repl[0], $me->format_link(array("text" => $m[1], "linktext" => $m[3] . $m[6], "title" => $me->encode_html_basic($m[4] . $m[7]), "url" => $m[8], "clsty" => $m[2] . $m[5])))'), $text);
|
|
|
|
$text = preg_replace_callback('{((?:^|(?<=[\s>\(])) # $1: open brace/bracket
|
|
(?: (?:" # quote character "
|
|
(' . $this->clstyre . '*)? # $2: optional CSS class/id
|
|
([^"]+?) # $3: link text "
|
|
(?:\( ( (?:[^()]|' . $parenre . ')*) \))? # $4: optional link title
|
|
" # closing quote # "
|
|
)
|
|
|
|
|
(?:\' # open single quote \'
|
|
(' . $this->clstyre . '*)? # $5: optional CSS class/id
|
|
([^\']+?) # $6: link text \'
|
|
(?:\( ( (?:[^()]|' . $parenre . ')*) \))? # $7: optional link title
|
|
\' # closing quote \'
|
|
)
|
|
)
|
|
:(\d+|' . $this->urlre . ') # $8: URL suffix
|
|
(?:$|(?=' . $this->punct . '{1,2}|\s))) # $9: closing brace/bracket
|
|
}mx', $this->_cb('$me->_repl($me->repl[0], $me->format_link(array("text" => $m[1], "linktext" => $m[3] . $m[6], "title" => $me->encode_html_basic($m[4] . $m[7]), "url" => $m[8], "clsty" => $m[2] . $m[5])))'), $text);
|
|
|
|
if (preg_match('/^xhtml2/', $this->flavor())) {
|
|
// citation with cite link
|
|
$text = preg_replace_callback('{(?:^|(?<=[\s>\'"\(])|([{[])) # $1: open brace/bracket \'
|
|
\?\? # opening \'??\'
|
|
([^\?]+?) # $2: characters (can\'t contain \'?\')
|
|
\?\? # closing \'??\'
|
|
:(\d+|' . $this->urlre . ') # $3: optional citation URL
|
|
(?:$|([\]}])|(?=' . $this->punct . '{1,2}|\s)) # $4: closing brace/bracket
|
|
}mx', $this->_cb('$me->_repl($me->repl[0], $me->format_cite(array("pre" => $m[1], "text" => $m[2], "cite" => $m[3], "post" => $m[4])))'), $text);
|
|
}
|
|
|
|
// footnotes
|
|
if (preg_match('/[^ ]\[\d+\]/', $text)) {
|
|
$fntag = '<sup';
|
|
if ($this->options['css']['class_footnote']) { $fntag .= ' class="' . $this->options['css']['class_footnote'] . '"'; }
|
|
$fntag .= '><a href="#' . ($this->options['css']['id_footnote_prefix'] ? $this->options['css']['id_footnote_prefix'] : 'fn');
|
|
$text = preg_replace('|([^ ])\[(\d+)\]|', '$1' . $fntag . '$2">$2</a></sup>', $text);
|
|
}
|
|
|
|
// translate macros:
|
|
$text = preg_replace_callback('{(\{)(.+?)(\})}x',
|
|
$this->_cb('$me->format_macro(array("pre" => $m[1], "post" => $m[3], "macro" => $m[2]))'), $text);
|
|
|
|
// these were present with textile 1 and are common enough
|
|
// to not require macro braces...
|
|
// (tm) -> ™
|
|
$text = preg_replace('|[\(\[]TM[\)\]]|i', '™', $text);
|
|
// (c) -> ©
|
|
$text = preg_replace('|[\(\[]C[\)\]]|i', '©', $text);
|
|
// (r) -> ®
|
|
$text = preg_replace('|[\(\[]R[\)\]]|i', '®', $text);
|
|
|
|
if ($this->preserve_spaces()) {
|
|
// replace two spaces with an em space
|
|
$text = preg_replace('/(?<!\s)\ \ (?!=\s)/', ' ', $text);
|
|
}
|
|
|
|
$redo = preg_match('/[\*_\?\-\+\^\~]/', $text);
|
|
$last = $text;
|
|
while ($redo) {
|
|
// simple replacements...
|
|
$redo = 0;
|
|
foreach ($qtags as $tag) {
|
|
list ($this->tmp['f'][], $this->tmp['r'][], $qf, $cls) = $tag;
|
|
if ($last != ($text = preg_replace_callback('{(?:^|(?<=[\s>\'"])|([{[])) # "\' $1 - pre
|
|
' . $qf . ' #
|
|
(?:(' . $this->clstyre . '*))? # $2 - attributes
|
|
([^' . $cls . '\s].*?) # $3 - content
|
|
(?<=\S)' . $qf . ' #
|
|
(?:$|([\]}])|(?=' . $this->punct . '{1,2}|\s)) # $4 - post
|
|
}mx', $this->_cb('$me->format_tag(array("tag" => end($me->tmp["r"]), "marker" => end($me->tmp["f"]), "pre" => $m[1], "text" => $m[3], "clsty" => $m[2], "post" => $m[4]))'), $text))) {
|
|
$redo = ($redo || ($last != $text));
|
|
$last = $text;
|
|
}
|
|
array_pop($this->tmp['f']); array_pop($this->tmp['r']);
|
|
}
|
|
}
|
|
|
|
// superscript is an even simpler replacement...
|
|
$text = preg_replace('/(?<!\^)\^(?!\^)(.+?)(?<!\^)\^(?!\^)/', '<sup>$1</sup>', $text);
|
|
|
|
// ABC(Aye Bee Cee) -> acronym
|
|
$text = preg_replace_callback('{\b([A-Z][A-Za-z0-9]*?[A-Z0-9]+?)\b(?:[(]([^)]*)[)])}',
|
|
$this->_cb('$me->_repl($me->repl[0],"<acronym title=\"" . $me->encode_html_basic($m[2]) . "\">$m[1]</acronym>")'), $text);
|
|
|
|
// ABC -> 'capped' span
|
|
if ($this->tmp['caps'][] = $this->options['css']['class_caps']) {
|
|
$text = preg_replace_callback('/(^|[^"][>\s]) # "
|
|
((?:[A-Z](?:[A-Z0-9\.,\']|\&){2,}\ *)+?) # \'
|
|
(?=[^A-Z\.0-9]|$)
|
|
/mx', $this->_cb('$m[1] . $me->_repl($me->repl[0], "<span class=\"" . end($me->tmp["caps"]) . "\">$m[2]</span>")'), $text);
|
|
}
|
|
array_pop($this->tmp['caps']);
|
|
|
|
// nxn -> n×n
|
|
$text = preg_replace('!((?:[0-9\.]0|[1-9]|\d[\'"])\ ?)x(\ ?\d)!', '$1×$2', $text);
|
|
|
|
// translate these entities to the Unicode equivalents:
|
|
$text = preg_replace('/…/', '…', $text);
|
|
$text = preg_replace('/‘/', '‘', $text);
|
|
$text = preg_replace('/’/', '’', $text);
|
|
$text = preg_replace('/“/', '“', $text);
|
|
$text = preg_replace('/”/', '”', $text);
|
|
$text = preg_replace('/–/', '–', $text);
|
|
$text = preg_replace('/—/', '—', $text);
|
|
|
|
// Restore replacements done earlier:
|
|
for ($i = count($this->repl[0]); $i > 0; $i--) {
|
|
$text = preg_replace("|<textile#$i>|", str_replace('$', '\\$', $this->repl[0][$i - 1]), $text);
|
|
}
|
|
array_shift($this->repl);
|
|
|
|
// translate entities to characters for highbit stuff since
|
|
// we're using utf8
|
|
// removed for backward compatability with older versions of Perl
|
|
//if (preg_match('/^utf-?8$/i', $this->options['charset'])) {
|
|
// // translate any unicode entities to native UTF-8
|
|
// $text = preg_replace('/\&\#(\d+);/e', '($1 > 127) ? pack('U', $1) : chr($1)', $text);
|
|
//}
|
|
|
|
return $text;
|
|
} // function format_inline
|
|
|
|
/**
|
|
* Responsible for processing a particular macro. Arguments passed
|
|
* include:
|
|
*
|
|
* <ul>
|
|
*
|
|
* <li><b>pre</b>
|
|
*
|
|
* open brace character</li>
|
|
*
|
|
* <li><b>post</b>
|
|
*
|
|
* close brace character</li>
|
|
*
|
|
* <li><b>macro</b>
|
|
*
|
|
* the macro to be executed</li>
|
|
*
|
|
* </ul>
|
|
*
|
|
* The return value from this method would be the replacement
|
|
* text for the macro given. If the macro is not defined, it will
|
|
* return pre + macro + post, thereby preserving the original
|
|
* macro string.
|
|
*
|
|
* @param $attrs An @c array containing the attributes for
|
|
* formatting the macro.
|
|
*
|
|
* @return A @c string containing the formatted macro.
|
|
*
|
|
* @private
|
|
*/
|
|
function format_macro($attrs) {
|
|
$macro = $attrs['macro'];
|
|
if ($this->options['macros'][$macro]) {
|
|
return $this->options['macros'][$macro];
|
|
}
|
|
|
|
return $attrs['pre'] . $macro . $attrs['post'];
|
|
} // function format_macro
|
|
|
|
/**
|
|
* Processes text for a citation tag. The following attributes
|
|
* are allowed:
|
|
*
|
|
* <ul>
|
|
*
|
|
* <li><b>pre</b>
|
|
*
|
|
* Any text that comes before the citation.</li>
|
|
*
|
|
* <li><b>text</b>
|
|
*
|
|
* The text that is being cited.</li>
|
|
*
|
|
* <li><b>cite</b>
|
|
*
|
|
* The URL of the citation.</li>
|
|
*
|
|
* <li><b>post</b>
|
|
*
|
|
* Any text that follows the citation.</li>
|
|
*
|
|
* </ul>
|
|
*
|
|
* @param $args An @c array specifying the attributes for formatting
|
|
* the citation.
|
|
*
|
|
* @return A @c string containing the formatted citation.
|
|
*
|
|
* @private
|
|
*/
|
|
function format_cite($args) {
|
|
$pre = (isset($args['pre']) ? $args['pre'] : '');
|
|
$text = (isset($args['text']) ? $args['text'] : '');
|
|
$cite = $args['cite'];
|
|
$post = (isset($args['post']) ? $args['post'] : '');
|
|
$this->_strip_borders($pre, $post);
|
|
$tag = $pre . '<cite';
|
|
if (preg_match('/^xhtml2/', $this->flavor()) && $cite) {
|
|
$cite = $this->format_url(array('url' => $cite));
|
|
$tag .= " cite=\"$cite\"";
|
|
} else {
|
|
$post .= ':';
|
|
}
|
|
$tag .= '>';
|
|
return $tag . $this->format_inline(array('text' => $text)) . '</cite>' . $post;
|
|
} // function format_cite
|
|
|
|
/**
|
|
* Processes '@...@' type blocks (code snippets). The following
|
|
* attributes are allowed:
|
|
*
|
|
* <ul>
|
|
*
|
|
* <li><b>text</b>
|
|
*
|
|
* The text of the code itself.</li>
|
|
*
|
|
* <li><b>lang</b>
|
|
*
|
|
* The language (programming language) for the code.</li>
|
|
*
|
|
* </ul>
|
|
*
|
|
* @param $args An @c array specifying the attributes for formatting
|
|
* the code.
|
|
*
|
|
* @return A @c string containing the formatted code.
|
|
*
|
|
* @private
|
|
*/
|
|
function format_code($args) {
|
|
$code = (isset($args['text']) ? $args['text'] : '');
|
|
$lang = $args['lang'];
|
|
$code = $this->encode_html($code, 1);
|
|
$code = preg_replace('/<textile#(\d+)>/', '<textile#$1>', $code);
|
|
$tag = '<code';
|
|
if ($lang) { $tag .= " language=\"$lang\""; }
|
|
return $tag . '>' . $code . '</code>';
|
|
} // function format_code
|
|
|
|
/**
|
|
* Returns a string of tag attributes to accomodate the class,
|
|
* style and symbols present in @c $clsty.
|
|
*
|
|
* @c $clsty is checked for:
|
|
*
|
|
* <ul>
|
|
*
|
|
* <li><b><code>{...}</code></b>
|
|
*
|
|
* style rules. If present, they are appended to <code>$style</code>.</li>
|
|
*
|
|
* <li><b><code>(...#...)</code></b>
|
|
*
|
|
* class and/or ID name declaration</li>
|
|
*
|
|
* <li><b><code>(</code> (one or more)</b>
|
|
*
|
|
* pad left characters</li>
|
|
*
|
|
* <li><b><code>)</code> (one or more)</b>
|
|
*
|
|
* pad right characters</li>
|
|
*
|
|
* <li><b><code>[ll]</code></b>
|
|
*
|
|
* language declaration</li>
|
|
*
|
|
* </ul>
|
|
*
|
|
* The attribute string returned will contain any combination
|
|
* of class, id, style and/or lang attributes.
|
|
*
|
|
* @param $clsty A @c string specifying the class/style to process.
|
|
* @param $class A @c string specifying the predetermined class.
|
|
* @param $style A @c string specifying the predetermined style.
|
|
*
|
|
* @return A @c string containing the formatted class, ID, style,
|
|
* and/or language.
|
|
*
|
|
* @private
|
|
*/
|
|
function format_classstyle($clsty = NULL, $class = NULL, $style = NULL) {
|
|
$class = preg_replace('/^ /', '', $class, 1);
|
|
|
|
unset($lang, $padleft, $padright, $id);
|
|
if ($clsty && preg_match('/{([^}]+)}/', $clsty, $matches)) {
|
|
$_style = $matches[1];
|
|
$_style = preg_replace('/\n/', ' ', $_style);
|
|
$style .= ';' . $_style;
|
|
$clsty = preg_replace('/{[^}]+}/', '', $clsty);
|
|
}
|
|
if ($clsty && (preg_match('/\(([A-Za-z0-9_\- ]+?)(?:#(.+?))?\)/', $clsty, $matches) ||
|
|
preg_match('/\(([A-Za-z0-9_\- ]+?)?(?:#(.+?))\)/', $clsty, $matches))) {
|
|
if ($matches[1] || $matches[2]) {
|
|
if ($class) {
|
|
$class = $matches[1] . ' ' . $class;
|
|
} else {
|
|
$class = $matches[1];
|
|
}
|
|
$id = $matches[2];
|
|
if ($class) {
|
|
$clsty = preg_replace('/\([A-Za-z0-9_\- ]+?(#.*?)?\)/', '', $clsty);
|
|
}
|
|
if ($id) {
|
|
$clsty = preg_replace('/\(#.+?\)/', '', $clsty);
|
|
}
|
|
}
|
|
}
|
|
if ($clsty && preg_match('/(\(+)/', $clsty, $matches)) {
|
|
$padleft = strlen($matches[1]);
|
|
$clsty = preg_replace('/\(+/', '', $clsty, 1);
|
|
}
|
|
if ($clsty && preg_match('/(\)+)/', $clsty, $matches)) {
|
|
$padright = strlen($matches[1]);
|
|
$clsty = preg_replace('/\)+/', '', $clsty, 1);
|
|
}
|
|
if ($clsty && preg_match('/\[(.+?)\]/', $clsty, $matches)) {
|
|
$lang = $matches[1];
|
|
$clsty = preg_replace('/\[.+?\]/', '', $clsty);
|
|
}
|
|
$attrs = '';
|
|
if ($padleft) { $style .= ";padding-left:${padleft}em"; }
|
|
if ($padright) { $style .= ";padding-right:${padright}em"; }
|
|
$style = preg_replace('/^;/', '', $style, 1);
|
|
$class = preg_replace('/^ /', '', $class, 1);
|
|
$class = preg_replace('/ $/', '', $class, 1);
|
|
if ($class) { $attrs .= " class=\"$class\""; }
|
|
if ($id) { $attrs .= " id=\"$id\""; }
|
|
if ($style) { $attrs .= " style=\"$style\""; }
|
|
if ($lang) { $attrs .= " lang=\"$lang\""; }
|
|
$attrs = preg_replace('/^ /', '', $attrs, 1);
|
|
return $attrs;
|
|
} // function format_classstyle
|
|
|
|
/**
|
|
* Constructs an HTML tag. Accepted arguments:
|
|
*
|
|
* <ul>
|
|
*
|
|
* <li><b>tag</b>
|
|
*
|
|
* the tag to produce</li>
|
|
*
|
|
* <li><b>text</b>
|
|
*
|
|
* the text to output inside the tag</li>
|
|
*
|
|
* <li><b>pre</b>
|
|
*
|
|
* text to produce before the tag</li>
|
|
*
|
|
* <li><b>post</b>
|
|
*
|
|
* text to produce following the tag</li>
|
|
*
|
|
* <li><b>clsty</b>
|
|
*
|
|
* class and/or style attributes that should be assigned to the tag.</li>
|
|
*
|
|
* </ul>
|
|
*
|
|
* @param $args @c array specifying the attributes for formatting
|
|
* the tag.
|
|
*
|
|
* @return A @c string containing the formatted tag.
|
|
*
|
|
* @private
|
|
*/
|
|
function format_tag($args) {
|
|
$tagname = $args['tag'];
|
|
$text = (isset($args['text']) ? $args['text'] : '');
|
|
$pre = (isset($args['pre']) ? $args['pre'] : '');
|
|
$post = (isset($args['post']) ? $args['post'] : '');
|
|
$clsty = (isset($args['clsty']) ? $args['clsty'] : '');
|
|
$this->_strip_borders($pre, $post);
|
|
$tag = "<$tagname";
|
|
$attr = $this->format_classstyle($clsty);
|
|
if ($attr) { $tag .= " $attr"; }
|
|
$tag .= ">$text</$tagname>";
|
|
return $pre . $tag . $post;
|
|
} // function format_tag
|
|
|
|
/**
|
|
* Takes a Textile formatted definition list and
|
|
* returns the markup for it. Arguments accepted:
|
|
*
|
|
* <ul>
|
|
*
|
|
* <li><b>text</b>
|
|
*
|
|
* The text to be processed.</li>
|
|
*
|
|
* </ul>
|
|
*
|
|
* @param $args An @c array specifying the attributes for formatting
|
|
* the definition list.
|
|
*
|
|
* @return A @c string containing the formatted definition list.
|
|
*
|
|
* @private
|
|
*/
|
|
function format_deflist($args) {
|
|
$str = (isset($args['text']) ? $args['text'] : '');
|
|
unset($clsty);
|
|
$lines = preg_split('/\n/', $str);
|
|
if (preg_match('{^(dl(' . $this->clstyre . '*?)\.\.?(?:\ +|$))}x', $lines[0], $matches)) {
|
|
$clsty = $matches[2];
|
|
$lines[0] = substr($lines[0], strlen($matches[1]));
|
|
}
|
|
|
|
unset($dt, $dd);
|
|
$out = '';
|
|
foreach ($lines as $line) {
|
|
if (preg_match('{^((?:' . $this->clstyre . '*)(?:[^\ ].*?)(?<!["\'\ ])):([^\ \/].*)$}x', $line, $matches)) {
|
|
if ($dt && $dd) { $out .= $this->add_term($dt, $dd); }
|
|
$dt = $matches[1];
|
|
$dd = $matches[2];
|
|
} else {
|
|
$dd .= "\n" . $line;
|
|
}
|
|
}
|
|
if ($dt && $dd) { $out .= $this->add_term($dt, $dd); }
|
|
|
|
$tag = '<dl';
|
|
if ($clsty) { $attr = $this->format_classstyle($clsty); }
|
|
if ($attr) { $tag .= " $attr"; }
|
|
$tag .= '>' . "\n";
|
|
|
|
return $tag . $out . "</dl>\n";
|
|
} // function format_deflist
|
|
|
|
/**
|
|
* Processes a single definition list item from the provided term
|
|
* and definition.
|
|
*
|
|
* @param $dt A @c string specifying the term to be defined.
|
|
* @param $dd A @c string specifying the definition for the term.
|
|
*
|
|
* @return A @c string containing the formatted definition list
|
|
* item.
|
|
*
|
|
* @private
|
|
*/
|
|
function add_term($dt, $dd) {
|
|
unset($dtattr, $ddattr);
|
|
unset($dtlang);
|
|
if (preg_match('{^(' . $this->clstyre . '*)}x', $dt, $matches)) {
|
|
$param = $matches[1];
|
|
$dtattr = $this->format_classstyle($param);
|
|
if (preg_match('/\[([A-Za-z]+?)\]/', $param, $matches)) {
|
|
$dtlang = $matches[1];
|
|
}
|
|
$dt = substr($dt, strlen($param));
|
|
}
|
|
if (preg_match('{^(' . $this->clstyre . '*)}x', $dd, $matches)) {
|
|
$param = $matches[1];
|
|
// if the language was specified for the term,
|
|
// then apply it to the definition as well (unless
|
|
// already specified of course)
|
|
if ($dtlang && preg_match('/\[([A-Za-z]+?)\]/', $param)) {
|
|
unset($dtlang);
|
|
}
|
|
$ddattr = $this->format_classstyle(($dtlang ? "[$dtlang]" : '') . $param);
|
|
$dd = substr($dd, strlen($param));
|
|
}
|
|
$out = '<dt';
|
|
if ($dtattr) { $out .= " $dtattr"; }
|
|
$out .= '>' . $this->format_inline(array('text' => $dt)) . '</dt>' . "\n";
|
|
if (preg_match('/\n\n/', $dd)) {
|
|
if (preg_match('/\n\n/', $dd)) { $dd = $this->process($dd); }
|
|
} else {
|
|
$dd = $this->format_paragraph(array('text' => $dd));
|
|
}
|
|
$out .= '<dd';
|
|
if ($ddattr) { $out .= " $ddattr"; }
|
|
$out .= '>' . $dd . '</dd>' . "\n";
|
|
return $out;
|
|
} // function add_term
|
|
|
|
/**
|
|
* Takes a Textile formatted list (numeric or bulleted) and
|
|
* returns the markup for it. Text that is passed in requires
|
|
* substantial parsing, so the @c format_list method is a little
|
|
* involved. But it should always produce a proper ordered
|
|
* or unordered list. If it cannot (due to misbalanced input),
|
|
* it will return the original text. Arguments accepted:
|
|
*
|
|
* <ul>
|
|
*
|
|
* <li><b>text</b>
|
|
*
|
|
* The text to be processed.</li>
|
|
*
|
|
* </ul>
|
|
*
|
|
* @param $args An @c array specifying the attributes for formatting
|
|
* the list.
|
|
*
|
|
* @return A @c string containing the formatted list.
|
|
*
|
|
* @private
|
|
*/
|
|
function format_list($args) {
|
|
$str = (isset($args['text']) ? $args['text'] : '');
|
|
|
|
$list_tags = array('*' => 'ul', '#' => 'ol');
|
|
|
|
$lines = preg_split('/\n/', $str);
|
|
|
|
unset($stack);
|
|
$last_depth = 0;
|
|
$item = '';
|
|
$out = '';
|
|
foreach ($lines as $line) {
|
|
if (preg_match('{^((?:' . $this->clstypadre . '*|' . $this->halignre . ')*)
|
|
([\#\*]+)
|
|
((?:' . $this->halignre . '|' . $this->clstypadre . '*)*)
|
|
\ (.+)$}x', $line, $matches)) {
|
|
if ($item != '') {
|
|
if (preg_match('/\n/', $item)) {
|
|
if ($this->options['_line_open']) {
|
|
$item = preg_replace('/(<li[^>]*>|^)/m', '$1' . $this->options['_line_open'], $item);
|
|
$item = preg_replace('/(\n|$)/s', $this->options['_line_close'] . '$1', $item);
|
|
} else {
|
|
$item = preg_replace('/(\n)/s', $this->options['_line_close'] . '$1', $item);
|
|
}
|
|
}
|
|
$out .= $item;
|
|
$item = '';
|
|
}
|
|
$type = substr($matches[2], 0, 1);
|
|
$depth = strlen($matches[2]);
|
|
$blockparam = $matches[1];
|
|
$itemparam = $matches[3];
|
|
$line = $matches[4];
|
|
unset ($blockclsty, $blockalign, $blockattr, $itemattr, $itemclsty,
|
|
$itemalign);
|
|
if (preg_match('{(' . $this->clstypadre . '+)}x', $blockparam, $matches)) {
|
|
$blockclsty = $matches[1];
|
|
}
|
|
if (preg_match('{(' . $this->halignre . '+)}', $blockparam, $matches)) {
|
|
$blockalign = $matches[1];
|
|
}
|
|
if (preg_match('{(' . $this->clstypadre . '+)}x', $itemparam, $matches)) {
|
|
$itemclsty = $matches[1];
|
|
}
|
|
if (preg_match('{(' . $this->halignre . '+)}', $itemparam, $matches)) {
|
|
$itemalign = $matches[1];
|
|
}
|
|
if ($itemclsty) { $itemattr = $this->format_classstyle($itemclsty); }
|
|
if ($depth > $last_depth) {
|
|
for ($j = $last_depth; $j < $depth; $j++) {
|
|
$out .= "\n<$list_tags[$type]";
|
|
$stack[] = $type;
|
|
if ($blockclsty) {
|
|
$blockattr = $this->format_classstyle($blockclsty);
|
|
if ($blockattr) { $out .= ' ' . $blockattr; }
|
|
}
|
|
$out .= ">\n<li";
|
|
if ($itemattr) { $out .= " $itemattr"; }
|
|
$out .= ">";
|
|
}
|
|
} elseif ($depth < $last_depth) {
|
|
for ($j = $depth; $j < $last_depth; $j++) {
|
|
if ($j == $depth) { $out .= "</li>\n"; }
|
|
$type = array_pop($stack);
|
|
$out .= "</$list_tags[$type]>\n</li>\n";
|
|
}
|
|
if ($depth) {
|
|
$out .= '<li';
|
|
if ($itemattr) { $out .= " $itemattr"; }
|
|
$out .= '>';
|
|
}
|
|
} else {
|
|
$out .= "</li>\n<li";
|
|
if ($itemattr) { $out .= " $itemattr"; }
|
|
$out .= '>';
|
|
}
|
|
$last_depth = $depth;
|
|
}
|
|
if ($item != '') { $item .= "\n"; }
|
|
$item .= $this->format_paragraph(array('text' => $line));
|
|
}
|
|
|
|
if (preg_match('/\n/', $item, $matches)) {
|
|
if ($this->options['_line_open']) {
|
|
$item = preg_replace('/(<li[^>]*>|^)/m', '$1' . $this->options['_line_open'], $item);
|
|
$item = preg_replace('/(\n|$)/s', $this->options['_line_close'] . '$1', $item);
|
|
} else {
|
|
$item = preg_replace('/(\n)/s', $this->options['_line_close'] . '$1', $item);
|
|
}
|
|
}
|
|
$out .= $item;
|
|
|
|
for ($j = 1; $j <= $last_depth; $j++) {
|
|
if ($j == 1) { $out .= '</li>'; }
|
|
$type = array_pop($stack);
|
|
$out .= "\n" . '</' . $list_tags[$type] . '>' . "\n";
|
|
if ($j != $last_depth) { $out .= '</li>'; }
|
|
}
|
|
|
|
return $out . "\n";
|
|
} // function format_list
|
|
|
|
/**
|
|
* Processes '==xxxxx==' type blocks for filters. A filter
|
|
* would follow the open '==' sequence and is specified within
|
|
* pipe characters, like so:
|
|
* <pre>
|
|
* ==|filter|text to be filtered==
|
|
* </pre>
|
|
* You may specify multiple filters in the filter portion of
|
|
* the string. Simply comma delimit the filters you desire
|
|
* to execute. Filters are defined using the filters method.
|
|
*
|
|
* @param $args An @c array specifying the attributes for formatting
|
|
* the block.
|
|
*
|
|
* @return A @c string containing the formatted block.
|
|
*
|
|
* @private
|
|
*/
|
|
function format_block($args) {
|
|
$str = (isset($args['text']) ? $args['text'] : '');
|
|
$inline = $args['inline'];
|
|
$pre = (isset($args['pre']) ? $args['pre'] : '');
|
|
$post = (isset($args['post']) ? $args['post'] : '');
|
|
$this->_strip_borders($pre, $post);
|
|
$filters = (preg_match('/^(\|(?:(?:[a-z0-9_\-]+)\|)+)/', $str, $matches) ? $matches[1] : '');
|
|
if ($filters) {
|
|
$filtreg = preg_replace('/[^A-Za-z0-9]/', '\\\\$1', $filters);
|
|
$str = preg_replace('/^' . $filtreg . '/', '', $str, 1);
|
|
$filters = preg_replace('/^\|/', '', $filters, 1);
|
|
$filters = preg_replace('/\|$/', '', $filter, 1);
|
|
$filters = preg_split('/\|/', $filters);
|
|
$str = $this->apply_filters(array('text' => $str, 'filters' => $filters));
|
|
$count = count($filters);
|
|
if ($str = preg_replace('!(<p>){' . $count . '}!se', '(++$i ? "$1" : "$1")', $str) && $i) {
|
|
$str = preg_replace('!(</p>){' . $count . '}!s', '$1', $str);
|
|
$str = preg_replace('!(<br( /)?>){' . $count . '}!s', '$1', $str);
|
|
}
|
|
}
|
|
if ($inline) {
|
|
// strip off opening para, closing para, since we're
|
|
// operating within an inline block
|
|
$str = preg_replace('/^\s*<p[^>]*>/', '', $str, 1);
|
|
$str = preg_replace('/<\/p>\s*$/', '', $str, 1);
|
|
}
|
|
return $pre . $str . $post;
|
|
} // function format_block
|
|
|
|
/**
|
|
* Takes the Textile link attributes and transforms them into
|
|
* a hyperlink.
|
|
*
|
|
* @param $args An @c array specifying the attributes for formatting
|
|
* the link.
|
|
*
|
|
* @return A @c string containing the formatted link.
|
|
*
|
|
* @private
|
|
*/
|
|
function format_link($args) {
|
|
$text = (isset($args['text']) ? $args['text'] : '');
|
|
$linktext = (isset($args['linktext']) ? $args['linktext'] : '');
|
|
$title = $args['title'];
|
|
$url = $args['url'];
|
|
$clsty = $args['clsty'];
|
|
|
|
if (!$url || ($url == '')) {
|
|
return $text;
|
|
}
|
|
if (isset($this->links) && isset($this->links[$url])) {
|
|
$title = ($title ? $title : $this->links[$url]['title']);
|
|
$url = $this->links[$url]['url'];
|
|
}
|
|
$linktext = preg_replace('/ +$/', '', $linktext, 1);
|
|
$linktext = $this->format_paragraph(array('text' => $linktext));
|
|
$url = $this->format_url(array('linktext' => $linktext, 'url' => $url));
|
|
$tag = "<a href=\"$url\"";
|
|
$attr = $this->format_classstyle($clsty);
|
|
if ($attr) { $tag .= " $attr"; }
|
|
if ($title) {
|
|
$title = preg_replace('/^\s+/', '', $title, 1);
|
|
if (strlen($title)) { $tag .= " title=\"$title\""; }
|
|
}
|
|
$tag .= ">$linktext</a>";
|
|
return $tag;
|
|
} // function format_link
|
|
|
|
/**
|
|
* Takes the given @c $url and transforms it appropriately.
|
|
*
|
|
* @param $args An @c array specifying the attributes for formatting
|
|
* the url.
|
|
*
|
|
* @return A @c string containing the formatted url.
|
|
*
|
|
* @private
|
|
*/
|
|
function format_url($args) {
|
|
$url = ($args['url'] ? $args['url'] : '');
|
|
if (preg_match('/^(mailto:)?([-\+\w]+@[-\w]+(\.\w[-\w]*)+)$/', $url, $matches)) {
|
|
$url = 'mailto:' . $this->mail_encode($matches[2]);
|
|
}
|
|
if (!preg_match('!^(/|\./|\.\./|#)!', $url)) {
|
|
if (!preg_match('!^(https?|ftp|mailto|nntp|telnet)!', $url)) { $url = "http://$url"; }
|
|
}
|
|
$url = preg_replace('/&(?!amp;)/', '&', $url);
|
|
$url = preg_replace('/\ /', '+', $url);
|
|
$url = preg_replace_callback('/^((?:.+?)\?)(.+)$/', $this->_cb('$m[1] . $me->encode_url($m[2])'), $url);
|
|
return $url;
|
|
} // function format_url
|
|
|
|
/**
|
|
* Takes a Textile formatted span and returns the markup for it.
|
|
*
|
|
* @return A @c string containing the formatted span.
|
|
*
|
|
* @private
|
|
*/
|
|
function format_span($args) {
|
|
$text = (isset($args['text']) ? $args['text'] : '');
|
|
$pre = (isset($args['pre']) ? $args['pre'] : '');
|
|
$post = (isset($args['post']) ? $args['post'] : '');
|
|
$align = $args['align'];
|
|
$cite = (isset($args['cite']) ? $args['cite'] : '');
|
|
$clsty = $args['clsty'];
|
|
$this->_strip_borders($pre, $post);
|
|
unset($class, $style);
|
|
$tag = "<span";
|
|
$style = '';
|
|
if ($align) {
|
|
if ($self->options['css_mode']) {
|
|
$alignment = $this->_halign($align);
|
|
if ($alignment) { $style .= ";float:$alignment"; }
|
|
if ($alignment) { $class .= ' ' . $this->options['css']["class_align_$alignment"]; }
|
|
} else {
|
|
$alignment = ($this->_halign($align) ? $this->_halign($align) : $this->_valign($align));
|
|
if ($alignment) { $tag .= " align=\"$alignment\""; }
|
|
}
|
|
}
|
|
$attr = $this->format_classstyle($clsty, $class, $style);
|
|
if ($attr) { $tag .= " $attr"; }
|
|
if ($cite) {
|
|
$cite = preg_replace('/^:/', '', $cite, 1);
|
|
$cite = $this->format_url(array('url' => $cite));
|
|
$tag .= " cite=\"$cite\"";
|
|
}
|
|
return $pre . $tag . '>' . $this->format_paragraph(array('text' => $text)) . '</span>' . $post;
|
|
} // function format_span
|
|
|
|
/**
|
|
* Returns markup for the given image. @c $src is the location of
|
|
* the image, @c $extra contains the optional height/width and/or
|
|
* alt text. @c $url is an optional hyperlink for the image. @c $class
|
|
* holds the optional CSS class attribute.
|
|
*
|
|
* Arguments you may pass:
|
|
*
|
|
* <ul>
|
|
*
|
|
* <li><b>src</b>
|
|
*
|
|
* The 'src' (URL) for the image. This may be a local path,
|
|
* ideally starting with a '/'. Images can be located within
|
|
* the file system if the docroot method is used to specify
|
|
* where the docroot resides. If the image can be found, the
|
|
* image_size method is used to determine the dimensions of
|
|
* the image.</li>
|
|
*
|
|
* <li><b>extra</b>
|
|
*
|
|
* Additional parameters for the image. This would include
|
|
* alt text, height/width specification or scaling instructions.</li>
|
|
*
|
|
* <li><b>align</b>
|
|
*
|
|
* Alignment attribute.</li>
|
|
*
|
|
* <li><b>pre</b>
|
|
*
|
|
* Text to produce prior to the tag.</li>
|
|
*
|
|
* <li><b>post</b>
|
|
*
|
|
* Text to produce following the tag.</li>
|
|
*
|
|
* <li><b>link</b>
|
|
*
|
|
* Optional URL to connect with the image tag.</li>
|
|
*
|
|
* <li><b>clsty</b>
|
|
*
|
|
* Class and/or style attributes.</li>
|
|
*
|
|
* </ul>
|
|
*
|
|
* @param $args An @c array specifying the attributes for formatting
|
|
* the image.
|
|
*
|
|
* @return A @c string containing the formatted image.
|
|
*
|
|
* @private
|
|
*/
|
|
function format_image($args) {
|
|
$src = (isset($args['src']) ? $args['src'] : '');
|
|
$extra = $args['extra'];
|
|
$align = $args['align'];
|
|
$pre = (isset($args['pre']) ? $args['pre'] : '');
|
|
$post = (isset($args['post']) ? $args['post'] : '');
|
|
$link = $args['url'];
|
|
$clsty = $args['clsty'];
|
|
$this->_strip_borders($pre, $post);
|
|
if (strlen($src) == 0) { return $pre . '!!' . $post; }
|
|
unset($tag);
|
|
if (preg_match('/^xhtml2/', $this->options['flavor'])) {
|
|
unset($type); // poor man's mime typing. need to extend this externally
|
|
if (preg_match('/(?:\.jpeg|\.jpg)$/i', $src)) {
|
|
$type = 'image/jpeg';
|
|
} elseif (preg_match('/\.gif$/i', $src)) {
|
|
$type = 'image/gif';
|
|
} elseif (preg_match('/\.png$/i', $src)) {
|
|
$type = 'image/png';
|
|
} elseif (preg_match('/\.tiff$/i', $src)) {
|
|
$type = 'image/tiff';
|
|
}
|
|
$tag = "<object";
|
|
if ($type) { $tag .= " type=\"$type\""; }
|
|
$tag .= " data=\"$src\"";
|
|
} else {
|
|
$tag = "<img src=\"$src\"";
|
|
}
|
|
unset($class, $style);
|
|
if ($align) {
|
|
if ($this->options['css_mode']) {
|
|
$alignment = $this->_halign($align);
|
|
if ($alignment) { $style .= ";float:$alignment"; }
|
|
if ($alignment) { $class .= ' ' . $alignment; }
|
|
$alignment = $this->_valign($align);
|
|
if ($alignment) {
|
|
$imgvalign = (preg_match('/(top|bottom)/', $alignment) ? 'text-' . $alignment : $alignment);
|
|
if ($imgvalign) { $style .= ";vertical-align:$imgvalign"; }
|
|
if ($alignment) { $class .= ' ' . $this->options['css']["class_align_$alignment"]; }
|
|
}
|
|
} else {
|
|
$alignment = ($this->_halign($align) ? $this->_halign($align) : $this->_valign($align));
|
|
if ($alignment) { $tag .= " align=\"$alignment\""; }
|
|
}
|
|
}
|
|
unset($pctw, $pcth, $w, $h, $alt);
|
|
if ($extra) {
|
|
$alt = (preg_match('/\(([^\)]+)\)/', $extra, $matches) ? $matches[1] : '');
|
|
$extra = preg_replace('/\([^\)]+\)/', '', $extra, 1);
|
|
$pct = (preg_match('/(^|\s)(\d+)%(\s|$)/', $extra, $matches) ? $matches[2] : '');
|
|
if (!$pct) {
|
|
list($pctw, $pcth) = (preg_match('/(^|\s)(\d+)%x(\d+)%(\s|$)/', $extra, $matches) ? array($matches[2], $matches[3]) : NULL);
|
|
} else {
|
|
$pctw = $pcth = $pct;
|
|
}
|
|
if (!$pctw && !$pcth) {
|
|
list($w,$h) = (preg_match('/(^|\s)(\d+|\*)x(\d+|\*)(\s|$)/', $extra, $matches) ? array($matches[2], $matches[3]) : NULL);
|
|
if ($w == '*') { $w = ''; }
|
|
if ($h == '*') { $h = ''; }
|
|
if (!$w) {
|
|
$w = (preg_match('/(^|[,\s])(\d+)w([\s,]|$)/', $extra, $matches) ? $matches[2] : '');
|
|
}
|
|
if (!$h) {
|
|
$h = (preg_match('/(^|[,\s])(\d+)h([\s,]|$)/', $extra, $matches) ? $matches[2] : '');
|
|
}
|
|
}
|
|
}
|
|
$alt = ($alt ? $alt : '');
|
|
if (!preg_match('/^xhtml2/', $this->options['flavor'])) {
|
|
$tag .= ' alt="' . $this->encode_html_basic($alt) . '"';
|
|
}
|
|
if ($w && $h) {
|
|
if (!preg_match('/^xhtml2/', $this->options['flavor'])) {
|
|
$tag .= " height=\"$h\" width=\"$w\"";
|
|
} else {
|
|
$style .= ";height:${h}px;width:${w}px";
|
|
}
|
|
} else {
|
|
list($image_w, $image_h) = $this->image_size($src);
|
|
if (($image_w && $image_h) && ($w || $h)) {
|
|
// image size determined, but only width or height specified
|
|
if ($w && !$h) {
|
|
// width defined, scale down height proportionately
|
|
$h = intval($image_h * ($w / $image_w));
|
|
} elseif ($h && !$w) {
|
|
$w = intval($image_w * ($h / $image_h));
|
|
}
|
|
} else {
|
|
$w = $image_w;
|
|
$h = $image_h;
|
|
}
|
|
if ($w && $h) {
|
|
if ($pctw || $pcth) {
|
|
$w = intval($w * $pctw / 100);
|
|
$h = intval($h * $pcth / 100);
|
|
}
|
|
if (!preg_match('/^xhtml2/', $this->options['flavor'])) {
|
|
$tag .= " height=\"$h\" width=\"$w\"";
|
|
} else {
|
|
$style .= ";height:{$h}px;width:{$w}px";
|
|
}
|
|
}
|
|
}
|
|
$attr = $this->format_classstyle($clsty, $class, $style);
|
|
if ($attr) { $tag .= " $attr"; }
|
|
if (preg_match('/^xhtml2/', $this->options['flavor'])) {
|
|
$tag .= '><p>' . $this->encode_html_basic($alt) . '</p></object>';
|
|
} elseif (preg_match('/^xhtml/', $this->options['flavor'])) {
|
|
$tag .= ' />';
|
|
} else {
|
|
$tag .= '>';
|
|
}
|
|
if ($link) {
|
|
$link = preg_replace('/^:/', '', $link, 1);
|
|
$link = $this->format_url(array('url' => $link));
|
|
$tag = '<a href="' . $link . '">' . $tag . '</a>';
|
|
}
|
|
return $pre . $tag . $post;
|
|
} // function format_image
|
|
|
|
/**
|
|
* Takes a Wiki-ish string of data and transforms it into a full
|
|
* table.
|
|
*
|
|
* @param $args An @c array specifying the attributes for formatting
|
|
* the table.
|
|
*
|
|
* @return A @c string containing the formatted table.
|
|
*
|
|
* @private
|
|
*/
|
|
function format_table($args) {
|
|
$str = (isset($args['text']) ? $args['text'] : '');
|
|
|
|
$lines = preg_split('/\n/', $str);
|
|
unset($rows);
|
|
$line_count = count($lines);
|
|
for ($i = 0; $i < $line_count; $i++) {
|
|
if (!preg_match('/\|\s*$/', $lines[$i])) {
|
|
if ($i + 1 < $line_count) {
|
|
if ($i + 1 <= count($lines) - 1) { $lines[$i + 1] = $lines[$i] . "\n" . $lines[$i + 1]; }
|
|
} else {
|
|
$rows[] = $lines[$i];
|
|
}
|
|
} else {
|
|
$rows[] = $lines[$i];
|
|
}
|
|
}
|
|
unset($tid, $tpadl, $tpadr, $tlang);
|
|
$tclass = '';
|
|
$tstyle = '';
|
|
$talign = '';
|
|
if (preg_match('/^table[^\.]/', $rows[0])) {
|
|
$row = $rows[0];
|
|
$row = preg_replace('/^table/', '', $row, 1);
|
|
$params = 1;
|
|
// process row parameters until none are left
|
|
while ($params) {
|
|
if (preg_match('{^(' . $this->tblalignre . ')}', $row, $matches)) {
|
|
// found row alignment
|
|
$talign .= $matches[1];
|
|
if ($matches[1]) { $row = substr($row, strlen($matches[1])); }
|
|
if ($matches[1]) { continue; }
|
|
}
|
|
if (preg_match('{^(' . $this->clstypadre . ')}x', $row, $matches)) {
|
|
// found a class/id/style/padding indicator
|
|
$clsty = $matches[1];
|
|
if ($clsty) { $row = substr($row, strlen($clsty)); }
|
|
if (preg_match('/{([^}]+)}/', $clsty, $matches)) {
|
|
$tstyle = $matches[1];
|
|
$clsty = preg_replace('/{([^}]+)}/', '', $clsty, 1);
|
|
if ($tstyle) { continue; }
|
|
}
|
|
if (preg_match('/\(([A-Za-z0-9_\- ]+?)(?:#(.+?))?\)/', $clsty, $matches) ||
|
|
preg_match('/\(([A-Za-z0-9_\- ]+?)?(?:#(.+?))\)/', $clsty, $matches)) {
|
|
if ($matches[1] || $matches[2]) {
|
|
$tclass = $matches[1];
|
|
$tid = $matches[2];
|
|
continue;
|
|
}
|
|
}
|
|
if (preg_match('/(\(+)/', $clsty, $matches)) { $tpadl = strlen($matches[1]); }
|
|
if (preg_match('/(\)+)/', $clsty, $matches)) { $tpadr = strlen($matches[1]); }
|
|
if (preg_match('/\[(.+?)\]/', $clsty, $matches)) { $tlang = $matches[1]; }
|
|
if ($clsty) { continue; }
|
|
}
|
|
$params = 0;
|
|
}
|
|
$row = preg_replace('/\.\s+/', '', $row, 1);
|
|
$rows[0] = $row;
|
|
}
|
|
$out = '';
|
|
$cols = preg_split('/\|/', $rows[0] . ' ');
|
|
unset($colaligns, $rowspans);
|
|
foreach ($rows as $row) {
|
|
$cols = preg_split('/\|/', $row . ' ');
|
|
$colcount = count($cols) - 1;
|
|
array_pop($cols);
|
|
$colspan = 0;
|
|
$row_out = '';
|
|
unset($rowclass, $rowid, $rowalign, $rowstyle, $rowheader);
|
|
if (!$cols[0]) { $cols[0] = ''; }
|
|
if (preg_match('/_/', $cols[0])) {
|
|
$cols[0] = preg_replace('/_/', '', $cols[0]);
|
|
$rowheader = 1;
|
|
}
|
|
if (preg_match('/{([^}]+)}/', $cols[0], $matches)) {
|
|
$rowstyle = $matches[1];
|
|
$cols[0] = preg_replace('/{[^}]+}/', '', $cols[0]);
|
|
}
|
|
if (preg_match('/\(([^\#]+?)?(#(.+))?\)/', $cols[0], $matches)) {
|
|
$rowclass = $matches[1];
|
|
$rowid = $matches[3];
|
|
$cols[0] = preg_replace('/\([^\)]+\)/', '', $cols[0]);
|
|
}
|
|
if (preg_match('{(' . $this->alignre . ')}', $cols[0], $matches)) { $rowalign = $matches[1]; }
|
|
for ($c = $colcount - 1; $c > 0; $c--) {
|
|
if ($rowspans[$c]) {
|
|
$rowspans[$c]--;
|
|
if ($rowspans[$c] > 1) { continue; }
|
|
}
|
|
unset($colclass, $colid, $header, $colparams, $colpadl, $colpadr, $collang);
|
|
$colstyle = '';
|
|
$colalign = $colaligns[$c];
|
|
$col = array_pop($cols);
|
|
$col = ($col ? $col : '');
|
|
$attrs = '';
|
|
if (preg_match('{^(((_|[/\\\\]\d+|' . $this->alignre . '|' . $this->clstypadre . ')+)\.\ )}x', $col, $matches)) {
|
|
$colparams = $matches[2];
|
|
$col = substr($col, strlen($matches[1]));
|
|
$params = 1;
|
|
// keep processing column parameters until there
|
|
// are none left...
|
|
while ($params) {
|
|
if (preg_match('{^(_|' . $this->alignre . ')(.*)$}', $colparams, $matches)) {
|
|
// found alignment or heading indicator
|
|
$attrs .= $matches[1];
|
|
if ($matches[1]) { $colparams = $matches[2]; }
|
|
if ($matches[1]) { continue; }
|
|
}
|
|
if (preg_match('{^(' . $this->clstypadre . ')(.*)$}x', $colparams, $matches)) {
|
|
// found a class/id/style/padding marker
|
|
$clsty = $matches[1];
|
|
if ($clsty) { $colparams = $matches[2]; }
|
|
if (preg_match('/{([^}]+)}/', $clsty, $matches)) {
|
|
$colstyle = $matches[1];
|
|
$clsty = preg_replace('/{([^}]+)}/', '', $clsty, 1);
|
|
}
|
|
if (preg_match('/\(([A-Za-z0-9_\- ]+?)(?:#(.+?))?\)/', $clsty, $matches) ||
|
|
preg_match('/\(([A-Za-z0-9_\- ]+?)?(?:#(.+?))\)/', $clsty, $matches)) {
|
|
if ($matches[1] || $matches[2]) {
|
|
$colclass = $matches[1];
|
|
$colid = $matches[2];
|
|
if ($colclass) {
|
|
$clsty = preg_replace('/\([A-Za-z0-9_\- ]+?(#.*?)?\)/', '', $clsty);
|
|
} elseif ($colid) {
|
|
$clsty = preg_replace('/\(#.+?\)/', '', $clsty);
|
|
}
|
|
}
|
|
}
|
|
if (preg_match('/(\(+)/', $clsty, $matches)) {
|
|
$colpadl = strlen($matches[1]);
|
|
$clsty = preg_replace('/\(+/', '', $clsty, 1);
|
|
}
|
|
if (preg_match('/(\)+)/', $clsty, $matches)) {
|
|
$colpadr = strlen($matches[1]);
|
|
$clsty = preg_replace('/\)+/', '', $clsty, 1);
|
|
}
|
|
if (preg_match('/\[(.+?)\]/', $clsty, $matches)) {
|
|
$collang = $matches[1];
|
|
$clsty = preg_replace('/\[.+?\]/', '', $clsty, 1);
|
|
}
|
|
if ($clsty) { continue; }
|
|
}
|
|
if (preg_match('/^\\\\(\d+)/', $colparams, $matches)) {
|
|
$colspan = $matches[1];
|
|
$colparams = substr($colparams, strlen($matches[1]) + 1);
|
|
if ($matches[1]) { continue; }
|
|
}
|
|
if (preg_match('/\/(\d+)/', $colparams, $matches)) {
|
|
if ($matches[1]) { $rowspans[$c] = $matches[1]; }
|
|
$colparams = substr($colparams, strlen($matches[1]) + 1);
|
|
if ($matches[1]) { continue; }
|
|
}
|
|
$params = 0;
|
|
}
|
|
}
|
|
if (strlen($attrs)) {
|
|
if (preg_match('/_/', $attrs)) { $header = 1; }
|
|
if (preg_match('{(' . $this->alignre . ')}', $attrs, $matches) && strlen($matches[1])) { $colalign = ''; }
|
|
// determine column alignment
|
|
if (preg_match('/<>/', $attrs)) {
|
|
$colalign .= '<>';
|
|
} elseif (preg_match('/</', $attrs)) {
|
|
$colalign .= '<';
|
|
} elseif (preg_match('/=/', $attrs)) {
|
|
$colalign = '=';
|
|
} elseif (preg_match('/>/', $attrs)) {
|
|
$colalign = '>';
|
|
}
|
|
if (preg_match('/\^/', $attrs)) {
|
|
$colalign .= '^';
|
|
} elseif (preg_match('/~/', $attrs)) {
|
|
$colalign .= '~';
|
|
} elseif (preg_match('/-/', $attrs)) {
|
|
$colalign .= '-';
|
|
}
|
|
}
|
|
if ($rowheader) { $header = 1; }
|
|
if ($header) { $colaligns[$c] = $colalign; }
|
|
$col = preg_replace('/^ +/', '', $col, 1); $col = preg_replace('/ +$/', '', $col, 1);
|
|
if (strlen($col)) {
|
|
// create one cell tag
|
|
$rowspan = ($rowspans[$c] ? $rowspans[$c] : 0);
|
|
$col_out = '<' . ($header ? 'th' : 'td');
|
|
if ($colalign) {
|
|
// horizontal, vertical alignment
|
|
$halign = $this->_halign($colalign);
|
|
if ($halign) { $col_out .= " align=\"$halign\""; }
|
|
$valign = $this->_valign($colalign);
|
|
if ($valign) { $col_out .= " valign=\"$valign\""; }
|
|
}
|
|
// apply css attributes, row, column spans
|
|
if ($colpadl) { $colstyle .= ";padding-left:${colpadl}em"; }
|
|
if ($colpadr) { $colstyle .= ";padding-right:${colpadr}em"; }
|
|
if ($colclass) { $col_out .= " class=\"$colclass\""; }
|
|
if ($colid) { $col_out .= " id=\"$colid\""; }
|
|
if ($colstyle) { $colstyle = preg_replace('/^;/', '', $colstyle, 1); }
|
|
if ($colstyle) { $col_out .= " style=\"$colstyle\""; }
|
|
if ($collang) { $col_out .= " lang=\"$collang\""; }
|
|
if ($colspan > 1) { $col_out .= " colspan=\"$colspan\""; }
|
|
if ($rowspan > 1) { $col_out .= " rowspan=\"$rowspan\""; }
|
|
$col_out .= '>';
|
|
// if the content of this cell has newlines OR matches
|
|
// our paragraph block signature, process it as a full-blown
|
|
// textile document
|
|
if (preg_match('/\n\n/', $col) ||
|
|
preg_match('{^(?:' . $this->halignre . '|' . $this->clstypadre . '*)*
|
|
[\*\#]
|
|
(?:' . $this->clstypadre . '*|' . $this->halignre . ')*\ }x', $col)) {
|
|
$col_out .= $this->process($col);
|
|
} else {
|
|
$col_out .= $this->format_paragraph(array('text' => $col));
|
|
}
|
|
$col_out .= '</' . ($header ? 'th' : 'td') . '>';
|
|
$row_out = $col_out . $row_out;
|
|
if ($colspan) { $colspan = 0; }
|
|
} else {
|
|
if ($colspan == 0) { $colspan = 1; }
|
|
$colspan++;
|
|
}
|
|
}
|
|
if ($colspan > 1) {
|
|
// handle the spanned column if we came up short
|
|
$colspan--;
|
|
$row_out = "<td"
|
|
. ($colspan > 1 ? " colspan=\"$colspan\"" : '')
|
|
. "></td>$row_out";
|
|
}
|
|
|
|
// build one table row
|
|
$out .= "<tr";
|
|
if ($rowalign) {
|
|
$valign = $this->_valign($rowalign);
|
|
if ($valign) { $out .= " valign=\"$valign\""; }
|
|
}
|
|
if ($rowclass) { $out .= " class=\"$rowclass\""; }
|
|
if ($rowid) { $out .= " id=\"$rowid\""; }
|
|
if ($rowstyle) { $out .= " style=\"$rowstyle\""; }
|
|
$out .= ">$row_out</tr>";
|
|
}
|
|
|
|
// now, form the table tag itself
|
|
$table = '';
|
|
$table .= "<table";
|
|
if ($talign) {
|
|
if ($this->options['css_mode']) {
|
|
// horizontal alignment
|
|
$alignment = $this->_halign($talign);
|
|
if ($talign == '=') {
|
|
$tstyle .= ';margin-left:auto;margin-right:auto';
|
|
} else {
|
|
if ($alignment) { $tstyle .= ';float:' . $alignment; }
|
|
}
|
|
if ($alignment) { $tclass .= ' ' . $alignment; }
|
|
} else {
|
|
$alignment = $this->_halign($talign);
|
|
if ($alignment) { $table .= " align=\"$alignment\""; }
|
|
}
|
|
}
|
|
if ($tpadl) { $tstyle .= ";padding-left:${tpadl}em"; }
|
|
if ($tpadr) { $tstyle .= ";padding-right:${tpadr}em"; }
|
|
if ($tclass) { $tclass = preg_replace('/^ /', '', $tclass, 1); }
|
|
if ($tclass) { $table .= " class=\"$tclass\""; }
|
|
if ($tid) { $table .= " id=\"$tid\""; }
|
|
if ($tstyle) { $tstyle = preg_replace('/^;/', '', $tstyle, 1); }
|
|
if ($tstyle) { $table .= " style=\"$tstyle\""; }
|
|
if ($tlang) { $table .= " lang=\"$tlang\""; }
|
|
if ($tclass || $tid || $tstyle) { $table .= " cellspacing=\"0\""; }
|
|
$table .= ">$out</table>";
|
|
|
|
if (preg_match('|<tr></tr>|', $table)) {
|
|
// exception -- something isn't right so return fail case
|
|
return NULL;
|
|
}
|
|
|
|
return $table;
|
|
} // function format_table
|
|
|
|
/**
|
|
* The following attributes are allowed:
|
|
*
|
|
* <ul>
|
|
*
|
|
* <li><b>text</b>
|
|
*
|
|
* The text to be processed.</li>
|
|
*
|
|
* <li><b>filters</b>
|
|
*
|
|
* An array reference of filter names to run for the given text.</li>
|
|
*
|
|
* </ul>
|
|
*
|
|
* @param $args An @c array specifying the text and filters to
|
|
* apply.
|
|
*
|
|
* @return A @c string containing the filtered text.
|
|
*
|
|
* @private
|
|
*/
|
|
function apply_filters($args) {
|
|
$text = $args['text'];
|
|
if (!$text) { return ''; }
|
|
$list = $args['filters'];
|
|
$filters = $this->options['filters'];
|
|
if (!is_array($filters)) { return $text; }
|
|
|
|
$param = $this->filter_param();
|
|
foreach ($list as $filter) {
|
|
if (!isset($filters[$filter])) { continue; }
|
|
if (is_string($filters[$filter])) {
|
|
$text = (($f = create_function('$text, $param', $filters[$filter])) ? $f($text, $param) : $text);
|
|
}
|
|
}
|
|
return $text;
|
|
} // function apply_filters
|
|
|
|
// minor utility / formatting routines
|
|
|
|
var $Have_Entities = 1;
|
|
|
|
/**
|
|
* Encodes input @c $html string, escaping characters as needed
|
|
* to HTML entities. This relies on the @c htmlentities function
|
|
* for full effect. If unavailable, @c encode_html_basic is used
|
|
* as a fallback technique. If the "char_encoding" flag is
|
|
* set to false, @c encode_html_basic is used exclusively.
|
|
*
|
|
* @param $html A @c string specifying the HTML to be encoded.
|
|
* @param $can_double_encode If provided, a @c bool indicating
|
|
* whether or not ampersand characters should be
|
|
* unconditionally encoded.
|
|
*
|
|
* @return A @c string containing the encoded HTML.
|
|
*
|
|
* @private
|
|
*/
|
|
function encode_html($html, $can_double_encode = FALSE) {
|
|
if (!$html) { return ''; }
|
|
if ($this->Have_Entities && $this->options['char_encoding']) {
|
|
$html = htmlentities($html);
|
|
} else {
|
|
$html = $this->encode_html_basic($html, $can_double_encode);
|
|
}
|
|
return $html;
|
|
} // function encode_html
|
|
|
|
/**
|
|
* Decodes HTML entities in @c $html to their natural character
|
|
* equivalents.
|
|
*
|
|
* @param $html A @c string specifying the HTML to be decoded.
|
|
*
|
|
* @return A @c string containing the decode HTML
|
|
*
|
|
* @private
|
|
*/
|
|
function decode_html($html) {
|
|
$html = preg_replace('!"!', '"', $html);
|
|
$html = preg_replace('!&!', '&', $html);
|
|
$html = preg_replace('!<!', '<', $html);
|
|
$html = preg_replace('!>!', '>', $html);
|
|
return $html;
|
|
} // function decode_html
|
|
|
|
/**
|
|
* Encodes the input @c $html string for the following characters:
|
|
* \<, \>, & and ". If @c $can_double_encode is true, all
|
|
* ampersand characters are escaped even if they already were.
|
|
* If @c $can_double_encode is false, ampersands are only escaped
|
|
* when they aren't part of a HTML entity already.
|
|
*
|
|
* @param $html A @c string specifying the HTML to be encoded.
|
|
* @param $can_double_encode If provided, a @c bool indicating
|
|
* whether or not ampersand characters should be
|
|
* unconditionally encoded.
|
|
*
|
|
* @return A @c string containing the encoded HTML.
|
|
*
|
|
* @private
|
|
*/
|
|
function encode_html_basic($html, $can_double_encode = FALSE) {
|
|
if (!$html) { return ''; }
|
|
if (!preg_match('/[^\w\s]/', $html)) { return $html; }
|
|
if ($can_double_encode) {
|
|
$html = preg_replace('!&!', '&', $html);
|
|
} else {
|
|
// Encode any & not followed by something that looks like
|
|
// an entity, numeric or otherwise.
|
|
$html = preg_replace('/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w{1,8});)/', '&', $html);
|
|
}
|
|
$html = preg_replace('!"!', '"', $html);
|
|
$html = preg_replace('!<!', '<', $html);
|
|
$html = preg_replace('!>!', '>', $html);
|
|
return $html;
|
|
} // function encode_html_basic
|
|
|
|
/**
|
|
* Returns the size for the image identified in @c $file. This
|
|
* method relies upon the @c getimagesize function. If unavailable,
|
|
* @c image_size will return @c NULL. Otherwise, the expected return
|
|
* value is an array of the width and height (in that order), in
|
|
* pixels.
|
|
*
|
|
* @param $file A @c string specifying the path or URL for the image
|
|
* file.
|
|
*
|
|
* @return An @c array containing the width and height
|
|
* (respectively) of the image.
|
|
*
|
|
* @private
|
|
*/
|
|
function image_size($file) {
|
|
$Have_ImageSize = function_exists('getimagesize');
|
|
|
|
if ($Have_ImageSize) {
|
|
if (file_exists($file)) {
|
|
return @getimagesize($file);
|
|
} else {
|
|
if ($docroot = ($this->docroot() ? $this->docroot() : $_SERVER['DOCUMENT_ROOT'])) {
|
|
$fullpath = $docroot . preg_replace('|^/*(.*)$|', '/$1', $file);
|
|
if (file_exists($fullpath)) {
|
|
return @getimagesize($fullpath);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return @getimagesize($file);
|
|
} // function image_size
|
|
|
|
/**
|
|
* Encodes the query portion of a URL, escaping characters
|
|
* as necessary.
|
|
*
|
|
* @param $str A @c string specifying the URL to be encoded.
|
|
*
|
|
* @return A @c string containing the encoded URL.
|
|
*
|
|
* @private
|
|
*/
|
|
function encode_url($str) {
|
|
$str = preg_replace_callback('!([^A-Za-z0-9_\.\-\+\&=%;])!x',
|
|
$this->_cb('ord($m[1]) > 255 ? \'%u\' . sprintf("%04X", ord($m[1]))
|
|
: \'%\' . sprintf("%02X", ord($m[1]))'), $str);
|
|
return $str;
|
|
} // function encode_url
|
|
|
|
/**
|
|
* Encodes the email address in @c $addr for 'mailto:' links.
|
|
*
|
|
* @param $addr A @c string specifying the email address to encode.
|
|
*
|
|
* @return A @c string containing the encoded email address.
|
|
*
|
|
* @private
|
|
*/
|
|
function mail_encode($addr) {
|
|
// granted, this is simple, but it gives off warm fuzzies
|
|
$addr = preg_replace_callback('!([^\$])!x',
|
|
$this->_cb('ord($m[1]) > 255 ? \'%u\' . sprintf("%04X", ord($m[1]))
|
|
: \'%\' . sprintf("%02X", ord($m[1]))'), $addr);
|
|
return $addr;
|
|
} // function mail_encode
|
|
|
|
/**
|
|
* Processes string, formatting plain quotes into curly quotes.
|
|
*
|
|
* @param $str A @c string specifying the text to process.
|
|
*
|
|
* @return A @c string containing the processed text.
|
|
*
|
|
* @private
|
|
*/
|
|
function process_quotes($str) {
|
|
// stub routine for now. subclass and implement.
|
|
return $str;
|
|
} // function process_quotes
|
|
|
|
// a default set of macros for the {...} macro syntax
|
|
// just a handy way to write a lot of the international characters
|
|
// and some commonly used symbols
|
|
|
|
/**
|
|
* Returns an associative @c array of macros that are assigned to be processed by
|
|
* default within the @c format_inline method.
|
|
*
|
|
* @return An @c array containing the default macros.
|
|
*
|
|
* @private
|
|
*/
|
|
function default_macros() {
|
|
// <, >, " must be html entities in the macro text since
|
|
// those values are escaped by the time they are processed
|
|
// for macros.
|
|
return array(
|
|
'c|' => '¢', // CENT SIGN
|
|
'|c' => '¢', // CENT SIGN
|
|
'L-' => '£', // POUND SIGN
|
|
'-L' => '£', // POUND SIGN
|
|
'Y=' => '¥', // YEN SIGN
|
|
'=Y' => '¥', // YEN SIGN
|
|
'(c)' => '©', // COPYRIGHT SIGN
|
|
'<<' => '«', // LEFT-POINTING DOUBLE ANGLE QUOTATION
|
|
'(r)' => '®', // REGISTERED SIGN
|
|
'+_' => '±', // PLUS-MINUS SIGN
|
|
'_+' => '±', // PLUS-MINUS SIGN
|
|
'>>' => '»', // RIGHT-POINTING DOUBLE ANGLE QUOTATION
|
|
'1/4' => '¼', // VULGAR FRACTION ONE QUARTER
|
|
'1/2' => '½', // VULGAR FRACTION ONE HALF
|
|
'3/4' => '¾', // VULGAR FRACTION THREE QUARTERS
|
|
'A`' => 'À', // LATIN CAPITAL LETTER A WITH GRAVE
|
|
'`A' => 'À', // LATIN CAPITAL LETTER A WITH GRAVE
|
|
'A\'' => 'Á', // LATIN CAPITAL LETTER A WITH ACUTE
|
|
'\'A' => 'Á', // LATIN CAPITAL LETTER A WITH ACUTE
|
|
'A^' => 'Â', // LATIN CAPITAL LETTER A WITH CIRCUMFLEX
|
|
'^A' => 'Â', // LATIN CAPITAL LETTER A WITH CIRCUMFLEX
|
|
'A~' => 'Ã', // LATIN CAPITAL LETTER A WITH TILDE
|
|
'~A' => 'Ã', // LATIN CAPITAL LETTER A WITH TILDE
|
|
'A"' => 'Ä', // LATIN CAPITAL LETTER A WITH DIAERESIS
|
|
'"A' => 'Ä', // LATIN CAPITAL LETTER A WITH DIAERESIS
|
|
'Ao' => 'Å', // LATIN CAPITAL LETTER A WITH RING ABOVE
|
|
'oA' => 'Å', // LATIN CAPITAL LETTER A WITH RING ABOVE
|
|
'AE' => 'Æ', // LATIN CAPITAL LETTER AE
|
|
'C,' => 'Ç', // LATIN CAPITAL LETTER C WITH CEDILLA
|
|
',C' => 'Ç', // LATIN CAPITAL LETTER C WITH CEDILLA
|
|
'E`' => 'È', // LATIN CAPITAL LETTER E WITH GRAVE
|
|
'`E' => 'È', // LATIN CAPITAL LETTER E WITH GRAVE
|
|
'E\'' => 'É', // LATIN CAPITAL LETTER E WITH ACUTE
|
|
'\'E' => 'É', // LATIN CAPITAL LETTER E WITH ACUTE
|
|
'E^' => 'Ê', // LATIN CAPITAL LETTER E WITH CIRCUMFLEX
|
|
'^E' => 'Ê', // LATIN CAPITAL LETTER E WITH CIRCUMFLEX
|
|
'E"' => 'Ë', // LATIN CAPITAL LETTER E WITH DIAERESIS
|
|
'"E' => 'Ë', // LATIN CAPITAL LETTER E WITH DIAERESIS
|
|
'I`' => 'Ì', // LATIN CAPITAL LETTER I WITH GRAVE
|
|
'`I' => 'Ì', // LATIN CAPITAL LETTER I WITH GRAVE
|
|
'I\'' => 'Í', // LATIN CAPITAL LETTER I WITH ACUTE
|
|
'\'I' => 'Í', // LATIN CAPITAL LETTER I WITH ACUTE
|
|
'I^' => 'Î', // LATIN CAPITAL LETTER I WITH CIRCUMFLEX
|
|
'^I' => 'Î', // LATIN CAPITAL LETTER I WITH CIRCUMFLEX
|
|
'I"' => 'Ï', // LATIN CAPITAL LETTER I WITH DIAERESIS
|
|
'"I' => 'Ï', // LATIN CAPITAL LETTER I WITH DIAERESIS
|
|
'D-' => 'Ð', // LATIN CAPITAL LETTER ETH
|
|
'-D' => 'Ð', // LATIN CAPITAL LETTER ETH
|
|
'N~' => 'Ñ', // LATIN CAPITAL LETTER N WITH TILDE
|
|
'~N' => 'Ñ', // LATIN CAPITAL LETTER N WITH TILDE
|
|
'O`' => 'Ò', // LATIN CAPITAL LETTER O WITH GRAVE
|
|
'`O' => 'Ò', // LATIN CAPITAL LETTER O WITH GRAVE
|
|
'O\'' => 'Ó', // LATIN CAPITAL LETTER O WITH ACUTE
|
|
'\'O' => 'Ó', // LATIN CAPITAL LETTER O WITH ACUTE
|
|
'O^' => 'Ô', // LATIN CAPITAL LETTER O WITH CIRCUMFLEX
|
|
'^O' => 'Ô', // LATIN CAPITAL LETTER O WITH CIRCUMFLEX
|
|
'O~' => 'Õ', // LATIN CAPITAL LETTER O WITH TILDE
|
|
'~O' => 'Õ', // LATIN CAPITAL LETTER O WITH TILDE
|
|
'O"' => 'Ö', // LATIN CAPITAL LETTER O WITH DIAERESIS
|
|
'"O' => 'Ö', // LATIN CAPITAL LETTER O WITH DIAERESIS
|
|
'O/' => 'Ø', // LATIN CAPITAL LETTER O WITH STROKE
|
|
'/O' => 'Ø', // LATIN CAPITAL LETTER O WITH STROKE
|
|
'U`' => 'Ù', // LATIN CAPITAL LETTER U WITH GRAVE
|
|
'`U' => 'Ù', // LATIN CAPITAL LETTER U WITH GRAVE
|
|
'U\'' => 'Ú', // LATIN CAPITAL LETTER U WITH ACUTE
|
|
'\'U' => 'Ú', // LATIN CAPITAL LETTER U WITH ACUTE
|
|
'U^' => 'Û', // LATIN CAPITAL LETTER U WITH CIRCUMFLEX
|
|
'^U' => 'Û', // LATIN CAPITAL LETTER U WITH CIRCUMFLEX
|
|
'U"' => 'Ü', // LATIN CAPITAL LETTER U WITH DIAERESIS
|
|
'"U' => 'Ü', // LATIN CAPITAL LETTER U WITH DIAERESIS
|
|
'Y\'' => 'Ý', // LATIN CAPITAL LETTER Y WITH ACUTE
|
|
'\'Y' => 'Ý', // LATIN CAPITAL LETTER Y WITH ACUTE
|
|
'a`' => 'à', // LATIN SMALL LETTER A WITH GRAVE
|
|
'`a' => 'à', // LATIN SMALL LETTER A WITH GRAVE
|
|
'a\'' => 'á', // LATIN SMALL LETTER A WITH ACUTE
|
|
'\'a' => 'á', // LATIN SMALL LETTER A WITH ACUTE
|
|
'a^' => 'â', // LATIN SMALL LETTER A WITH CIRCUMFLEX
|
|
'^a' => 'â', // LATIN SMALL LETTER A WITH CIRCUMFLEX
|
|
'a~' => 'ã', // LATIN SMALL LETTER A WITH TILDE
|
|
'~a' => 'ã', // LATIN SMALL LETTER A WITH TILDE
|
|
'a"' => 'ä', // LATIN SMALL LETTER A WITH DIAERESIS
|
|
'"a' => 'ä', // LATIN SMALL LETTER A WITH DIAERESIS
|
|
'ao' => 'å', // LATIN SMALL LETTER A WITH RING ABOVE
|
|
'oa' => 'å', // LATIN SMALL LETTER A WITH RING ABOVE
|
|
'ae' => 'æ', // LATIN SMALL LETTER AE
|
|
'c,' => 'ç', // LATIN SMALL LETTER C WITH CEDILLA
|
|
',c' => 'ç', // LATIN SMALL LETTER C WITH CEDILLA
|
|
'e`' => 'è', // LATIN SMALL LETTER E WITH GRAVE
|
|
'`e' => 'è', // LATIN SMALL LETTER E WITH GRAVE
|
|
'e\'' => 'é', // LATIN SMALL LETTER E WITH ACUTE
|
|
'\'e' => 'é', // LATIN SMALL LETTER E WITH ACUTE
|
|
'e^' => 'ê', // LATIN SMALL LETTER E WITH CIRCUMFLEX
|
|
'^e' => 'ê', // LATIN SMALL LETTER E WITH CIRCUMFLEX
|
|
'e"' => 'ë', // LATIN SMALL LETTER E WITH DIAERESIS
|
|
'"e' => 'ë', // LATIN SMALL LETTER E WITH DIAERESIS
|
|
'i`' => 'ì', // LATIN SMALL LETTER I WITH GRAVE
|
|
'`i' => 'ì', // LATIN SMALL LETTER I WITH GRAVE
|
|
'i\'' => 'í', // LATIN SMALL LETTER I WITH ACUTE
|
|
'\'i' => 'í', // LATIN SMALL LETTER I WITH ACUTE
|
|
'i^' => 'î', // LATIN SMALL LETTER I WITH CIRCUMFLEX
|
|
'^i' => 'î', // LATIN SMALL LETTER I WITH CIRCUMFLEX
|
|
'i"' => 'ï', // LATIN SMALL LETTER I WITH DIAERESIS
|
|
'"i' => 'ï', // LATIN SMALL LETTER I WITH DIAERESIS
|
|
'n~' => 'ñ', // LATIN SMALL LETTER N WITH TILDE
|
|
'~n' => 'ñ', // LATIN SMALL LETTER N WITH TILDE
|
|
'o`' => 'ò', // LATIN SMALL LETTER O WITH GRAVE
|
|
'`o' => 'ò', // LATIN SMALL LETTER O WITH GRAVE
|
|
'o\'' => 'ó', // LATIN SMALL LETTER O WITH ACUTE
|
|
'\'o' => 'ó', // LATIN SMALL LETTER O WITH ACUTE
|
|
'o^' => 'ô', // LATIN SMALL LETTER O WITH CIRCUMFLEX
|
|
'^o' => 'ô', // LATIN SMALL LETTER O WITH CIRCUMFLEX
|
|
'o~' => 'õ', // LATIN SMALL LETTER O WITH TILDE
|
|
'~o' => 'õ', // LATIN SMALL LETTER O WITH TILDE
|
|
'o"' => 'ö', // LATIN SMALL LETTER O WITH DIAERESIS
|
|
'"o' => 'ö', // LATIN SMALL LETTER O WITH DIAERESIS
|
|
':-' => '÷', // DIVISION SIGN
|
|
'-:' => '÷', // DIVISION SIGN
|
|
'o/' => 'ø', // LATIN SMALL LETTER O WITH STROKE
|
|
'/o' => 'ø', // LATIN SMALL LETTER O WITH STROKE
|
|
'u`' => 'ù', // LATIN SMALL LETTER U WITH GRAVE
|
|
'`u' => 'ù', // LATIN SMALL LETTER U WITH GRAVE
|
|
'u\'' => 'ú', // LATIN SMALL LETTER U WITH ACUTE
|
|
'\'u' => 'ú', // LATIN SMALL LETTER U WITH ACUTE
|
|
'u^' => 'û', // LATIN SMALL LETTER U WITH CIRCUMFLEX
|
|
'^u' => 'û', // LATIN SMALL LETTER U WITH CIRCUMFLEX
|
|
'u"' => 'ü', // LATIN SMALL LETTER U WITH DIAERESIS
|
|
'"u' => 'ü', // LATIN SMALL LETTER U WITH DIAERESIS
|
|
'y\'' => 'ý', // LATIN SMALL LETTER Y WITH ACUTE
|
|
'\'y' => 'ý', // LATIN SMALL LETTER Y WITH ACUTE
|
|
'y"' => 'ÿ', // LATIN SMALL LETTER Y WITH DIAERESIS
|
|
'"y' => 'ÿ', // LATIN SMALL LETTER Y WITH DIAERESIS
|
|
'OE' => 'Œ', // LATIN CAPITAL LIGATURE OE
|
|
'oe' => 'œ', // LATIN SMALL LIGATURE OE
|
|
'*' => '•', // BULLET
|
|
'Fr' => '₣', // FRENCH FRANC SIGN
|
|
'L=' => '₤', // LIRA SIGN
|
|
'=L' => '₤', // LIRA SIGN
|
|
'Rs' => '₨', // RUPEE SIGN
|
|
'C=' => '€', // EURO SIGN
|
|
'=C' => '€', // EURO SIGN
|
|
'tm' => '™', // TRADE MARK SIGN
|
|
'<-' => '←', // LEFTWARDS ARROW
|
|
'->' => '→', // RIGHTWARDS ARROW
|
|
'<=' => '⇐', // LEFTWARDS DOUBLE ARROW
|
|
'=>' => '⇒', // RIGHTWARDS DOUBLE ARROW
|
|
'=/' => '≠', // NOT EQUAL TO
|
|
'/=' => '≠', // NOT EQUAL TO
|
|
'<_' => '≤', // LESS-THAN OR EQUAL TO
|
|
'_<' => '≤', // LESS-THAN OR EQUAL TO
|
|
'>_' => '≥', // GREATER-THAN OR EQUAL TO
|
|
'_>' => '≥', // GREATER-THAN OR EQUAL TO
|
|
':(' => '☹', // WHITE FROWNING FACE
|
|
':)' => '☺', // WHITE SMILING FACE
|
|
'spade' => '♠', // BLACK SPADE SUIT
|
|
'club' => '♣', // BLACK CLUB SUIT
|
|
'heart' => '♥', // BLACK HEART SUIT
|
|
'diamond' => '♦', // BLACK DIAMOND SUIT
|
|
);
|
|
} // function default_macros
|
|
|
|
// "private", internal routines
|
|
|
|
/**
|
|
* Sets the default CSS names for CSS controlled markup. This
|
|
* is an internal function that should not be called directly.
|
|
*
|
|
* @private
|
|
*/
|
|
function _css_defaults() {
|
|
$css_defaults = array(
|
|
'class_align_right' => 'right',
|
|
'class_align_left' => 'left',
|
|
'class_align_center' => 'center',
|
|
'class_align_top' => 'top',
|
|
'class_align_bottom' => 'bottom',
|
|
'class_align_middle' => 'middle',
|
|
'class_align_justify' => 'justify',
|
|
'class_caps' => 'caps',
|
|
'class_footnote' => 'footnote',
|
|
'id_footnote_prefix' => 'fn',
|
|
);
|
|
$this->css($css_defaults);
|
|
} // function _css_defaults
|
|
|
|
/**
|
|
* Returns the alignment keyword depending on the symbol passed.
|
|
*
|
|
* <ul>
|
|
*
|
|
* <li><b><code>\<\></code></b>
|
|
*
|
|
* becomes 'justify'</li>
|
|
*
|
|
* <li><b><code>\<</code></b>
|
|
*
|
|
* becomes 'left'</li>
|
|
*
|
|
* <li><b><code>\></code></b>
|
|
*
|
|
* becomes 'right'</li>
|
|
*
|
|
* <li><b><code>=</code></b>
|
|
*
|
|
* becomes 'center'</li>
|
|
*
|
|
* </ul>
|
|
*
|
|
* @param $align A @c string specifying the alignment code.
|
|
*
|
|
* @return A @c string containing the alignment text.
|
|
*
|
|
* @private
|
|
*/
|
|
function _halign($align) {
|
|
if (preg_match('/<>/', $align)) {
|
|
return 'justify';
|
|
} elseif (preg_match('/</', $align)) {
|
|
return 'left';
|
|
} elseif (preg_match('/>/', $align)) {
|
|
return 'right';
|
|
} elseif (preg_match('/=/', $align)) {
|
|
return 'center';
|
|
}
|
|
return '';
|
|
} // function _halign
|
|
|
|
/**
|
|
* Returns the alignment keyword depending on the symbol passed.
|
|
*
|
|
* <ul>
|
|
*
|
|
* <li><b><code>^</code></b>
|
|
*
|
|
* becomes 'top'</li>
|
|
*
|
|
* <li><b><code>~</code></b>
|
|
*
|
|
* becomes 'bottom'</li>
|
|
*
|
|
* <li><b><code>-</code></b>
|
|
*
|
|
* becomes 'middle'</li>
|
|
*
|
|
* </ul>
|
|
*
|
|
* @param $align A @c string specifying the alignment code.
|
|
*
|
|
* @return A @c string containing the alignment text.
|
|
*
|
|
* @private
|
|
*/
|
|
function _valign($align) {
|
|
if (preg_match('/\^/', $align)) {
|
|
return 'top';
|
|
} elseif (preg_match('/~/', $align)) {
|
|
return 'bottom';
|
|
} elseif (preg_match('/-/', $align)) {
|
|
return 'middle';
|
|
}
|
|
return '';
|
|
} // function _valign
|
|
|
|
/**
|
|
* Returns the alignment keyword depending on the symbol passed.
|
|
* The following alignment symbols are recognized, and given
|
|
* preference in the order listed:
|
|
*
|
|
* <ul>
|
|
*
|
|
* <li><b><code>^</code></b>
|
|
*
|
|
* becomes 'top'</li>
|
|
*
|
|
* <li><b><code>~</code></b>
|
|
*
|
|
* becomes 'bottom'</li>
|
|
*
|
|
* <li><b><code>-</code></b>
|
|
*
|
|
* becomes 'middle'</li>
|
|
*
|
|
* <li><b><code>\<</code></b>
|
|
*
|
|
* becomes 'left'</li>
|
|
*
|
|
* <li><b><code>\></code></b>
|
|
*
|
|
* becomes 'right'</li>
|
|
*
|
|
* </ul>
|
|
*
|
|
* @param $align A @c string containing the alignment code.
|
|
*
|
|
* @return A @c string containing the alignment text.
|
|
*
|
|
* @private
|
|
*/
|
|
function _imgalign($align) {
|
|
$align = preg_replace('/(<>|=)/', '', $align);
|
|
return ($this->_valign($align) ? $this->_valign($align) : $this->_halign($align));
|
|
} // function _imgalign
|
|
|
|
/**
|
|
* This utility routine will take 'border' characters off of
|
|
* the given @c $pre and @c $post strings if they match one of these
|
|
* conditions:
|
|
* <pre>
|
|
* $pre starts with '[', $post ends with ']'
|
|
* $pre starts with '{', $post ends with '}'
|
|
* </pre>
|
|
* If neither condition is met, then the @c $pre and @c $post
|
|
* values are left untouched.
|
|
*
|
|
* @param $pre A @c string specifying the prefix.
|
|
* @param $post A @c string specifying the postfix.
|
|
*
|
|
* @private
|
|
*/
|
|
function _strip_borders(&$pre, &$post) {
|
|
if ($post && $pre && preg_match('/[{[]/', ($open = substr($pre, 0, 1)))) {
|
|
$close = substr($post, 0, 1);
|
|
if ((($open == '{') && ($close == '}')) ||
|
|
(($open == '[') && ($close == ']'))) {
|
|
$pre = substr($pre, 1);
|
|
$post = substr($post, 1);
|
|
} else {
|
|
if (!preg_match('/[}\]]/', $close)) { $close = substr($post, -1, 1); }
|
|
if ((($open == '{') && ($close == '}')) ||
|
|
(($open == '[') && ($close == ']'))) {
|
|
$pre = substr($pre, 1);
|
|
$post = substr($post, 0, strlen($post) - 1);
|
|
}
|
|
}
|
|
}
|
|
} // function _strip_borders
|
|
|
|
/**
|
|
* An internal routine that takes a string and appends it to an array.
|
|
* It returns a marker that is used later to restore the preserved
|
|
* string.
|
|
*
|
|
* @param $array The @c array in which to store the replacement
|
|
* text.
|
|
* @param $str A @c string specifying the replacement text.
|
|
*
|
|
* @return A @c string containing a temporary marker for the
|
|
* replacement.
|
|
*
|
|
* @private
|
|
*/
|
|
function _repl(&$array, $str) {
|
|
$array[] = $str;
|
|
return '<textile#' . count($array) . '>';
|
|
} // function _repl
|
|
|
|
/**
|
|
* An internal routine responsible for breaking up a string into
|
|
* individual tag and plaintext elements.
|
|
*
|
|
* @param $str A @c string specifying the text to tokenize.
|
|
*
|
|
* @return An @c array containing the tag and text tokens.
|
|
*
|
|
* @private
|
|
*/
|
|
function _tokenize($str) {
|
|
$pos = 0;
|
|
$len = strlen($str);
|
|
unset($tokens);
|
|
|
|
$depth = 6;
|
|
$nested_tags = substr(str_repeat('(?:</?[A-Za-z0-9:]+ \s? (?:[^<>]|', $depth), 0, -1)
|
|
. str_repeat(')*>)', $depth);
|
|
$match = '(?s: <! ( -- .*? -- \s* )+ > )| # comment
|
|
(?s: <\? .*? \?> )| # processing instruction
|
|
(?s: <% .*? %> )| # ASP-like
|
|
(?:' . $nested_tags . ')|
|
|
(?:' . $this->codere . ')'; // nested tags
|
|
|
|
while (preg_match('{(' . $match . ')}x', substr($str, $pos), $matches, PREG_OFFSET_CAPTURE)) {
|
|
$whole_tag = $matches[1][0];
|
|
$sec_start = $pos + $matches[1][1] + strlen($whole_tag);
|
|
$tag_start = $sec_start - strlen($whole_tag);
|
|
if ($pos < $tag_start) {
|
|
$tokens[] = array('text', substr($str, $pos, $tag_start - $pos));
|
|
}
|
|
if (preg_match('/^[[{]?@/', $whole_tag)) {
|
|
$tokens[] = array('text', $whole_tag);
|
|
} else {
|
|
// this clever hack allows us to preserve \n within tags.
|
|
// this is restored at the end of the format_paragraph method
|
|
//$whole_tag = preg_replace('/\n/', "\r", $whole_tag);
|
|
$whole_tag = preg_replace('/\n/', "\001", $whole_tag);
|
|
$tokens[] = array('tag', $whole_tag);
|
|
}
|
|
$pos = $sec_start;
|
|
}
|
|
if ($pos < $len) { $tokens[] = array('text', substr($str, $pos, $len - $pos)); }
|
|
return $tokens;
|
|
} // function _tokenize
|
|
|
|
/**
|
|
* Returns the version of this release of Textile.php. *JHR*
|
|
*
|
|
* @return An @c array with keys 'text' and 'build' containing the
|
|
* text version and build ID of this release, respectively.
|
|
*
|
|
* @static
|
|
*/
|
|
function version() {
|
|
/* Why text and an ID? Well, the text is easier for the user to
|
|
* read and understand while the build ID, being a number (a date
|
|
* with a serial, specifically), is easier for the developer to
|
|
* use to determine newer/older versions for upgrade and
|
|
* installation purposes.
|
|
*/
|
|
return array("text" => "2.0.8", "build" => 2005032100);
|
|
} // function version
|
|
|
|
/**
|
|
* Creates a custom callback function from the provided PHP
|
|
* code. The result is used as the callback in
|
|
* @c preg_replace_callback calls. *JHR*
|
|
*
|
|
* @param $function A @c string specifying the PHP code for the
|
|
* function body.
|
|
*
|
|
* @return A @c function to be used for the callback.
|
|
*
|
|
* @private
|
|
*/
|
|
function _cb($function) {
|
|
$current =& Textile::_current_store($this);
|
|
return create_function('$m', '$me =& Textile::_current(); return ' . $function . ';');
|
|
} // function _cb
|
|
|
|
/**
|
|
* Stores a static variable for the Textile class. This helper
|
|
* function is used by @c _current to simulate a static
|
|
* class variable in PHP. *JHR*
|
|
*
|
|
* @param $new If a non-@c NULL object reference, the Textile object
|
|
* to be set as the current object.
|
|
*
|
|
* @return The @c array containing a reference to the current
|
|
* Textile object at index 0. An array is used because PHP
|
|
* does not allow static variables to be references.
|
|
*
|
|
* @static
|
|
* @private
|
|
*/
|
|
/* static */ function &_current_store(&$new) {
|
|
static $current = array();
|
|
|
|
if ($new != NULL) {
|
|
$current = array(&$new);
|
|
}
|
|
|
|
return $current;
|
|
} // function _current_store
|
|
|
|
/**
|
|
* Returns the "current" Textile object. This is used within
|
|
* anonymous callback functions which cannot have the scope of a
|
|
* specific object. *JHR*
|
|
*
|
|
* @return An @c object reference to the current Textile object.
|
|
*
|
|
* @static
|
|
* @private
|
|
*/
|
|
/* static */ function &_current() {
|
|
$current =& Textile::_current_store($null = NULL);
|
|
return $current[0];
|
|
} // function _current
|
|
} // class Textile
|
|
|
|
/**
|
|
* Brad Choate's mttextile Movable Type plugin adds some additional
|
|
* functionality to the Textile.pm Perl module. This includes optional
|
|
* "SmartyPants" processing of text to produce smart quotes, dashes,
|
|
* etc., code colorizing using Beautifier, and some special lookup
|
|
* links (imdb, google, dict, and amazon). The @c MTLikeTextile class
|
|
* is a subclass of @c Textile that provides an MT-like implementation
|
|
* of Textile to produce results similar to that of the mttextile
|
|
* plugin. Currently only the SmartyPants and special lookup links are
|
|
* implemented.
|
|
*
|
|
* Using the @c MTLikeTextile class is exactly the same as using @c
|
|
* Textile. Simply use <code>$textile = new MTLikeTextile;</code>
|
|
* instead of <code>$textile = new Textile;</code> to create a Textile
|
|
* object. This will enable the special lookup links. To enable
|
|
* SmartyPants processing, you must install the SmartyPants-PHP
|
|
* implementation available at
|
|
* <a
|
|
* href="http://monauraljerk.org/smartypants-php/">http://monauraljerk.org/smartypants-php/</a>
|
|
* and include the
|
|
* SmartyPants-PHP.inc file.
|
|
*
|
|
* <pre><code>
|
|
* include_once("Textile.php");
|
|
* include_once("SmartyPants-PHP.inc");
|
|
* $text = \<\<\<EOT
|
|
* h1. Heading
|
|
*
|
|
* A _simple_ demonstration of Textile markup.
|
|
*
|
|
* * One
|
|
* * Two
|
|
* * Three
|
|
*
|
|
* "More information":http://www.textism.com/tools/textile is available.
|
|
* EOT;
|
|
*
|
|
* $textile = new MTLikeTextile;
|
|
* $html = $textile->process($text);
|
|
* print $html;
|
|
* </code></pre>
|
|
*
|
|
* @brief A Textile implementation providing additional
|
|
* Movable-Type-like formatting to produce results similar to
|
|
* the mttextile plugin.
|
|
*
|
|
* @author Jim Riggs \<textile at jimandlisa dot com\>
|
|
*/
|
|
class MTLikeTextile extends Textile {
|
|
/**
|
|
* Instantiates a new MTLikeTextile object. Optional options
|
|
* can be passed to initialize the object. Attributes for the
|
|
* options key are the same as the get/set method names
|
|
* documented here.
|
|
*
|
|
* @param $options The @c array specifying the options to use for
|
|
* this object.
|
|
*
|
|
* @public
|
|
*/
|
|
function MTLikeTextile($options = array()) {
|
|
parent::Textile($options);
|
|
} // function MTLikeTextile
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
function process_quotes($str) {
|
|
if (!$this->options['do_quotes'] || !function_exists('SmartyPants')) {
|
|
return $str;
|
|
}
|
|
|
|
return SmartyPants($str, $this->options['smarty_mode']);
|
|
} // function process_quotes
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
function format_url($args) {
|
|
$url = ($args['url'] ? $args['url'] : '');
|
|
|
|
if (preg_match('/^(imdb|google|dict|amazon)(:(.+))?$/x', $url, $matches)) {
|
|
$term = $matches[3];
|
|
$term = ($term ? $term : strip_tags($args['linktext']));
|
|
|
|
switch ($matches[1]) {
|
|
case 'imdb':
|
|
$args['url'] = 'http://www.imdb.com/Find?for=' . $term;
|
|
break;
|
|
|
|
case 'google':
|
|
$args['url'] = 'http://www.google.com/search?q=' . $term;
|
|
break;
|
|
|
|
case 'dict':
|
|
$args['url'] = 'http://www.dictionary.com/search?q=' . $term;
|
|
break;
|
|
|
|
case 'amazon':
|
|
$args['url'] = 'http://www.amazon.com/exec/obidos/external-search?index=blended&keyword=' . $term;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return parent::format_url($args);
|
|
} // function format_url
|
|
} // class MTLikeTextile
|
|
|
|
/**
|
|
* @mainpage
|
|
* Textile - A Humane Web Text Generator.
|
|
*
|
|
* @section synopsis SYNOPSIS
|
|
*
|
|
* <pre><code>
|
|
* include_once("Textile.php");
|
|
* $text = \<\<\<EOT
|
|
* h1. Heading
|
|
*
|
|
* A _simple_ demonstration of Textile markup.
|
|
*
|
|
* * One
|
|
* * Two
|
|
* * Three
|
|
*
|
|
* "More information":http://www.textism.com/tools/textile is available.
|
|
* EOT;
|
|
*
|
|
* $textile = new Textile;
|
|
* $html = $textile->process($text);
|
|
* print $html;
|
|
* </code></pre>
|
|
*
|
|
* @section abstract ABSTRACT
|
|
*
|
|
* Textile.php is a PHP-based implementation of Dean Allen's Textile
|
|
* syntax. Textile is shorthand for doing common formatting tasks.
|
|
*
|
|
* @section syntax SYNTAX
|
|
*
|
|
* Textile processes text in units of blocks and lines.
|
|
* A block might also be considered a paragraph, since blocks
|
|
* are separated from one another by a blank line. Blocks
|
|
* can begin with a signature that helps identify the rest
|
|
* of the block content. Block signatures include:
|
|
*
|
|
* <ul>
|
|
*
|
|
* <li><b>p</b>
|
|
*
|
|
* A paragraph block. This is the default signature if no
|
|
* signature is explicitly given. Paragraphs are formatted
|
|
* with all the inline rules (see inline formatting) and
|
|
* each line receives the appropriate markup rules for
|
|
* the flavor of HTML in use. For example, newlines for XHTML
|
|
* content receive a \<br /\> tag at the end of the line
|
|
* (with the exception of the last line in the paragraph).
|
|
* Paragraph blocks are enclosed in a \<p\> tag.</li>
|
|
*
|
|
* <li><b>pre</b>
|
|
*
|
|
* A pre-formatted block of text. Textile will not add any
|
|
* HTML tags for individual lines. Whitespace is also preserved.
|
|
*
|
|
* Note that within a "pre" block, \< and \> are
|
|
* translated into HTML entities automatically.</li>
|
|
*
|
|
* <li><b>bc</b>
|
|
*
|
|
* A "bc" signature is short for "block code", which implies
|
|
* a preformatted section like the 'pre' block, but it also
|
|
* gets a \<code\> tag (or for XHTML 2, a \<blockcode\>
|
|
* tag is used instead).
|
|
*
|
|
* Note that within a "bc" block, \< and \> are
|
|
* translated into HTML entities automatically.</li>
|
|
*
|
|
* <li><b>table</b>
|
|
*
|
|
* For composing HTML tables. See the "TABLES" section for more
|
|
* information.</li>
|
|
*
|
|
* <li><b>bq</b>
|
|
*
|
|
* A "bq" signature is short for "block quote". Paragraph text
|
|
* formatting is applied to these blocks and they are enclosed
|
|
* in a \<blockquote\> tag as well as \<p\> tags
|
|
* within.</li>
|
|
*
|
|
* <li><b>h1, h2, h3, h4, h5, h6</b>
|
|
*
|
|
* Headline signatures that produce \<h1\>, etc. tags.
|
|
* You can adjust the relative output of these using the
|
|
* head_offset attribute.</li>
|
|
*
|
|
* <li><b>clear</b>
|
|
*
|
|
* A 'clear' signature is simply used to indicate that the next
|
|
* block should emit a CSS style attribute that clears any
|
|
* floating elements. The default behavior is to clear "both",
|
|
* but you can use the left (\< or right \>) alignment
|
|
* characters to indicate which side to clear.</li>
|
|
*
|
|
* <li><b>dl</b>
|
|
*
|
|
* A "dl" signature is short for "definition list". See the
|
|
* "LISTS" section for more information.</li>
|
|
*
|
|
* <li><b>fn</b>
|
|
*
|
|
* A "fn" signature is short for "footnote". You add a number
|
|
* following the "fn" keyword to number the footnote. Footnotes
|
|
* are output as paragraph tags but are given a special CSS
|
|
* class name which can be used to style them as you see fit.</li>
|
|
*
|
|
* </ul>
|
|
*
|
|
* All signatures should end with a period and be followed
|
|
* with a space. Inbetween the signature and the period, you
|
|
* may use several parameters to further customize the block.
|
|
* These include:
|
|
*
|
|
* <ul>
|
|
*
|
|
* <li><b><code>{style rule}</code></b>
|
|
*
|
|
* A CSS style rule. Style rules can span multiple lines.</li>
|
|
*
|
|
* <li><b><code>[ll]</code></b>
|
|
*
|
|
* A language identifier (for a "lang" attribute).</li>
|
|
*
|
|
* <li><b><code>(class)</code> or <code>(#id)</code> or <code>(class#id)</code></b>
|
|
*
|
|
* For CSS class and id attributes.</li>
|
|
*
|
|
* <li><b><code>\></code>, <code>\<</code>, <code>=</code>, <code>\<\></code></b>
|
|
*
|
|
* Modifier characters for alignment. Right-justification, left-justification,
|
|
* centered, and full-justification.</li>
|
|
*
|
|
* <li><b><code>(</code> (one or more)</b>
|
|
*
|
|
* Adds padding on the left. 1em per "(" character is applied.
|
|
* When combined with the align-left or align-right modifier,
|
|
* it makes the block float.</li>
|
|
*
|
|
* <li><b><code>)</code> (one or more)</b>
|
|
*
|
|
* Adds padding on the right. 1em per ")" character is applied.
|
|
* When combined with the align-left or align-right modifier,
|
|
* it makes the block float.</li>
|
|
*
|
|
* <li><b><code>|filter|</code> or <code>|filter|filter|filter|</code></b>
|
|
*
|
|
* A filter may be invoked to further format the text for this
|
|
* signature. If one or more filters are identified, the text
|
|
* will be processed first using the filters and then by
|
|
* Textile's own block formatting rules.</li>
|
|
*
|
|
* </ul>
|
|
*
|
|
* @subsection extendedblocks Extended Blocks
|
|
*
|
|
* Normally, a block ends with the first blank line encountered.
|
|
* However, there are situations where you may want a block to continue
|
|
* for multiple paragraphs of text. To cause a given block signature
|
|
* to stay active, use two periods in your signature instead of one.
|
|
* This will tell Textile to keep processing using that signature
|
|
* until it hits the next signature is found.
|
|
*
|
|
* For example:
|
|
* <pre>
|
|
* bq.. This is paragraph one of a block quote.
|
|
*
|
|
* This is paragraph two of a block quote.
|
|
*
|
|
* p. Now we're back to a regular paragraph.
|
|
* </pre>
|
|
* You can apply this technique to any signature (although for
|
|
* some it doesn't make sense, like "h1" for example). This is
|
|
* especially useful for "bc" blocks where your code may
|
|
* have many blank lines scattered through it.
|
|
*
|
|
* @subsection escaping Escaping
|
|
*
|
|
* Sometimes you want Textile to just get out of the way and
|
|
* let you put some regular HTML markup in your document. You
|
|
* can disable Textile formatting for a given block using the '=='
|
|
* escape mechanism:
|
|
* <pre>
|
|
* p. Regular paragraph
|
|
*
|
|
* ==
|
|
* Escaped portion -- will not be formatted
|
|
* by Textile at all
|
|
* ==
|
|
*
|
|
* p. Back to normal.
|
|
* </pre>
|
|
* You can also use this technique within a Textile block,
|
|
* temporarily disabling the inline formatting functions:
|
|
* <pre>
|
|
* p. This is ==*a test*== of escaping.
|
|
* </pre>
|
|
* @subsection inlineformatting Inline Formatting
|
|
*
|
|
* Formatting within a block of text is covered by the "inline"
|
|
* formatting rules. These operators must be placed up against
|
|
* text/punctuation to be recognized. These include:
|
|
*
|
|
* <ul>
|
|
*
|
|
* <li><b><code>*strong*</code></b>
|
|
*
|
|
* Translates into \<strong\>strong\</strong\>.</li>
|
|
*
|
|
* <li><b>_emphasis_</b>
|
|
*
|
|
* Translates into \<em\>emphasis\</em\>.</li>
|
|
*
|
|
* <li><b><code>**bold**</code></b>
|
|
*
|
|
* Translates into \<b\>bold\</b\>.</li>
|
|
*
|
|
* <li><b><code>__italics__</code></b>
|
|
*
|
|
* Translates into \<i\>italics\</i\>.</li>
|
|
*
|
|
* <li><b><code>++bigger++</code></b>
|
|
*
|
|
* Translates into \<big\>bigger\</big\>.</li>
|
|
*
|
|
* <li><b><code>--smaller--</code></b>
|
|
*
|
|
* Translates into: \<small\>smaller\</small\>.</li>
|
|
*
|
|
* <li><b><code>-deleted text-</code></b>
|
|
*
|
|
* Translates into \<del\>deleted text\</del\>.</li>
|
|
*
|
|
* <li><b><code>+inserted text+</code></b>
|
|
*
|
|
* Translates into \<ins\>inserted text\</ins\>.</li>
|
|
*
|
|
* <li><b><code>^superscript^</code></b>
|
|
*
|
|
* Translates into \<sup\>superscript\</sup\>.</li>
|
|
*
|
|
* <li><b><code>~subscript~</code></b>
|
|
*
|
|
* Translates into \<sub\>subscript\</sub\>.</li>
|
|
*
|
|
* <li><b><code>\%span\%</code></b>
|
|
*
|
|
* Translates into \<span\>span\</span\>.</li>
|
|
*
|
|
* <li><b><code>\@code\@</code></b>
|
|
*
|
|
* Translates into \<code\>code\</code\>. Note
|
|
* that within a '\@...\@' section, \< and \> are
|
|
* translated into HTML entities automatically.</li>
|
|
*
|
|
* </ul>
|
|
*
|
|
* Inline formatting operators accept the following modifiers:
|
|
*
|
|
* <ul>
|
|
*
|
|
* <li><b><code>{style rule}</code></b>
|
|
*
|
|
* A CSS style rule.</li>
|
|
*
|
|
* <li><b><code>[ll]</code></b>
|
|
*
|
|
* A language identifier (for a "lang" attribute).</li>
|
|
*
|
|
* <li><b><code>(class)</code> or <code>(#id)</code> or <code>(class#id)</code></b>
|
|
*
|
|
* For CSS class and id attributes.</li>
|
|
*
|
|
* </ul>
|
|
*
|
|
* @subsubsection examples Examples
|
|
* <pre>
|
|
* Textile is *way* cool.
|
|
*
|
|
* Textile is *_way_* cool.
|
|
* </pre>
|
|
* Now this won't work, because the formatting
|
|
* characters need whitespace before and after
|
|
* to be properly recognized.
|
|
* <pre>
|
|
* Textile is way c*oo*l.
|
|
* </pre>
|
|
* However, you can supply braces or brackets to
|
|
* further clarify that you want to format, so
|
|
* this would work:
|
|
* <pre>
|
|
* Textile is way c[*oo*]l.
|
|
* </pre>
|
|
* @subsection footnotes Footnotes
|
|
*
|
|
* You can create footnotes like this:
|
|
* <pre>
|
|
* And then he went on a long trip[1].
|
|
* </pre>
|
|
* By specifying the brackets with a number inside, Textile will
|
|
* recognize that as a footnote marker. It will replace that with
|
|
* a construct like this:
|
|
* <pre>
|
|
* And then he went on a long
|
|
* trip<sup class="footnote"><a href="#fn1">1</a></sup>
|
|
* </pre>
|
|
* To supply the content of the footnote, place it at the end of your
|
|
* document using a "fn" block signature:
|
|
* <pre>
|
|
* fn1. And there was much rejoicing.
|
|
* </pre>
|
|
* Which creates a paragraph that looks like this:
|
|
* <pre>
|
|
* <p class="footnote" id="fn1"><sup>1</sup> And there was
|
|
* much rejoicing.</p>
|
|
* </pre>
|
|
* @subsection links Links
|
|
*
|
|
* Textile defines a shorthand for formatting hyperlinks.
|
|
* The format looks like this:
|
|
* <pre>
|
|
* "Text to display":http://example.com
|
|
* </pre>
|
|
* In addition to this, you can add 'title' text to your link:
|
|
* <pre>
|
|
* "Text to display (Title text)":http://example.com
|
|
* </pre>
|
|
* The URL portion of the link supports relative paths as well
|
|
* as other protocols like ftp, mailto, news, telnet, etc.
|
|
* <pre>
|
|
* "E-mail me please":mailto:someone\@example.com
|
|
* </pre>
|
|
* You can also use single quotes instead of double-quotes if
|
|
* you prefer. As with the inline formatting rules, a hyperlink
|
|
* must be surrounded by whitespace to be recognized (an
|
|
* exception to this is common punctuation which can reside
|
|
* at the end of the URL). If you have to place a URL next to
|
|
* some other text, use the bracket or brace trick to do that:
|
|
* <pre>
|
|
* You["gotta":http://example.com]seethis!
|
|
* </pre>
|
|
* Textile supports an alternate way to compose links. You can
|
|
* optionally create a lookup list of links and refer to them
|
|
* separately. To do this, place one or more links in a block
|
|
* of it's own (it can be anywhere within your document):
|
|
* <pre>
|
|
* [excom]http://example.com
|
|
* [exorg]http://example.org
|
|
* </pre>
|
|
* For a list like this, the text in the square brackets is
|
|
* used to uniquely identify the link given. To refer to that
|
|
* link, you would specify it like this:
|
|
* <pre>
|
|
* "Text to display":excom
|
|
* </pre>
|
|
* Once you've defined your link lookup table, you can use
|
|
* the identifiers any number of times.
|
|
*
|
|
* @subsection images Images
|
|
*
|
|
* Images are identified by the following pattern:
|
|
* <pre>
|
|
* !/path/to/image!
|
|
* </pre>
|
|
* Image attributes may also be specified:
|
|
* <pre>
|
|
* !/path/to/image 10x20!
|
|
* </pre>
|
|
* Which will render an image 10 pixels wide and 20 pixels high.
|
|
* Another way to indicate width and height:
|
|
* <pre>
|
|
* !/path/to/image 10w 20h!
|
|
* </pre>
|
|
* You may also redimension the image using a percentage.
|
|
* <pre>
|
|
* !/path/to/image 20%x40%!
|
|
* </pre>
|
|
* Which will render the image at 20% of it's regular width
|
|
* and 40% of it's regular height.
|
|
*
|
|
* Or specify one percentage to resize proprotionately:
|
|
* <pre>
|
|
* !/path/to/image 20%!
|
|
* </pre>
|
|
* Alt text can be given as well:
|
|
* <pre>
|
|
* !/path/to/image (Alt text)!
|
|
* </pre>
|
|
* The path of the image may refer to a locally hosted image or
|
|
* can be a full URL.
|
|
*
|
|
* You can also use the following modifiers after the opening '!'
|
|
* character:
|
|
*
|
|
* <ul>
|
|
*
|
|
* <li><b><code>\<</code></b>
|
|
*
|
|
* Align the image to the left (causes the image to float if
|
|
* CSS options are enabled).</li>
|
|
*
|
|
* <li><b><code>\></code></b>
|
|
*
|
|
* Align the image to the right (causes the image to float if
|
|
* CSS options are enabled).</li>
|
|
*
|
|
* <li><b><code>-</code> (dash)</b>
|
|
*
|
|
* Aligns the image to the middle.</li>
|
|
*
|
|
* <li><b><code>^</code></b>
|
|
*
|
|
* Aligns the image to the top.</li>
|
|
*
|
|
* <li><b><code>~</code> (tilde)</b>
|
|
*
|
|
* Aligns the image to the bottom.</li>
|
|
*
|
|
* <li><b><code>{style rule}</code></b>
|
|
*
|
|
* Applies a CSS style rule to the image.</li>
|
|
*
|
|
* <li><b><code>(class)</code> or <code>(#id)</code> or <code>(class#id)</code></b>
|
|
*
|
|
* Applies a CSS class and/or id to the image.</li>
|
|
*
|
|
* <li><b><code>(</code> (one or more)</b>
|
|
*
|
|
* Pads 1em on the left for each '(' character.</li>
|
|
*
|
|
* <li><b><code>)</code> (one or more)</b>
|
|
*
|
|
* Pads 1em on the right for each ')' character.</li>
|
|
*
|
|
* </ul>
|
|
*
|
|
* @subsection characterreplacements Character Replacements
|
|
*
|
|
* A few simple, common symbols are automatically replaced:
|
|
* <pre>
|
|
* (c)
|
|
* (r)
|
|
* (tm)
|
|
* </pre>
|
|
* In addition to these, there are a whole set of character
|
|
* macros that are defined by default. All macros are enclosed
|
|
* in curly braces. These include:
|
|
* <pre>
|
|
* {c|} or {|c} cent sign
|
|
* {L-} or {-L} pound sign
|
|
* {Y=} or {=Y} yen sign
|
|
* </pre>
|
|
* Many of these macros can be guessed. For example:
|
|
* <pre>
|
|
* {A'} or {'A}
|
|
* {a"} or {"a}
|
|
* {1/4}
|
|
* {*}
|
|
* {:)}
|
|
* {:(}
|
|
* </pre>
|
|
* @subsection lists Lists
|
|
*
|
|
* Textile also supports ordered and unordered lists.
|
|
* You simply place an asterisk or pound sign, followed
|
|
* with a space at the start of your lines.
|
|
*
|
|
* Simple lists:
|
|
* <pre>
|
|
* * one
|
|
* * two
|
|
* * three
|
|
* </pre>
|
|
* Multi-level lists:
|
|
* <pre>
|
|
* * one
|
|
* ** one A
|
|
* ** one B
|
|
* *** one B1
|
|
* * two
|
|
* ** two A
|
|
* ** two B
|
|
* * three
|
|
* </pre>
|
|
* Ordered lists:
|
|
* <pre>
|
|
* # one
|
|
* # two
|
|
* # three
|
|
* </pre>
|
|
* Styling lists:
|
|
* <pre>
|
|
* (class#id)* one
|
|
* * two
|
|
* * three
|
|
* </pre>
|
|
* The above sets the class and id attributes for the \<ul\>
|
|
* tag.
|
|
* <pre>
|
|
* *(class#id) one
|
|
* * two
|
|
* * three
|
|
* </pre>
|
|
* The above sets the class and id attributes for the first \<li\>
|
|
* tag.
|
|
*
|
|
* Definition lists:
|
|
* <pre>
|
|
* dl. textile:a cloth, especially one manufactured by weaving
|
|
* or knitting; a fabric
|
|
* format:the arrangement of data for storage or display.
|
|
* </pre>
|
|
* Note that there is no space between the term and definition. The
|
|
* term must be at the start of the line (or following the "dl"
|
|
* signature as shown above).
|
|
*
|
|
* @subsection tables Tables
|
|
*
|
|
* Textile supports tables. Tables must be in their own block and
|
|
* must have pipe characters delimiting the columns. An optional
|
|
* block signature of "table" may be used, usually for applying
|
|
* style, class, id or other options to the table element itself.
|
|
*
|
|
* From the simple:
|
|
* <pre>
|
|
* |a|b|c|
|
|
* |1|2|3|
|
|
* </pre>
|
|
* To the complex:
|
|
* <pre>
|
|
* table(fig). {color:red}_|Top|Row|
|
|
* {color:blue}|/2. Second|Row|
|
|
* |_{color:green}. Last|
|
|
* </pre>
|
|
* Modifiers can be specified for the table signature itself,
|
|
* for a table row (prior to the first '|' character) and
|
|
* for any cell (following the '|' for that cell). Note that for
|
|
* cells, a period followed with a space must be placed after
|
|
* any modifiers to distinguish the modifier from the cell content.
|
|
*
|
|
* Modifiers allowed are:
|
|
*
|
|
* <ul>
|
|
*
|
|
* <li><b><code>{style rule}</code></b>
|
|
*
|
|
* A CSS style rule.</li>
|
|
*
|
|
* <li><b><code>(class)</code> or <code>(#id)</code> or <code>(class#id)</code></b>
|
|
*
|
|
* A CSS class and/or id attribute.</li>
|
|
*
|
|
* <li><b><code>(</code> (one or more)</b>
|
|
*
|
|
* Adds 1em of padding to the left for each '(' character.</li>
|
|
*
|
|
* <li><b><code>)</code> (one or more)</b>
|
|
*
|
|
* Adds 1em of padding to the right for each ')' character.</li>
|
|
*
|
|
* <li><b><code>\<</code></b>
|
|
*
|
|
* Aligns to the left (floats to left for tables if combined with the
|
|
* ')' modifier).</li>
|
|
*
|
|
* <li><b><code>\></code></b>
|
|
*
|
|
* Aligns to the right (floats to right for tables if combined with
|
|
* the '(' modifier).</li>
|
|
*
|
|
* <li><b><code>=</code></b>
|
|
*
|
|
* Aligns to center (sets left, right margins to 'auto' for tables).</li>
|
|
*
|
|
* <li><b><code>\<\></code></b>
|
|
*
|
|
* For cells only. Justifies text.</li>
|
|
*
|
|
* <li><b><code>^</code></b>
|
|
*
|
|
* For rows and cells only. Aligns to the top.</li>
|
|
*
|
|
* <li><b><code>~</code> (tilde)</b>
|
|
*
|
|
* For rows and cells only. Aligns to the bottom.</li>
|
|
*
|
|
* <li><b><code>_</code> (underscore)</b>
|
|
*
|
|
* Can be applied to a table row or cell to indicate a header
|
|
* row or cell.</li>
|
|
*
|
|
* <li><b><code>\\2</code> or <code>\\3</code> or <code>\\4</code>, etc.</b>
|
|
*
|
|
* Used within cells to indicate a colspan of 2, 3, 4, etc. columns.
|
|
* When you see "\\", think "push forward".</li>
|
|
*
|
|
* <li><b><code>/2</code> or <code>/3</code> or <code>/4</code>, etc.</b>
|
|
*
|
|
* Used within cells to indicate a rowspan of 2, 3, 4, etc. rows.
|
|
* When you see "/", think "push downward".</li>
|
|
*
|
|
* </ul>
|
|
*
|
|
* When a cell is identified as a header cell and an alignment
|
|
* is specified, that becomes the default alignment for
|
|
* cells below it. You can always override this behavior by
|
|
* specifying an alignment for one of the lower cells.
|
|
*
|
|
* @subsection cssnotes CSS Notes
|
|
*
|
|
* When CSS is enabled (and it is by default), CSS class names
|
|
* are automatically applied in certain situations.
|
|
*
|
|
* <ul>
|
|
*
|
|
* <li>Aligning a block or span or other element to
|
|
* left, right, etc.
|
|
*
|
|
* "left" for left justified, "right" for right justified,
|
|
* "center" for centered text, "justify" for full-justified
|
|
* text.</li>
|
|
*
|
|
* <li>Aligning an image to the top or bottom
|
|
*
|
|
* "top" for top alignment, "bottom" for bottom alignment,
|
|
* "middle" for middle alignment.</li>
|
|
*
|
|
* <li>Footnotes
|
|
*
|
|
* "footnote" is applied to the paragraph tag for the
|
|
* footnote text itself. An id of "fn" plus the footnote
|
|
* number is placed on the paragraph for the footnote as
|
|
* well. For the footnote superscript tag, a class of
|
|
* "footnote" is used.</li>
|
|
*
|
|
* <li>Capped text
|
|
*
|
|
* For a series of characters that are uppercased, a
|
|
* span is placed around them with a class of "caps".</li>
|
|
*
|
|
* </ul>
|
|
*
|
|
* @subsection miscellaneous Miscellaneous
|
|
*
|
|
* Textile tries to do it's very best to ensure proper XHTML
|
|
* syntax. It will even attempt to fix errors you may introduce
|
|
* writing in HTML yourself. Unescaped '&' characters within
|
|
* URLs will be properly escaped. Singlet tags such as br, img
|
|
* and hr are checked for the '/' terminator (and it's added
|
|
* if necessary). The best way to make sure you produce valid
|
|
* XHTML with Textile is to not use any HTML markup at all--
|
|
* use the Textile syntax and let it produce the markup for you.
|
|
*
|
|
* @section license LICENSE
|
|
*
|
|
* Text::Textile is licensed under the same terms as Perl
|
|
* itself. Textile.php is licensed under the terms of the GNU General
|
|
* Public License.
|
|
*
|
|
* @section authorandcopyright AUTHOR & COPYRIGHT
|
|
*
|
|
* Text::Textile was written by Brad Choate, \<brad at bradchoate dot com\>.
|
|
* It is an adaptation of Textile, developed by Dean Allen of Textism.com.
|
|
*
|
|
* Textile.php is a PHP port of Brad Choate's Text::Textile
|
|
* (Textile.pm) Perl module.
|
|
*
|
|
* Textile.php was ported by Jim Riggs \<textile at jimandlissa dot
|
|
* com\>. Great care has been taken to leave the Perl code in much the
|
|
* same form as Textile.pm. While changes were required due to
|
|
* syntactical differences between Perl and PHP, much of the code was
|
|
* left intact (even if alternative syntax or code optimizations could
|
|
* have been made in PHP), even to the point where one can compare
|
|
* functions/subroutines side by side between the two implementations.
|
|
* This has been done to ensure compatibility, reduce the possibility
|
|
* of introducing errors, and simplify maintainance as one version or
|
|
* the other is updated.
|
|
*
|
|
* @author Jim Riggs \<textile at jimandlissa dot com\>
|
|
* @author Brad Choate \<brad at bradchoate dot com\>
|
|
* @copyright Copyright © 2004 Jim Riggs and Brad Choate
|
|
* @version @(#) $Id: Textile.php,v 1.13 2005/03/21 15:26:55 jhriggs Exp $
|
|
*/
|
|
?>
|