for refactoring * Textile's procedural code into a class framework * * Additions and fixes Copyright (c) 2006 Alex Shiels https://twitter.com/tellyworth * Additions and fixes Copyright (c) 2010 Stef Dawson http://stefdawson.com/ * Additions and fixes Copyright (c) 2010-17 Netcarver https://github.com/netcarver * Additions and fixes Copyright (c) 2011 Jeff Soo http://ipsedixit.net/ * Additions and fixes Copyright (c) 2012 Robert Wetzlmayr http://wetzlmayr.com/ * Additions and fixes Copyright (c) 2012-19 Jukka Svahn http://rahforum.biz/ * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * * Neither the name Textile nor the names of its contributors may be used to * endorse or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* Textile usage examples. Block modifier syntax: Header: h(1-6). Paragraphs beginning with 'hn. ' (where n is 1-6) are wrapped in header tags. Example: h1. Header... ->
Text
Blockquote: bq. Example: bq. Block quotation... ->Block quotation...Blockquote with citation: bq.:http://citation.url Example: bq.:http://example.com/ Text... ->
Text...Footnote: fn(1-100). Example: fn1. Footnote... ->
Footnote...
Numeric list: #, ## Consecutive paragraphs beginning with # are wrapped in ordered list tags. Example:computer code
%(bob)span% -> span
==notextile== -> leave text alone (do not format)
"linktext":url -> linktext
"linktext(title)":url -> linktext
"$":url -> url
"$(title)":url -> url
!imageurl! -> Scientists say1 the moon is small.
The 'a b c' backlink characters can be altered too. For example if you wanted the notes to have numeric backlinks starting from 1: notelist:1. Table syntax: Simple tables: |a|simple|table|row| |And|Another|table|row| |With an||empty|cell| |=. My table caption goes here |_. A|_. table|_. header|_.row| |A|simple|table|row| Note: Table captions *must* be the first line of the table else treated as a center-aligned cell. Tables with attributes: table{border:1px solid black}. My table summary here {background:#ddd;color:red}. |{}| | | | To specify thead / tfoot / tbody groups, add one of these on its own line above the row(s) you wish to wrap (you may specify attributes before the dot): |^. # thead |-. # tbody |~. # tfoot Column groups: |:\3. 100| Becomes:paragraph
p(#fluid). paragraph ->paragraph
(classes and ids can be combined) p(hector#fluid). paragraph ->paragraph
Curly {brackets} insert arbitrary css style p{line-height:18px}. paragraph ->paragraph
h3{color:red}. header 3 ->paragraph
%[fr]phrase% -> phrase Usually Textile block element syntax requires a dot and space before the block begins, but since lists don't, they can be styled just using braces #{color:blue} one ->h1. Headings are disabled too
* * This doesn't prevent unsafe input values. If you wish to parse untrusted * user-given Textile input, also enable the restricted parser mode with * Parser::setRestricted(). * * bc. $parser = new \Netcarver\Textile\Parser(); * echo $parser * ->setRestricted(true) * ->setLite(true) * ->parse('h1. Hello World!'); * * @param bool $lite TRUE to enable * @return Parser This instance * @since 3.6.0 * @see Parser::isLiteModeEnabled() * @see Parser::setRestricted() * @api */ public function setLite($lite) { $this->lite = (bool) $lite; return $this; } /** * Gets the lite mode status. * * bc. $parser = new \Netcarver\Textile\Parser(); * if ($parser->isLiteModeEnabled() === true) { * echo 'Lite mode is enabled.'; * } * * @return bool TRUE if enabled, FALSE otherwise * @since 3.6.0 * @see Parser::setLite() * @api */ public function isLiteModeEnabled() { return (bool) $this->lite; } /** * Disables and enables images. * * If disabled, image tags are not generated. This option is ideal for * minimalist output such as text-only comments. * * bc. $parser = new \Netcarver\Textile\Parser(); * echo $parser * ->setImages(true) * ->parse('!image.png!'); * * Generates: * * bc.!image.png!
* * @param bool $enabled TRUE to enable, FALSE to disable * @return Parser This instance * @since 3.6.0 * @see Parser::isImageTagEnabled() * @api */ public function setImages($enabled) { $this->noimage = !$enabled; return $this; } /** * Whether images are enabled. * * bc. $parser = new \Netcarver\Textile\Parser(); * if ($parser->isImageTagEnabled() === true) { * echo 'Images are enabled.'; * } * * @return bool TRUE if enabled, FALSE otherwise * @since 3.6.0 * @see Parser::setImages() * @api */ public function isImageTagEnabled() { return !$this->noimage; } /** * Sets link relationship status value. * * This method sets the HTML relationship tokens that are applied to links * generated by PHP-Textile. * * bc. $parser = new \Netcarver\Textile\Parser(); * echo $parser * ->setLinkRelationShip('nofollow') * ->parse('"Link":http://example.com/'); * * Generates: * * bc. * * @param string|array $relationship The HTML rel attribute value * @return Parser This instance * @since 3.6.0 * @see Parser::getLinkRelationShip() * @api */ public function setLinkRelationShip($relationship) { $this->rel = (string) implode(' ', (array) $relationship); return $this; } /** * Gets the link relationship status value. * * bc. $parser = new \Netcarver\Textile\Parser(); * echo $parse * ->setLinkRelationShip('nofollow') * ->getLinkRelationShip(); * * The above outputs "nofollow". * * @return string The value * @since 3.6.0 * @see Parser::setLinkRelationShip() * @api */ public function getLinkRelationShip() { return $this->rel; } /** * Enables restricted parser mode. * * This option should be enabled when parsing untrusted user input, * including comments or forum posts. When enabled, the parser escapes any * raw HTML input, ignores unsafe attributes and links only whitelisted URL * schemes. * * For instance the following malicious input: * * bc. $parser = new \Netcarver\Textile\Parser(); * echo $parser * ->setRestricted(true) * ->parse('Innocent _looking_ "link":javacript:window.alert().'); * * Returns safe, sanitized HTML with valid Textile input still parsed: * * bc.Innocent looking “link”:javacript:window.alert().
* * If left disabled, the parser allows users to mix raw HTML and Textile. * Using the parser in non-restricted on untrusted input, like comments * and forum posts, will lead to XSS issues, as users will be able to use * any HTML code, JavaScript links and Textile attributes in their input. * * @param bool $enabled TRUE to enable, FALSE to disable * @return Parser This instance * @since 3.6.0 * @see Parser::isRestrictedModeEnabled() * @api */ public function setRestricted($enabled) { if ($enabled) { $this->url_schemes = $this->restricted_url_schemes; $this->restricted = true; } else { $this->url_schemes = $this->unrestricted_url_schemes; $this->restricted = false; } return $this; } /** * Whether restricted parser mode is enabled. * * bc. $parser = new \Netcarver\Textile\Parser(); * if ($parser->isRestrictedModeEnabled() === true) { * echo 'PHP-Textile is in restricted mode.'; * } * * @return bool TRUE if enabled, FALSE otherwise * @since 3.6.0 * @see Parser::setRestricted() * @api */ public function isRestrictedModeEnabled() { return (bool) $this->restricted; } /** * Enables and disables raw blocks. * * When raw blocks are enabled, any paragraph blocks wrapped in a tag * not matching Parser::$blockContent or Parser::$phrasingContent will not * be parsed, and instead is left as is. * * bc. $parser = new \Netcarver\Textile\Parser(); * echo $parser * ->setRawBlocks(true) * ->parse('Hello world!
* * @param bool $enabled TRUE to enable, FALSE to disable * @return Parser This instance * @since 3.6.0 * @see Parser::isLineWrapEnabled() * @api */ public function setLineWrap($enabled) { $this->lineWrapEnabled = (bool) $enabled; return $this; } /** * Whether line-wrapping is enabled. * * bc. $parser = new \Netcarving\Textile\Parser(); * if ($parser->isLineWrapEnabled() === true) { * echo 'Line-wrapping is enabled.'; * } * * @return bool TRUE if enabled, FALSE otherwise * @see Parser::setLineWrap() * @since 3.6.0 * @api */ public function isLineWrapEnabled() { return (bool) $this->lineWrapEnabled; } /** * Sets a substitution symbol. * * This method lets you to redefine a substitution symbol. The following * sets the 'half' glyph: * * bc. $parser = new \Netcarver\Textile\Parser(); * echo $parser * ->setSymbol('half', '1⁄2') * ->parse('Hello [1/2] World!'); * * Generates: * * bc.Hello 1⁄2 World!
* * Symbol can be set to FALSE to disable it: * * bc. $parser = new \Netcarver\Textile\Parser(); * $parser->setSymbol('dimension', false); * * See Parser::getSymbol() to find out all available symbols. * * @param string $name Name of the symbol to assign a new value to * @param string|bool $value New value for the symbol, or FALSE to disable * @return Parser This instance * @see Parser::getSymbol() * @api */ public function setSymbol($name, $value) { if ($value !== false) { $value = (string) $value; } $this->symbols[(string) $name] = $value; $this->rebuild_glyphs = true; return $this; } /** * Gets a symbol definitions. * * This method gets a symbol definition by name, or the full symbol table * as an array. * * bc. $parser = new \Netcarver\Textile\Parser(); * echo $parser->getSymbol('dimension'); * * To get all available symbol definitions: * * bc. $parser = new \Netcarver\Textile\Parser(); * print_r($parser->getSymbol()); * * @param string|null $name The name of the symbol, or NULL if requesting the symbol table * @return array|string The symbol table or the requested symbol * @throws \InvalidArgumentException * @see Parser::setSymbol() * @api */ public function getSymbol($name = null) { if ($name !== null) { if (isset($this->symbols[$name])) { return $this->symbols[$name]; } throw new \InvalidArgumentException('The specified name does not match any symbols.'); } return $this->symbols; } /** * Sets base relative image prefix. * * The given string is used to prefix relative image paths, usually an * absolute HTTP address pointing a the site's image, or upload, directory. * PHP-Textile to convert relative paths to absolute, or prefixed paths. * * bc. $parser = new \Netcarver\Textile\Parser(); * $parser->setImagePrefix('https://static.example.com/images/'); * * @param string $prefix The prefix * @return Parser This instance * @since 3.7.0 * @see Parser::getImagePrefix() * @api */ public function setImagePrefix($prefix) { $this->relImagePrefix = (string) $prefix; return $this; } /** * Gets base relative image prefix. * * bc. $parser = new \Netcarver\Textile\Parser(); * echo $parser->getImagePrefix(); * * @return string The prefix * @since 3.7.0 * @see Parser::setImagePrefix() * @api */ public function getImagePrefix() { return (string) $this->relImagePrefix; } /** * Sets base relative link prefix. * * The given string is used to prefix relative link paths. This allows * PHP-Textile convert relative paths to absolute, or prefixed, links. * * bc. $parser = new \Netcarver\Textile\Parser(); * $parser->setLinkPrefix('https://example.com/'); * * @param string $prefix The prefix * @return Parser This instance * @since 3.7.0 * @see Parser::getLinkPrefix() * @api */ public function setLinkPrefix($prefix) { $this->relLinkPrefix = (string) $prefix; return $this; } /** * Gets base relative link prefix. * * bc. $parser = new \Netcarver\Textile\Parser(); * echo $parser->getLinkPrefix(); * * @return string The prefix * @since 3.7.0 * @see Parser::setLinkPrefix() * @api */ public function getLinkPrefix() { return (string) $this->relLinkPrefix; } /** * Sets base relative image and link directory path. * * This is used when Textile is supplied with a relative image or link path. * Allows client systems to have PHP-Textile convert relative paths to * absolute or prefixed paths. This method is used to set that base path, * usually an absolute HTTP address pointing to a directory. Note that * despite its name it applies to both links and images. * * bc. $parser = new \Netcarver\Textile\Parser(); * $parser->setRelativeImagePrefix('https://example.com/'); * * @param string $prefix The string to prefix all relative image paths with * @return Parser This instance * @deprecated in 3.7.0 * @see Parser::setImagePrefix * @see Parser::setLinkPrefix * @api */ public function setRelativeImagePrefix($prefix = '') { trigger_error( 'Parser::setRelativeImagePrefix() is deprecated.'. 'Use Parser::setImagePrefix() and Parser::setLinkPrefix() instead.', E_USER_DEPRECATED ); $this->relativeImagePrefix = $prefix; return $this; } /** * Enables dimensionless images. * * If enabled, image width and height attributes will not be included in * rendered image tags. Normally, PHP-Textile will add width and height * to images linked with a local relative path, as long as the image file * can be accessed. * * bc. $parser = new \Netcarver\Textile\Parser(); * echo $parser * ->setDimensionlessImages(true) * ->parse('!image.jpg!'); * * @param bool $dimensionless TRUE to disable image dimensions, FALSE to enable * @return Parser This instance * @see Parser::getDimensionlessImages() * @api */ public function setDimensionlessImages($dimensionless = true) { $this->dimensionless_images = (bool) $dimensionless; return $this; } /** * Whether dimensionless images are enabled. * * bc. $parser = new \Netcarver\Textile\Parser(); * if ($parser->getDimensionlessImages() === true) { * echo 'Images do not get dimensions.'; * } * * @return bool TRUE if images will not get dimensions, FALSE otherwise * @see Parser::setDimensionlessImages() * @api */ public function getDimensionlessImages() { return (bool) $this->dimensionless_images; } /** * Gets PHP-Textile version number. * * bc. $parser = new \Netcarver\Textile\Parser(); * echo $parser->getVersion(); * * @return string Version number * @api */ public function getVersion() { return $this->ver; } /** * Encodes the given text. * * bc. $parser = new \Netcarver\Textile\Parser(); * $parser->textileEncode('Some content to encode.'); * * @param string $text The text to be encoded * @return string The encoded text * @api */ public function textileEncode($text) { return (string)preg_replace('/&(?!(?:[a-z][a-z\d]*|#(?:\d+|x[a-f\d]+));)/i', '&', $text); } /** * Parses the given Textile input according to the previously set options. * * The parser's features can be changed by using the various public setter * methods this class has. The most basic use case is: * * bc. $parser = new \Netcarver\Textile\Parser(); * echo $parser->parse('h1. Hello World!'); * * The above parses trusted input in full-feature mode, generating: * * bc.' . $def . '
'; } if ($this->isLineWrapEnabled()) { $term = str_replace("\n", "