From b866c646ab68a66337129a834d998b8a63b034ca Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?"J.=20Rene=CC=81e=20Beach"?= <splendidnoise@gmail.com>
Date: Thu, 13 Jun 2013 00:13:46 -0400
Subject: [PATCH] Issue #2014895
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

commit 2b1a9c5884ff4d70d0bcb713c5d719ec96ecd48b
Author: J. Renée Beach <splendidnoise@gmail.com>
Date:   Thu Jun 13 00:10:44 2013 -0400

    Finished styling.

    Signed-off-by: J. Renée Beach <splendidnoise@gmail.com>

commit 2bf4b01125b19a73849762c959ba0f67402a5aa8
Author: J. Renée Beach <splendidnoise@gmail.com>
Date:   Thu Jun 13 00:06:55 2013 -0400

    Basic structure styling.

    Signed-off-by: J. Renée Beach <splendidnoise@gmail.com>

commit 15a26a8d222f573b04b82c6c67db16242452cdee
Author: J. Renée Beach <splendidnoise@gmail.com>
Date:   Wed Jun 12 23:56:02 2013 -0400

    Updated the HTML and CSS

    Signed-off-by: J. Renée Beach <splendidnoise@gmail.com>

commit 50da7bfc4c7c96d66b91355ded39e860b4468495
Author: J. Renée Beach <splendidnoise@gmail.com>
Date:   Wed Jun 12 13:17:30 2013 -0400

    2014895-4

    Signed-off-by: J. Renée Beach <splendidnoise@gmail.com>

Signed-off-by: J. Renée Beach <splendidnoise@gmail.com>
---
 core/modules/filter/css/filter.caption-rtl.css     |   18 +++
 core/modules/filter/css/filter.caption.css         |   34 +++++
 core/modules/filter/filter.module                  |   56 +++++++-
 .../Drupal/filter/Plugin/Filter/FilterCaption.php  |  137 ++++++++++++++++++++
 .../standard/config/filter.format.basic_html.yml   |    5 +
 .../standard/config/filter.format.full_html.yml    |    5 +
 core/themes/bartik/css/style.css                   |   13 ++
 7 files changed, 264 insertions(+), 4 deletions(-)
 create mode 100644 core/modules/filter/css/filter.caption-rtl.css
 create mode 100644 core/modules/filter/css/filter.caption.css
 create mode 100644 core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterCaption.php

diff --git a/core/modules/filter/css/filter.caption-rtl.css b/core/modules/filter/css/filter.caption-rtl.css
new file mode 100644
index 0000000..f4ed201
--- /dev/null
+++ b/core/modules/filter/css/filter.caption-rtl.css
@@ -0,0 +1,18 @@
+/**
+ * @file
+ * Caption filter: rtl styling for displaying image captions.
+ */
+
+/**
+ * Caption alignment.
+ */
+.caption-left {
+  float: right;
+  margin-left: auto;
+  margin-right: 0;
+}
+.caption-right {
+  float: left;
+  margin-left: 0;
+  margin-right: auto;
+}
diff --git a/core/modules/filter/css/filter.caption.css b/core/modules/filter/css/filter.caption.css
new file mode 100644
index 0000000..374cb5db
--- /dev/null
+++ b/core/modules/filter/css/filter.caption.css
@@ -0,0 +1,34 @@
+/**
+ * @file
+ * Caption filter: default styling for displaying image captions.
+ */
+
+.caption {
+  -moz-box-sizing: border-box
+  -webkit-box-sizing: border-box
+  box-sizing: border-box;
+}
+
+/**
+ * Caption alignment.
+ */
+.caption-left {
+  float: left; /* LTR */
+  margin-left: 0; /* LTR */
+}
+.caption-right {
+  float: right; /* LTR */
+  margin-right: 0; /* LTR */
+}
+.caption-center {
+  margin-left: 0;
+  margin-right: 0;
+  text-align: center;
+}
+.caption-center,
+.caption-center .caption-inner {
+  display: inline-block;
+}
+.caption-center {
+  width: 100%;
+}
diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module
index cf0eaef..47e8c05 100644
--- a/core/modules/filter/filter.module
+++ b/core/modules/filter/filter.module
@@ -89,6 +89,14 @@ function filter_theme() {
     'filter_html_image_secure_image' => array(
       'variables' => array('image' => NULL),
     ),
+    'filter_caption' => array(
+      'variables' => array(
+        'image'   => NULL,
+        'caption' => NULL,
+        'align'   => NULL,
+        'width'   => NULL,
+      )
+    )
   );
 }
 
