diff --git a/Utils/Parser/Markdown/Markdown.php b/Utils/Parser/Markdown/Markdown.php
index 8a26a8f01..6a9476d3d 100755
--- a/Utils/Parser/Markdown/Markdown.php
+++ b/Utils/Parser/Markdown/Markdown.php
@@ -29,50 +29,70 @@ use phpOMS\Uri\UriFactory;
*/
class Markdown
{
- # ~
-
+ /**
+ * Parsedown version
+ *
+ * @var string
+ * @since 1.0.0
+ */
public const version = '1.8.0-beta-7';
+ /**
+ * Parsing options
+ *
+ * @var string
+ * @since 1.0.0
+ */
private array $options = [];
- # ~
-
+ /**
+ * Table of content id
+ *
+ * @var string
+ * @since 1.0.0
+ */
private string $idToc = '';
+ /**
+ * Constructor.
+ *
+ * @param array $params Parameters
+ *
+ * @since 1.0.0
+ */
public function __construct(array $params = [])
{
- $this->options = $params;
-
+ $this->options = $params;
$this->options['toc'] = $this->options['toc'] ?? false;
// Marks
$state = $this->options['mark'] ?? true;
if ($state !== false) {
$this->InlineTypes['='][] = 'mark';
- $this->inlineMarkerList .= '=';
+ $this->inlineMarkerList .= '=';
}
// Keystrokes
$state = $this->options['keystrokes'] ?? true;
if ($state !== false) {
$this->InlineTypes['['][] = 'Keystrokes';
- $this->inlineMarkerList .= '[';
+ $this->inlineMarkerList .= '[';
}
// Inline Math
$state = $this->options['math'] ?? false;
if ($state !== false) {
$this->InlineTypes['\\'][] = 'Math';
- $this->inlineMarkerList .= '\\';
- $this->InlineTypes['$'][] = 'Math';
- $this->inlineMarkerList .= '$';
+ $this->inlineMarkerList .= '\\';
+ $this->InlineTypes['$'][] = 'Math';
+ $this->inlineMarkerList .= '$';
}
// Superscript
$state = $this->options['sup'] ?? false;
if ($state !== false) {
$this->InlineTypes['^'][] = 'Superscript';
- $this->inlineMarkerList .= '^';
+ $this->inlineMarkerList .= '^';
}
// Subscript
@@ -85,48 +105,43 @@ class Markdown
$state = $this->options['emojis'] ?? true;
if ($state !== false) {
$this->InlineTypes[':'][] = 'Emojis';
- $this->inlineMarkerList .= ':';
+ $this->inlineMarkerList .= ':';
}
// Typographer
$state = $this->options['typographer'] ?? false;
if ($state !== false) {
$this->InlineTypes['('][] = 'Typographer';
- $this->inlineMarkerList .= '(';
+ $this->inlineMarkerList .= '(';
$this->InlineTypes['.'][] = 'Typographer';
- $this->inlineMarkerList .= '.';
+ $this->inlineMarkerList .= '.';
$this->InlineTypes['+'][] = 'Typographer';
- $this->inlineMarkerList .= '+';
+ $this->inlineMarkerList .= '+';
$this->InlineTypes['!'][] = 'Typographer';
- $this->inlineMarkerList .= '!';
+ $this->inlineMarkerList .= '!';
$this->InlineTypes['?'][] = 'Typographer';
- $this->inlineMarkerList .= '?';
+ $this->inlineMarkerList .= '?';
}
// Smartypants
$state = $this->options['smarty'] ?? false;
if ($state !== false) {
$this->InlineTypes['<'][] = 'Smartypants';
- $this->inlineMarkerList .= '<';
+ $this->inlineMarkerList .= '<';
$this->InlineTypes['>'][] = 'Smartypants';
- $this->inlineMarkerList .= '>';
+ $this->inlineMarkerList .= '>';
$this->InlineTypes['-'][] = 'Smartypants';
- $this->inlineMarkerList .= '-';
+ $this->inlineMarkerList .= '-';
$this->InlineTypes['.'][] = 'Smartypants';
- $this->inlineMarkerList .= '.';
+ $this->inlineMarkerList .= '.';
$this->InlineTypes["'"][] = 'Smartypants';
- $this->inlineMarkerList .= "'";
+ $this->inlineMarkerList .= "'";
$this->InlineTypes['"'][] = 'Smartypants';
- $this->inlineMarkerList .= '"';
+ $this->inlineMarkerList .= '"';
$this->InlineTypes['`'][] = 'Smartypants';
- $this->inlineMarkerList .= '`';
+ $this->inlineMarkerList .= '`';
}
- /*
- * Blocks
- * ------------------------------------------------------------------------
- */
-
// Block Math
$state = $this->options['math'] ?? false;
if ($state !== false) {
@@ -141,26 +156,18 @@ class Markdown
}
}
- public function textParent($text)
+ public function textParent($text) : string
{
$Elements = $this->textElements($text);
+ $markup = $this->elements($Elements);
+ $markup = \trim($markup, "\n");
- # convert to markup
- $markup = $this->elements($Elements);
-
- # trim line breaks
- $markup = \trim($markup, "\n");
-
- # merge consecutive dl elements
-
+ // Merge consecutive dl elements
$markup = \preg_replace('/<\/dl>\s+
\s+/', '', $markup);
- # add footnotes
-
- if (isset($this->DefinitionData['Footnote']))
- {
+ // Add footnotes
+ if (isset($this->DefinitionData['Footnote'])) {
$Element = $this->buildFootnoteElement();
-
$markup .= "\n" . $this->element($Element);
}
@@ -168,13 +175,18 @@ class Markdown
}
/**
- * Parses the given markdown string to an HTML string but it leaves the ToC
- * tag as is. It's an alias of the parent method "\DynamicParent::text()".
+ * Parses the given markdown string to an HTML string but it ignores ToC
+ *
+ * @param string $text Markdown text to parse
+ *
+ * @return string
+ *
+ * @since 1.0.0
*/
- public function body($text) : string
+ public function body(string $text) : string
{
$text = $this->encodeTagToHash($text); // Escapes ToC tag temporary
- $html = $this->textParent($text); // Parses the markdown text
+ $html = $this->textParent($text); // Parses the markdown text
return $this->decodeTagFromHash($html); // Unescape the ToC tag
}
@@ -183,7 +195,7 @@ class Markdown
* Parses markdown string to HTML and also the "[toc]" tag as well.
* It overrides the parent method: \Parsedown::text().
*/
- public function text($text)
+ public function text($text) : string
{
// Parses the markdown text except the ToC tag. This also searches
// the list of contents and available to get from "contentsList()"
@@ -194,6 +206,7 @@ class Markdown
return $html;
}
+ // Handle toc
$tagOrigin = $this->getTagToC();
if (\strpos($text, $tagOrigin) === false) {
@@ -202,8 +215,8 @@ class Markdown
$tocData = $this->contentsList();
$tocId = $this->getIdAttributeToC();
- $needle = ''.$tagOrigin.'
';
- $replace = "{$tocData}
";
+ $needle = '' . $tagOrigin . '
';
+ $replace = '' . $tocData . '
';
return \str_replace($needle, $replace, $html);
}
@@ -214,8 +227,10 @@ class Markdown
* @param string $typeReturn Type of the return format. "html" or "json".
*
* @return string HTML/JSON string of ToC
+ *
+ * @since 1.0.0
*/
- public function contentsList($typeReturn = 'html')
+ public function contentsList($typeReturn = 'html') : string
{
if (\strtolower($typeReturn) === 'html') {
$result = '';
@@ -225,121 +240,127 @@ class Markdown
}
return $result;
- }
-
- if (\strtolower($typeReturn) === 'json') {
+ } elseif (\strtolower($typeReturn) === 'json') {
return \json_encode($this->contentsListArray);
}
- // Forces to return ToC as "html"
- \error_log(
- 'Unknown return type given while parsing ToC.'
- .' At: '.__FUNCTION__.'() '
- .' in Line:'.__LINE__.' (Using default type)'
- );
-
return $this->contentsList('html');
}
/**
- * ------------------------------------------------------------------------
- * Inline
- * ------------------------------------------------------------------------.
+ * Handle inline code
+ *
+ * @param array{text:string, context:string, before:string} $excerpt Inline data
+ *
+ * @return null|array
+ *
+ * @since 1.0.0
*/
-
- // inlineCode
- protected function inlineCode($Excerpt)
+ protected function inlineCode(array $excerpt) : ?array
{
- $codeSnippets = $this->options['code']['inline'] ?? true;
- $codeMain = $this->options['code'] ?? true;
-
- if ($codeSnippets !== true || $codeMain !== true) {
- return;
+ if (($this->options['code']['inline'] ?? true) !== true
+ || ($this->options['code'] ?? true) !== true
+ ) {
+ return null;
}
- $marker = $Excerpt['text'][0];
+ $marker = $excerpt['text'][0];
- if (\preg_match('/^(['.$marker.']++)[ ]*+(.+?)[ ]*+(? \strlen($matches[0]),
- 'element' => [
- 'name' => 'code',
- 'text' => $text,
- ],
- ];
+ if (\preg_match(
+ '/^([' . $marker . ']++)[ ]*+(.+?)[ ]*+(? \strlen($matches[0]),
+ 'element' => [
+ 'name' => 'code',
+ 'text' => $text,
+ ],
+ ];
}
- protected function inlineEmailTag($Excerpt)
+ /**
+ * Handle inline email
+ *
+ * @param array{text:string, context:string, before:string} $excerpt Inline data
+ *
+ * @return null|array
+ *
+ * @since 1.0.0
+ */
+ protected function inlineEmailTag(array $excerpt) : ?array
{
- $mainState = $this->options['links'] ?? true;
- $state = $this->options['links']['email_links'] ?? true;
-
- if (!$mainState || !$state) {
- return;
+ if (!($this->options['links'] ?? true)
+ || !($this->options['links']['email_links'] ?? true)
+ ) {
+ return null;
}
- $hostnameLabel = '[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?';
+ $hostnameLabel = '[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?';
+ $commonMarkEmail = '[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]++@' . $hostnameLabel . '(?:\.' . $hostnameLabel . ')*';
- $commonMarkEmail = '[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]++@'
- . $hostnameLabel . '(?:\.' . $hostnameLabel . ')*';
+ if (\strpos($excerpt['text'], '>') === false
+ || \preg_match('/^<((mailto:)?{' . $commonMarkEmail . '})>/i', $excerpt['text'], $matches) !== 1
+ ) {
+ return null;
+ }
- if (\strpos($Excerpt['text'], '>') !== false
- && \preg_match("/^<((mailto:)?{$commonMarkEmail})>/i", $Excerpt['text'], $matches)
- ){
- $url = UriFactory::build($matches[1]);
+ $url = UriFactory::build($matches[1]);
- if (!isset($matches[2]))
- {
- $url = "mailto:{$url}";
- }
+ if (!isset($matches[2])) {
+ $url = "mailto:{$url}";
+ }
- return [
- 'extent' => \strlen($matches[0]),
- 'element' => [
- 'name' => 'a',
- 'text' => $matches[1],
- 'attributes' => [
- 'href' => $url,
- ],
+ return [
+ 'extent' => \strlen($matches[0]),
+ 'element' => [
+ 'name' => 'a',
+ 'text' => $matches[1],
+ 'attributes' => [
+ 'href' => $url,
],
- ];
- }
+ ],
+ ];
}
- protected function inlineEmphasis($Excerpt)
+ /**
+ * Inline emphasis
+ *
+ * @param array{text:string, context:string, before:string} $excerpt Inline data
+ *
+ * @return null|array
+ *
+ * @since 1.0.0
+ */
+ protected function inlineEmphasis(array $excerpt) : ?array
{
- $state = $this->options['emphasis'] ?? true;
- if (!$state) {
- return;
+ if (!($this->options['emphasis'] ?? true)
+ || !isset($excerpt['text'][1])
+ ) {
+ return null;
}
- if (!isset($Excerpt['text'][1]))
- {
- return;
- }
+ $marker = $excerpt['text'][0];
- $marker = $Excerpt['text'][0];
-
- if ($Excerpt['text'][1] === $marker && isset($this->StrongRegex[$marker]) && \preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches))
- {
+ if ($excerpt['text'][1] === $marker
+ && isset($this->StrongRegex[$marker]) && \preg_match($this->StrongRegex[$marker], $excerpt['text'], $matches)
+ ) {
$emphasis = 'strong';
- }
- elseif ($Excerpt['text'][1] === $marker && isset($this->UnderlineRegex[$marker]) && \preg_match($this->UnderlineRegex[$marker], $Excerpt['text'], $matches))
- {
+ } elseif ($excerpt['text'][1] === $marker
+ && isset($this->UnderlineRegex[$marker]) && \preg_match($this->UnderlineRegex[$marker], $excerpt['text'], $matches)
+ ) {
$emphasis = 'u';
- }
- elseif (\preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches))
- {
+ } elseif (\preg_match($this->EmRegex[$marker], $excerpt['text'], $matches)) {
$emphasis = 'em';
- }
- else
- {
- return;
+ } else {
+ return null;
}
return [
@@ -355,161 +376,169 @@ class Markdown
];
}
- protected function inlineImage($Excerpt)
+ /**
+ * Handle image
+ *
+ * @param array{text:string, context:string, before:string} $excerpt Inline data
+ *
+ * @return null|array
+ *
+ * @since 1.0.0
+ */
+ protected function inlineImage(array $excerpt) : ?array
{
- $state = $this->options['images'] ?? true;
- if (!$state) {
- return;
+ if (!($this->options['images'] ?? true)
+ || !isset($excerpt['text'][1]) || $excerpt['text'][1] !== '['
+ ) {
+ return null;
}
- if (!isset($Excerpt['text'][1]) || $Excerpt['text'][1] !== '[')
- {
- return;
+ $excerpt['text'] = \substr($excerpt['text'], 1);
+ $link = $this->inlineLink($excerpt);
+
+ if ($link === null) {
+ return null;
}
- $Excerpt['text']= \substr($Excerpt['text'], 1);
-
- $Link = $this->inlineLink($Excerpt);
-
- if ($Link === null)
- {
- return;
- }
-
- $Inline = [
- 'extent' => $Link['extent'] + 1,
+ $inline = [
+ 'extent' => $link['extent'] + 1,
'element' => [
'name' => 'img',
'attributes' => [
- 'src' => $Link['element']['attributes']['href'],
- 'alt' => $Link['element']['handler']['argument'],
+ 'src' => $link['element']['attributes']['href'],
+ 'alt' => $link['element']['handler']['argument'],
],
'autobreak' => true,
],
];
- $Inline['element']['attributes'] += $Link['element']['attributes'];
+ $inline['element']['attributes'] += $link['element']['attributes'];
- unset($Inline['element']['attributes']['href']);
+ unset($inline['element']['attributes']['href']);
- return $Inline;
+ return $inline;
}
- protected function inlineLink($Excerpt)
+ /**
+ * Handle link
+ *
+ * @param array{text:string, context:string, before:string} $excerpt Inline data
+ *
+ * @return null|array
+ *
+ * @since 1.0.0
+ */
+ protected function inlineLink(array $excerpt) : ?array
{
- $state = $this->options['links'] ?? true;
- if (!$state) {
- return;
+ if (!($this->options['links'] ?? true)) {
+ return null;
}
- $Link = $this->inlineLinkParent($Excerpt);
+ $link = $this->inlineLinkParent($excerpt);
+ $remainder = $link !== null ? \substr($excerpt['text'], $link['extent']) : '';
- $remainder = $Link !== null ? \substr($Excerpt['text'], $Link['extent']) : '';
-
- if (\preg_match('/^[ ]*{('.$this->regexAttribute.'+)}/', $remainder, $matches))
- {
- $Link['element']['attributes'] += $this->parseAttributeData($matches[1]);
-
- $Link['extent'] += \strlen($matches[0]);
+ if (\preg_match('/^[ ]*{(' . $this->regexAttribute . '+)}/', $remainder, $matches)) {
+ $link['element']['attributes'] += $this->parseAttributeData($matches[1]);
+ $link['extent'] += \strlen($matches[0]);
}
- return $Link;
+ return $link;
}
- protected function inlineMarkup($Excerpt)
+ /**
+ * Handle markup
+ *
+ * @param array{text:string, context:string, before:string} $excerpt Inline data
+ *
+ * @return null|array
+ *
+ * @since 1.0.0
+ */
+ protected function inlineMarkup(array $excerpt) : ?array
{
- $state = $this->options['markup'] ?? true;
- if (!$state) {
- return;
- }
-
- if ($this->markupEscaped || $this->safeMode || \strpos($Excerpt['text'], '>') === false)
- {
- return;
- }
-
- if ($Excerpt['text'][1] === '/' && \preg_match('/^<\/\w[\w-]*+[ ]*+>/s', $Excerpt['text'], $matches))
- {
- return [
- 'element' => ['rawHtml' => $matches[0]],
- 'extent' => \strlen($matches[0]),
- ];
- }
-
- if ($Excerpt['text'][1] === '!' && \preg_match('/^/s', $Excerpt['text'], $matches))
- {
- return [
- 'element' => ['rawHtml' => $matches[0]],
- 'extent' => \strlen($matches[0]),
- ];
- }
-
- if ($Excerpt['text'][1] !== ' ' && \preg_match('/^<\w[\w-]*+(?:[ ]*+'.$this->regexHtmlAttribute.')*+[ ]*+\/?>/s', $Excerpt['text'], $matches))
- {
- return [
- 'element' => ['rawHtml' => $matches[0]],
- 'extent' => \strlen($matches[0]),
- ];
- }
- }
-
- protected function inlineStrikethrough($Excerpt)
- {
- $state = $this->options['strikethroughs'] ?? true;
- if (!$state) {
- return;
- }
-
- if (!isset($Excerpt['text'][1]))
- {
- return;
- }
-
- if ($Excerpt['text'][1] === '~' && \preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches))
- {
- return [
- 'extent' => \strlen($matches[0]),
- 'element' => [
- 'name' => 'del',
- 'handler' => [
- 'function' => 'lineElements',
- 'argument' => $matches[1],
- 'destination' => 'elements',
- ],
- ],
- ];
- }
- }
-
- protected function inlineUrl($Excerpt)
- {
- $state = $this->options['links'] ?? true;
- if (!$state) {
- return;
- }
-
- if ($this->urlsLinked !== true || !isset($Excerpt['text'][2]) || $Excerpt['text'][2] !== '/')
- {
- return;
- }
-
- if (\strpos($Excerpt['context'], 'http') !== false
- && \preg_match('/\bhttps?+:[\/]{2}[^\s<]+\b\/*+/ui', $Excerpt['context'], $matches, \PREG_OFFSET_CAPTURE)
+ if (!($this->options['markup'] ?? true)
+ || $this->markupEscaped || $this->safeMode || \strpos($excerpt['text'], '>') === false
) {
- $url = UriFactory::build($matches[0][0]);
+ return null;
+ }
+ if (($excerpt['text'][1] === '/' && \preg_match('/^<\/\w[\w-]*+[ ]*+>/s', $excerpt['text'], $matches))
+ || ($excerpt['text'][1] === '!' && \preg_match('/^/s', $excerpt['text'], $matches))
+ || ($excerpt['text'][1] !== ' ' && \preg_match('/^<\w[\w-]*+(?:[ ]*+' . $this->regexHtmlAttribute . ')*+[ ]*+\/?>/s', $excerpt['text'], $matches))
+ ) {
return [
- 'extent' => \strlen($matches[0][0]),
- 'position' => $matches[0][1],
- 'element' => [
- 'name' => 'a',
- 'text' => $url,
- 'attributes' => [
- 'href' => $url,
- ],
- ],
+ 'element' => ['rawHtml' => $matches[0]],
+ 'extent' => \strlen($matches[0]),
];
}
+
+ return null;
+ }
+
+ /**
+ * Handle striketrhough
+ *
+ * @param array{text:string, context:string, before:string} $excerpt Inline data
+ *
+ * @return null|array
+ *
+ * @since 1.0.0
+ */
+ protected function inlineStrikethrough($excerpt) : ?array
+ {
+ if (!($this->options['strikethroughs'] ?? true)
+ || !isset($excerpt['text'][1])
+ || $excerpt['text'][1] !== '~'
+ || \preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $excerpt['text'], $matches) !== 1
+ ) {
+ return null;
+ }
+
+ return [
+ 'extent' => \strlen($matches[0]),
+ 'element' => [
+ 'name' => 'del',
+ 'handler' => [
+ 'function' => 'lineElements',
+ 'argument' => $matches[1],
+ 'destination' => 'elements',
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * Handle url
+ *
+ * @param array{text:string, context:string, before:string} $excerpt Inline data
+ *
+ * @return null|array
+ *
+ * @since 1.0.0
+ */
+ protected function inlineUrl($excerpt) : ?array
+ {
+ if (!($this->options['links'] ?? true)
+ || $this->urlsLinked !== true || !isset($excerpt['text'][2]) || $excerpt['text'][2] !== '/'
+ || \strpos($excerpt['context'], 'http') === false
+ || \preg_match('/\bhttps?+:[\/]{2}[^\s<]+\b\/*+/ui', $excerpt['context'], $matches, \PREG_OFFSET_CAPTURE) !== 1
+ ) {
+ return null;
+ }
+
+ $url = UriFactory::build($matches[0][0]);
+
+ return [
+ 'extent' => \strlen($matches[0][0]),
+ 'position' => $matches[0][1],
+ 'element' => [
+ 'name' => 'a',
+ 'text' => $url,
+ 'attributes' => [
+ 'href' => $url,
+ ],
+ ],
+ ];
}
protected function inlineUrlTag($Excerpt)
@@ -1847,24 +1876,22 @@ class Markdown
{
$Elements = [];
- $nonNestables = (
- empty($nonNestables)
+ $nonNestables = empty($nonNestables)
? []
- : \array_combine($nonNestables, $nonNestables)
- );
+ : \array_combine($nonNestables, $nonNestables);
// $excerpt is based on the first occurrence of a marker
- while ($excerpt = \strpbrk($text, $this->inlineMarkerList)) {
- $marker = $excerpt[0];
+ while ($exc = \strpbrk($text, $this->inlineMarkerList)) {
+ $marker = $exc[0];
- $markerPosition = \strlen($text) - \strlen($excerpt);
+ $markerPosition = \strlen($text) - \strlen($exc);
// Get the first char before the marker
$beforeMarkerPosition = $markerPosition - 1;
$charBeforeMarker = $beforeMarkerPosition >= 0 ? $text[$markerPosition - 1] : '';
- $Excerpt = ['text' => $excerpt, 'context' => $text, 'before' => $charBeforeMarker];
+ $excerpt = ['text' => $exc, 'context' => $text, 'before' => $charBeforeMarker];
foreach ($this->InlineTypes[$marker] as $inlineType) {
// check to see if the current inline type is nestable in the current context
@@ -1873,7 +1900,7 @@ class Markdown
continue;
}
- $Inline = $this->{"inline{$inlineType}"}($Excerpt);
+ $Inline = $this->{"inline{$inlineType}"}($excerpt);
if (!isset($Inline)) {
continue;
@@ -3552,6 +3579,7 @@ class Markdown
'`' => ['Code'],
'~' => ['Strikethrough'],
'\\' => ['EscapeSequence'],
+ '=' => ['mark'],
];
# ~