Commit 500c0820 authored by alexpott's avatar alexpott

Issue #2014895 by Wim Leers, jessebeach: Image captions & alignment.

parent 6e4b4135
/**
* @file
* Caption filter: default styling for displaying image captions.
*/
/**
* Essentials, based on http://stackoverflow.com/a/13363408.
*/
.caption {
display: table;
}
.caption > * {
display: block;
max-width: 100%;
}
.caption > figcaption {
display: table-caption;
caption-side: bottom;
max-width: none;
}
/**
* Caption alignment.
*/
.caption-left {
float: left; /* LTR */
margin-left: 0; /* LTR */
}
[dir=rtl] .caption-left {
float: right;
margin-left: auto;
margin-right: 0;
}
.caption-right {
float: right; /* LTR */
margin-right: 0; /* LTR */
}
[dir=rtl] .caption-right {
float: left;
margin-left: 0;
margin-right: auto;
}
.caption-center {
margin-left: auto;
margin-right: auto;
text-align: center;
}
......@@ -89,6 +89,15 @@ function filter_theme() {
'filter_html_image_secure_image' => array(
'variables' => array('image' => NULL),
),
'filter_caption' => array(
'variables' => array(
'node' => NULL,
'tag' => NULL,
'caption' => NULL,
'align' => NULL,
),
'template' => 'filter-caption',
)
);
}
......@@ -1576,6 +1585,13 @@ function theme_filter_html_image_secure_image(&$variables) {
* @} End of "defgroup standard_filters".
*/
/**
* Implements hook_page_build().
*/
function filter_page_build(&$page) {
$page['#attached']['library'][] = array('filter', 'caption');
}
/**
* Implements hook_library_info().
*/
......@@ -1625,6 +1641,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;
}
<?php
/**
* @file
* Contains \Drupal\filter\Plugin\Filter\FilterCaption.
*/
namespace Drupal\filter\Plugin\Filter;
use Drupal\Component\Utility\String;
use Drupal\Component\Utility\Unicode;
use Drupal\Component\Utility\Xss;
use Drupal\Core\Annotation\Translation;
use Drupal\filter\Annotation\Filter;
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();
if (stristr($text, 'data-caption') !== FALSE || stristr($text, 'data-align') !== FALSE) {
$dom = filter_dom_load($text);
$xpath = new \DOMXPath($dom);
foreach ($xpath->query('//*[@data-caption or @data-align]') as $node) {
$caption = NULL;
$align = NULL;
// Retrieve, then remove the data-caption and data-align attributes.
if ($node->hasAttribute('data-caption')) {
$caption = String::checkPlain($node->getAttribute('data-caption'));
$node->removeAttribute('data-caption');
// Sanitize caption: decode HTML encoding, limit allowed HTML tags.
$caption = String::decodeEntities($caption);
$caption = Xss::filter($caption);
// The caption must be non-empty.
if (Unicode::strlen($caption) === 0) {
$caption = NULL;
}
}
if ($node->hasAttribute('data-align')) {
$align = $node->getAttribute('data-align');
$node->removeAttribute('data-align');
// Only allow 3 values: 'left', 'center' and 'right'.
if (!in_array($align, array('left', 'center', 'right'))) {
$align = NULL;
}
}
// If neither attribute has a value after validation, then don't
// transform the HTML.
if ($caption === NULL && $align === NULL) {
continue;
}
// Given the updated node, caption and alignment: re-render it with a
// caption.
$altered_html = theme('filter_caption', array(
'node' => $node->C14N(),
'tag' => $node->tagName,
'caption' => $caption,
'align' => $align,
));
// Load the altered HTML into a new DOMDocument and retrieve the element.
$updated_node = filter_dom_load($altered_html)->getElementsByTagName('body')
->item(0)
->childNodes
->item(0);
// Import the updated node from the new DOMDocument into the original
// one, importing also the child nodes of the updated node.
$updated_node = $dom->importNode($updated_node, TRUE);
// Finally, replace the original image node with the new image node!
$node->parentNode->replaceChild($updated_node, $node);
}
return filter_dom_serialize($dom);
}
return $text;
}
/**
* {@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>Align 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 (data-caption="Text") and align images (data-align="center"), but also video, blockquotes, and so on.');
}
}
}
{#
/**
* Returns HTML for a captioned image, audio, video or other tag.
*
* Available variables
* - string node: The complete HTML tag whose contents are being captioned.
* - string tag: The name of the HTML tag whose contents are being captioned.
* - string|NULL caption: (optional) The caption text, or NULL.
* - string|NULL align: (optional) The alignment: 'left', 'center', 'right' or
* NULL.
*/
#}
<figure class="caption caption-{{ tag }} {%- if align %} caption-{{ align }} {%- endif %}">
{{ node }}
{% if caption %}
<figcaption>{{ caption }}</figcaption>
{% endif %}
</figure>
......@@ -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'
......
......@@ -6,6 +6,11 @@ roles:
- administrator
cache: '1'
filters:
filter_caption:
module: filter
status: '1'
weight: '9'
settings: { }
filter_htmlcorrector:
module: filter
status: '1'
......
......@@ -1611,6 +1611,37 @@ ol.search-results {
padding-left: 0;
}
/* -------------- Captions -------------- */
.caption > * {
background: #F3F3F3;
padding: 0.5ex;
border: 1px solid #CCC;
}
.caption > figcaption {
border: 1px solid #CCC;
border-top: none;
padding-top: 0.5ex;
font-size: small;
text-align: center;
}
/* Override Bartik's default blockquote and pre styles when captioned. */
.caption-pre > pre,
.caption-blockquote > blockquote {
margin: 0;
}
.caption-blockquote > figcaption::before {
content: "— ";
}
.caption-blockquote > figcaption {
text-align: left;
}
[dir=rtl] .caption-blockquote > figcaption {
text-align: right;
}
/* -------------- Shortcut Links -------------- */
.shortcut-wrapper {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment