CKEditor ACF image width and height - ckeditor

I am trying to force CKEditor to set image width and height as attributes not styles. According to docs i need to set in CKEditor config allowedContent = img[!src,alt,width,height] but when i do this CKEditor mode changes from automatic to custom and filters all other html tags.
How to change allowedContent only this specific case?
As i understand correctly each plugin registers its own allowedContent, so i changed in image plugin following line allowed = 'img[alt,!src] to allowed = 'img[alt,!src, width, height] but it does not work

ACF lacks one thing - #feature event. There's currently no convenient way to alter allowedContent of a feature being registered.
In your case, you will be able to use a temporary solution I described on CKEditor forum.
Your previous attempt to alter image's allowedContent wasn't successful most likely because you haven't fully replaced it. This is a code from image plugin:
var allowed = 'img[alt,!src]{border-style,border-width,float,height,margin,margin-bottom,margin-left,margin-right,margin-top,width}',
required = 'img[alt,src]';
if ( CKEDITOR.dialog.isTabEnabled( editor, pluginName, 'advanced' ) )
allowed = 'img[alt,dir,id,lang,longdesc,!src,title]{*}(*)';
So if you have changed just the first occurrence, then the second overrides it.

Many thanks Rainmar for help. It seems that dialog is removing the attributes. I managed to fix that changing commit function for width and height in image/dialogs/image.js file.
The old function looked like this (for width only):
commit: function( type, element, internalCommit ) {
var value = this.getValue();
if ( type == IMAGE ) {
if ( value )
element.setStyle( 'width', CKEDITOR.tools.cssLength( value ) );
else
element.removeStyle( 'width' );
!internalCommit && element.removeAttribute( 'width' );
} else if ( type == PREVIEW ) {
var aMatch = value.match( regexGetSize );
if ( !aMatch ) {
var oImageOriginal = this.getDialog().originalElement;
if ( oImageOriginal.getCustomData( 'isReady' ) == 'true' )
element.setStyle( 'width', oImageOriginal.$.width + 'px' );
} else
element.setStyle( 'width', CKEDITOR.tools.cssLength( value ) );
} else if ( type == CLEANUP ) {
element.removeAttribute( 'width' );
element.removeStyle( 'width' );
}
}
And change it to this:
commit: function( type, element, internalCommit ) {
var value = this.getValue();
if ( type == IMAGE ) {
if ( value ) {
element.setAttribute('width', value + 'px');
element.setStyle( 'width', CKEDITOR.tools.cssLength( value ) );
}else {
element.removeAttribute('width');
element.removeStyle( 'width' );
}
!internalCommit && element.removeStyle( 'width' );
} else if ( type == PREVIEW ) {
var aMatch = value.match( regexGetSize );
if ( !aMatch ) {
var oImageOriginal = this.getDialog().originalElement;
if ( oImageOriginal.getCustomData( 'isReady' ) == 'true' )
element.setStyle( 'width', oImageOriginal.$.width + 'px' );
} else
element.setStyle( 'width', CKEDITOR.tools.cssLength( value ) );
} else if ( type == CLEANUP ) {
element.removeAttribute( 'width' );
element.removeStyle( 'width' );
}
}

This is now super simple:
config.allowedContent = {
$1: {
// Use the ability to specify elements as an object.
elements: CKEDITOR.dtd,
attributes: true,
styles: true,
classes: true
}
};
config.disallowedContent = 'img{width,height}';
Which will alow everything, and convert inline image width/height to attributes.
http://docs.ckeditor.com/#!/guide/dev_acf-section-example%3A-disallow-inline-styles-and-use-attributes-instead

Related

Wordpress only admin can crop images. Get error There has been an error cropping your image

