mirror of
https://github.com/Neetpone/ponepaste.git
synced 2025-03-12 22:50:07 +01:00
445 lines
No EOL
14 KiB
PHP
445 lines
No EOL
14 KiB
PHP
<?php
|
|
|
|
#
|
|
#
|
|
# Beam Parsedown
|
|
# https://github.com/ardissoebrata/beam-parsedown
|
|
#
|
|
# (c) Emanuil Rusev
|
|
# http://erusev.com
|
|
#
|
|
# (c) Ardi Soebrata
|
|
# https://mybeam.me
|
|
#
|
|
# For the full license information, view the LICENSE file that was distributed
|
|
# with this source code.
|
|
#
|
|
#
|
|
|
|
namespace ArdiSSoebrata\BeamParsedown;
|
|
|
|
use ParsedownExtra;
|
|
|
|
class BeamParsedown extends ParsedownExtra {
|
|
const version = '0.0.1';
|
|
protected $isUrlRegex = "/(https?|ftp)\:\/\//i";
|
|
protected $regexAttribute = '(?:([#.][\w-]+\s*)|([\w-]+=[\w-]+\s*))+';
|
|
|
|
function __construct() {
|
|
parent::__construct();
|
|
|
|
// @codeCoverageIgnoreStart
|
|
if (version_compare(parent::version, '0.8.1') < 0) {
|
|
throw new Exception('BeamParsedown requires a later version of ParsedownExtra');
|
|
}
|
|
// @codeCoverageIgnoreEnd
|
|
|
|
$this->InlineTypes['['][] = 'Icon';
|
|
$this->InlineTypes['['][] = 'Audio';
|
|
|
|
// Identify our blocks before definition list.
|
|
array_unshift($this->BlockTypes[':'], 'Alert');
|
|
array_unshift($this->BlockTypes[':'], 'Mermaid');
|
|
array_unshift($this->BlockTypes[':'], 'Chart');
|
|
// Identify our blocks before Reference.
|
|
array_unshift($this->BlockTypes['['], 'Youtube');
|
|
array_unshift($this->BlockTypes['['], 'Drawio');
|
|
}
|
|
|
|
// Base path.
|
|
|
|
protected $basePath = '';
|
|
|
|
public function setBasePath($url) {
|
|
$this->basePath = preg_replace('{/$}', '', $url) . '/';
|
|
return $this;
|
|
}
|
|
|
|
protected function inlineImage($excerpt) {
|
|
$image = parent::inlineImage($excerpt);
|
|
|
|
if (!isset($image)) {
|
|
return null;
|
|
}
|
|
|
|
// Add basePath if src is relative.
|
|
$src = $image['element']['attributes']['src'];
|
|
if (!preg_match($this->isUrlRegex, $src, $urlmatch)) {
|
|
$image['element']['attributes']['src'] = $this->basePath . $src;
|
|
}
|
|
|
|
return $image;
|
|
}
|
|
|
|
// Heading id & attributes.
|
|
|
|
protected function blockHeader($Line) {
|
|
$Block = parent::blockHeader($Line);
|
|
|
|
if (!isset($Block)) {
|
|
return null;
|
|
}
|
|
|
|
if (!isset($Block['element']['attributes']['id'])) {
|
|
$text = $Block['element']['text'];
|
|
$text = preg_replace('/(\[.+:.*\]\s)/', '', $text); // remove [tag: value]. Ex. [icon: fa fa-home].
|
|
$Block['element']['attributes']['id'] = $this->slugify($text);
|
|
}
|
|
|
|
return $Block;
|
|
}
|
|
|
|
protected function blockSetextHeader($Line, array $Block = null) {
|
|
$Block = parent::blockSetextHeader($Line, $Block);
|
|
|
|
if (isset($Block['element']) && !isset($Block['element']['attributes']['id'])) {
|
|
$text = $Block['element']['text'];
|
|
$text = preg_replace('/(\[.+:.*\]\s)/', '', $text); // remove [tag: value]. Ex. [icon: fa fa-home].
|
|
$Block['element']['attributes']['id'] = $this->slugify($text);
|
|
}
|
|
|
|
return $Block;
|
|
}
|
|
|
|
protected function parseAttributeData($attributeString) {
|
|
$Data = array();
|
|
|
|
$attributes = preg_split('/[ ]+/', $attributeString, -1, PREG_SPLIT_NO_EMPTY);
|
|
|
|
foreach ($attributes as $attribute) {
|
|
if ($attribute[0] === '#') {
|
|
$Data['id'] = substr($attribute, 1);
|
|
} elseif ($attribute[0] === '.') {
|
|
$classes [] = substr($attribute, 1);
|
|
} elseif (preg_match('/([\w-]+)=([\w-]+)/', $attribute, $match)) {
|
|
$Data[$match[1]] = $match[2];
|
|
}
|
|
}
|
|
|
|
if (isset($classes)) {
|
|
$Data['class'] = implode(' ', $classes);
|
|
}
|
|
|
|
return $Data;
|
|
}
|
|
|
|
public static function slugify($text) {
|
|
// replace non letter or digits by -
|
|
$text = preg_replace('~[^\pL\d]+~u', '-', $text);
|
|
|
|
// transliterate
|
|
$text = iconv('utf-8', 'us-ascii//TRANSLIT', $text);
|
|
|
|
// remove unwanted characters
|
|
$text = preg_replace('~[^-\w]+~', '', $text);
|
|
|
|
// trim
|
|
$text = trim($text, '-');
|
|
|
|
// remove duplicate -
|
|
$text = preg_replace('~-+~', '-', $text);
|
|
|
|
// lowercase
|
|
$text = strtolower($text);
|
|
|
|
if (empty($text)) {
|
|
return 'n-a';
|
|
}
|
|
|
|
return $text;
|
|
}
|
|
|
|
// Icon
|
|
|
|
protected function InlineIcon($excerpt) {
|
|
if (preg_match('/\[icon:(.+?)\]/', $excerpt['text'], $matches)) {
|
|
return array(
|
|
// How many characters to advance the Parsedown's
|
|
// cursor after being done processing this tag.
|
|
'extent' => strlen($matches[0]),
|
|
'element' => array(
|
|
'name' => 'i',
|
|
'attributes' => array(
|
|
'class' => trim($matches[1]),
|
|
),
|
|
'rawHtml' => '',
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
// Audio
|
|
|
|
protected function InlineAudio($excerpt) {
|
|
if (preg_match('/\[audio:(.+?)\]/', $excerpt['text'], $matches)) {
|
|
// Add basePath if src is relative.
|
|
$src = trim($matches[1]);
|
|
if (!preg_match($this->isUrlRegex, $src, $urlmatch)) {
|
|
$src = $this->basePath . $src;
|
|
}
|
|
|
|
return array(
|
|
// How many characters to advance the Parsedown's
|
|
// cursor after being done processing this tag.
|
|
'extent' => strlen($matches[0]),
|
|
'element' => array(
|
|
'name' => 'audio',
|
|
'attributes' => array(
|
|
'controls' => '',
|
|
'preload' => 'none',
|
|
),
|
|
'handler' => 'element',
|
|
'text' => array(
|
|
'name' => 'source',
|
|
'attributes' => array(
|
|
'src' => $src,
|
|
)
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
// Youtube
|
|
|
|
protected function BlockYoutube($excerpt) {
|
|
if (preg_match('/\[youtube:\s*https\:\/\/youtu\.be\/(.+?)\]/', $excerpt['text'], $matches)) {
|
|
$video_id = trim($matches[1]);
|
|
return array(
|
|
// How many characters to advance the Parsedown's
|
|
// cursor after being done processing this tag.
|
|
'extent' => strlen($matches[0]),
|
|
'element' => array(
|
|
'name' => 'div',
|
|
'attributes' => array(
|
|
'class' => 'overflow-hidden relative h-0',
|
|
'style' => 'padding-bottom: 56.25%',
|
|
),
|
|
'handler' => 'element',
|
|
'text' => array(
|
|
'name' => 'iframe',
|
|
'attributes' => array(
|
|
'src' => 'https://www.youtube.com/embed/' . $video_id,
|
|
'frameborder' => '0',
|
|
'allow' => 'accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture',
|
|
'allowfullscreen' => '',
|
|
'class' => 'left-0 top-0 h-full w-full absolute',
|
|
),
|
|
'rawHtml' => '',
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
// Alerts
|
|
|
|
protected $alert_types = array(
|
|
'info' => array(
|
|
'container-class' => 'bg-indigo-100 rounded shadow-sm flex overflow-hidden',
|
|
'icon-bg-class' => 'bg-indigo-500 w-20 flex justify-center items-center',
|
|
'icon-class' => 'fa fa-info-circle fa-2x text-white',
|
|
),
|
|
'warning' => array(
|
|
'container-class' => 'bg-yellow-50 rounded shadow-sm flex overflow-hidden',
|
|
'icon-bg-class' => 'bg-yellow-300 w-20 flex justify-center items-center',
|
|
'icon-class' => 'fa fa-exclamation-triangle fa-2x',
|
|
)
|
|
);
|
|
|
|
protected function BlockAlert($line, $block) {
|
|
$types = implode('|', array_keys($this->alert_types));
|
|
if (preg_match('/^:::(' . $types . ')/', $line['text'], $matches)) {
|
|
$type = trim($matches[1]);
|
|
return array(
|
|
'char' => $line['text'][0],
|
|
'element' => array(
|
|
'name' => 'div',
|
|
'attributes' => array(
|
|
'class' => $this->alert_types[$type]['container-class'],
|
|
'role' => 'alert',
|
|
),
|
|
'handler' => 'elements',
|
|
'text' => array(
|
|
array(
|
|
'name' => 'div',
|
|
'attributes' => array(
|
|
'class' => $this->alert_types[$type]['icon-bg-class']
|
|
),
|
|
'handler' => 'element',
|
|
'text' => array(
|
|
'name' => 'i',
|
|
'attributes' => array(
|
|
'class' => $this->alert_types[$type]['icon-class'],
|
|
),
|
|
'rawHtml' => ''
|
|
),
|
|
),
|
|
array(
|
|
'name' => 'div',
|
|
'attributes' => array(
|
|
'class' => 'flex-1 px-4',
|
|
),
|
|
'handler' => 'lines',
|
|
'text' => array(),
|
|
)
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
protected function BlockAlertContinue($line, $block) {
|
|
if (isset($block['complete'])) {
|
|
return;
|
|
}
|
|
|
|
// A blank newline has occurred.
|
|
if (isset($block['interrupted'])) {
|
|
unset($block['interrupted']);
|
|
}
|
|
|
|
// Check for end of the block.
|
|
if (preg_match('/^:::/', $line['text'])) {
|
|
$block['complete'] = true;
|
|
return $block;
|
|
}
|
|
|
|
$block['element']['text'][1]['text'][] = $line['body'];
|
|
|
|
return $block;
|
|
}
|
|
|
|
protected function BlockAlertComplete($block) {
|
|
return $block;
|
|
}
|
|
|
|
// draw.io
|
|
|
|
protected function BlockDrawio($excerpt) {
|
|
if (preg_match('/\[drawio:\s*(.+?)\]/', $excerpt['text'], $matches)) {
|
|
$file = trim($matches[1]);
|
|
if (!preg_match($this->isUrlRegex, $file, $urlmatch)) {
|
|
$file = $this->basePath . $file;
|
|
}
|
|
return array(
|
|
// How many characters to advance the Parsedown's
|
|
// cursor after being done processing this tag.
|
|
'extent' => strlen($matches[0]),
|
|
'element' => array(
|
|
'name' => 'div',
|
|
'handler' => 'elements',
|
|
'text' => array(
|
|
array(
|
|
'name' => 'div',
|
|
'attributes' => array(
|
|
'class' => 'mxgraph w-full border',
|
|
'data-mxgraph' => json_encode(array(
|
|
'highlight' => '#0000ff',
|
|
'target' => 'blank',
|
|
'nav' => true,
|
|
'resize' => true,
|
|
'toolbar' => 'zoom layers lightbox',
|
|
'url' => $file,
|
|
))
|
|
),
|
|
'rawHtml' => '',
|
|
),
|
|
array(
|
|
'name' => 'script',
|
|
'attributes' => array(
|
|
'type' => 'text/javascript',
|
|
'src' => 'https://viewer.diagrams.net/js/viewer-static.min.js'
|
|
),
|
|
'rawHtml' => '',
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
// Mermaid
|
|
|
|
protected function BlockMermaid($line, $block) {
|
|
if (preg_match('/^:::\s*mermaid/', $line['text'], $matches)) {
|
|
return array(
|
|
'char' => $line['text'][0],
|
|
'element' => array(
|
|
'name' => 'div',
|
|
'attributes' => array(
|
|
'class' => 'mermaid',
|
|
),
|
|
'rawHtml' => "\n",
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
protected function BlockMermaidContinue($line, $block) {
|
|
if (isset($block['complete'])) {
|
|
return;
|
|
}
|
|
|
|
// A blank newline has occurred.
|
|
if (isset($block['interrupted'])) {
|
|
unset($block['interrupted']);
|
|
}
|
|
|
|
// Check for end of the block.
|
|
if (preg_match('/^:::/', $line['text'])) {
|
|
$block['complete'] = true;
|
|
return $block;
|
|
}
|
|
|
|
$block['element']['rawHtml'] .= $line['body'] . "\n";
|
|
|
|
return $block;
|
|
}
|
|
|
|
protected function BlockMermaidComplete($block) {
|
|
return $block;
|
|
}
|
|
|
|
// Chart JS
|
|
|
|
protected function BlockChart($line, $block) {
|
|
if (preg_match('/^:::\s*chart/', $line['text'], $matches)) {
|
|
return array(
|
|
'char' => $line['text'][0],
|
|
'element' => array(
|
|
'name' => 'canvas',
|
|
'attributes' => array(
|
|
'class' => 'chartjs',
|
|
),
|
|
'rawHtml' => "\n",
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
protected function BlockChartContinue($line, $block) {
|
|
if (isset($block['complete'])) {
|
|
return;
|
|
}
|
|
|
|
// A blank newline has occurred.
|
|
if (isset($block['interrupted'])) {
|
|
unset($block['interrupted']);
|
|
}
|
|
|
|
// Check for end of the block.
|
|
if (preg_match('/^:::/', $line['text'])) {
|
|
$block['complete'] = true;
|
|
return $block;
|
|
}
|
|
|
|
$block['element']['rawHtml'] .= $line['body'] . "\n";
|
|
|
|
return $block;
|
|
}
|
|
|
|
protected function BlockChartComplete($block) {
|
|
return $block;
|
|
}
|
|
} |