341 lines
11 KiB
PHP
341 lines
11 KiB
PHP
<?php
|
|
|
|
class MyParsedown extends ParsedownExtended {
|
|
|
|
function __construct(
|
|
?array $opts = null,
|
|
protected bool $useImagePreviews = false
|
|
) {
|
|
$parsedown_opts = [
|
|
'tables' => [
|
|
'tablespan' => true
|
|
]
|
|
];
|
|
if (!is_null($opts)) {
|
|
$parsedown_opts = array_merge($parsedown_opts, $opts);
|
|
}
|
|
parent::__construct($parsedown_opts);
|
|
|
|
$this->InlineTypes['{'][] = 'FileAttach';
|
|
$this->InlineTypes['{'][] = 'Image';
|
|
$this->InlineTypes['{'][] = 'Video';
|
|
$this->inlineMarkerList .= '{';
|
|
}
|
|
|
|
protected function inlineFileAttach($excerpt) {
|
|
if (preg_match('/^{fileAttach:([\w]{8})}{\/fileAttach}/', $excerpt['text'], $matches)) {
|
|
$random_id = $matches[1];
|
|
$upload = uploads::getUploadByRandomId($random_id);
|
|
$result = [
|
|
'extent' => strlen($matches[0]),
|
|
'element' => [
|
|
'name' => 'span',
|
|
'text' => '',
|
|
],
|
|
'type' => ''
|
|
];
|
|
|
|
if (!$upload) {
|
|
return $result;
|
|
}
|
|
|
|
unset($result['element']['text']);
|
|
|
|
$ctx = self::getSkinContext();
|
|
$result['element']['rawHtml'] = $ctx->fileupload($upload->name, $upload->getDirectUrl(), $upload->note, $upload->getSize());
|
|
|
|
return $result;
|
|
}
|
|
}
|
|
|
|
protected function inlineImage($excerpt) {
|
|
global $config;
|
|
|
|
if (preg_match('/^{image:([\w]{8}),(.*?)}{\/image}/', $excerpt['text'], $matches)) {
|
|
$random_id = $matches[1];
|
|
|
|
$opts = [
|
|
'w' => 'auto',
|
|
'h' => 'auto',
|
|
'align' => 'left',
|
|
'nolabel' => false,
|
|
];
|
|
$inputopts = explode(',', $matches[2]);
|
|
|
|
foreach ($inputopts as $opt) {
|
|
if ($opt == 'nolabel')
|
|
$opts[$opt] = true;
|
|
else {
|
|
list($k, $v) = explode('=', $opt);
|
|
if (!isset($opts[$k]))
|
|
continue;
|
|
$opts[$k] = $v;
|
|
}
|
|
}
|
|
|
|
$image = uploads::getUploadByRandomId($random_id);
|
|
$result = [
|
|
'extent' => strlen($matches[0]),
|
|
'element' => [
|
|
'name' => 'span',
|
|
'text' => '',
|
|
],
|
|
'type' => ''
|
|
];
|
|
|
|
if (!$image) {
|
|
return $result;
|
|
}
|
|
|
|
list($w, $h) = $image->getImagePreviewSize(
|
|
$opts['w'] == 'auto' ? null : $opts['w'],
|
|
$opts['h'] == 'auto' ? null : $opts['h']
|
|
);
|
|
$opts['w'] = $w;
|
|
// $opts['h'] = $h;
|
|
|
|
if (!$this->useImagePreviews)
|
|
$image_url = $image->getDirectUrl();
|
|
else
|
|
$image_url = $image->getDirectPreviewUrl($w, $h);
|
|
|
|
unset($result['element']['text']);
|
|
|
|
$ctx = self::getSkinContext();
|
|
$result['element']['rawHtml'] = $ctx->image(
|
|
w: $opts['w'],
|
|
nolabel: $opts['nolabel'],
|
|
align: $opts['align'],
|
|
padding_top: round($h / $w * 100, 4),
|
|
may_have_alpha: $image->imageMayHaveAlphaChannel(),
|
|
|
|
url: $image_url,
|
|
direct_url: $image->getDirectUrl(),
|
|
note: $image->note
|
|
);
|
|
|
|
return $result;
|
|
}
|
|
}
|
|
|
|
protected function inlineVideo($excerpt) {
|
|
if (preg_match('/^{video:([\w]{8})(?:,(.*?))?}{\/video}/', $excerpt['text'], $matches)) {
|
|
$random_id = $matches[1];
|
|
|
|
$opts = [
|
|
'w' => 'auto',
|
|
'h' => 'auto',
|
|
'align' => 'left',
|
|
'nolabel' => false,
|
|
];
|
|
$inputopts = !empty($matches[2]) ? explode(',', $matches[2]) : [];
|
|
|
|
foreach ($inputopts as $opt) {
|
|
if ($opt == 'nolabel')
|
|
$opts[$opt] = true;
|
|
else {
|
|
list($k, $v) = explode('=', $opt);
|
|
if (!isset($opts[$k]))
|
|
continue;
|
|
$opts[$k] = $v;
|
|
}
|
|
}
|
|
|
|
$video = uploads::getUploadByRandomId($random_id);
|
|
$result = [
|
|
'extent' => strlen($matches[0]),
|
|
'element' => [
|
|
'name' => 'span',
|
|
'text' => '',
|
|
],
|
|
'type' => ''
|
|
];
|
|
|
|
if (!$video) {
|
|
return $result;
|
|
}
|
|
|
|
$video_url = $video->getDirectUrl();
|
|
|
|
unset($result['element']['text']);
|
|
|
|
$ctx = self::getSkinContext();
|
|
$result['element']['rawHtml'] = $ctx->video(
|
|
url: $video_url,
|
|
w: $opts['w'],
|
|
h: $opts['h']
|
|
);
|
|
|
|
return $result;
|
|
}
|
|
}
|
|
|
|
protected function paragraph($line) {
|
|
if (preg_match('/^{fileAttach:([\w]{8})}{\/fileAttach}$/', $line['text'])) {
|
|
return $this->inlineFileAttach($line);
|
|
}
|
|
if (preg_match('/^{image:([\w]{8}),(?:.*?)}{\/image}/', $line['text'])) {
|
|
return $this->inlineImage($line);
|
|
}
|
|
if (preg_match('/^{video:([\w]{8})(?:,(?:.*?))?}{\/video}/', $line['text'])) {
|
|
return $this->inlineVideo($line);
|
|
}
|
|
return parent::paragraph($line);
|
|
}
|
|
|
|
protected function blockFencedCodeComplete($block) {
|
|
if (!isset($block['element']['element']['attributes'])) {
|
|
return $block;
|
|
}
|
|
|
|
$code = $block['element']['element']['text'];
|
|
$languageClass = $block['element']['element']['attributes']['class'];
|
|
$language = explode('-', $languageClass);
|
|
|
|
if ($language[1] == 'term') {
|
|
$lines = explode("\n", $code);
|
|
for ($i = 0; $i < count($lines); $i++) {
|
|
$line = $lines[$i];
|
|
if (str_starts_with($line, '$ ') || str_starts_with($line, '# ')) {
|
|
$lines[$i] = '<span class="term-prompt">'.substr($line, 0, 2).'</span>'.htmlspecialchars(substr($line, 2), ENT_NOQUOTES, 'UTF-8');
|
|
} else {
|
|
$lines[$i] = htmlspecialchars($line, ENT_NOQUOTES, 'UTF-8');
|
|
}
|
|
}
|
|
$block['element']['element']['rawHtml'] = implode("\n", $lines);
|
|
unset($block['element']['element']['text']);
|
|
|
|
return $block;
|
|
}
|
|
|
|
return parent::blockFencedCodeComplete($block);
|
|
}
|
|
|
|
protected static function getSkinContext(): SkinContext {
|
|
return new SkinContext('\\skin\\markdown');
|
|
}
|
|
|
|
protected function blockTableComplete(array $Block)
|
|
{
|
|
if ( ! isset($Block))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
$HeaderElements =& $Block['element']['elements'][0]['elements'][0]['elements'];
|
|
|
|
for ($index = count($HeaderElements) - 1; $index >= 0; --$index)
|
|
{
|
|
$colspan = 1;
|
|
$HeaderElement =& $HeaderElements[$index];
|
|
|
|
while ($index && $HeaderElements[$index - 1]['handler']['argument'] === '>')
|
|
{
|
|
$colspan++;
|
|
$PreviousHeaderElement =& $HeaderElements[--$index];
|
|
$PreviousHeaderElement['merged'] = true;
|
|
if (isset($PreviousHeaderElement['attributes']))
|
|
{
|
|
$HeaderElement['attributes'] = $PreviousHeaderElement['attributes'];
|
|
}
|
|
}
|
|
|
|
if ($colspan > 1)
|
|
{
|
|
if ( ! isset($HeaderElement['attributes']))
|
|
{
|
|
$HeaderElement['attributes'] = array();
|
|
}
|
|
$HeaderElement['attributes']['colspan'] = $colspan;
|
|
}
|
|
}
|
|
|
|
for ($index = count($HeaderElements) - 1; $index >= 0; --$index)
|
|
{
|
|
if (isset($HeaderElements[$index]['merged']))
|
|
{
|
|
array_splice($HeaderElements, $index, 1);
|
|
}
|
|
}
|
|
|
|
$Rows =& $Block['element']['elements'][1]['elements'];
|
|
|
|
foreach ($Rows as $RowNo => &$Row)
|
|
{
|
|
$Elements =& $Row['elements'];
|
|
|
|
for ($index = count($Elements) - 1; $index >= 0; --$index)
|
|
{
|
|
$colspan = 1;
|
|
$Element =& $Elements[$index];
|
|
|
|
while ($index && $Elements[$index - 1]['handler']['argument'] === '>')
|
|
{
|
|
$colspan++;
|
|
$PreviousElement =& $Elements[--$index];
|
|
$PreviousElement['merged'] = true;
|
|
if (isset($PreviousElement['attributes']))
|
|
{
|
|
$Element['attributes'] = $PreviousElement['attributes'];
|
|
}
|
|
}
|
|
|
|
if ($colspan > 1)
|
|
{
|
|
if ( ! isset($Element['attributes']))
|
|
{
|
|
$Element['attributes'] = array();
|
|
}
|
|
$Element['attributes']['colspan'] = $colspan;
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach ($Rows as $RowNo => &$Row)
|
|
{
|
|
$Elements =& $Row['elements'];
|
|
|
|
foreach ($Elements as $index => &$Element)
|
|
{
|
|
$rowspan = 1;
|
|
|
|
if (isset($Element['merged']))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
while ($RowNo + $rowspan < count($Rows) && $index < count($Rows[$RowNo + $rowspan]['elements']) && $Rows[$RowNo + $rowspan]['elements'][$index]['handler']['argument'] === '^' && (@$Element['attributes']['colspan'] ?: null) === (@$Rows[$RowNo + $rowspan]['elements'][$index]['attributes']['colspan'] ?: null))
|
|
{
|
|
$Rows[$RowNo + $rowspan]['elements'][$index]['merged'] = true;
|
|
$rowspan++;
|
|
}
|
|
|
|
if ($rowspan > 1)
|
|
{
|
|
if ( ! isset($Element['attributes']))
|
|
{
|
|
$Element['attributes'] = array();
|
|
}
|
|
$Element['attributes']['rowspan'] = $rowspan;
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach ($Rows as $RowNo => &$Row)
|
|
{
|
|
$Elements =& $Row['elements'];
|
|
|
|
for ($index = count($Elements) - 1; $index >= 0; --$index)
|
|
{
|
|
if (isset($Elements[$index]['merged']))
|
|
{
|
|
array_splice($Elements, $index, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
return $Block;
|
|
}
|
|
|
|
}
|