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; } }