I added wp.media via js to crop images. I've got it working for admin but not for any other role specifically 'subscriber'. It sounds crazy but there are no errors at all in the console or in debug and the only error I get is There has been an error cropping your image.
I don't think its a GC issue or Imagick etc as it does work for admin.
I've tried adding permissions to the 'subscriber' role: upload_files and edit_files (which seems to be deprecated anyways). Please help and thank you in advance! Here's the code. I unfortunately do not know who to credit for this, but majority of it is not my original code:
var mediaUploader;
function myTheme_calculateImageSelectOptions(attachment, controller) {
var control = controller.get( 'control' );
var flexWidth = !! parseInt( control.params.flex_width, 10 );
var flexHeight = !! parseInt( control.params.flex_height, 10 );
var realWidth = attachment.get( 'width' );
var realHeight = attachment.get( 'height' );
var xInit = parseInt(control.params.width, 10);
var yInit = parseInt(control.params.height, 10);
var ratio = xInit / yInit;
controller.set( 'canSkipCrop', ! control.mustBeCropped( flexWidth, flexHeight, xInit, yInit, realWidth, realHeight ) );
var xImg = xInit;
var yImg = yInit;
if ( realWidth / realHeight > ratio ) {
yInit = realHeight;
xInit = yInit * ratio;
} else {
xInit = realWidth;
yInit = xInit / ratio;
}
var x1 = ( realWidth - xInit ) / 2;
var y1 = ( realHeight - yInit ) / 2;
var imgSelectOptions = {
handles: true,
keys: true,
instance: true,
persistent: true,
imageWidth: realWidth,
imageHeight: realHeight,
minWidth: xImg > xInit ? xInit : xImg,
minHeight: yImg > yInit ? yInit : yImg,
x1: x1,
y1: y1,
x2: xInit + x1,
y2: yInit + y1
};
return imgSelectOptions;
}
function myTheme_setImageFromURL(url, attachmentId, width, height) {
var choice, data = {};
data.url = url;
data.thumbnail_url = url;
data.timestamp = _.now();
if (attachmentId) {
data.attachment_id = attachmentId;
}
if (width) {
data.width = width;
}
if (height) {
data.height = height;
}
// $("#heading_picture").val( url );
// $("#heading_picture_preview").prop("src", url);
$('[name="entry-img"]').val(attachmentId)
$('.activity-add-image').css({ 'background-image': `url(${url})` }).addClass('has-image')
$('.activity-remove-image').css('display', 'block');
fieldValidation.image.validated = true
validateNewLetter()
}
function myTheme_setImageFromAttachment(attachment) {
// $("#heading_picture").val( attachment.url );
// $("#heading_picture_preview").prop("src", attachment.url);
$('[name="entry-img"]').val(attachment.id)
$('.activity-add-image').css({ 'background-image': `url(${attachement.url})` }).addClass('has-image')
$('.activity-remove-image').css('display', 'block');
fieldValidation.image.validated = true
validateNewLetter()
}
$('.activity-remove-image').on('click', function(e){
e.preventDefault();
$('.activity-add-image').css({ 'background-image': 'none' }).removeClass('has-image')
$('.activity-remove-image').hide();
$('[name="entry-img"]').val(null)
fieldValidation.image.validated = false
validateNewLetter()
});
$(".activity-upload-image").on("click", function(e) {
e.preventDefault();
/* We need to setup a Crop control that contains a few parameters
and a method to indicate if the CropController can skip cropping the image.
In this example I am just creating a control on the fly with the expected properties.
However, the controls used by WordPress Admin are api.CroppedImageControl and api.SiteIconControl
*/
var cropControl = {
id: "control-id",
params : {
flex_width : false, // set to true if the width of the cropped image can be different to the width defined here
flex_height : false, // set to true if the height of the cropped image can be different to the height defined here
width : 1100, // set the desired width of the destination image here
height : 460, // set the desired height of the destination image here
}
};
cropControl.mustBeCropped = function(flexW, flexH, dstW, dstH, imgW, imgH) {
// If the width and height are both flexible
// then the user does not need to crop the image.
if ( true === flexW && true === flexH ) {
return false;
}
// If the width is flexible and the cropped image height matches the current image height,
// then the user does not need to crop the image.
if ( true === flexW && dstH === imgH ) {
return false;
}
// If the height is flexible and the cropped image width matches the current image width,
// then the user does not need to crop the image.
if ( true === flexH && dstW === imgW ) {
return false;
}
// If the cropped image width matches the current image width,
// and the cropped image height matches the current image height
// then the user does not need to crop the image.
if ( dstW === imgW && dstH === imgH ) {
return false;
}
// If the destination width is equal to or greater than the cropped image width
// then the user does not need to crop the image...
if ( imgW <= dstW ) {
return false;
}
return true;
};
/* NOTE: Need to set this up every time instead of reusing if already there
as the toolbar button does not get reset when doing the following:
mediaUploader.setState('library');
mediaUploader.open();
*/
console.log('control', cropControl)
mediaUploader = wp.media({
button: {
text: 'Select', // l10n.selectAndCrop,
close: false
},
states: [
new wp.media.controller.Library({
title: 'Select and Crop', // l10n.chooseImage,
library: wp.media.query({ type: 'image' }),
multiple: false,
date: false,
priority: 20,
suggestedWidth: 1100,
suggestedHeight: 460
}),
new wp.media.controller.CustomizeImageCropper({
imgSelectOptions: myTheme_calculateImageSelectOptions,
control: cropControl
})
]
});
mediaUploader.on('cropped', function(croppedImage) {
console.log('cropped', croppedImage)
var url = croppedImage.url,
attachmentId = croppedImage.attachment_id,
w = croppedImage.width,
h = croppedImage.height;
myTheme_setImageFromURL(url, attachmentId, w, h);
});
mediaUploader.on('skippedcrop', function(selection) {
console.log('skipped', selection)
var url = selection.get('url'),
w = selection.get('width'),
h = selection.get('height');
myTheme_setImageFromURL(url, selection.id, w, h);
});
mediaUploader.on("select", function() {
var attachment = mediaUploader.state().get( 'selection' ).first().toJSON();
console.log('select', attachment)
if ( cropControl.params.width === attachment.width
&& cropControl.params.height === attachment.height
&& ! cropControl.params.flex_width
&& ! cropControl.params.flex_height ) {
myTheme_setImageFromAttachment( attachment );
mediaUploader.close();
} else {
console.log(mediaUploader)
mediaUploader.setState( 'cropper' );
}
});
mediaUploader.open();
});```

Get all elements of type in CKEditor 5

My editor contains custom elements, created like this:
this.editor.model.change( writer => {
const id = `a${ Math.random().toString().replace('.', '') }`;
const autocomplete = writer.createElement( 'autocomplete', { 'data-id': id );
this.editor.model.insertContent( autocomplete );
} );
I want to get all 'autocomplete' elements at a later time in my plugin so I can read their content (they're editable elements).
Is there something like querySelectorAll('autocomplete') for the model of the editor?
It's possible using range.getWalker.
const findNodes = function(writer, type, root) {
const nodes = [];
const range = writer.createRangeIn( root );
for ( const value of range.getWalker({ ignoreElementEnd: true }) ) {
const node = value.item;
if ( node.is( type ) ) {
nodes.push(node);
}
}
return nodes;
};
this.editor.model.change( writer => {
const autocompletes = findNodes(writer, 'autocomplete', this.editor.model.document.getRoot());
} );

How to callback on certain text

I'm trying to find a reliable way to perform a callback when certain text is typed into the editor. I want to run certain code when an # is typed (to then allow a selection of user's to link to).
Currently I am doing it by using the "change" event and then trying to look back at what is before the current selection:
CKEDITOR.plugins.add( 'ipsmentions', {
init: function( editor ) {
/* When the content of the editor is changed, check if it was an # */
editor.on( 'change', function(e) {
/* If we currently have just normal text selected, we're typing normally and there might be an # */
var selection = editor.getSelection();
if ( selection.getType() == CKEDITOR.SELECTION_TEXT ) {
/* Loop the ranges... */
var ranges = selection.getRanges( true );
for ( var i = 0; i < ranges.length; i++ ) {
/* If the start container and end container are the same (meaning we just have a normal caret, indicating normal typing, nothing highlighted) and that is a text node... */
if ( ranges[i].startContainer.equals( ranges[i].endContainer ) && ranges[i].endContainer instanceof CKEDITOR.dom.text ) {
if ( ranges[i].startContainer.getText().substr( ranges[i].startOffset - 1, 1 ) == '#' ) {
console.log('# pressed!');
}
}
}
}
});
}
});
But I'm guessing this isn't the best way. In particular, I notice that if I type # and then hit enter (to start a new paragraph) I get "# pressed" in the console again.
Is there a better way to go about this?
Asynchronous check should help with enter key. I would also use some backwards range.cloneContents because you never know in what type of node the selection might be anchored. JSFiddle.
CKEDITOR.replace( 'editor', {
on: {
change: function() {
CKEDITOR.tools.setTimeout( function() {
var sel = this.getSelection(),
range = sel.getRanges()[ 0 ];
if ( !range.collapsed ) {
console.warn( 'non–collapsed range' );
return;
}
if ( !range.startOffset ) {
console.warn( 'at the beginning of the node' );
return;
}
range.setStart( range.startContainer, 0 );
if ( range.cloneContents().$.textContent.substr( -1 ) == '#' ) {
console.log( '# inserted' );
}
}, 0, this );
}
}
} );

Three.js Error creating WebGL context

Friends, I need your help, the following code in three.js, all return null in this line:
_context || _canvas.getContext( 'webgl', attributes ) || _canvas.getContext( 'experimental-webgl', attributes );
function initGL() {
try {
var attributes = {
alpha: _alpha,
depth: _depth,
stencil: _stencil,
antialias: _antialias,
premultipliedAlpha: _premultipliedAlpha,
preserveDrawingBuffer: _preserveDrawingBuffer
};
_gl = _context || _canvas.getContext( 'webgl', attributes ) || _canvas.getContext( 'experimental-webgl', attributes );
if ( _gl === null ) {
throw 'Error creating WebGL context.';
}
} catch ( error ) {
console.error( error );
}
_glExtensionTextureFloat = _gl.getExtension( 'OES_texture_float' );
the attributes value:
this is canvas:
Thanks a lot!
~Rosone
I found the reason: It's because another context (canvas.getContext('2d') ) already occupied this same canvas.

Temporary disable button on ckeditor

How can i enable or disable the paragraph button of CKEditor on selection of an image. I did not want to remove it completely.
am currently using ckeditor version 4.4
Use editor.getCommand() + CKEDITOR.command API (enable and disable):
editor.getCommand( 'justifyleft' ).disable();
editor.getCommand( 'justifyleft' ).enable();
For example:
CKEDITOR.instances.editor1.on( 'selectionChange', function( evt ) {
var jLeftCommand = this.getCommand( 'justifyleft' ),
jRightCommand = this.getCommand( 'justifyright' );
if ( evt.data.path.lastElement.is( 'img' ) ) {
jLeftCommand.disable();
jRightCommand.disable();
} else {
jLeftCommand.enable();
jRightCommand.enable();
}
} );

Resources