diff --git a/README.md b/README.md index 799e775..a11983d 100644 --- a/README.md +++ b/README.md @@ -121,25 +121,35 @@ I've composed a wiki page to describe features of this extension. Hello I should be in red text :D :color +:color red this is inline! :color + # 3 Character hex :color #AAA Hello! :color +:color #AAA this is inline! :color + # 6 Character hex :color #DADADA Hello! :color +:color #DADADA this is inline! :color + # RGB :color 255,255,255 Hello! :color +:color 255,255,255 this is inline! :color + # RGBA :color 255,255,255,50 Hello! :color + +:color 255,255,255,50 this is inline! :color ``` [More info](https://github.com/johnnyhuy/laravel-useful-commonmark-extension/wiki/Color) diff --git a/src/Inline/Element/Color.php b/src/Inline/Element/Color.php new file mode 100644 index 0000000..b9e8e64 --- /dev/null +++ b/src/Inline/Element/Color.php @@ -0,0 +1,7 @@ +getCursor(); + $savedState = $cursor->saveState(); + + $cursor->advance(); + + $regex = '/(?:color|colour)(?:\s(?:(\#?[A-z]+|\d{1,3}\,\s?\d{1,3}\,\s?\d{1,3}(\,\s?\d{1,3})?)))\s(.*(?!:color))\s(?:\:color)/'; + $validate = $cursor->match($regex); + + if (!$validate) { + $cursor->restoreState($savedState); + return false; + } + + $matches = []; + preg_match($regex, $validate, $matches); + [, $color, $alpha, $content] = $matches; + + if (preg_match('/[\,]+/', $color, $_)) { + if (empty($alpha)) { + $color = "rgb({$color})"; + } else { + $color = "rgba({$color})"; + } + } else if (preg_match('/[\#]+/', $color, $_)) { + $color = "#{$color}"; + } + + $data['color'] = $color; + + $inlineContext->getContainer()->appendChild(new Color($content, $data)); + + return true; + } + + /** + * @return string[] + */ + public function getCharacters() + { + return [':']; + } +} diff --git a/src/Inline/Renderer/CodepenRenderer.php b/src/Inline/Renderer/CodepenRenderer.php index 4b1ed85..912afdf 100644 --- a/src/Inline/Renderer/CodepenRenderer.php +++ b/src/Inline/Renderer/CodepenRenderer.php @@ -2,14 +2,14 @@ namespace JohnnyHuy\Laravel\Inline\Renderer; -use League\CommonMark\HtmlElement; -use League\CommonMark\Util\Configuration; +use ErrorException; use JohnnyHuy\Laravel\Inline\Element\Codepen; use League\CommonMark\ElementRendererInterface; +use League\CommonMark\HtmlElement; use League\CommonMark\Inline\Element\AbstractInline; -use League\CommonMark\Util\ConfigurationAwareInterface; use League\CommonMark\Inline\Element\AbstractWebResource; use League\CommonMark\Inline\Renderer\InlineRendererInterface; +use League\CommonMark\Util\Configuration; class CodepenRenderer implements InlineRendererInterface { @@ -20,10 +20,10 @@ class CodepenRenderer implements InlineRendererInterface /** * @param AbstractInline|AbstractWebResource $inline - * @param \League\CommonMark\ElementRendererInterface $htmlRenderer + * @param ElementRendererInterface $htmlRenderer * - * @return \League\CommonMark\HtmlElement|string - * @throws \ErrorException + * @return HtmlElement|string + * @throws ErrorException */ public function render(AbstractInline $inline, ElementRendererInterface $htmlRenderer) { @@ -39,7 +39,7 @@ public function render(AbstractInline $inline, ElementRendererInterface $htmlRen //seems that the used codepen url is invalid //or codepen is currently not available if (is_null($apiResponse)) { - throw new \ErrorException('Codepen request returned null: ' . $apiUrl); + throw new ErrorException('Codepen request returned null: ' . $apiUrl); } //parse the oembed response diff --git a/src/Inline/Renderer/ColorRenderer.php b/src/Inline/Renderer/ColorRenderer.php new file mode 100644 index 0000000..f149ee5 --- /dev/null +++ b/src/Inline/Renderer/ColorRenderer.php @@ -0,0 +1,45 @@ + "color: {$inline->getData('color')}"], $inline->getContent()); + } + + /** + * @param Configuration $configuration + */ + public function setConfiguration(Configuration $configuration) + { + $this->config = $configuration; + } +} diff --git a/src/Inline/Renderer/GistRenderer.php b/src/Inline/Renderer/GistRenderer.php index 0468d9e..34ae663 100644 --- a/src/Inline/Renderer/GistRenderer.php +++ b/src/Inline/Renderer/GistRenderer.php @@ -2,14 +2,13 @@ namespace JohnnyHuy\Laravel\Inline\Renderer; -use League\CommonMark\HtmlElement; -use League\CommonMark\Util\Configuration; use JohnnyHuy\Laravel\Inline\Element\Gist; use League\CommonMark\ElementRendererInterface; +use League\CommonMark\HtmlElement; use League\CommonMark\Inline\Element\AbstractInline; -use League\CommonMark\Util\ConfigurationAwareInterface; use League\CommonMark\Inline\Element\AbstractWebResource; use League\CommonMark\Inline\Renderer\InlineRendererInterface; +use League\CommonMark\Util\Configuration; class GistRenderer implements InlineRendererInterface { @@ -20,9 +19,9 @@ class GistRenderer implements InlineRendererInterface /** * @param AbstractInline|AbstractWebResource $inline - * @param \League\CommonMark\ElementRendererInterface $htmlRenderer + * @param ElementRendererInterface $htmlRenderer * - * @return \League\CommonMark\HtmlElement|string + * @return HtmlElement|string */ public function render(AbstractInline $inline, ElementRendererInterface $htmlRenderer) { @@ -30,13 +29,10 @@ public function render(AbstractInline $inline, ElementRendererInterface $htmlRen throw new \InvalidArgumentException('Incompatible inline type: ' . get_class($inline)); } - //generates the same script element, which you can see - //in the "embed gist" input field $script = new HtmlElement('script', [ 'src' => $inline->getUrl().'.js' ]); - //add a div wrapper around the script element return new HtmlElement('div', ['class' => 'gist-container'], $script); } diff --git a/src/Inline/Renderer/SoundCloudRenderer.php b/src/Inline/Renderer/SoundCloudRenderer.php index 352e41c..d618f5c 100644 --- a/src/Inline/Renderer/SoundCloudRenderer.php +++ b/src/Inline/Renderer/SoundCloudRenderer.php @@ -2,14 +2,14 @@ namespace JohnnyHuy\Laravel\Inline\Renderer; -use League\CommonMark\HtmlElement; -use League\CommonMark\Util\Configuration; -use League\CommonMark\ElementRendererInterface; +use ErrorException; use JohnnyHuy\Laravel\Inline\Element\SoundCloud; +use League\CommonMark\ElementRendererInterface; +use League\CommonMark\HtmlElement; use League\CommonMark\Inline\Element\AbstractInline; -use League\CommonMark\Util\ConfigurationAwareInterface; use League\CommonMark\Inline\Element\AbstractWebResource; use League\CommonMark\Inline\Renderer\InlineRendererInterface; +use League\CommonMark\Util\Configuration; class SoundCloudRenderer implements InlineRendererInterface { @@ -20,10 +20,10 @@ class SoundCloudRenderer implements InlineRendererInterface /** * @param AbstractInline|AbstractWebResource $inline - * @param \League\CommonMark\ElementRendererInterface $htmlRenderer + * @param ElementRendererInterface $htmlRenderer * - * @return \League\CommonMark\HtmlElement|string - * @throws \ErrorException + * @return HtmlElement|string + * @throws ErrorException */ public function render(AbstractInline $inline, ElementRendererInterface $htmlRenderer) { @@ -35,16 +35,16 @@ public function render(AbstractInline $inline, ElementRendererInterface $htmlRen $url = "https://soundcloud.com/oembed?&format=json&url={$inline->getUrl()}&maxheight=166"; $soundCloud = $this->getContent($url); - //seems that the used soundcloud url is invalid - //or soundcloud is currently not available - if (is_null($soundCloud)) { - throw new \ErrorException('SoundCloud request returned null: ' . $url); + // Seems that the used SoundCloud url is invalid + // or SoundCloud is currently not available + if ($soundCloud === null) { + throw new ErrorException('SoundCloud request returned null: ' . $url); } - //parse the oembed response + // Parse the embed response $soundCloud = json_decode($soundCloud); - //use the oembed html snippet as response + // Use the embed html snippet as response return $soundCloud->html; } diff --git a/src/Inline/Renderer/YouTubeRenderer.php b/src/Inline/Renderer/YouTubeRenderer.php index 52cca42..2940a88 100644 --- a/src/Inline/Renderer/YouTubeRenderer.php +++ b/src/Inline/Renderer/YouTubeRenderer.php @@ -2,14 +2,14 @@ namespace JohnnyHuy\Laravel\Inline\Renderer; -use League\CommonMark\HtmlElement; -use League\CommonMark\Util\Configuration; use JohnnyHuy\Laravel\Inline\Element\YouTube; use League\CommonMark\ElementRendererInterface; +use League\CommonMark\HtmlElement; use League\CommonMark\Inline\Element\AbstractInline; -use League\CommonMark\Util\ConfigurationAwareInterface; use League\CommonMark\Inline\Element\AbstractWebResource; use League\CommonMark\Inline\Renderer\InlineRendererInterface; +use League\CommonMark\Util\Configuration; +use League\CommonMark\Util\ConfigurationAwareInterface; class YouTubeRenderer implements InlineRendererInterface, ConfigurationAwareInterface { @@ -20,9 +20,9 @@ class YouTubeRenderer implements InlineRendererInterface, ConfigurationAwareInte /** * @param AbstractInline|AbstractWebResource $inline - * @param \League\CommonMark\ElementRendererInterface $htmlRenderer + * @param ElementRendererInterface $htmlRenderer * - * @return \League\CommonMark\HtmlElement|string + * @return HtmlElement|string */ public function render(AbstractInline $inline, ElementRendererInterface $htmlRenderer) { @@ -30,7 +30,7 @@ public function render(AbstractInline $inline, ElementRendererInterface $htmlRen throw new \InvalidArgumentException('Incompatible inline type: ' . get_class($inline)); } - //create a new iframe with the given youtube url + // Create a new iframe with the given youtube url $iframe = new HtmlElement('iframe', [ 'width' => 640, 'height' => 390, @@ -39,7 +39,7 @@ public function render(AbstractInline $inline, ElementRendererInterface $htmlRen 'frameborder' => 0, ]); - //return the iframe with a span as wrapper element + // Return the iframe with a span as wrapper element return new HtmlElement('span', ['class' => 'youtube-video'], $iframe); } diff --git a/src/UsefulCommonMarkExtension.php b/src/UsefulCommonMarkExtension.php index 58ef9ff..1714cc0 100755 --- a/src/UsefulCommonMarkExtension.php +++ b/src/UsefulCommonMarkExtension.php @@ -23,7 +23,11 @@ use JohnnyHuy\Laravel\Inline\Element\Codepen; use JohnnyHuy\Laravel\Inline\Parser\CodepenParser; use JohnnyHuy\Laravel\Inline\Renderer\CodepenRenderer; +use League\CommonMark\Block\Parser\BlockParserInterface; +use League\CommonMark\Block\Renderer\BlockRendererInterface; use League\CommonMark\Extension\Extension; +use League\CommonMark\Inline\Parser\InlineParserInterface; +use League\CommonMark\Inline\Renderer\InlineRendererInterface; /** * This is the useful CommonMark extension class. @@ -64,6 +68,7 @@ public function __construct(Container $container) $container->make(CodepenParser::class), $container->make(YouTubeParser::class), $container->make(SoundCloudParser::class), + $container->make(Inline\Parser\ColorParser::class), ]; $this->inlineRenderers = [ @@ -71,21 +76,22 @@ public function __construct(Container $container) Codepen::class => $container->make(CodepenRenderer::class), YouTube::class => $container->make(YouTubeRenderer::class), SoundCloud::class => $container->make(SoundCloudRenderer::class), + Inline\Element\Color::class => $container->make(Inline\Renderer\ColorRenderer::class) ]; $this->blockParsers = [ $container->make(TextAlignmentParser::class), - $container->make(ColorParser::class), + $container->make(Block\Parser\ColorParser::class), ]; $this->blockRenderers = [ TextAlignment::class => $container->make(TextAlignmentRenderer::class), - Color::class => $container->make(ColorRenderer::class), + Block\Element\Color::class => $container->make(Block\Renderer\ColorRenderer::class), ]; } /** - * @return array|\League\CommonMark\Inline\Renderer\InlineRendererInterface[] + * @return array|InlineRendererInterface[] */ public function getInlineRenderers() { @@ -93,7 +99,7 @@ public function getInlineRenderers() } /** - * @return \League\CommonMark\Block\Renderer\BlockRendererInterface[] + * @return BlockRendererInterface[] */ public function getBlockRenderers() { @@ -101,7 +107,7 @@ public function getBlockRenderers() } /** - * @return \League\CommonMark\Inline\Parser\InlineParserInterface[] + * @return InlineParserInterface[] */ public function getInlineParsers() { @@ -109,7 +115,7 @@ public function getInlineParsers() } /** - * @return array|\League\CommonMark\Block\Parser\BlockParserInterface[] + * @return array|BlockParserInterface[] */ public function getBlockParsers() { diff --git a/src/UsefulCommonMarkExtensionServiceProvider.php b/src/UsefulCommonMarkExtensionServiceProvider.php index 7010b86..0a65b5d 100755 --- a/src/UsefulCommonMarkExtensionServiceProvider.php +++ b/src/UsefulCommonMarkExtensionServiceProvider.php @@ -37,10 +37,11 @@ public function register() protected function registerParser() { $this->app->singleton(GistParser::class, function () { return new GistParser(); }); - $this->app->singleton(ColorParser::class, function () { return new ColorParser(); }); $this->app->singleton(CodepenParser::class, function () { return new CodepenParser(); }); $this->app->singleton(YouTubeParser::class, function () { return new YouTubeParser(); }); $this->app->singleton(SoundCloudParser::class, function () { return new SoundCloudParser(); }); + $this->app->singleton(Block\Parser\ColorParser::class, function () { return new Block\Parser\ColorParser(); }); + $this->app->singleton(Inline\Parser\ColorParser::class, function () { return new Inline\Parser\ColorParser(); }); } /** diff --git a/tests/Elements/Inline/ColorTest.php b/tests/Elements/Inline/ColorTest.php new file mode 100644 index 0000000..9f11700 --- /dev/null +++ b/tests/Elements/Inline/ColorTest.php @@ -0,0 +1,61 @@ + + */ +class ColorTest extends BaseTestCase +{ + public static function successfulStrings(): array + { + return [ + [":color red Heading :color test\ntest", "

Heading test\ntest

"], + [":color 155,123,422 red **Heading** :color\ntest", "

red **Heading**\ntest

"], + [':color 155,123,422,50 red **Heading** :color', '

red **Heading**

'], + ['testing :color 155,123,422,50 red **Heading** :color testing', '

testing red **Heading** testing

'], + ]; + } + + public static function failedStrings(): array + { + return [ + [':color @Heading **bold text** test :color test', '

:color @Heading bold text test :color test

'], + [':coloor red Heading **bold text** test :color test', '

:coloor red Heading bold text test :color test

'], + ['color red Heading **bold text** test :color test', '

color red Heading bold text test :color test

'], + ['color red Heading **bold text** test color pink test', '

color red Heading bold text test color pink test

'], + ]; + } + + /** + * @dataProvider successfulStrings + * @param $input + * @param $output + * @throws ExpectationFailedException + * @throws InvalidArgumentException + */ + public function testShouldRender($input, $output) + { + $this->assertSame("$output\n", $this->app->markdown->convertToHtml($input)); + } + + /** + * @dataProvider failedStrings + * @param $input + * @param $output + * @throws ExpectationFailedException + * @throws InvalidArgumentException + */ + public function testShouldNotRender($input, $output) + { + $this->assertSame("$output\n", $this->app->markdown->convertToHtml($input)); + } +} \ No newline at end of file