Skip to content

Instantly share code, notes, and snippets.

@CanRau
Last active April 18, 2019 10:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CanRau/662a559a07d6d7c492159d1cd497944f to your computer and use it in GitHub Desktop.
Save CanRau/662a559a07d6d7c492159d1cd497944f to your computer and use it in GitHub Desktop.
Please read https://processwire.com/talk/topic/13471-better-ckeditor-image-insertion-at-least-for-me/ I'm fixing some bugs right now and am rearranging the whole thing
<?php
/**
* Adding ->cdn property to page images
* returning assets url
* Can be placed wherever you like, e.g. _init.php
* just need to be included before using TextformatterBlogImages.module
*
* return //assets.YOURDOMAIN...
* removing http: makes it work with http and https connections
*/
$wire->addHookProperty('Pageimage::cdn', function($event) {
$image = $event->object;
$event->return = str_replace('www.', 'assets.', $image->httpUrl);
$event->return = str_replace('http:', '', $event->return);
});
/**
* This will hook into inputfield render process (stopping if not InputfieldImage)
* and appending a little script and stylesheet
* the js will add a little clickable area to each image in the image field
*
* I have only one images field set to multiple
*
* Works only with CKEditor textareas right now
* Can be placed in an owm module or wherever you like
* but backend needs to be able to include it in boot process..
*
* NOTE: This code requires at least PW 3.0.17 with new image field
*/
$wire->addHookAfter('Inputfield::render', function($event) {
$inputfield = $event->object;
// optional, but better to limit to those you want
if(!$inputfield instanceof InputfieldImage) return;
$name = $inputfield->name;
$event->return .= <<< _SCRIPT
<script type='text/javascript'>
document.addEventListener('DOMContentLoaded', function() {
var editor = CKEDITOR.instances.Inputfield_body,
images = document.querySelectorAll('.gridImage'),
imagesLen = images.length;
for (i=0; i<imagesLen; i++) {
images[i].insertAdjacentHTML('beforeEnd', '<div class="HappyImageSelector" title="insert into text (at caret)">+</div>');
images[i].addEventListener('click', function(e) {
var imageSrc = e.target.parentNode.querySelector('img').src.split('?')[0];
editor.insertHtml('<img src="'+imageSrc+'">');
});
}
editor.on( 'instanceReady', function(e) {
if ( editor.contextMenu ) {
editor.addCommand('alignLeft', { exec: function (editor) {
var element = editor.getSelection().getStartElement();
element.removeClass('align_right');
element.addClass('align_left');
} });
editor.addCommand('alignRight', { exec: function (editor) {
var element = editor.getSelection().getStartElement();
element.removeClass('align_left');
element.addClass('align_right');
} });
editor.addCommand('removeAlignment', { exec: function (editor) {
var element = editor.getSelection().getStartElement();
element.removeClass('align_left');
element.removeClass('align_right');
} });
editor.addCommand('small', { exec: function (editor) {
var element = editor.getSelection().getStartElement().$;
element.setAttribute('width', 200);
} });
editor.addCommand('medium', { exec: function (editor) {
var element = editor.getSelection().getStartElement().$;
element.setAttribute('width', 350);
} });
editor.addCommand('removeWidth', { exec: function (editor) {
var element = editor.getSelection().getStartElement().$;
element.removeAttribute('width');
} });
editor.addMenuGroup( 'HappyImages' );
editor.addMenuItems({
alignLeft : {
label : 'Rechts umfließen',
command : 'alignLeft',
group : 'HappyImages',
order : 1
},
alignRight : {
label : 'Links umfließen',
command : 'alignRight',
group : 'HappyImages',
order : 2
},
removeAlignment : {
label : 'Nicht umfließen',
command : 'removeAlignment',
group : 'HappyImages',
order : 3
},
small : {
label : 'klein',
command : 'small',
group : 'HappyImages',
order : 4
},
medium : {
label : 'medium',
command : 'medium',
group : 'HappyImages',
order : 5
},
removeWidth : {
label : 'original',
command : 'removeWidth',
group : 'HappyImages',
order : 6
}
});
}
editor.contextMenu.addListener( function( element ) {
if ( element.getAscendant( 'img', true ) ) {
return {
alignLeft: CKEDITOR.TRISTATE_OFF,
alignRight: CKEDITOR.TRISTATE_OFF,
removeAlignment: CKEDITOR.TRISTATE_OFF,
small: CKEDITOR.TRISTATE_OFF,
medium: CKEDITOR.TRISTATE_OFF,
removeWidth: CKEDITOR.TRISTATE_OFF
};
}
});
});
},
false);
</script>
<style type='text/css'>
.HappyImageSelector {
position: relative;
background: #e3e3e3;
text-align: center;
cursor: pointer;
}
</style>
_SCRIPT;
});
.HappyRow {
margin-bottom: 1em;
}
.HappyRow__block {
background: #e6e6e6;
}
.HappyRow__description {
display: inline-block;
padding: 0em .2em .1em;
}
.HappyRow--multi .HappyRow__block {
display: inline-block;
}
@media @max40 {
.HappyRow__block {
width: 100% !important;
}
.HappyRow--multi .HappyRow__block + .HappyRow__block {
margin-top: 1em;
}
}
@media @min40 {
.HappyRow--single .HappyRow__block {
margin: 0 auto;
width: 60%;
}
// HappyRow's containing more than one child get a gutter
.HappyRow--multi .HappyRow__block + .HappyRow__block {
margin-left: 2%;
}
/* HappyRow with 2 children */
.HappyRow--2 .HappyRow__block {
width: 49%;
}
/* HappyRow with 3 children */
.HappyRow--3 .HappyRow__block {
width: 32%
}
/* HappyRow with 4 children */
.HappyRow--4 .HappyRow__block {
width: 23.5%
}
/* 1 landscapes 1 portrait */
.HappyRow--1lx1p {
.HappyRow__block--landscape {
width: 62.7%;
}
.HappyRow__block--portrait {
width: 35.3%;
}
}
/* 2 landscapes 1 portrait */
.HappyRow--2lx1p {
.HappyRow__block--landscape {
width: 37.5%;
}
.HappyRow__block--portrait {
width: 21%;
}
}
/* 1 landscape 2 portrait */
.HappyRow--1lx2p {
.HappyRow__block--landscape {
width: 45.2%;
}
.HappyRow__block--portrait {
width: 25.4%;
}
}
/* 2 landscape 2 portrait */
.HappyRow--2lx2p {
.HappyRow__block--landscape {
width: 30%;
}
.HappyRow__block--portrait {
width: 16.95%;
}
}
/* 1 landscape 3 portrait */
.HappyRow--1lx3p {
.HappyRow__block--landscape {
width: 34.9%;
}
.HappyRow__block--portrait {
width: 19.6%;
}
}
/* 3 landscape 1 portrait */
.HappyRow--3lx1p {
.HappyRow__block--landscape {
width: 26.3%;
}
.HappyRow__block--portrait {
width: 14.8%;
}
}
}
<?php
/**
* Add to any textarea field from field settings
* or place the following code into any template file
*
* $modules->TextformatterBlogImages->formatValue($page, new Field(), $page->body);
* where $page->body is your textarea
*
* make sure to review path to simple_html_dom.php
*
* To make use of the lazyloading and
* proper selection of the right image
* you need to include https://github.com/aFarkas/lazysizes into your project
*/
class TextformatterBlogImages extends Textformatter {
public static function getModuleInfo() {
return array(
'title' => 'TextformatterBlogImages',
'summary' => __("Links images to full version, wraps linked image into span or figure based on the context, moves figures into own container", __FILE__), // Module Summary
'version' => 1,
'auhor' => 'Can Rau'
);
}
/**
* Format the given text string with Page and Field provided.
*
* Override this function completely when providing your own text formatter. No need to call the parent.
*
* @param Page $page
* @param Field $field
* @param mixed $value
*
*/
public function formatValue(Page $page, Field $field, &$value) {
require_once '../../../../classes/simple_html_dom.php';
$user = wire('user');
// turn double linebreaks into paragraphs <br><br> to </p><p>
$value = preg_replace('#(?:<br\s*/?>\s*?){2,}#', '</p><p>', $value->getLanguageValue($user->language));
$dom = str_get_html($value);
$paragraphs = $dom->find('p');
foreach ($paragraphs as $p) {
$imgs = $p->find('img');
$inGallery = false;
$gallery = "<div class='HappyRow'>";
foreach ($imgs as $img) {
$src = $img->src;
$class = $img->class;
$width = $img->width;
$height = $img->height;
$imgName = strstr(substr($src, strrpos($src, '/')+1), '.', true); // strip path and variation from image name
$pageimage = $page->images->get("name^=$imgName");
$small = $pageimage->maxWidth(100, array('quality'=>15));
$medium = $pageimage->maxWidth(300);
// don't stuff image in HappyRow if image got a class 'align_' prepended
if (strpos($class, 'align_left') === false && strpos($class, 'align_right') === false) {
$inGallery = true;
}
$img->class = str_replace(array('align_left', 'align_right'), '', $img->class);
$img->class .= ' HappyImage';
$img->src = $pageimage->cdn;
$img->srcset = 'data:image/png;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
$img->{'data-srcset'} = "$small->cdn {$small->width}w, $medium->cdn {$medium->width}w, $pageimage->cdn {$pageimage->width}w";
$img->{'data-sizes'} = 'auto';
// @todo is it bad that width could be higher than rendered with?
$img->width = $pageimage->width;
$i = $img->outertext;
$i = "<a class='modal modal-image' href='$pageimage->detailpage' data-href='$pageimage->cdn' data-name='$pageimage->name'>$i</a>";
$class .= $pageimage->width > $pageimage->height ? ' HappyRow__block--landscape' : ' HappyRow__block--portrait';
// if image will be moved into HappyRow wrap in figure and remove from paragraph
if ($inGallery) {
$i = "<figure class='HappyRow__block $class'>$i";
if ($pageimage->description) $i .= "<figcaption class='HappyRow__description'>$pageimage->description</figcaption>";
$i .= "</figure>";
$gallery .= $i;
$img->outertext = '';
} else {
// otherwise wrap in span and replace
$i = "<span class='HappyRow__block HappyRow__block--inline $class' style='width:{$width}px'>$i";
if ($pageimage->description) $i .= "<span class='HappyRow__description'>$pageimage->description</span>";
$i .= "</span>";
$img->outertext = $i;
}
}
$gallery .= "</div>";
$newParagraph = trim(preg_replace( '#^\s*(?:<br\s?\/?>)*\s*|(?:<br\s?\/?>)*\s*$#', '', trim($p->innertext)));
$newParagraph = "<p>$newParagraph</p>";
if ($inGallery) {
$p->outertext = $gallery . $newParagraph;
}
}
// save dom to $value
$value = $dom->save();
// clear dom
$dom->clear();
/**
* 2. round
* reload dom
*/
$dom = str_get_html($value);
// search for newly created HappyRows
$galleries = $dom->find('.HappyRow');
foreach ($galleries as $g) {
$numChildren = count($g->children);
if ($numChildren) {
$landscapes = count($g->find('.HappyRow__block--landscape'));
$portraits = count($g->find('.HappyRow__block--portrait'));
// add single or multiple class
$class = $numChildren === 1 ? 'HappyRow--single' : 'HappyRow--multi';
// if multiple add number of children
$class .= $numChildren > 1 ? " HappyRow--$numChildren" : '';
$class .= " HappyRow--{$landscapes}lx{$portraits}p";
// append classes to gallery container
$g->class .= " $class";
} else {
// if no children remove gallery
// shouldn't happen anyways, just in case..
$g->outertext = '';
}
}
// save dom back to $value
$value = $dom->save();
// clear dom again
$dom->clear();
// remove empty paragraphs which can occur when a paragraph only contained images
$value = str_replace('<p></p>', '', $value);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment