Author: Peter Cowburn (salathe)
Date: 2021-07-29T12:56:49+01:00
Commit:
https://github.com/php/web-doc/commit/86f244045a4edcfa02f93b121795544286bd474e
Raw diff:
https://github.com/php/web-doc/commit/86f244045a4edcfa02f93b121795544286bd474e.diff
upgrade Parsedown to 1.7.4
from
https://github.com/erusev/parsedown/releases/tag/1.7.4
Changed paths:
M include/LICENSE.PARSEDOWN
M include/Parsedown.php
Diff:
diff --git a/include/LICENSE.PARSEDOWN b/include/LICENSE.PARSEDOWN
index b5a9d32..e6253d1 100644
--- a/include/LICENSE.PARSEDOWN
+++ b/include/LICENSE.PARSEDOWN
@@ -1,6 +1,6 @@
The MIT License (MIT)
-Copyright (c) 2013 Emanuil Rusev,
erusev.com
+Copyright (c) 2013-2018 Emanuil Rusev,
erusev.com
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
@@ -17,4 +17,4 @@ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/include/Parsedown.php b/include/Parsedown.php
index 7e34540..926f2a5 100644
--- a/include/Parsedown.php
+++ b/include/Parsedown.php
@@ -8,35 +8,26 @@
# (c) Emanuil Rusev
#
http://erusev.com
#
-# For the full license information, view the LICENSE.PARSEDOWN file that was distributed
+# For the full license information, view the LICENSE file that was distributed
# with this source code.
#
#
class Parsedown
{
- #
- # Philosophy
+ # ~
- # Parsedown recognises that the Markdown syntax is optimised for humans so
- # it tries to read like one. It goes through text line by line. It looks at
- # how lines start to identify blocks. It looks for special characters to
- # identify inline elements.
+ const version = '1.7.4';
- #
# ~
function text($text)
{
# make sure no definitions are set
- $this->Definitions = array();
+ $this->DefinitionData = array();
# standardize line breaks
- $text = str_replace("\r\n", "\n", $text);
- $text = str_replace("\r", "\n", $text);
-
- # replace tabs with spaces
- $text = str_replace("\t", ' ', $text);
+ $text = str_replace(array("\r\n", "\r"), "\n", $text);
# remove surrounding line breaks
$text = trim($text, "\n");
@@ -57,8 +48,6 @@ function text($text)
# Setters
#
- private $breaksEnabled;
-
function setBreaksEnabled($breaksEnabled)
{
$this->breaksEnabled = $breaksEnabled;
@@ -66,15 +55,61 @@ function setBreaksEnabled($breaksEnabled)
return $this;
}
+ protected $breaksEnabled;
+
+ function setMarkupEscaped($markupEscaped)
+ {
+ $this->markupEscaped = $markupEscaped;
+
+ return $this;
+ }
+
+ protected $markupEscaped;
+
+ function setUrlsLinked($urlsLinked)
+ {
+ $this->urlsLinked = $urlsLinked;
+
+ return $this;
+ }
+
+ protected $urlsLinked = true;
+
+ function setSafeMode($safeMode)
+ {
+ $this->safeMode = (bool) $safeMode;
+
+ return $this;
+ }
+
+ protected $safeMode;
+
+ protected $safeLinksWhitelist = array(
+ 'http://',
+ 'https://',
+ 'ftp://',
+ 'ftps://',
+ 'mailto:',
+ 'data:image/png;base64,',
+ 'data:image/gif;base64,',
+ 'data:image/jpeg;base64,',
+ 'irc:',
+ 'ircs:',
+ 'git:',
+ 'ssh:',
+ 'news:',
+ 'steam:',
+ );
+
#
# Lines
#
protected $BlockTypes = array(
- '#' => array('Atx'),
+ '#' => array('Header'),
'*' => array('Rule', 'List'),
'+' => array('List'),
- '-' => array('Setext', 'Table', 'Rule', 'List'),
+ '-' => array('SetextHeader', 'Table', 'Rule', 'List'),
'0' => array('List'),
'1' => array('List'),
'2' => array('List'),
@@ -87,8 +122,9 @@ function setBreaksEnabled($breaksEnabled)
'9' => array('List'),
':' => array('Table'),
'<' => array('Comment', 'Markup'),
- '=' => array('Setext'),
+ '=' => array('SetextHeader'),
'>' => array('Quote'),
+ '[' => array('Reference'),
'_' => array('Rule'),
'`' => array('FencedCode'),
'|' => array('Table'),
@@ -97,21 +133,15 @@ function setBreaksEnabled($breaksEnabled)
# ~
- protected $DefinitionTypes = array(
- '[' => array('Reference'),
- );
-
- # ~
-
protected $unmarkedBlockTypes = array(
- 'CodeBlock',
+ 'Code',
);
#
# Blocks
#
- private function lines(array $lines)
+ protected function lines(array $lines)
{
$CurrentBlock = null;
@@ -127,6 +157,23 @@ private function lines(array $lines)
continue;
}
+ if (strpos($line, "\t") !== false)
+ {
+ $parts = explode("\t", $line);
+
+ $line = $parts[0];
+
+ unset($parts[0]);
+
+ foreach ($parts as $part)
+ {
+ $shortage = 4 - mb_strlen($line, 'utf-8') % 4;
+
+ $line .= str_repeat(' ', $shortage);
+ $line .= $part;
+ }
+ }
+
$indent = 0;
while (isset($line[$indent]) and $line[$indent] === ' ')
@@ -142,9 +189,9 @@ private function lines(array $lines)
# ~
- if (isset($CurrentBlock['incomplete']))
+ if (isset($CurrentBlock['continuable']))
{
- $Block = $this->{'addTo'.$CurrentBlock['type']}($Line, $CurrentBlock);
+ $Block = $this->{'block'.$CurrentBlock['type'].'Continue'}($Line, $CurrentBlock);
if (isset($Block))
{
@@ -154,12 +201,10 @@ private function lines(array $lines)
}
else
{
- if (method_exists($this, 'complete'.$CurrentBlock['type']))
+ if ($this->isBlockCompletable($CurrentBlock['type']))
{
- $CurrentBlock = $this->{'complete'.$CurrentBlock['type']}($CurrentBlock);
+ $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
}
-
- unset($CurrentBlock['incomplete']);
}
}
@@ -167,21 +212,6 @@ private function lines(array $lines)
$marker = $text[0];
- if (isset($this->DefinitionTypes[$marker]))
- {
- foreach ($this->DefinitionTypes[$marker] as $definitionType)
- {
- $Definition = $this->{'identify'.$definitionType}($Line, $CurrentBlock);
-
- if (isset($Definition))
- {
- $this->Definitions[$definitionType][$Definition['id']] = $Definition['data'];
-
- continue 2;
- }
- }
- }
-
# ~
$blockTypes = $this->unmarkedBlockTypes;
@@ -199,7 +229,7 @@ private function lines(array $lines)
foreach ($blockTypes as $blockType)
{
- $Block = $this->{'identify'.$blockType}($Line, $CurrentBlock);
+ $Block = $this->{'block'.$blockType}($Line, $CurrentBlock);
if (isset($Block))
{
@@ -207,14 +237,14 @@ private function lines(array $lines)
if ( ! isset($Block['identified']))
{
- $Elements []= $CurrentBlock['element'];
+ $Blocks []= $CurrentBlock;
$Block['identified'] = true;
}
- if (method_exists($this, 'addTo'.$blockType))
+ if ($this->isBlockContinuable($blockType))
{
- $Block['incomplete'] = true;
+ $Block['continuable'] = true;
}
$CurrentBlock = $Block;
@@ -231,9 +261,9 @@ private function lines(array $lines)
}
else
{
- $Elements []= $CurrentBlock['element'];
+ $Blocks []= $CurrentBlock;
- $CurrentBlock = $this->buildParagraph($Line);
+ $CurrentBlock = $this->paragraph($Line);
$CurrentBlock['identified'] = true;
}
@@ -241,59 +271,59 @@ private function lines(array $lines)
# ~
- if (isset($CurrentBlock['incomplete']) and method_exists($this, 'complete'.$CurrentBlock['type']))
+ if (isset($CurrentBlock['continuable']) and $this->isBlockCompletable($CurrentBlock['type']))
{
- $CurrentBlock = $this->{'complete'.$CurrentBlock['type']}($CurrentBlock);
+ $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
}
# ~
- $Elements []= $CurrentBlock['element'];
+ $Blocks []= $CurrentBlock;
- unset($Elements[0]);
+ unset($Blocks[0]);
# ~
- $markup = $this->elements($Elements);
+ $markup = '';
+
+ foreach ($Blocks as $Block)
+ {
+ if (isset($Block['hidden']))
+ {
+ continue;
+ }
+
+ $markup .= "\n";
+ $markup .= isset($Block['markup']) ? $Block['markup'] : $this->element($Block['element']);
+ }
+
+ $markup .= "\n";
# ~
return $markup;
}
- #
- # Atx
-
- protected function identifyAtx($Line)
+ protected function isBlockContinuable($Type)
{
- if (isset($Line['text'][1]))
- {
- $level = 1;
-
- while (isset($Line['text'][$level]) and $Line['text'][$level] === '#')
- {
- $level ++;
- }
-
- $text = trim($Line['text'], '# ');
-
- $Block = array(
- 'element' => array(
- 'name' => 'h'.$level,
- 'text' => $text,
- 'handler' => 'line',
- ),
- );
+ return method_exists($this, 'block'.$Type.'Continue');
+ }
- return $Block;
- }
+ protected function isBlockCompletable($Type)
+ {
+ return method_exists($this, 'block'.$Type.'Complete');
}
#
# Code
- protected function identifyCodeBlock($Line)
+ protected function blockCode($Line, $Block = null)
{
+ if (isset($Block) and ! isset($Block['type']) and ! isset($Block['interrupted']))
+ {
+ return;
+ }
+
if ($Line['indent'] >= 4)
{
$text = substr($Line['body'], 4);
@@ -313,7 +343,7 @@ protected function identifyCodeBlock($Line)
}
}
- protected function addToCodeBlock($Line, $Block)
+ protected function blockCodeContinue($Line, $Block)
{
if ($Line['indent'] >= 4)
{
@@ -334,12 +364,10 @@ protected function addToCodeBlock($Line, $Block)
}
}
- protected function completeCodeBlock($Block)
+ protected function blockCodeComplete($Block)
{
$text = $Block['element']['text']['text'];
- $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
-
$Block['element']['text']['text'] = $text;
return $Block;
@@ -348,12 +376,17 @@ protected function completeCodeBlock($Block)
#
# Comment
- protected function identifyComment($Line)
+ protected function blockComment($Line)
{
+ if ($this->markupEscaped or $this->safeMode)
+ {
+ return;
+ }
+
if (isset($Line['text'][3]) and $Line['text'][3] === '-' and $Line['text'][2] === '-' and $Line['text'][1] === '!')
{
$Block = array(
- 'element' => $Line['body'],
+ 'markup' => $Line['body'],
);
if (preg_match('/-->$/', $Line['text']))
@@ -365,14 +398,14 @@ protected function identifyComment($Line)
}
}
- protected function addToComment($Line, array $Block)
+ protected function blockCommentContinue($Line, array $Block)
{
if (isset($Block['closed']))
{
return;
}
- $Block['element'] .= "\n" . $Line['body'];
+ $Block['markup'] .= "\n" . $Line['body'];
if (preg_match('/-->$/', $Line['text']))
{
@@ -385,18 +418,32 @@ protected function addToComment($Line, array $Block)
#
# Fenced Code
- protected function identifyFencedCode($Line)
+ protected function blockFencedCode($Line)
{
- if (preg_match('/^(['.$Line['text'][0].']{3,})[ ]*([\w-]+)?[ ]*$/', $Line['text'], $matches))
+ if (preg_match('/^['.$Line['text'][0].']{3,}[ ]*([^`]+)?[ ]*$/', $Line['text'], $matches))
{
$Element = array(
'name' => 'code',
'text' => '',
);
- if (isset($matches[2]))
+ if (isset($matches[1]))
{
- $class = 'language-'.$matches[2];
+ /**
+ *
https://www.w3.org/TR/2011/WD-html5-20110525/elements.html#classes
+ * Every HTML element may have a class attribute specified.
+ * The attribute, if specified, must have a value that is a set
+ * of space-separated tokens representing the various classes
+ * that the element belongs to.
+ * [...]
+ * The space characters, for the purposes of this specification,
+ * are U+0020 SPACE, U+0009 CHARACTER TABULATION (tab),
+ * U+000A LINE FEED (LF), U+000C FORM FEED (FF), and
+ * U+000D CARRIAGE RETURN (CR).
+ */
+ $language = substr($matches[1], 0, strcspn($matches[1], " \t\n\f\r"));
+
+ $class = 'language-'.$language;
$Element['attributes'] = array(
'class' => $class,
@@ -416,7 +463,7 @@ protected function identifyFencedCode($Line)
}
}
- protected function addToFencedCode($Line, $Block)
+ protected function blockFencedCodeContinue($Line, $Block)
{
if (isset($Block['complete']))
{
@@ -439,26 +486,57 @@ protected function addToFencedCode($Line, $Block)
return $Block;
}
- $Block['element']['text']['text'] .= "\n".$Line['body'];;
+ $Block['element']['text']['text'] .= "\n".$Line['body'];
return $Block;
}
- protected function completeFencedCode($Block)
+ protected function blockFencedCodeComplete($Block)
{
$text = $Block['element']['text']['text'];
- $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
-
$Block['element']['text']['text'] = $text;
return $Block;
}
+ #
+ # Header
+
+ protected function blockHeader($Line)
+ {
+ if (isset($Line['text'][1]))
+ {
+ $level = 1;
+
+ while (isset($Line['text'][$level]) and $Line['text'][$level] === '#')
+ {
+ $level ++;
+ }
+
+ if ($level > 6)
+ {
+ return;
+ }
+
+ $text = trim($Line['text'], '# ');
+
+ $Block = array(
+ 'element' => array(
+ 'name' => 'h' . min(6, $level),
+ 'text' => $text,
+ 'handler' => 'line',
+ ),
+ );
+
+ return $Block;
+ }
+ }
+
#
# List
- protected function identifyList($Line)
+ protected function blockList($Line)
{
list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]+[.]');
@@ -473,6 +551,16 @@ protected function identifyList($Line)
),
);
+ if($name === 'ol')
+ {
+ $listStart = stristr($matches[0], '.', true);
+
+ if($listStart !== '1')
+ {
+ $Block['element']['attributes'] = array('start' => $listStart);
+ }
+ }
+
$Block['li'] = array(
'name' => 'li',
'handler' => 'li',
@@ -487,24 +575,28 @@ protected function identifyList($Line)
}
}
- protected function addToList($Line, array $Block)
+ protected function blockListContinue($Line, array $Block)
{
- if ($Block['indent'] === $Line['indent'] and preg_match('/^'.$Block['pattern'].'[ ]+(.*)/', $Line['text'], $matches))
+ if ($Block['indent'] === $Line['indent'] and preg_match('/^'.$Block['pattern'].'(?:[ ]+(.*)|$)/', $Line['text'], $matches))
{
if (isset($Block['interrupted']))
{
$Block['li']['text'] []= '';
+ $Block['loose'] = true;
+
unset($Block['interrupted']);
}
unset($Block['li']);
+ $text = isset($matches[1]) ? $matches[1] : '';
+
$Block['li'] = array(
'name' => 'li',
'handler' => 'li',
'text' => array(
- $matches[1],
+ $text,
),
);
@@ -513,6 +605,11 @@ protected function addToList($Line, array $Block)
return $Block;
}
+ if ($Line['text'][0] === '[' and $this->blockReference($Line))
+ {
+ return $Block;
+ }
+
if ( ! isset($Block['interrupted']))
{
$text = preg_replace('/^[ ]{0,4}/', '', $Line['body']);
@@ -536,10 +633,26 @@ protected function addToList($Line, array $Block)
}
}
+ protected function blockListComplete(array $Block)
+ {
+ if (isset($Block['loose']))
+ {
+ foreach ($Block['element']['text'] as &$li)
+ {
+ if (end($li['text']) !== '')
+ {
+ $li['text'] []= '';
+ }
+ }
+ }
+
+ return $Block;
+ }
+
#
# Quote
- protected function identifyQuote($Line)
+ protected function blockQuote($Line)
{
if (preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
{
@@ -555,7 +668,7 @@ protected function identifyQuote($Line)
}
}
- protected function addToQuote($Line, array $Block)
+ protected function blockQuoteContinue($Line, array $Block)
{
if ($Line['text'][0] === '>' and preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
{
@@ -582,9 +695,9 @@ protected function addToQuote($Line, array $Block)
#
# Rule
- protected function identifyRule($Line)
+ protected function blockRule($Line)
{
- if (preg_match('/^(['.$Line['text'][0].'])([ ]{0,2}\1){2,}[ ]*$/', $Line['text']))
+ if (preg_match('/^(['.$Line['text'][0].'])([ ]*\1){2,}[ ]*$/', $Line['text']))
{
$Block = array(
'element' => array(
@@ -599,7 +712,7 @@ protected function identifyRule($Line)
#
# Setext
- protected function identifySetext($Line, array $Block = null)
+ protected function blockSetextHeader($Line, array $Block = null)
{
if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
{
@@ -617,46 +730,71 @@ protected function identifySetext($Line, array $Block = null)
#
# Markup
- protected function identifyMarkup($Line)
+ protected function blockMarkup($Line)
{
- if (preg_match('/^<(\w[\w\d]*)(?:[ ][^>\/]*)?(\/?)[ ]*>/', $Line['text'], $matches))
+ if ($this->markupEscaped or $this->safeMode)
+ {
+ return;
+ }
+
+ if (preg_match('/^<(\w[\w-]*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches))
{
- if (in_array($matches[1], $this->textLevelElements))
+ $element = strtolower($matches[1]);
+
+ if (in_array($element, $this->textLevelElements))
{
return;
}
$Block = array(
- 'element' => $Line['body'],
+ 'name' => $matches[1],
+ 'depth' => 0,
+ 'markup' => $Line['text'],
);
- if ($matches[2] or $matches[1] === 'hr' or preg_match('/<\/'.$matches[1].'>[ ]*$/', $Line['text']))
+ $length = strlen($matches[0]);
+
+ $remainder = substr($Line['text'], $length);
+
+ if (trim($remainder) === '')
{
- $Block['closed'] = true;
+ if (isset($matches[2]) or in_array($matches[1], $this->voidElements))
+ {
+ $Block['closed'] = true;
+
+ $Block['void'] = true;
+ }
}
else
{
- $Block['depth'] = 0;
- $Block['name'] = $matches[1];
+ if (isset($matches[2]) or in_array($matches[1], $this->voidElements))
+ {
+ return;
+ }
+
+ if (preg_match('/<\/'.$matches[1].'>[ ]*$/i', $remainder))
+ {
+ $Block['closed'] = true;
+ }
}
return $Block;
}
}
- protected function addToMarkup($Line, array $Block)
+ protected function blockMarkupContinue($Line, array $Block)
{
if (isset($Block['closed']))
{
return;
}
- if (preg_match('/<'.$Block['name'].'([ ][^\/]+)?>/', $Line['text'])) # opening tag
+ if (preg_match('/^<'.$Block['name'].'(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*>/i', $Line['text'])) # open
{
$Block['depth'] ++;
}
- if (stripos($Line['text'], '</'.$Block['name'].'>') !== false) # closing tag
+ if (preg_match('/(.*?)<\/'.$Block['name'].'>[ ]*$/i', $Line['text'], $matches)) # close
{
if ($Block['depth'] > 0)
{
@@ -668,15 +806,51 @@ protected function addToMarkup($Line, array $Block)
}
}
- $Block['element'] .= "\n".$Line['body'];
+ if (isset($Block['interrupted']))
+ {
+ $Block['markup'] .= "\n";
+
+ unset($Block['interrupted']);
+ }
+
+ $Block['markup'] .= "\n".$Line['body'];
return $Block;
}
+ #
+ # Reference
+
+ protected function blockReference($Line)
+ {
+ if (preg_match('/^\[(.+?)\]:[ ]*<?(\S+?)>?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches))
+ {
+ $id = strtolower($matches[1]);
+
+ $Data = array(
+ 'url' => $matches[2],
+ 'title' => null,
+ );
+
+ if (isset($matches[3]))
+ {
+ $Data['title'] = $matches[3];
+ }
+
+ $this->DefinitionData['Reference'][$id] = $Data;
+
+ $Block = array(
+ 'hidden' => true,
+ );
+
+ return $Block;
+ }
+ }
+
#
# Table
- protected function identifyTable($Line, array $Block = null)
+ protected function blockTable($Line, array $Block = null)
{
if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
{
@@ -710,7 +884,7 @@ protected function identifyTable($Line, array $Block = null)
$alignment = 'left';
}
- if (substr($dividerCell, -1) === ':')
+ if (substr($dividerCell, - 1) === ':')
{
$alignment = $alignment === 'left' ? 'center' : 'right';
}
@@ -744,7 +918,7 @@ protected function identifyTable($Line, array $Block = null)
$alignment = $alignments[$index];
$HeaderElement['attributes'] = array(
- 'align' => $alignment,
+ 'style' => 'text-align: '.$alignment.';',
);
}
@@ -783,8 +957,13 @@ protected function identifyTable($Line, array $Block = null)
}
}
- protected function addToTable($Line, array $Block)
+ protected function blockTableContinue($Line, array $Block)
{
+ if (isset($Block['interrupted']))
+ {
+ return;
+ }
+
if ($Line['text'][0] === '|' or strpos($Line['text'], '|'))
{
$Elements = array();
@@ -794,9 +973,9 @@ protected function addToTable($Line, array $Block)
$row = trim($row);
$row = trim($row, '|');
- $cells = explode('|', $row);
+ preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]+`|`)+/', $row, $matches);
- foreach ($cells as $index => $cell)
+ foreach ($matches[0] as $index => $cell)
{
$cell = trim($cell);
@@ -809,7 +988,7 @@ protected function addToTable($Line, array $Block)
if (isset($Block['alignments'][$index]))
{
$Element['attributes'] = array(
- 'align' => $Block['alignments'][$index],
+ 'style' => 'text-align: '.$Block['alignments'][$index].';',
);
}
@@ -828,35 +1007,11 @@ protected function addToTable($Line, array $Block)
}
}
- #
- # Definitions
- #
-
- protected function identifyReference($Line)
- {
- if (preg_match('/^\[(.+?)\]:[ ]*<?(\S+?)>?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches))
- {
- $Definition = array(
- 'id' => strtolower($matches[1]),
- 'data' => array(
- 'url' => $matches[2],
- ),
- );
-
- if (isset($matches[3]))
- {
- $Definition['data']['title'] = $matches[3];
- }
-
- return $Definition;
- }
- }
-
#
# ~
#
- protected function buildParagraph($Line)
+ protected function paragraph($Line)
{
$Block = array(
'element' => array(
@@ -869,205 +1024,363 @@ protected function buildParagraph($Line)
return $Block;
}
+ #
+ # Inline Elements
+ #
+
+ protected $InlineTypes = array(
+ '"' => array('SpecialCharacter'),
+ '!' => array('Image'),
+ '&' => array('SpecialCharacter'),
+ '*' => array('Emphasis'),
+ ':' => array('Url'),
+ '<' => array('UrlTag', 'EmailTag', 'Markup', 'SpecialCharacter'),
+ '>' => array('SpecialCharacter'),
+ '[' => array('Link'),
+ '_' => array('Emphasis'),
+ '`' => array('Code'),
+ '~' => array('Strikethrough'),
+ '\\' => array('EscapeSequence'),
+ );
+
+ # ~
+
+ protected $inlineMarkerList = '!"*_&[:<>`~\\';
+
#
# ~
#
- protected function element(array $Element)
+ public function line($text, $nonNestables=array())
{
- $markup = '<'.$Element['name'];
+ $markup = '';
- if (isset($Element['attributes']))
- {
- foreach ($Element['attributes'] as $name => $value)
- {
- $markup .= ' '.$name.'="'.$value.'"';
- }
- }
+ # $excerpt is based on the first occurrence of a marker
- if (isset($Element['text']))
+ while ($excerpt = strpbrk($text, $this->inlineMarkerList))
{
- $markup .= '>';
+ $marker = $excerpt[0];
- if (isset($Element['handler']))
- {
- $markup .= $this->{$Element['handler']}($Element['text']);
- }
- else
+ $markerPosition = strpos($text, $marker);
+
+ $Excerpt = array('text' => $excerpt, 'context' => $text);
+
+ foreach ($this->InlineTypes[$marker] as $inlineType)
{
- $markup .= $Element['text'];
- }
+ # check to see if the current inline type is nestable in the current context
- $markup .= '</'.$Element['name'].'>';
- }
- else
- {
- $markup .= ' />';
- }
+ if ( ! empty($nonNestables) and in_array($inlineType, $nonNestables))
+ {
+ continue;
+ }
- return $markup;
- }
+ $Inline = $this->{'inline'.$inlineType}($Excerpt);
- protected function elements(array $Elements)
- {
- $markup = '';
+ if ( ! isset($Inline))
+ {
+ continue;
+ }
- foreach ($Elements as $Element)
- {
- if ($Element === null)
- {
- continue;
- }
+ # makes sure that the inline belongs to "our" marker
- $markup .= "\n";
+ if (isset($Inline['position']) and $Inline['position'] > $markerPosition)
+ {
+ continue;
+ }
- if (is_string($Element)) # because of Markup
- {
- $markup .= $Element;
+ # sets a default inline position
- continue;
+ if ( ! isset($Inline['position']))
+ {
+ $Inline['position'] = $markerPosition;
+ }
+
+ # cause the new element to 'inherit' our non nestables
+
+ foreach ($nonNestables as $non_nestable)
+ {
+ $Inline['element']['nonNestables'][] = $non_nestable;
+ }
+
+ # the text that comes before the inline
+ $unmarkedText = substr($text, 0, $Inline['position']);
+
+ # compile the unmarked text
+ $markup .= $this->unmarkedText($unmarkedText);
+
+ # compile the inline
+ $markup .= isset($Inline['markup']) ? $Inline['markup'] : $this->element($Inline['element']);
+
+ # remove the examined text
+ $text = substr($text, $Inline['position'] + $Inline['extent']);
+
+ continue 2;
}
- $markup .= $this->element($Element);
+ # the marker does not belong to an inline
+
+ $unmarkedText = substr($text, 0, $markerPosition + 1);
+
+ $markup .= $this->unmarkedText($unmarkedText);
+
+ $text = substr($text, $markerPosition + 1);
}
- $markup .= "\n";
+ $markup .= $this->unmarkedText($text);
return $markup;
}
#
- # Spans
+ # ~
#
- protected $SpanTypes = array(
- '!' => array('Link'), # ?
- '&' => array('Ampersand'),
- '*' => array('Emphasis'),
- '/' => array('Url'),
- '<' => array('UrlTag', 'EmailTag', 'Tag', 'LessThan'),
- '[' => array('Link'),
- '_' => array('Emphasis'),
- '`' => array('InlineCode'),
- '~' => array('Strikethrough'),
- '\\' => array('EscapeSequence'),
- );
+ protected function inlineCode($Excerpt)
+ {
+ $marker = $Excerpt['text'][0];
- # ~
+ if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(?<!'.$marker.')\1(?!'.$marker.')/s', $Excerpt['text'], $matches))
+ {
+ $text = $matches[2];
+ $text = preg_replace("/[ ]*\n/", ' ', $text);
+
+ return array(
+ 'extent' => strlen($matches[0]),
+ 'element' => array(
+ 'name' => 'code',
+ 'text' => $text,
+ ),
+ );
+ }
+ }
- protected $spanMarkerList = '*_!&[</`~\\';
+ protected function inlineEmailTag($Excerpt)
+ {
+ if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<((mailto:)?\S+?@\S+?)>/i', $Excerpt['text'], $matches))
+ {
+ $url = $matches[1];
- #
- # ~
- #
+ if ( ! isset($matches[2]))
+ {
+ $url = 'mailto:' . $url;
+ }
- public function line($text)
+ return array(
+ 'extent' => strlen($matches[0]),
+ 'element' => array(
+ 'name' => 'a',
+ 'text' => $matches[1],
+ 'attributes' => array(
+ 'href' => $url,
+ ),
+ ),
+ );
+ }
+ }
+
+ protected function inlineEmphasis($Excerpt)
{
- $markup = '';
+ if ( ! isset($Excerpt['text'][1]))
+ {
+ return;
+ }
- $remainder = $text;
+ $marker = $Excerpt['text'][0];
- $markerPosition = 0;
+ if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches))
+ {
+ $emphasis = 'strong';
+ }
+ elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches))
+ {
+ $emphasis = 'em';
+ }
+ else
+ {
+ return;
+ }
+
+ return array(
+ 'extent' => strlen($matches[0]),
+ 'element' => array(
+ 'name' => $emphasis,
+ 'handler' => 'line',
+ 'text' => $matches[1],
+ ),
+ );
+ }
- while ($excerpt = strpbrk($remainder, $this->spanMarkerList))
+ protected function inlineEscapeSequence($Excerpt)
+ {
+ if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters))
{
- $marker = $excerpt[0];
+ return array(
+ 'markup' => $Excerpt['text'][1],
+ 'extent' => 2,
+ );
+ }
+ }
- $markerPosition += strpos($remainder, $marker);
+ protected function inlineImage($Excerpt)
+ {
+ if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[')
+ {
+ return;
+ }
- $Excerpt = array('text' => $excerpt, 'context' => $text);
+ $Excerpt['text']= substr($Excerpt['text'], 1);
- foreach ($this->SpanTypes[$marker] as $spanType)
- {
- $handler = 'identify'.$spanType;
+ $Link = $this->inlineLink($Excerpt);
- $Span = $this->$handler($Excerpt);
+ if ($Link === null)
+ {
+ return;
+ }
- if ( ! isset($Span))
- {
- continue;
- }
+ $Inline = array(
+ 'extent' => $Link['extent'] + 1,
+ 'element' => array(
+ 'name' => 'img',
+ 'attributes' => array(
+ 'src' => $Link['element']['attributes']['href'],
+ 'alt' => $Link['element']['text'],
+ ),
+ ),
+ );
- # The identified span can be ahead of the marker.
+ $Inline['element']['attributes'] += $Link['element']['attributes'];
- if (isset($Span['position']) and $Span['position'] > $markerPosition)
- {
- continue;
- }
+ unset($Inline['element']['attributes']['href']);
- # Spans that start at the position of their marker don't have to set a position.
+ return $Inline;
+ }
- if ( ! isset($Span['position']))
- {
- $Span['position'] = $markerPosition;
- }
+ protected function inlineLink($Excerpt)
+ {
+ $Element = array(
+ 'name' => 'a',
+ 'handler' => 'line',
+ 'nonNestables' => array('Url', 'Link'),
+ 'text' => null,
+ 'attributes' => array(
+ 'href' => null,
+ 'title' => null,
+ ),
+ );
- $plainText = substr($text, 0, $Span['position']);
+ $extent = 0;
- $markup .= $this->readPlainText($plainText);
+ $remainder = $Excerpt['text'];
- $markup .= isset($Span['markup']) ? $Span['markup'] : $this->element($Span['element']);
+ if (preg_match('/\[((?:[^][]++|(?R))*+)\]/', $remainder, $matches))
+ {
+ $Element['text'] = $matches[1];
- $text = substr($text, $Span['position'] + $Span['extent']);
+ $extent += strlen($matches[0]);
- $remainder = $text;
+ $remainder = substr($remainder, $extent);
+ }
+ else
+ {
+ return;
+ }
- $markerPosition = 0;
+ if (preg_match('/^[(]\s*+((?:[^ ()]++|[(][^ )]+[)])++)(?:[ ]+("[^"]*"|\'[^\']*\'))?\s*[)]/', $remainder, $matches))
+ {
+ $Element['attributes']['href'] = $matches[1];
- continue 2;
+ if (isset($matches[2]))
+ {
+ $Element['attributes']['title'] = substr($matches[2], 1, - 1);
}
- $remainder = substr($excerpt, 1);
-
- $markerPosition ++;
+ $extent += strlen($matches[0]);
}
+ else
+ {
+ if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches))
+ {
+ $definition = strlen($matches[1]) ? $matches[1] : $Element['text'];
+ $definition = strtolower($definition);
+
+ $extent += strlen($matches[0]);
+ }
+ else
+ {
+ $definition = strtolower($Element['text']);
+ }
- $markup .= $this->readPlainText($text);
+ if ( ! isset($this->DefinitionData['Reference'][$definition]))
+ {
+ return;
+ }
- return $markup;
- }
+ $Definition = $this->DefinitionData['Reference'][$definition];
- #
- # ~
- #
+ $Element['attributes']['href'] = $Definition['url'];
+ $Element['attributes']['title'] = $Definition['title'];
+ }
- protected function identifyUrl($Excerpt)
+ return array(
+ 'extent' => $extent,
+ 'element' => $Element,
+ );
+ }
+
+ protected function inlineMarkup($Excerpt)
{
- if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '/')
+ if ($this->markupEscaped or $this->safeMode or strpos($Excerpt['text'], '>') === false)
{
return;
}
- if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE))
+ if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w[\w-]*[ ]*>/s', $Excerpt['text'], $matches))
{
- $url = str_replace(array('&', '<'), array('&', '<'), $matches[0][0]);
+ return array(
+ 'markup' => $matches[0],
+ 'extent' => strlen($matches[0]),
+ );
+ }
+ if ($Excerpt['text'][1] === '!' and preg_match('/^<!---?[^>-](?:-?[^-])*-->/s', $Excerpt['text'], $matches))
+ {
return array(
- 'extent' => strlen($matches[0][0]),
- 'position' => $matches[0][1],
- 'element' => array(
- 'name' => 'a',
- 'text' => $url,
- 'attributes' => array(
- 'href' => $url,
- ),
- ),
+ 'markup' => $matches[0],
+ 'extent' => strlen($matches[0]),
+ );
+ }
+
+ if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w[\w-]*(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s', $Excerpt['text'], $matches))
+ {
+ return array(
+ 'markup' => $matches[0],
+ 'extent' => strlen($matches[0]),
);
}
}
- protected function identifyAmpersand($Excerpt)
+ protected function inlineSpecialCharacter($Excerpt)
{
- if ( ! preg_match('/^&#?\w+;/', $Excerpt['text']))
+ if ($Excerpt['text'][0] === '&' and ! preg_match('/^&#?\w+;/', $Excerpt['text']))
{
return array(
'markup' => '&',
'extent' => 1,
);
}
+
+ $SpecialCharacter = array('>' => 'gt', '<' => 'lt', '"' => 'quot');
+
+ if (isset($SpecialCharacter[$Excerpt['text'][0]]))
+ {
+ return array(
+ 'markup' => '&'.$SpecialCharacter[$Excerpt['text'][0]].';',
+ 'extent' => 1,
+ );
+ }
}
- protected function identifyStrikethrough($Excerpt)
+ protected function inlineStrikethrough($Excerpt)
{
if ( ! isset($Excerpt['text'][1]))
{
@@ -1087,33 +1400,20 @@ protected function identifyStrikethrough($Excerpt)
}
}
- protected function identifyEscapeSequence($Excerpt)
+ protected function inlineUrl($Excerpt)
{
- if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters))
+ if ($this->urlsLinked !== true or ! isset($Excerpt['text'][2]) or $Excerpt['text'][2] !== '/')
{
- return array(
- 'markup' => $Excerpt['text'][1],
- 'extent' => 2,
- );
+ return;
}
- }
-
- protected function identifyLessThan()
- {
- return array(
- 'markup' => '<',
- 'extent' => 1,
- );
- }
- protected function identifyUrlTag($Excerpt)
- {
- if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(https?:[\/]{2}[^\s]+?)>/i', $Excerpt['text'], $matches))
+ if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE))
{
- $url = str_replace(array('&', '<'), array('&', '<'), $matches[1]);
+ $url = $matches[0][0];
- return array(
- 'extent' => strlen($matches[0]),
+ $Inline = array(
+ 'extent' => strlen($matches[0][0]),
+ 'position' => $matches[0][1],
'element' => array(
'name' => 'a',
'text' => $url,
@@ -1122,195 +1422,135 @@ protected function identifyUrlTag($Excerpt)
),
),
);
+
+ return $Inline;
}
}
- protected function identifyEmailTag($Excerpt)
+ protected function inlineUrlTag($Excerpt)
{
- if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\S+?@\S+?)>/', $Excerpt['text'], $matches))
+ if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches))
{
+ $url = $matches[1];
+
return array(
'extent' => strlen($matches[0]),
'element' => array(
'name' => 'a',
- 'text' => $matches[1],
+ 'text' => $url,
'attributes' => array(
- 'href' => 'mailto:'.$matches[1],
+ 'href' => $url,
),
),
);
}
}
- protected function identifyTag($Excerpt)
+ # ~
+
+ protected function unmarkedText($text)
{
- if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<\/?\w.*?>/', $Excerpt['text'], $matches))
+ if ($this->breaksEnabled)
{
- return array(
- 'markup' => $matches[0],
- 'extent' => strlen($matches[0]),
- );
+ $text = preg_replace('/[ ]*\n/', "<br />\n", $text);
}
- }
-
- protected function identifyInlineCode($Excerpt)
- {
- $marker = $Excerpt['text'][0];
-
- if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(?<!'.$marker.')\1(?!'.$marker.')/', $Excerpt['text'], $matches))
+ else
{
- $text = $matches[2];
- $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
-
- return array(
- 'extent' => strlen($matches[0]),
- 'element' => array(
- 'name' => 'code',
- 'text' => $text,
- ),
- );
+ $text = preg_replace('/(?:[ ][ ]+|[ ]*\\\\)\n/', "<br />\n", $text);
+ $text = str_replace(" \n", "\n", $text);
}
+
+ return $text;
}
- protected function identifyLink($Excerpt)
- {
- $extent = $Excerpt['text'][0] === '!' ? 1 : 0;
+ #
+ # Handlers
+ #
- if (strpos($Excerpt['text'], ']') and preg_match('/\[((?:[^][]|(?R))*)\]/', $Excerpt['text'], $matches))
+ protected function element(array $Element)
+ {
+ if ($this->safeMode)
{
- $Link = array('text' => $matches[1], 'label' => strtolower($matches[1]));
-
- $extent += strlen($matches[0]);
-
- $substring = substr($Excerpt['text'], $extent);
-
- if (preg_match('/^\s*\[([^][]+)\]/', $substring, $matches))
- {
- $Link['label'] = strtolower($matches[1]);
-
- if (isset($this->Definitions['Reference'][$Link['label']]))
- {
- $Link += $this->Definitions['Reference'][$Link['label']];
+ $Element = $this->sanitiseElement($Element);
+ }
- $extent += strlen($matches[0]);
- }
- else
- {
- return;
- }
- }
- elseif (isset($this->Definitions['Reference'][$Link['label']]))
- {
- $Link += $this->Definitions['Reference'][$Link['label']];
+ $markup = '<'.$Element['name'];
- if (preg_match('/^[ ]*\[\]/', $substring, $matches))
- {
- $extent += strlen($matches[0]);
- }
- }
- elseif (preg_match('/^\([ ]*(.*?)(?:[ ]+[\'"](.+?)[\'"])?[ ]*\)/', $substring, $matches))
+ if (isset($Element['attributes']))
+ {
+ foreach ($Element['attributes'] as $name => $value)
{
- $Link['url'] = $matches[1];
-
- if (isset($matches[2]))
+ if ($value === null)
{
- $Link['title'] = $matches[2];
+ continue;
}
- $extent += strlen($matches[0]);
+ $markup .= ' '.$name.'="'.self::escape($value).'"';
}
- else
- {
- return;
- }
- }
- else
- {
- return;
}
- $url = str_replace(array('&', '<'), array('&', '<'), $Link['url']);
+ $permitRawHtml = false;
- if ($Excerpt['text'][0] === '!')
+ if (isset($Element['text']))
{
- $Element = array(
- 'name' => 'img',
- 'attributes' => array(
- 'alt' => $Link['text'],
- 'src' => $url,
- ),
- );
+ $text = $Element['text'];
}
- else
+ // very strongly consider an alternative if you're writing an
+ // extension
+ elseif (isset($Element['rawHtml']))
{
- $Element = array(
- 'name' => 'a',
- 'handler' => 'line',
- 'text' => $Link['text'],
- 'attributes' => array(
- 'href' => $url,
- ),
- );
+ $text = $Element['rawHtml'];
+ $allowRawHtmlInSafeMode = isset($Element['allowRawHtmlInSafeMode']) && $Element['allowRawHtmlInSafeMode'];
+ $permitRawHtml = !$this->safeMode || $allowRawHtmlInSafeMode;
}
- if (isset($Link['title']))
+ if (isset($text))
{
- $Element['attributes']['title'] = $Link['title'];
- }
-
- return array(
- 'extent' => $extent,
- 'element' => $Element,
- );
- }
+ $markup .= '>';
- protected function identifyEmphasis($Excerpt)
- {
- if ( ! isset($Excerpt['text'][1]))
- {
- return;
- }
+ if (!isset($Element['nonNestables']))
+ {
+ $Element['nonNestables'] = array();
+ }
- $marker = $Excerpt['text'][0];
+ if (isset($Element['handler']))
+ {
+ $markup .= $this->{$Element['handler']}($text, $Element['nonNestables']);
+ }
+ elseif (!$permitRawHtml)
+ {
+ $markup .= self::escape($text, true);
+ }
+ else
+ {
+ $markup .= $text;
+ }
- if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches))
- {
- $emphasis = 'strong';
- }
- elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches))
- {
- $emphasis = 'em';
+ $markup .= '</'.$Element['name'].'>';
}
else
{
- return;
+ $markup .= ' />';
}
- return array(
- 'extent' => strlen($matches[0]),
- 'element' => array(
- 'name' => $emphasis,
- 'handler' => 'line',
- 'text' => $matches[1],
- ),
- );
+ return $markup;
}
- #
- # ~
-
- protected function readPlainText($text)
+ protected function elements(array $Elements)
{
- $breakMarker = $this->breaksEnabled ? "\n" : " \n";
+ $markup = '';
- $text = str_replace($breakMarker, "<br />\n", $text);
+ foreach ($Elements as $Element)
+ {
+ $markup .= "\n" . $this->element($Element);
+ }
- return $text;
+ $markup .= "\n";
+
+ return $markup;
}
- #
# ~
- #
protected function li($lines)
{
@@ -1332,60 +1572,130 @@ protected function li($lines)
}
#
- # Multiton
+ # Deprecated Methods
#
- static function instance($name = 'default')
+ function parse($text)
{
- if (isset(self::$instances[$name]))
+ $markup = $this->text($text);
+
+ return $markup;
+ }
+
+ protected function sanitiseElement(array $Element)
+ {
+ static $goodAttribute = '/^[a-zA-Z0-9][a-zA-Z0-9-_]*+$/';
+ static $safeUrlNameToAtt = array(
+ 'a' => 'href',
+ 'img' => 'src',
+ );
+
+ if (isset($safeUrlNameToAtt[$Element['name']]))
{
- return self::$instances[$name];
+ $Element = $this->filterUnsafeUrlInAttribute($Element, $safeUrlNameToAtt[$Element['name']]);
}
- $instance = new self();
-
- self::$instances[$name] = $instance;
+ if ( ! empty($Element['attributes']))
+ {
+ foreach ($Element['attributes'] as $att => $val)
+ {
+ # filter out badly parsed attribute
+ if ( ! preg_match($goodAttribute, $att))
+ {
+ unset($Element['attributes'][$att]);
+ }
+ # dump onevent attribute
+ elseif (self::striAtStart($att, 'on'))
+ {
+ unset($Element['attributes'][$att]);
+ }
+ }
+ }
- return $instance;
+ return $Element;
}
- private static $instances = array();
+ protected function filterUnsafeUrlInAttribute(array $Element, $attribute)
+ {
+ foreach ($this->safeLinksWhitelist as $scheme)
+ {
+ if (self::striAtStart($Element['attributes'][$attribute], $scheme))
+ {
+ return $Element;
+ }
+ }
+
+ $Element['attributes'][$attribute] = str_replace(':', '%3A', $Element['attributes'][$attribute]);
+
+ return $Element;
+ }
#
- # Deprecated Methods
+ # Static Methods
#
- /**
- * @deprecated in favor of "text"
- */
- function parse($text)
+ protected static function escape($text, $allowQuotes = false)
{
- $markup = $this->text($text);
+ return htmlspecialchars($text, $allowQuotes ? ENT_NOQUOTES : ENT_QUOTES, 'UTF-8');
+ }
- return $markup;
+ protected static function striAtStart($string, $needle)
+ {
+ $len = strlen($needle);
+
+ if ($len > strlen($string))
+ {
+ return false;
+ }
+ else
+ {
+ return strtolower(substr($string, 0, $len)) === strtolower($needle);
+ }
+ }
+
+ static function instance($name = 'default')
+ {
+ if (isset(self::$instances[$name]))
+ {
+ return self::$instances[$name];
+ }
+
+ $instance = new static();
+
+ self::$instances[$name] = $instance;
+
+ return $instance;
}
+ private static $instances = array();
+
#
# Fields
#
- protected $Definitions;
+ protected $DefinitionData;
#
- # Read-only
+ # Read-Only
protected $specialCharacters = array(
- '\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!',
+ '\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|',
);
protected $StrongRegex = array(
- '*' => '/^[*]{2}((?:[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s',
- '_' => '/^__((?:[^_]|_[^_]*_)+?)__(?!_)/us',
+ '*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s',
+ '_' => '/^__((?:\\\\_|[^_]|_[^_]*_)+?)__(?!_)/us',
);
protected $EmRegex = array(
- '*' => '/^[*]((?:[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s',
- '_' => '/^_((?:[^_]|__[^_]*__)+?)_(?!_)\b/us',
+ '*' => '/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s',
+ '_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us',
+ );
+
+ protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*(?:\s*=\s*(?:[^"\'=<>`\s]+|"[^"]*"|\'[^\']*\'))?';
+
+ protected $voidElements = array(
+ 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source',
);
protected $textLevelElements = array(
@@ -1393,10 +1703,10 @@ function parse($text)
'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing',
'i', 'rp', 'del', 'code', 'strike', 'marquee',
'q', 'rt', 'ins', 'font', 'strong',
- 's', 'tt', 'sub', 'mark',
- 'u', 'xm', 'sup', 'nobr',
- 'var', 'ruby',
- 'wbr', 'span',
- 'time',
+ 's', 'tt', 'kbd', 'mark',
+ 'u', 'xm', 'sub', 'nobr',
+ 'sup', 'ruby',
+ 'var', 'span',
+ 'wbr', 'time',
);
-}
+}
\ No newline at end of file
--
Documentation Website Mailing List (
http://doc.php.net/)
To unsubscribe, visit:
http://www.php.net/unsub.php