--- /dev/null
+<?php
+
+namespace App\Markdown\YouTube;
+
+use League\CommonMark\Block\Element\AbstractBlock;
+use League\CommonMark\Cursor;
+
+
+class Element extends AbstractBlock {
+
+ private $URL;
+
+ public function __construct($URL) {
+ $this->URL = $URL;
+ }
+
+ public function getURL() {
+ return $this->URL;
+ }
+
+ public function getVideoID() {
+
+ // Extract the video ID from the URLs. This should support all the main YouTube URL formats
+ // Instead of regex, we could use parse_url() here - it's more readable / maintainable but also harder
+ // to support multiple URL formats. See example:
+ // https://github.com/zoonru/commonmark-ext-youtube-iframe/blob/master/src/YouTubeLongUrlParser.php
+ preg_match("/(?:https?:\/\/)?(?:www\.)?youtu\.?be(?:\.com)?\/?.*(?:watch|embed)?(?:.*v=|v\/|\/)([\w\-_]+)/", $this->URL, $matches);
+ if ($matches[1]) {
+ return $matches[1];
+ }
+ return null;
+ }
+
+ public function canContain(AbstractBlock $block): bool {
+ return false;
+ }
+
+ public function acceptsLines(): bool {
+ return false;
+ }
+
+ public function isCode(): bool {
+ return false;
+ }
+
+ public function shouldLastLineBeBlank(Cursor $cursor, $currentLineNumber): bool {
+ return false;
+ }
+
+ public function matchesNextLine(Cursor $cursor): bool {
+ return false;
+ }
+
+}
--- /dev/null
+<?php
+
+namespace App\Markdown\YouTube;
+
+use League\CommonMark\ConfigurableEnvironmentInterface;
+use League\CommonMark\Extension\ExtensionInterface;
+
+class Extension implements ExtensionInterface
+{
+ public function register(ConfigurableEnvironmentInterface $environment)
+ {
+ // This YouTube extension will find bare YouTube URLs that exist on their own line in the markdown
+ // and convert them to a lite-youtube embed (a bit like oEmbed).
+ // If we could use CommonMark 2.3+, we could use this extension instead, which does a lot more:
+ // https://commonmark.thephpleague.com/2.3/extensions/embed/
+ $environment
+ ->addBlockParser(new Parser())
+ ->addBlockRenderer(Element::class, new Renderer(
+ (string) $environment->getConfig('youtube_iframe_width', 560),
+ (string) $environment->getConfig('youtube_iframe_height', 315),
+ (bool) $environment->getConfig('youtube_iframe_allowfullscreen', true)
+ ));
+ }
+}
--- /dev/null
+<?php
+
+namespace App\Markdown\YouTube;
+
+use League\CommonMark\Block\Parser\BlockParserInterface;
+use League\CommonMark\ContextInterface;
+use League\CommonMark\Cursor;
+
+class Parser implements BlockParserInterface {
+
+ public function parse(ContextInterface $context, Cursor $cursor): bool {
+ if ($cursor->isIndented()) {
+ return false;
+ }
+
+ // Make sure that we have a valid YouTube video URL to work with
+ $matched = $cursor->match("~(?:https?://)?(?:www\.)?youtu\.?be(?:\.com)?/?.*(?:watch|embed)?(?:.*v=|v/|/)([\w\-_]+)~");
+ if (!$matched) {
+ return false; // No match, let this line be handled by another parser
+ }
+ $context->addBlock(new Element($matched));
+
+ return true;
+ }
+
+}
--- /dev/null
+<?php
+
+namespace App\Markdown\YouTube;
+
+use League\CommonMark\Block\Element\AbstractBlock;
+use League\CommonMark\Block\Renderer\BlockRendererInterface;
+use League\CommonMark\ElementRendererInterface;
+use League\CommonMark\HtmlElement;
+
+class Renderer implements BlockRendererInterface {
+
+ private $width;
+ private $height;
+ private $allowFullScreen;
+
+ /**
+ * YouTubeIframeRenderer constructor.
+ * @param string $width
+ * @param string $height
+ * @param bool $allowFullScreen
+ */
+ public function __construct(string $width, string $height, bool $allowFullScreen) {
+ $this->width = $width;
+ $this->height = $height;
+ $this->allowFullScreen = $allowFullScreen;
+ }
+
+ public function render(AbstractBlock $block, ElementRendererInterface $htmlRenderer, $inTightList = false): HtmlElement {
+
+ // Make sure we're rendering a YouTube Element
+ if (!($block instanceof Element)) {
+ throw new \InvalidArgumentException('Incompatible block type: ' . get_class($block));
+ }
+
+ /* @var $block Element */
+ $video_ID = $block->getVideoID();
+
+ // Javascript to ensure the lite-youtube script is only loaded once...
+ // Note: This is problematic for sites with Vue wrappers (eg. PMI) because a raw <script> is not allowed inside
+ // the Vue app's content. Although not ideal, having the script tag included with each video instance doesn't
+ // seem to cause any issues...
+ // In the future, we should use the embed extension for CommonMark (https://commonmark.thephpleague.com/2.3/extensions/embed/)
+ // but this requires newer package versions than we can support currently.
+ // Alternatively, we could use the standard YouTube iframe but this is less performant.
+ // $selective_loader = '
+ // if (!document.getElementById("liteYouTube") && customElements.get("lite-youtube") === undefined) {
+ // const script = document.createElement("script");
+ // script.src = "https://cdn.jsdelivr.net/npm/@justinribeiro/lite-youtube@1.3.1/lite-youtube.js";
+ // script.id = "liteYouTube";
+ // document.body.appendChild(script);
+ // }
+ // ';
+
+ // TODO: instead of loading this each time, it should be done just once per page (see note above).
+ $script = new HtmlElement('script', ['src' => 'https://cdn.jsdelivr.net/npm/@justinribeiro/lite-youtube@1.3.1/lite-youtube.js', 'type' => 'module']);
+ $video = new HtmlElement('lite-youtube', ['videoid' => $video_ID]);
+
+ return new HtmlElement('p', ['class' => 'yt-wrapper'], $video . $script);
+ }
+
+}
],
"license": "proprietary",
"require": {
+ "ext-json": "*",
"cubist/cms-back": "dev-backpack3.6",
"league/csv": "^9.2",
"nothingworks/blade-svg": "^0.3.1",
- "spatie/laravel-blade-x": "^2.2",
- "ext-json": "*"
+ "spatie/laravel-blade-x": "^2.2"
},
"config": {
"optimize-autoloader": true,
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "2684a572ce69945f107df7cadc2a92e1",
+ "content-hash": "40b6079df94541721a0f10d3a1575039",
"packages": [
{
"name": "almasaeed2010/adminlte",
"ext-json": "*"
},
"platform-dev": [],
- "plugin-api-version": "2.0.0"
+ "plugin-api-version": "2.1.0"
}
League\CommonMark\Ext\Autolink\AutolinkExtension::class,
League\CommonMark\Ext\Table\TableExtension::class,
Cubist\Backpack\app\Markdown\InternalLink\Extension::class,
-
+ App\Markdown\YouTube\Extension::class,
],
/*