From 2fb5923db660b6b71f889d5ea4207de08fbff721 Mon Sep 17 00:00:00 2001 From: Colin O'Dell Date: Tue, 26 May 2020 10:06:22 -0400 Subject: [PATCH] Import the Attributes extension (#484) (#489) This extension is based https://github.com/webuni/commonmark-attributes-extension, imported and relicensed with permission from the maintainer: https://github.com/thephpleague/commonmark/issues/474#issuecomment-629954998 --- CHANGELOG.md | 1 + docs/1.5/extensions/attributes.md | 58 ++++++ docs/1.5/extensions/overview.md | 1 + docs/_data/menu.yml | 1 + .../Attributes/AttributesExtension.php | 32 ++++ .../Attributes/Event/AttributesListener.php | 174 ++++++++++++++++++ src/Extension/Attributes/Node/Attributes.php | 62 +++++++ .../Attributes/Node/AttributesInline.php | 50 +++++ .../Parser/AttributesBlockParser.php | 45 +++++ .../Parser/AttributesInlineParser.php | 59 ++++++ .../Parser/AttributesParserTrait.php | 91 +++++++++ .../Extension/Attributes/LocalDataTest.php | 51 +++++ .../Extension/Attributes/data/code.html | 16 ++ .../Extension/Attributes/data/code.md | 18 ++ .../Extension/Attributes/data/image.html | 1 + .../Extension/Attributes/data/image.md | 1 + .../Attributes/data/list_attributes.html | 25 +++ .../Attributes/data/list_attributes.md | 29 +++ .../Attributes/data/more_attributes.html | 7 + .../Attributes/data/more_attributes.md | 20 ++ .../Attributes/data/special_attributes.html | 11 ++ .../Attributes/data/special_attributes.md | 24 +++ .../Attributes/data/table_attributes.html | 14 ++ .../Attributes/data/table_attributes.md | 4 + 24 files changed, 795 insertions(+) create mode 100644 docs/1.5/extensions/attributes.md create mode 100644 src/Extension/Attributes/AttributesExtension.php create mode 100644 src/Extension/Attributes/Event/AttributesListener.php create mode 100644 src/Extension/Attributes/Node/Attributes.php create mode 100644 src/Extension/Attributes/Node/AttributesInline.php create mode 100644 src/Extension/Attributes/Parser/AttributesBlockParser.php create mode 100644 src/Extension/Attributes/Parser/AttributesInlineParser.php create mode 100644 src/Extension/Attributes/Parser/AttributesParserTrait.php create mode 100644 tests/functional/Extension/Attributes/LocalDataTest.php create mode 100644 tests/functional/Extension/Attributes/data/code.html create mode 100644 tests/functional/Extension/Attributes/data/code.md create mode 100644 tests/functional/Extension/Attributes/data/image.html create mode 100644 tests/functional/Extension/Attributes/data/image.md create mode 100644 tests/functional/Extension/Attributes/data/list_attributes.html create mode 100644 tests/functional/Extension/Attributes/data/list_attributes.md create mode 100644 tests/functional/Extension/Attributes/data/more_attributes.html create mode 100644 tests/functional/Extension/Attributes/data/more_attributes.md create mode 100644 tests/functional/Extension/Attributes/data/special_attributes.html create mode 100644 tests/functional/Extension/Attributes/data/special_attributes.md create mode 100644 tests/functional/Extension/Attributes/data/table_attributes.html create mode 100644 tests/functional/Extension/Attributes/data/table_attributes.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b1d1417ae..c7923b4ac0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Updates should follow the [Keep a CHANGELOG](https://keepachangelog.com/) princi ### Added + - Added new `AttributesExtension` based on (#474) - Added new `FootnoteExtension` based on (#474) - Added a new `MentionParser` to replace `InlineMentionParser` with more flexibility and customization - Added the ability to render `TableOfContents` nodes anywhere in a document (given by a placeholder) diff --git a/docs/1.5/extensions/attributes.md b/docs/1.5/extensions/attributes.md new file mode 100644 index 0000000000..3df0176962 --- /dev/null +++ b/docs/1.5/extensions/attributes.md @@ -0,0 +1,58 @@ +--- +layout: default +title: Attributes Extension +description: The AttributesExtension allows HTML attributes to be added from within the document. +redirect_from: /extensions/attributes/ +--- + +# Attributes + +The `AttributesExtension` allows HTML attributes to be added from within the document. + +## Attribute Syntax + +The basic syntax was inspired by [Kramdown](http://kramdown.gettalong.org/syntax.html#attribute-list-definitions)'s Attribute Lists feature. + +You can assign any attribute to a block-level element. Just directly prepend or follow the block with a block inline attribute list. +That consists of a left curly brace, optionally followed by a colon, the attribute definitions and a right curly brace: + +```markdown +> A nice blockquote +{: title="Blockquote title"} + +{#id .class} +## Header +``` + +As with a block-level element you can assign any attribute to a span-level elements using a span inline attribute list, +that has the same syntax and must immediately follow the span-level element: + +```markdown +This is *red*{style="color: red"}. +``` + +## Usage + +Configure your `Environment` as usual and simply add the `AttributesExtension`: + +```php +addExtension(new AttributesExtension()); + +// Set your configuration if needed +$config = [ + // ... +]; + +// Instantiate the converter engine and start converting some Markdown! +$converter = new CommonMarkConverter($config, $environment); +echo $converter->convertToHtml('# Hello World!'); +``` diff --git a/docs/1.5/extensions/overview.md b/docs/1.5/extensions/overview.md index d8bb535913..21ce49ad71 100644 --- a/docs/1.5/extensions/overview.md +++ b/docs/1.5/extensions/overview.md @@ -73,6 +73,7 @@ These extensions are not part of GFM, but can be useful in many cases: | Extension | Purpose | Documentation | | --------- | ------- | ------------- | +| `AttributesExtension` | Add HTML attributes (like `id` and `class`) from within the Markdown content | [Documentation](/1.5/extensions/attributes/) | | `ExternalLinkExtension` | Tags external links with additional markup | [Documentation](/1.5/extensions/external-links/) | | `FootnoteExtension` | Add footnote references throughout the document and show a listing of them at the bottom | [Documentation](/1.5/extensions/footnotes/) | | `HeadingPermalinkExtension` | Makes heading elements linkable | [Documentation](/1.5/extensions/heading-permalinks/) | diff --git a/docs/_data/menu.yml b/docs/_data/menu.yml index 7c0137e181..c35f967ace 100644 --- a/docs/_data/menu.yml +++ b/docs/_data/menu.yml @@ -14,6 +14,7 @@ version: 'Overview': '/1.5/extensions/overview/' 'CommonMark': '/1.5/extensions/commonmark/' 'Github-Flavored Markdown': '/1.5/extensions/github-flavored-markdown/' + 'Attributes': '/1.5/extensions/attributes/' 'Autolinks': '/1.5/extensions/autolinks/' 'Disallowed Raw HTML': '/1.5/extensions/disallowed-raw-html/' 'External Links': '/1.5/extensions/external-links/' diff --git a/src/Extension/Attributes/AttributesExtension.php b/src/Extension/Attributes/AttributesExtension.php new file mode 100644 index 0000000000..f98ac26acd --- /dev/null +++ b/src/Extension/Attributes/AttributesExtension.php @@ -0,0 +1,32 @@ + + * (c) 2015 Martin Hasoň + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace League\CommonMark\Extension\Attributes; + +use League\CommonMark\ConfigurableEnvironmentInterface; +use League\CommonMark\Event\DocumentParsedEvent; +use League\CommonMark\Extension\Attributes\Event\AttributesListener; +use League\CommonMark\Extension\Attributes\Parser\AttributesBlockParser; +use League\CommonMark\Extension\Attributes\Parser\AttributesInlineParser; +use League\CommonMark\Extension\ExtensionInterface; + +final class AttributesExtension implements ExtensionInterface +{ + public function register(ConfigurableEnvironmentInterface $environment) + { + $environment->addBlockParser(new AttributesBlockParser()); + $environment->addInlineParser(new AttributesInlineParser()); + $environment->addEventListener(DocumentParsedEvent::class, [new AttributesListener(), 'processDocument']); + } +} diff --git a/src/Extension/Attributes/Event/AttributesListener.php b/src/Extension/Attributes/Event/AttributesListener.php new file mode 100644 index 0000000000..7de120aa93 --- /dev/null +++ b/src/Extension/Attributes/Event/AttributesListener.php @@ -0,0 +1,174 @@ + + * (c) 2015 Martin Hasoň + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace League\CommonMark\Extension\Attributes\Event; + +use League\CommonMark\Block\Element\AbstractBlock; +use League\CommonMark\Block\Element\FencedCode; +use League\CommonMark\Block\Element\ListBlock; +use League\CommonMark\Block\Element\ListItem; +use League\CommonMark\Event\DocumentParsedEvent; +use League\CommonMark\Extension\Attributes\Node\Attributes; +use League\CommonMark\Extension\Attributes\Node\AttributesInline; +use League\CommonMark\Inline\Element\AbstractInline; +use League\CommonMark\Node\Node; + +final class AttributesListener +{ + private const DIRECTION_PREFIX = 'prefix'; + private const DIRECTION_SUFFIX = 'suffix'; + + public function processDocument(DocumentParsedEvent $event): void + { + $walker = $event->getDocument()->walker(); + while ($event = $walker->next()) { + $node = $event->getNode(); + if (!$node instanceof AttributesInline && ($event->isEntering() || !$node instanceof Attributes)) { + continue; + } + + [$target, $direction] = self::findTargetAndDirection($node); + + if ($target instanceof AbstractBlock || $target instanceof AbstractInline) { + $parent = $target->parent(); + if ($parent instanceof ListItem && $parent->parent() instanceof ListBlock && $parent->parent()->isTight()) { + $target = $parent; + } + + if ($direction === self::DIRECTION_SUFFIX) { + $attributes = self::merge($target, $node->getAttributes()); + } else { + $attributes = self::merge($node->getAttributes(), $target); + } + + $target->data['attributes'] = $attributes; + } + + if ($node instanceof AbstractBlock && $node->endsWithBlankLine() && $node->next() && $node->previous()) { + $previous = $node->previous(); + if ($previous instanceof AbstractBlock) { + $previous->setLastLineBlank(true); + } + } + + $node->detach(); + } + } + + /** + * @param Node $node + * + * @return array + */ + private static function findTargetAndDirection(Node $node): array + { + $target = null; + $direction = null; + $previous = $next = $node; + while (true) { + $previous = self::getPrevious($previous); + $next = self::getNext($next); + + if ($previous === null && $next === null) { + if (!$node->parent() instanceof FencedCode) { + $target = $node->parent(); + $direction = self::DIRECTION_SUFFIX; + } + + break; + } + + if ($node instanceof AttributesInline && ($previous === null || ($previous instanceof AbstractInline && $node->isBlock()))) { + continue; + } + + if ($previous !== null && !self::isAttributesNode($previous)) { + $target = $previous; + $direction = self::DIRECTION_SUFFIX; + + break; + } + + if ($next !== null && !self::isAttributesNode($next)) { + $target = $next; + $direction = self::DIRECTION_PREFIX; + + break; + } + } + + return [$target, $direction]; + } + + private static function getPrevious(?Node $node = null): ?Node + { + $previous = $node instanceof Node ? $node->previous() : null; + + if ($previous instanceof AbstractBlock && $previous->endsWithBlankLine()) { + $previous = null; + } + + return $previous; + } + + private static function getNext(?Node $node = null): ?Node + { + $next = $node instanceof Node ? $node->next() : null; + + if ($node instanceof AbstractBlock && $node->endsWithBlankLine()) { + $next = null; + } + + return $next; + } + + private static function isAttributesNode(Node $node): bool + { + return $node instanceof Attributes || $node instanceof AttributesInline; + } + + /** + * @param AbstractBlock|AbstractInline|array $attributes1 + * @param AbstractBlock|AbstractInline|array $attributes2 + * + * @return array + */ + private static function merge($attributes1, $attributes2): array + { + $attributes = []; + foreach ([$attributes1, $attributes2] as $arg) { + if ($arg instanceof AbstractBlock || $arg instanceof AbstractInline) { + $arg = $arg->data['attributes'] ?? []; + } + + /** @var array $arg */ + $arg = (array) $arg; + if (isset($arg['class'])) { + foreach (\array_filter(\explode(' ', \trim($arg['class']))) as $class) { + $attributes['class'][] = $class; + } + + unset($arg['class']); + } + + $attributes = \array_merge($attributes, $arg); + } + + if (isset($attributes['class'])) { + $attributes['class'] = \implode(' ', $attributes['class']); + } + + return $attributes; + } +} diff --git a/src/Extension/Attributes/Node/Attributes.php b/src/Extension/Attributes/Node/Attributes.php new file mode 100644 index 0000000000..13cd5fe28d --- /dev/null +++ b/src/Extension/Attributes/Node/Attributes.php @@ -0,0 +1,62 @@ + + * (c) 2015 Martin Hasoň + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace League\CommonMark\Extension\Attributes\Node; + +use League\CommonMark\Block\Element\AbstractBlock; +use League\CommonMark\Cursor; + +final class Attributes extends AbstractBlock +{ + /** @var array */ + private $attributes; + + /** + * @param array $attributes + */ + public function __construct(array $attributes) + { + $this->attributes = $attributes; + } + + /** + * @return array + */ + public function getAttributes(): array + { + return $this->attributes; + } + + public function canContain(AbstractBlock $block): bool + { + return false; + } + + public function isCode(): bool + { + return false; + } + + public function matchesNextLine(Cursor $cursor): bool + { + $this->setLastLineBlank($cursor->isBlank()); + + return false; + } + + public function shouldLastLineBeBlank(Cursor $cursor, int $currentLineNumber): bool + { + return false; + } +} diff --git a/src/Extension/Attributes/Node/AttributesInline.php b/src/Extension/Attributes/Node/AttributesInline.php new file mode 100644 index 0000000000..01cfa1825c --- /dev/null +++ b/src/Extension/Attributes/Node/AttributesInline.php @@ -0,0 +1,50 @@ + + * (c) 2015 Martin Hasoň + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace League\CommonMark\Extension\Attributes\Node; + +use League\CommonMark\Inline\Element\AbstractInline; + +final class AttributesInline extends AbstractInline +{ + /** @var array */ + public $attributes; + + /** @var bool */ + public $block; + + /** + * @param array $attributes + * @param bool $block + */ + public function __construct(array $attributes, bool $block) + { + $this->attributes = $attributes; + $this->block = $block; + $this->data = ['delim' => true]; // TODO: Re-implement as a delimiter? + } + + /** + * @return array + */ + public function getAttributes(): array + { + return $this->attributes; + } + + public function isBlock(): bool + { + return $this->block; + } +} diff --git a/src/Extension/Attributes/Parser/AttributesBlockParser.php b/src/Extension/Attributes/Parser/AttributesBlockParser.php new file mode 100644 index 0000000000..8839def847 --- /dev/null +++ b/src/Extension/Attributes/Parser/AttributesBlockParser.php @@ -0,0 +1,45 @@ + + * (c) 2015 Martin Hasoň + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace League\CommonMark\Extension\Attributes\Parser; + +use League\CommonMark\Block\Parser\BlockParserInterface; +use League\CommonMark\ContextInterface; +use League\CommonMark\Cursor; +use League\CommonMark\Extension\Attributes\Node\Attributes; + +final class AttributesBlockParser implements BlockParserInterface +{ + use AttributesParserTrait; + + public function parse(ContextInterface $context, Cursor $cursor): bool + { + $state = $cursor->saveState(); + $attributes = $this->parseAttributes($cursor); + if ($attributes === []) { + return false; + } + + if ($cursor->getNextNonSpaceCharacter() !== null) { + $cursor->restoreState($state); + + return false; + } + + $context->addBlock(new Attributes($attributes)); + $context->setBlocksParsed(true); + + return true; + } +} diff --git a/src/Extension/Attributes/Parser/AttributesInlineParser.php b/src/Extension/Attributes/Parser/AttributesInlineParser.php new file mode 100644 index 0000000000..8b052c47c3 --- /dev/null +++ b/src/Extension/Attributes/Parser/AttributesInlineParser.php @@ -0,0 +1,59 @@ + + * (c) 2015 Martin Hasoň + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace League\CommonMark\Extension\Attributes\Parser; + +use League\CommonMark\Extension\Attributes\Node\AttributesInline; +use League\CommonMark\Inline\Parser\InlineParserInterface; +use League\CommonMark\InlineParserContext; + +final class AttributesInlineParser implements InlineParserInterface +{ + use AttributesParserTrait; + + /** + * {@inheritdoc} + */ + public function getCharacters(): array + { + return [' ', '{']; + } + + public function parse(InlineParserContext $inlineContext): bool + { + $cursor = $inlineContext->getCursor(); + if ($cursor->getNextNonSpaceCharacter() !== '{') { + return false; + } + + $char = $cursor->getCharacter(); + if ($char === '{') { + $char = (string) $cursor->getCharacter($cursor->getPosition() - 1); + } + + $attributes = $this->parseAttributes($cursor); + if ($attributes === []) { + return false; + } + + if ($char === '') { + $cursor->advanceToNextNonSpaceOrNewline(); + } + + $node = new AttributesInline($attributes, $char === ' ' || $char === ''); + $inlineContext->getContainer()->appendChild($node); + + return true; + } +} diff --git a/src/Extension/Attributes/Parser/AttributesParserTrait.php b/src/Extension/Attributes/Parser/AttributesParserTrait.php new file mode 100644 index 0000000000..bfcaf68dc7 --- /dev/null +++ b/src/Extension/Attributes/Parser/AttributesParserTrait.php @@ -0,0 +1,91 @@ + + * (c) 2015 Martin Hasoň + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace League\CommonMark\Extension\Attributes\Parser; + +use League\CommonMark\Cursor; +use League\CommonMark\Util\RegexHelper; + +trait AttributesParserTrait +{ + /** + * @param Cursor $cursor + * + * @return array + */ + private function parseAttributes(Cursor $cursor): array + { + $state = $cursor->saveState(); + $cursor->advanceToNextNonSpaceOrNewline(); + if ($cursor->getCharacter() !== '{') { + $cursor->restoreState($state); + + return []; + } + + $cursor->advanceBy(1); + if ($cursor->getCharacter() === ':') { + $cursor->advanceBy(1); + } + + $attributes = []; + $regex = '/^\s*([.#][_a-z0-9-]+|' . RegexHelper::PARTIAL_ATTRIBUTENAME . RegexHelper::PARTIAL_ATTRIBUTEVALUESPEC . ')(?match($regex))) { + if ($attribute[0] === '#') { + $attributes['id'] = \substr($attribute, 1); + + continue; + } + + if ($attribute[0] === '.') { + $attributes['class'][] = \substr($attribute, 1); + + continue; + } + + [$name, $value] = \explode('=', $attribute, 2); + $first = $value[0]; + $last = \substr($value, -1); + if ((($first === '"' && $last === '"') || ($first === "'" && $last === "'")) && \strlen($value) > 1) { + $value = \substr($value, 1, -1); + } + + if (\strtolower(\trim($name)) === 'class') { + foreach (\array_filter(\explode(' ', \trim($value))) as $class) { + $attributes['class'][] = $class; + } + } else { + $attributes[trim($name)] = trim($value); + } + } + + if ($cursor->match('/}/') === null) { + $cursor->restoreState($state); + + return []; + } + + if ($attributes === []) { + $cursor->restoreState($state); + + return []; + } + + if (isset($attributes['class'])) { + $attributes['class'] = \implode(' ', (array) $attributes['class']); + } + + return $attributes; + } +} diff --git a/tests/functional/Extension/Attributes/LocalDataTest.php b/tests/functional/Extension/Attributes/LocalDataTest.php new file mode 100644 index 0000000000..c30fa7e406 --- /dev/null +++ b/tests/functional/Extension/Attributes/LocalDataTest.php @@ -0,0 +1,51 @@ + + * (c) 2015 Martin Hasoň + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace League\CommonMark\Tests\Functional\Extension\Attributes; + +use League\CommonMark\CommonMarkConverter; +use League\CommonMark\Environment; +use League\CommonMark\Extension\Attributes\AttributesExtension; +use League\CommonMark\Tests\Functional\AbstractLocalDataTest; + +/** + * @internal + */ +final class LocalDataTest extends AbstractLocalDataTest +{ + protected function setUp(): void + { + $environment = Environment::createGFMEnvironment(); + $environment->addExtension(new AttributesExtension()); + $this->converter = new CommonMarkConverter([], $environment); + } + + /** + * @dataProvider dataProvider + */ + public function testRenderer(string $markdown, string $html, string $testName): void + { + $this->assertMarkdownRendersAs($markdown, $html, $testName); + } + + /** + * @return iterable + */ + public function dataProvider(): iterable + { + foreach ($this->loadTests(__DIR__ . '/data', '*.md') as $test) { + yield $test; + } + } +} diff --git a/tests/functional/Extension/Attributes/data/code.html b/tests/functional/Extension/Attributes/data/code.html new file mode 100644 index 0000000000..34dd68ec64 --- /dev/null +++ b/tests/functional/Extension/Attributes/data/code.html @@ -0,0 +1,16 @@ +
{#par1}
+Paragraph with a.
+
+Header with attributes {#header1}
+----------------------
+
+### Header with attributes ### {#header2}
+
+### Header no attributes ###
+
+{#par1}
+Paragraph **with bold**.
+
+Paragraph with *emphasis*{.chello}
+   {#par2}
+
diff --git a/tests/functional/Extension/Attributes/data/code.md b/tests/functional/Extension/Attributes/data/code.md new file mode 100644 index 0000000000..6e34a89fe8 --- /dev/null +++ b/tests/functional/Extension/Attributes/data/code.md @@ -0,0 +1,18 @@ +{.classA} +```markdown +{#par1} +Paragraph with a. + +Header with attributes {#header1} +---------------------- + +### Header with attributes ### {#header2} + +### Header no attributes ### + +{#par1} +Paragraph **with bold**. + +Paragraph with *emphasis*{.chello} + {#par2} +``` diff --git a/tests/functional/Extension/Attributes/data/image.html b/tests/functional/Extension/Attributes/data/image.html new file mode 100644 index 0000000000..5cd0fee5be --- /dev/null +++ b/tests/functional/Extension/Attributes/data/image.html @@ -0,0 +1 @@ +

some image title

diff --git a/tests/functional/Extension/Attributes/data/image.md b/tests/functional/Extension/Attributes/data/image.md new file mode 100644 index 0000000000..5eee1d4775 --- /dev/null +++ b/tests/functional/Extension/Attributes/data/image.md @@ -0,0 +1 @@ +![some image title](url-of-the-image.jpg){.center} diff --git a/tests/functional/Extension/Attributes/data/list_attributes.html b/tests/functional/Extension/Attributes/data/list_attributes.html new file mode 100644 index 0000000000..45cfb6d3eb --- /dev/null +++ b/tests/functional/Extension/Attributes/data/list_attributes.html @@ -0,0 +1,25 @@ +
    +
  • list item
  • +
  • list item
  • +
+
    +
  • +

    list item

    +

    Another paragraph

    +
  • +
  • +

    list item

    +
  • +
+
    +
  • +

    list item

    +
  • +
  • +

    list item

    +
  • +
+
    +
  • this li will get the class lala
  • +
  • this one will get the class foo
  • +
diff --git a/tests/functional/Extension/Attributes/data/list_attributes.md b/tests/functional/Extension/Attributes/data/list_attributes.md new file mode 100644 index 0000000000..64a2ffc18e --- /dev/null +++ b/tests/functional/Extension/Attributes/data/list_attributes.md @@ -0,0 +1,29 @@ +* list item +* list item {.classA} + {.classB} +{.classC} + + +{.classA} +* {.classD} + + {.classB} + {.classC} list item + + {#id1} + Another paragraph + + {.classE} +* list item + + +{.classA} +* list item {.classB} + {.classC} + +* list item {.classD} + {.classE} + +- {.lala} this li will get the class lala +- this one will get the class foo {.foo} +{.bar} diff --git a/tests/functional/Extension/Attributes/data/more_attributes.html b/tests/functional/Extension/Attributes/data/more_attributes.html new file mode 100644 index 0000000000..d239ae5515 --- /dev/null +++ b/tests/functional/Extension/Attributes/data/more_attributes.html @@ -0,0 +1,7 @@ +

Paragraph with a.

+

Header with attributes

+

Header with attributes ###

+

Header no attributes

+

Paragraph with a.

+

Paragraph with emphasis

+

Invalid

diff --git a/tests/functional/Extension/Attributes/data/more_attributes.md b/tests/functional/Extension/Attributes/data/more_attributes.md new file mode 100644 index 0000000000..c7940cd84c --- /dev/null +++ b/tests/functional/Extension/Attributes/data/more_attributes.md @@ -0,0 +1,20 @@ +{#par1} +Paragraph with a. + +Header with attributes {#header1} +---------------------- + +### Header with attributes ### {#header2} + +### Header no attributes ### + +{#par1} +{.first} +{.second}Paragraph with a. {.third} +{.fourth} +{.fifth} + +Paragraph with *emphasis*{.chello} + {#par2} + +{#invalid} [Invalid](example.com) diff --git a/tests/functional/Extension/Attributes/data/special_attributes.html b/tests/functional/Extension/Attributes/data/special_attributes.html new file mode 100644 index 0000000000..b5c1710e51 --- /dev/null +++ b/tests/functional/Extension/Attributes/data/special_attributes.html @@ -0,0 +1,11 @@ +

Header 1

+

Header 2

+

The Site

+

The Site

+

link img +bold paragraph

+

this is just normal text

+

some { brackets

+

some } brackets

+

some { } brackets

+

A link inside of an emphasis tag: link.

diff --git a/tests/functional/Extension/Attributes/data/special_attributes.md b/tests/functional/Extension/Attributes/data/special_attributes.md new file mode 100644 index 0000000000..7ed275489f --- /dev/null +++ b/tests/functional/Extension/Attributes/data/special_attributes.md @@ -0,0 +1,24 @@ +Header 1 {#header1} +======== + +## Header 2 ## +{#header2} + +## The Site {.main} + +## The Site ## +{.main .shine #the-site} + +[link](url){#id1 .class1} ![img](url){#id2 .class2} +**bold**{.bold} paragraph +{#text} + +this is just normal text {.main .shine #the-site} + +some { brackets + +some } brackets + +some { } brackets + +A link inside of an emphasis tag: *[link](http://url.com){target="_blank"}*. diff --git a/tests/functional/Extension/Attributes/data/table_attributes.html b/tests/functional/Extension/Attributes/data/table_attributes.html new file mode 100644 index 0000000000..3d301b7398 --- /dev/null +++ b/tests/functional/Extension/Attributes/data/table_attributes.html @@ -0,0 +1,14 @@ + + + + + + + + + + + + + +
hh
c1c2
diff --git a/tests/functional/Extension/Attributes/data/table_attributes.md b/tests/functional/Extension/Attributes/data/table_attributes.md new file mode 100644 index 0000000000..3d778ce929 --- /dev/null +++ b/tests/functional/Extension/Attributes/data/table_attributes.md @@ -0,0 +1,4 @@ +h | h +----------|--- +{.head} c1 |{.head} c2 +{summary="Table summary" .class1 style="color:red" border=3 width="50%" frame=lhs rules=cols cellspacing=2em cellpadding=4px}