Artifact 2ee604cfb3762498c4d57da2eae29fc1f534fe3dd5b98f4597287f7332545463:


<?php

namespace Garradin\Web\Render;

//use Parsedown;
use ParsedownExtra;

use Garradin\Entities\Files\File;

use Garradin\Utils;

/**
 * Custom Parsedown extension to enable the use of Skriv extensions inside Markdown markup
 *
 * @see https://github.com/erusev/parsedown/wiki/Tutorial:-Create-Extensions
 */
class Parsedown extends ParsedownExtra
{
	protected $skriv;
	protected $rawTextTOC;

	function __construct(?File $file)
	{
		$this->BlockTypes['<'][] = 'SkrivExtension';
		$this->BlockTypes['['][]= 'TOC';

		parent::__construct();
		$this->skriv = new Skriv($file);
	}

	protected function blockSkrivExtension(array $line): ?array
	{
		$line = $line['text'];

		if (strpos($line, '<<') === 0 && preg_match('/^<<<?([a-z_]+)((?:(?!>>>?).)*?)(>>>?$|$)/i', trim($line), $match)) {
			$text = $this->skriv->callExtension($match);

			return [
				'char'    => $line[0],
				'element' => [
					'name'                   => 'div',
					'rawHtml'                => $text,
					'allowRawHtmlInSafeMode' => true,
				],
				'complete' => true,
			];
		}

		return null;
	}

	protected function blockHeader($line)
	{
		$block = parent::blockHeader($line);

		if (is_array($block)) {
			if (!isset($block['element']['attributes']['id'])) {
				$block['element']['attributes']['id'] = Utils::transformTitleToURI($block['element']['text']);
			}

			$level = substr($block['element']['name'], 1); // h1, h2... -> 1, 2...
			$id = $block['element']['attributes']['id'];
			$label = $block['element']['text'];

			$this->toc[] = compact('level', 'id', 'label');
		}

		return $block;
	}

	protected function blockTOC(array $line): ?array
	{
		if (!preg_match('/^\[(?:toc|sommaire)\]$/', trim($line['text']))) {
			return null;
		}

		return [
			'char'     => $line['text'][0],
			'complete' => true,
			'element'  => [
				'name'                   => 'div',
				'rawHtml'                => '<toc></toc>',
				'allowRawHtmlInSafeMode' => true,
			],
		];
	}

	public function buildTOC(): string
	{
		if (!count($this->toc)) {
			return '';
		}

		$out = '<div class="toc">';

		$level = 0;

		foreach ($this->toc as $h) {
			if ($h['level'] > $level) {
				$out .= str_repeat('<ol>', $h['level'] - $level);
				$level = $h['level'];
			}
			elseif ($h['level'] < $level) {
				$out .= str_repeat('</ol>', $level - $h['level']);
				$level = $h['level'];
			}

			$out .= sprintf('<li><a href="#%s">%s</a></li>', $h['id'], $h['label']);
		}

		if ($level > 0) {
			$out .= str_repeat('</ol>', $level);
		}

		$out .= '</div>';

		return $out;
	}

	public function text($text)
	{
		$out = parent::text($text);

		if (false !== strpos($out, '<toc></toc>')) {
			$toc = $this->buildTOC();
			$out = str_replace('<toc></toc>', $toc, $out);
		}

		return $out;
	}
}