@@ -1425,21 +1433,54 @@ function theme_filter_html_image_secure_image(&$variables) {
 }
 
 /**
+ * Returns HTML for a captioned image.
+ *
+ * @param $variables
+ *   An associative array containing:
+ *   - image: The complete image tag whose image is being captioned.
+ *   - caption: The caption text, or NULL.
+ *   - align: The alignment: 'left', 'center', 'right' or NULL.
+ *   - width: The width of the image.
+ *
+ * @ingroup themeable
+ */
+function theme_filter_caption($variables) {
+  $image   = $variables['image'];
+  $caption = $variables['caption'];
+  $align   = $variables['align'];
+  $width   = $variables['width'];
+  return '<figure class="caption' . (($align === NULL) ? '' : ' caption-' . $align) . '">' .
+         '<div class="caption-inner" style="width: ' . $width . ';">'.
+         $image .
+         '<figcaption class="caption-text">' . $caption . '</figcaption>' .
+         '</div>' .
+         '</figure>';
+}
+
+/**
  * @} End of "defgroup standard_filters".
  */
 
 /**
+ * Implements hook_page_build().
+ */
+function filter_page_build(&$page) {
+  $page['#attached']['library'][] = array('filter', 'caption');
+}
+
+/**
  * Implements hook_library_info().
  */
 function filter_library_info() {
+  $path = drupal_get_path('module', 'filter');
   $libraries['drupal.filter.admin'] = array(
     'title' => 'Filter',
     'version' => VERSION,
     'js' => array(
-      drupal_get_path('module', 'filter') . '/filter.admin.js' => array(),
+       $path . '/filter.admin.js' => array(),
     ),
     'css' => array(
-      drupal_get_path('module', 'filter') . '/css/filter.admin.css'
+      $path . '/css/filter.admin.css'
     ),
     'dependencies' => array(
       array('system', 'jquery'),
@@ -1452,10 +1493,10 @@ function filter_library_info() {
     'title' => 'Filter',
     'version' => VERSION,
     'js' => array(
-      drupal_get_path('module', 'filter') . '/filter.js' => array(),
+      $path . '/filter.js' => array(),
     ),
     'css' => array(
-      drupal_get_path('module', 'filter') . '/css/filter.admin.css'
+      $path . '/css/filter.admin.css'
     ),
     'dependencies' => array(
       array('system', 'jquery'),
@@ -1463,6 +1504,13 @@ function filter_library_info() {
       array('system', 'jquery.once'),
     ),
   );
+  $libraries['caption'] = array(
+    'title' => 'Captions for images and alignments',
+    'version' => VERSION,
+    'css' => array(
+      $path . '/css/filter.caption.css',
+    ),
+  );
 
   return $libraries;
 }
diff --git a/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterCaption.php b/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterCaption.php
new file mode 100644
index 0000000..1815155
--- /dev/null
+++ b/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterCaption.php
@@ -0,0 +1,137 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filter\Plugin\Filter\FilterCaption.
+ */
+
+namespace Drupal\filter\Plugin\Filter;
+
+use Drupal\filter\Annotation\Filter;
+use Drupal\Core\Annotation\Translation;
+use Drupal\filter\Plugin\FilterBase;
+
+/**
+ * Provides a filter to display image captions and align images.
+ *
+ * @Filter(
+ *   id = "filter_caption",
+ *   module = "filter",
+ *   title = @Translation("Display image captions and align images"),
+ *   description = @Translation("Uses data-caption and data-align attributes on &lt;img&gt; tags to caption and align images."),
+ *   type = FILTER_TYPE_TRANSFORM_REVERSIBLE
+ * )
+ */
+class FilterCaption extends FilterBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function process($text, $langcode, $cache, $cache_id) {
+    $search = array();
+    $replace = array();
+
+    // Prevent useless processing if there are no data-caption attributes at all.
+    if (stristr($text, 'data-caption') !== FALSE || stristr($text, 'data-align') !== FALSE) {
+      // Iterate over all images that have data-caption and/or data-align
+      // attributes. Remove these attributes and wrap the image in a caption.
+      $pattern = "#(?:(?:<img\s+|<img\s+[^>]*\s+)(?:data-caption|data-align)\s*.*(?:\s*>|\s*/>|\s*></img>))#i";
+      if (preg_match_all($pattern, $text, $matches)) {
+        foreach ($matches[0] as $image_html) {
+          $dom = filter_dom_load($image_html);
+          $image_node = $dom->getElementsByTagName('img')->item(0);
+          $caption = NULL;
+          $align = NULL;
+
+          // Retrieve, then remove the data-caption and data-align attributes.
+          if ($image_node->hasAttribute('data-caption')) {
+            $caption = $image_node->getAttribute('data-caption');
+            $image_node->removeAttribute('data-caption');
+          }
+          if ($image_node->hasAttribute('data-align')) {
+            $align = $image_node->getAttribute('data-align');
+            $image_node->removeAttribute('data-align');
+          }
+
+          // Given the updated image node, caption, alignment and width: re-render
+          // the image with a caption.
+          $altered_image_html = theme('filter_caption', array(
+            'image'   => $image_node->C14N(),
+            'caption' => $caption,
+            'align'   => $align,
+            'width'   => $this->getImageWidth($image_node),
+          ));
+
+          // Load the new HTML into a new DOMDocument.
+          $dom2 = filter_dom_load($altered_image_html);
+
+          // Locate the snippet of HTML we're interested in.
+          $dom2_image_node = $dom2->getElementsByTagName('body')->item(0)
+                                  ->childNodes->item(0);
+          // Import the new "image" node from the second DOMDocument into the main
+          // one, importing also the child nodes of the new "image" node.
+          $new_image_node = $dom->importNode($dom2_image_node, TRUE);
+          // Finally, replace the original image node with the new image node!
+          $image_node->parentNode->replaceChild($new_image_node, $image_node);
+
+          $search[] = $image_html;
+          $replace[] = filter_dom_serialize($dom);
+        }
+      }
+    }
+
+    return str_replace($search, $replace, $text);
+  }
+
+  /**
+   * Determines the width of the image tag that is being captioned.
+   *
+   * @param DOMNode $image_node
+   *   A DOMNode for an image tag from a DOMDocument. @see filter_dom_load().
+   *
+   * @return string
+   *   The CSS declaration for the image's width, typically a number with "px"
+   *   appended to it; possibly "auto" or even a percentage.
+   */
+  protected function getImageWidth(DOMNode $image_node) {
+    // Retrieve the width attribute, or calculate the width directly from
+    // the image.
+    if ($image_node->hasAttribute('width')) {
+      $width = $image_node->getAttribute('width');
+    }
+    else {
+      if ($image_node->hasAttribute('src')) {
+        list($width) = getimagesize($image_node->getAttribute('src'));
+      }
+      else {
+        // We cannot determine the width so just set it to the default CSS value.
+        $width = 'auto';
+      }
+    }
+
+    // We need to append the 'px' to any numeric widths.
+    if (is_numeric($width)) {
+      $width = $width . 'px';
+    }
+
+    return $width;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function tips($long = FALSE) {
+    if ($long) {
+      return t('
+        <p>You can add image captions and align images left, right or centered. Examples:</p>
+        <ul>
+          <li>Caption an image: <code>&lt;img src="" data-caption="This is a caption" /&gt;</code></li>
+          <li>Algin an image: <code>&lt;img src="" data-align="center" /&gt;</code></li>
+          <li>Caption & align an image: <code>&lt;img src="" data-caption="Alpaca" data-align="right" /&gt;</code></li>
+        </ul>');
+    }
+    else {
+      return t('You can caption images (data-caption="Text") and align images (data-align="center").');
+    }
+  }
+}
diff --git a/core/profiles/standard/config/filter.format.basic_html.yml b/core/profiles/standard/config/filter.format.basic_html.yml
index 5810786..69fc520 100644
--- a/core/profiles/standard/config/filter.format.basic_html.yml
+++ b/core/profiles/standard/config/filter.format.basic_html.yml
@@ -14,6 +14,11 @@ filters:
       allowed_html: '<a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd> <h4> <h5> <h6> <p> <span> <img>'
       filter_html_help: '0'
       filter_html_nofollow: '0'
+  filter_caption:
+    module: filter
+    status: '1'
+    weight: '8'
+    settings: {  }
   filter_html_image_secure:
     module: filter
     status: '1'
diff --git a/core/profiles/standard/config/filter.format.full_html.yml b/core/profiles/standard/config/filter.format.full_html.yml
index 204a342..0265845 100644
--- a/core/profiles/standard/config/filter.format.full_html.yml
+++ b/core/profiles/standard/config/filter.format.full_html.yml
@@ -6,6 +6,11 @@ roles:
   - administrator
 cache: '1'
 filters:
+  filter_caption:
+    module: filter
+    status: '1'
+    weight: '9'
+    settings: {  }
   filter_htmlcorrector:
     module: filter
     status: '1'
diff --git a/core/themes/bartik/css/style.css b/core/themes/bartik/css/style.css
index e312fc7..fb3be54 100644
--- a/core/themes/bartik/css/style.css
+++ b/core/themes/bartik/css/style.css
@@ -1417,6 +1417,19 @@ ol.search-results {
   padding-left: 0;
 }
 
+/* -------------- Captions -------------- */
+
+.caption-inner {
+  background: #F3F3F3;
+  border: 1px solid #CCC;
+  font-size: small;
+  padding: 0.5ex;
+  text-align: center;
+}
+.caption .caption-text {
+  margin-top: 1.5ex;
+}
+
 /* -------------- Shortcut Links -------------- */
 
 .shortcut-wrapper {
-- 
1.7.10.4

