style tags."),
+ * )
+ */
+class CkeditorAccordionVariantDefault extends CkeditorAccordionVariantBase{
+
+ /**
+ * {@inheritdoc}
+ */
+ protected $list_tag = [
+ 'tag' => 'div',
+ 'attributes' => [],
+ ];
+
+ /**
+ * {@inheritdoc}
+ */
+ protected $title_tag = [
+ 'tag' => 'h2',
+ 'attributes' => [],
+ ];
+
+ /**
+ * {@inheritdoc}
+ */
+ protected $description_tag = [
+ 'tag' => 'div',
+ 'attributes' => [],
+ ];
+
+}
diff --git a/src/Plugin/CkeditorAccordionVariantBase.php b/src/Plugin/CkeditorAccordionVariantBase.php
new file mode 100644
index 0000000..5442a8c
--- /dev/null
+++ b/src/Plugin/CkeditorAccordionVariantBase.php
@@ -0,0 +1,79 @@
+ '',
+ 'attributes' => [],
+ ];
+
+ /**
+ * The title tag to be used.
+ *
+ * @var array
+ */
+ protected $title_tag = [
+ 'tag' => '',
+ 'attributes' => [],
+ ];
+
+ /**
+ * The description tag to be used.
+ *
+ * @var array
+ */
+ protected $description_tag = [
+ 'tag' => '',
+ 'attributes' => [],
+ ];
+
+ /**
+ * Retrieves the CKEditor Accordion variant definition.
+ *
+ * @return array
+ * An associative array containing tag and optional attributes for each type
+ * of accordion's element (list, title, description):
+ * - list
+ * - tag
+ * - attributes
+ * - class
+ * - class_name_1
+ * - class_name_2
+ * - ...
+ * - ...
+ * - title
+ * - tag
+ * - attributes
+ * - description
+ * - tag
+ * - attributes
+ */
+ public function getVariantDefinition() {
+ $definition = [
+ 'list' => $this->list_tag,
+ 'title' => $this->title_tag,
+ 'description' => $this->description_tag,
+ ];
+
+ // Allow other modules to alter the variant definition.
+ $context = [
+ 'variant_id' => $this->getBaseId(),
+ ];
+ \Drupal::moduleHandler()->alter('ckeditor_accordion_variant', $definition, $context);
+
+ return $definition;
+ }
+
+}
diff --git a/src/Plugin/CkeditorAccordionVariantInterface.php b/src/Plugin/CkeditorAccordionVariantInterface.php
new file mode 100644
index 0000000..7d76c89
--- /dev/null
+++ b/src/Plugin/CkeditorAccordionVariantInterface.php
@@ -0,0 +1,15 @@
+alterInfo('ckeditor_accordion_ckeditor_accordion_variant_info');
+ $this->setCacheBackend($cache_backend, 'ckeditor_accordion_ckeditor_accordion_variant_plugins');
+ }
+
+}
diff --git a/src/Plugin/Filter/CKEditorAccordion.php b/src/Plugin/Filter/CKEditorAccordion.php
new file mode 100644
index 0000000..49d4965
--- /dev/null
+++ b/src/Plugin/Filter/CKEditorAccordion.php
@@ -0,0 +1,145 @@
+createInstance($config->get('variant'));
+ $defintion = $variant->getVariantDefinition();
+
+ $tags = [
+ 'intial' => [
+ 'list' => [
+ 'tag' => 'dl',
+ ],
+ 'title' => [
+ 'tag' => 'dt',
+ ],
+ 'description' => [
+ 'tag' => 'dd',
+ ],
+ ],
+ 'target' => $defintion,
+ ];
+
+ // Add the needed attributes for the accessible script.
+ $tags['target']['list']['attributes']['data-aria-accordion'] = 'data-aria-accordion';
+ $tags['target']['list']['attributes']['data-aria-accordion-allow-toggle'] = 'data-aria-accordion-allow-toggle';
+ $tags['target']['list']['attributes']['data-aria-accordion-panel-transition'] = 'data-aria-accordion-panel-transition';
+ $tags['target']['title']['attributes']['data-aria-accordion-heading'] = 'data-aria-accordion-heading';
+ $tags['target']['description']['attributes']['data-aria-accordion-panel'] = 'data-aria-accordion-panel';
+
+ if (!$config->get('collapse_all')) {
+ $tags['target']['list']['attributes']['data-aria-accordion-open-default'] = '';
+ }
+ if ($config->get('keep_rows_open')) {
+ $tags['target']['list']['attributes']['data-aria-accordion-allow-multiple'] = 'data-aria-accordion-allow-multiple';
+ }
+
+ $initial_class_name = 'ckeditor-accordion';
+
+ // Load the text into a dom object.
+ $dom = new \DOMDocument();
+ $dom->loadHTML(mb_convert_encoding($text, 'HTML-ENTITIES', 'UTF-8'));
+ $xpath = new \DOMXPath($dom);
+
+ // Find and replace title tags.
+ foreach ($xpath->query("//" . $tags['intial']['list']['tag'] . "[contains(@class, '$initial_class_name')]/" . $tags['intial']['title']['tag']) as $title) {
+ // Create the new title tag.
+ $this->replaceElementTag($dom, $title, $tags['target']['title']['tag'], $tags['target']['title']['attributes']);
+ }
+
+ // Find and replace description tags.
+ foreach ($xpath->query("//" . $tags['intial']['list']['tag'] . "[contains(@class, '$initial_class_name')]/" . $tags['intial']['description']['tag']) as $description) {
+ // Create the new description tag.
+ $this->replaceElementTag($dom, $description, $tags['target']['description']['tag'], $tags['target']['description']['attributes']);
+ }
+
+ // Find and replace list tags.
+ foreach ($xpath->query("//" . $tags['intial']['list']['tag'] . "[contains(@class, '$initial_class_name')]") as $accordion) {
+ // Remove the legacy class to avoid conflicts.
+ $classes = explode(' ', $accordion->getAttribute('class'));
+ if (($key = array_search($initial_class_name, $classes)) !== FALSE) {
+ unset($classes[$key]);
+ }
+ empty($classes) ? $accordion->removeAttribute('class') : $accordion->setAttribute('class', implode(' ', $classes));
+
+ // Create the new list tag.
+ $this->replaceElementTag($dom, $accordion, $tags['target']['list']['tag'], $tags['target']['list']['attributes']);
+ }
+
+ $new_text = $dom->saveXml($dom->documentElement);
+
+ return new FilterProcessResult($new_text);
+ }
+
+ /**
+ * Replaces an element's tag with a new one.
+ *
+ * @param \DOMDocument $dom
+ * The DOMDocument the element is attached to.
+ *
+ * @param \DOMElement $element
+ * The element for which the tag needs to be replaced.
+ *
+ * @param string $new_tag
+ * The new tag to be used.
+ *
+ * @param array $new_attributes
+ * The attributes to be added to the new tag as key => value.
+ *
+ * @param bool $preserve_attributes
+ * Wether to keep attributes from the given element in the new one.
+ */
+ protected function replaceElementTag(\DOMDocument &$dom, \DOMElement $element, string $new_tag, array $new_attributes = [], $preserve_attributes = TRUE) {
+ $new_element = $dom->createElement($new_tag);
+
+ // Add new attributes to the new element.
+ foreach ($new_attributes as $attribute => $value) {
+ if (is_array($value)) {
+ $value = implode(' ', $value);
+ }
+ $new_element->setAttribute($attribute, $value);
+ }
+
+ if ($preserve_attributes) {
+ // Add all attributes from the intial element.
+ foreach ($element->attributes as $attribute) {
+ $value = $attribute->nodeValue;
+
+ // Make sure we keep the intial attribute value if one exists.
+ if (!empty($new_element->getAttribute($attribute->nodeName))) {
+ $value = implode(' ', [$new_element->getAttribute($attribute->nodeName), $attribute->nodeValue]);
+ }
+
+ $new_element->setAttribute($attribute->nodeName, $value);
+ }
+ }
+
+ // Move the children of the current node to the new one
+ while ($element->hasChildNodes()) {
+ $new_element->appendChild($element->firstChild);
+ }
+
+ // Replace the current element with the new one.
+ $element->parentNode->replaceChild($new_element, $element);
+ }
+}