]*>/i', function($matches) use (&$htmlTags) {
$id = count($htmlTags);
$placeholder = "XHTMLTAGREPLACEX" . $id . "XHTMLTAGREPLACEX";
$htmlTags[$placeholder] = $matches[0];
return $placeholder;
}, $text);
$text = preg_replace_callback('/<\/(img|a|p|div|span|picture|source|br|hr)>/i', function($matches) use (&$htmlTags) {
$id = count($htmlTags);
$placeholder = "XHTMLTAGREPLACEX" . $id . "XHTMLTAGREPLACEX";
$htmlTags[$placeholder] = $matches[0];
return $placeholder;
}, $text);
$text = preg_replace_callback('//s', function($matches) use (&$htmlTags) {
$id = count($htmlTags);
$placeholder = "XHTMLTAGREPLACEX" . $id . "XHTMLTAGREPLACEX";
$htmlTags[$placeholder] = $matches[0];
return $placeholder;
}, $text);
$text = preg_replace_callback('/&[a-zA-Z]+;|[0-9]+;|[0-9a-fA-F]+;/', function($matches) use (&$htmlTags) {
$id = count($htmlTags);
$placeholder = "XHTMLTAGREPLACEX" . $id . "XHTMLTAGREPLACEX";
$htmlTags[$placeholder] = $matches[0];
return $placeholder;
}, $text);
$text = preg_replace_callback('/```([a-zA-Z0-9\-_]*)\n?(.*?)\n?```/s', function($matches) use (&$codeBlocks) {
$id = count($codeBlocks);
$placeholder = "XCODEBLOCKREPLACEX" . $id . "XCODEBLOCKREPLACEX";
$codeBlocks[$placeholder] = trim($matches[2]);
return "\n" . $placeholder . "\n";
}, $text);
$text = preg_replace_callback('/`([^`\n]+?)`/', function($matches) use (&$inlineCodes) {
$id = count($inlineCodes);
$placeholder = "XINLINECODEREPLACEX" . $id . "XINLINECODEREPLACEX";
$inlineCodes[$placeholder] = $matches[1];
return $placeholder;
}, $text);
$text = htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
$text = preg_replace_callback('/^###### (.+?)$/m', function($m) {
$id = self::generateHeaderId($m[1]);
return '
' . $m[1] . '
';
}, $text);
$text = preg_replace_callback('/^##### (.+?)$/m', function($m) {
$id = self::generateHeaderId($m[1]);
return '' . $m[1] . '
';
}, $text);
$text = preg_replace_callback('/^#### (.+?)$/m', function($m) {
$id = self::generateHeaderId($m[1]);
return '' . $m[1] . '
';
}, $text);
$text = preg_replace_callback('/^### (.+?)$/m', function($m) {
$id = self::generateHeaderId($m[1]);
return '' . $m[1] . '
';
}, $text);
$text = preg_replace_callback('/^## (.+?)$/m', function($m) {
$id = self::generateHeaderId($m[1]);
return '' . $m[1] . '
';
}, $text);
$text = preg_replace_callback('/^# (.+?)$/m', function($m) {
$id = self::generateHeaderId($m[1]);
return '' . $m[1] . '
';
}, $text);
$text = preg_replace_callback('/!\[([^\]]*?)\]\(([^)]+?)\)/', function($matches) {
$alt = $matches[1];
$src = $matches[2];
if (!preg_match('/^(https?:\/\/|\/\/|data:)/', $src)) {
$baseUrl = self::$currentFilePath;
$src = rtrim($baseUrl, '/') . '/' . ltrim($src, '/');
}
return '
';
}, $text);
$text = preg_replace_callback('/\[([^\]]+?)\]\(([^)]+?)\)/', function($matches) {
$linkText = $matches[1];
$linkUrl = $matches[2];
$processedUrl = self::processMarkdownLink($linkUrl);
return '' . $linkText . '';
}, $text);
$text = preg_replace('/(?$1', $text);
$text = preg_replace('/(?$1', $text);
$text = preg_replace('/(?$1', $text);
$text = preg_replace('/(?$1', $text);
$text = preg_replace('/(?$1', $text);
$text = preg_replace('/(?$1', $text);
$text = preg_replace('/~~([^~\n]+?)~~/', '$1', $text);
$text = preg_replace('/^\s*---\s*$/m', '
', $text);
$text = preg_replace('/^\s*\*\*\*\s*$/m', '
', $text);
$text = preg_replace('/^> (.+?)$/m', '$1
', $text);
$text = preg_replace('/^(\s*)[\*\-\+] (.+?)$/m', '$1$2', $text);
$text = preg_replace('/^(\s*)\d+\. (.+?)$/m', '$1$2', $text);
$text = self::wrapLists($text);
$text = preg_replace_callback('/(?:^\|.+\|\s*$\n?)+/m', function($matches) {
return self::parseTable($matches[0]);
}, $text);
$text = self::wrapParagraphs($text);
foreach ($codeBlocks as $placeholder => $content) {
$escapedContent = htmlspecialchars($content, ENT_QUOTES, 'UTF-8');
$codeHtml = '' . $escapedContent . '
';
$text = str_replace($placeholder, $codeHtml, $text);
}
foreach ($inlineCodes as $placeholder => $content) {
$escapedContent = htmlspecialchars($content, ENT_QUOTES, 'UTF-8');
$codeHtml = '' . $escapedContent . '';
$text = str_replace($placeholder, $codeHtml, $text);
}
foreach ($htmlTags as $placeholder => $content) {
if (preg_match('/
]*?)>/i', function($m) {
$attrs = $m[1];
$style = '';
if (preg_match('/width\s*=\s*["\']?(\d+)["\']?/i', $attrs, $widthMatch)) {
$style .= 'width: ' . $widthMatch[1] . 'px; ';
}
if (preg_match('/height\s*=\s*["\']?(\d+)["\']?/i', $attrs, $heightMatch)) {
$style .= 'height: ' . $heightMatch[1] . 'px; ';
}
if ($style) {
$style = 'max-width: 100%; ' . $style;
if (preg_match('/style\s*=\s*["\']([^"\']*)["\']/', $attrs)) {
$attrs = preg_replace('/style\s*=\s*["\']([^"\']*)["\']/', 'style="$1 ' . $style . '"', $attrs);
} else {
$attrs .= ' style="' . trim($style) . '"';
}
}
return '
';
}, $content);
if (preg_match('/class\s*=\s*["\']([^"\']*)["\']/', $content, $classMatch)) {
if (strpos($classMatch[1], 'markdown-img') === false) {
$newClass = trim($classMatch[1] . ' markdown-img');
$content = preg_replace('/class\s*=\s*["\']([^"\']*)["\']/', 'class="' . $newClass . '"', $content);
}
} else {
$content = preg_replace('/
0) {
array_pop($parts);
}
} else {
$parts[] = $part;
}
}
return implode('/', $parts);
}
private static function generateHeaderId($headerText) {
$headerText = strip_tags($headerText);
$id = strtolower($headerText);
$id = preg_replace('/[^a-z0-9]+/', '-', $id);
$id = trim($id, '-');
return $id;
}
private static function wrapLists($text) {
$lines = explode("\n", $text);
$result = [];
$inList = false;
$listType = '';
$lastWasListItem = false;
foreach ($lines as $line) {
if (preg_match('/^(\s*)";
$listType = $newListType;
$inList = true;
} elseif ($listType !== $newListType) {
if (!($listType === 'ol' && $newListType === 'ol')) {
$result[] = "$listType>";
$result[] = "<$newListType class=\"markdown-list\">";
$listType = $newListType;
}
}
$result[] = $line;
$lastWasListItem = true;
} else {
if ($inList && trim($line) === '' && $lastWasListItem) {
$lastWasListItem = false;
continue;
}
if ($inList && trim($line) !== '') {
$result[] = "$listType>";
$inList = false;
}
if (trim($line) !== '') {
$result[] = $line;
$lastWasListItem = false;
}
}
}
if ($inList) {
$result[] = "$listType>";
}
return implode("\n", $result);
}
private static function parseTable($table) {
$table = trim($table);
$rows = explode("\n", $table);
$html = '';
$isHeader = true;
foreach ($rows as $row) {
if (empty(trim($row))) continue;
if (preg_match('/^\|[\s\-\|:]+\|$/', $row)) {
$isHeader = false;
continue;
}
$cells = explode('|', trim($row, '|'));
$cells = array_map('trim', $cells);
$tag = $isHeader ? 'th' : 'td';
$class = $isHeader ? 'markdown-th' : 'markdown-td';
$html .= '';
foreach ($cells as $cell) {
$html .= "<$tag class=\"$class\">$cell$tag>";
}
$html .= '
';
if ($isHeader) $isHeader = false;
}
$html .= '
';
return $html;
}
private static function wrapParagraphs($text) {
$paragraphs = preg_split('/\n\s*\n/', $text);
$result = [];
foreach ($paragraphs as $paragraph) {
$paragraph = trim($paragraph);
if (empty($paragraph)) continue;
if (preg_match('/^XCODEBLOCKREPLACEX\d+XCODEBLOCKREPLACEX$/', $paragraph)) {
$result[] = $paragraph;
}
elseif (preg_match('/^<(h[1-6]|ul|ol|blockquote|pre|hr|table|div)/i', $paragraph)) {
$result[] = $paragraph;
}
else {
if (preg_match('/<(p|div|a|picture|img)[^>]*>/', $paragraph)) {
$result[] = $paragraph;
} else {
$paragraph = preg_replace('/\n(?!<)/', '
', $paragraph);
$result[] = '' . $paragraph . '
';
}
}
}
return implode("\n\n", $result);
}
}
?>