CKEditor 4 + Enhanced Image, center by default. FYI - ckeditor

It's not a question, but rather a solution (with a small dirty trick).
I needed to insert an image in CKEditor with "center" alignment by default. I could not find working example, so I spent lots of time and come up with the following answer.

In your editor's config.js
CKEDITOR.on( 'dialogDefinition', function( ev ) {
// Take the dialog name and its definition from the event data.
var dialogName = ev.data.name;
var dialogDefinition = ev.data.definition;
if ( dialogName == 'image2' ) {
ev.data.definition.dialog.on('show', function() {
//debugger;
var widget = ev.data.definition.dialog.widget;
// To prevent overwriting saved alignment
if (widget.data['src'].length == 0)
widget.data['align'] = 'center';
});
}
});
Enjoy!

I've used the above solution for a few years successfully - although now (CKEditor Version 4.14.0) it throws an error and no longer works. After a bunch of troubleshooting and assistance from admittedly old documentation from here:
https://nightly.ckeditor.com/20-05-20-06-04/standard/samples/old/dialog/dialog.html
... the following seems to work here:
In editor config.js file:
CKEDITOR.on('dialogDefinition', function(ev) {
let dialogName = ev.data.name;
let dialogDefinition = ev.data.definition;
console.log(ev);
if (dialogName == 'image2') {
dialogDefinition.onFocus = function() {
/**
* 'none' is no good for us - if is none - reset to 'center'
* if it's already 'left','center', or 'right' - leave alone.
*/
if (this.getContentElement('info', 'align')
.getValue() === 'none') {
this.getContentElement('info', 'align')
.setValue('center');
}
};
}
});

Related

Trying to add a plugin to CKEditor

I'm trying to add a plugin to set a default URL when adding a link.
I followed the instructions here:
https://apostrophecms.org/docs/tutorials/howtos/ckeditor.html
And I ended up with:
// lib/modules/apostrophe-areas/public/js/user.js
apos.define('apostrophe-areas', {
construct: function(self, options) {
var superEnableCkeditor = self.enableCkeditor;
self.enableCkeditor = function() {
superEnableCkeditor();
CKEDITOR.plugins.addExternal('defaulturl', '/modules/my-apostrophe-areas/js/ckeditorPlugins/defaulturl/', 'plugin.js');
};
}
});
and this is my apostrophe-areas/public/js/ckeditorPlugins/defaulturl/plugin.js
CKEDITOR.on( 'dialogDefinition', function( ev ) {
var dialogName = ev.data.name;
var dialogDefinition = ev.data.definition;
if ( dialogName == 'link' ) {
var infoTab = dialogDefinition.getContents( 'info' );
var urlField = infoTab.get( 'url' );
urlField[ 'default' ] = 'www.example.com';
}
});
However, this is not working for me, I tried to do what is suggested here:
Ckeditor plugin configuration not working
But it doesn't worked.
What I've tried and worked was to append the plugin.js file at the end of the plugin.js of the split plugin at the apostrophe-area folder, but I think this is not the correct way to go
Thanks!
I think you can solve the issue two ways:
1. If you want to keep the external plugin file:
Wrap your code according to the API, see e.g. https://sdk.ckeditor.com/samples/timestamp.html and the plugin it references https://sdk.ckeditor.com/samples/assets/plugins/timestamp/plugin.js for an example. You need to use CKEDITOR.plugins.add( 'defaulturl', { init: ... }) in your plugin.js. Not sure if there is anything special to do otherwise as you want to modify the behavior of a CKEDITOR core plugin. That's why it would go with the next option...
2. If you don't need the extra plugin.js:
You can also replace the CKEDITOR.plugins.addExternal() call with the contents of your plugin.js file. I did this to modify the default link target to be _blank:
// /lib/modules/apostrophe-areas/public/js/user.js
'use strict';
// See https://apostrophecms.org/docs/tutorials/howtos/ckeditor.html and
apos.define('apostrophe-areas', {
construct: function(self, options) {
var superEnableCkeditor = self.enableCkeditor;
self.enableCkeditor = function enableCkeditor() {
superEnableCkeditor();
// https://docs.ckeditor.com/ckeditor4/latest/guide/dev_howtos_dialog_windows.html
CKEDITOR.on('dialogDefinition', function redefineDialog(ev) {
var dialogName = ev.data.name;
var dialogDefinition = ev.data.definition;
if (dialogName === 'link') {
var targetTab = dialogDefinition.getContents('target');
var targetField = targetTab.get('linkTargetType');
targetField.default = '_blank';
}
});
};
}
});
Good luck!

Upload Image: Cannot read property 'setCustomData' of undefined

I am trying to embed into the editor an uploaded image. My filebrowserUploadUrl is /api/m/image and it seems to be working fine. After I clicked the Send it to the Server button, there is a script error as follows:
image.js?t=H4PG:19 Uncaught TypeError: Cannot read property 'setCustomData'
of undefined
at textInput.onChange (image.js?t=H4PG:19)
at textInput.n (ckeditor.js:10)
at textInput.CKEDITOR.event.CKEDITOR.event.fire (ckeditor.js:12)
at textInput.setValue (ckeditor.js:619)
at textInput.setValue (ckeditor.js:545)
at a.q (ckeditor.js:841)
at ckeditor.js:31
at Object.callFunction (ckeditor.js:31)
at image?CKEditor=editor&CKEditorFuncNum=1&langCode=en:1
The last line in the above is the call to filebrowserUploadUrl and the response from that is:
window.parent.CKEDITOR.tools.callFunction(1, '/images/bulletins.jpg', 'Uploaded successfully');
The Uploaded successfully message is shown in an alert. The Preview box under Image Info tab is not updated. But if I clicked OK to close the dialog, the image (bulletins.jpg) is embedded in the editor alright.
What could be causing the error and how do I fix it?
I found what was causing it. I wanted to set the default tab when the insert image dialog is launched to the Upload tab. I use the following code:
CKEDITOR.on("dialogDefinition", function(ev) {
var dialogName = ev.data.name;
var dialogDefinition = ev.data.definition;
if (dialogName === "image") {
dialogDefinition.onShow = function() {
this.selectPage("Upload");
}
}
});
When the above code is used, that error happens when a file is uploaded.
I was having same issue, and after using proposed solution from vikram, editor was generating error while pasting image link into the text. Better approach here, not to completely override default onShow method, but add more to it in the following way:
CKEDITOR.on('dialogDefinition', function (ev) {
var dialogName = ev.data.name;
var dialogDefinition = ev.data.definition;
if (dialogName == 'image') {
var oldOnShow = dialogDefinition.onShow;
var newOnShow = function () {
this.selectPage('Upload');
this.hidePage('Link');
// change tabs order
$('a[title=Upload].cke_dialog_tab').css('float', 'left');
};
dialogDefinition.onShow = function () {
oldOnShow.call(this, arguments);
newOnShow.call(this, arguments);
};
}
});
After debugging a lot I found a solution on this.
If you will see onChange method of txtUrl textBox in image.js
See line number 507 and 513
you will understand cause of this error.
at line 513 setCustomData is called.
original.setCustomData( 'isReady', 'false' );
CKEDITOR.on("dialogDefinition", function(ev) {
var dialogName = ev.data.name;
//current editor
var editor = ev.editor;
var dialogDefinition = ev.data.definition;
if (dialogName === "image") {
dialogDefinition.dialog.on('show', function(e){
var dialogBox = e.sender;
//This line is the answer of your question
//this line will get rid of the error setCustomData
dialogBox.originalElement = editor.getSelection().getStartElement();
this.selectPage("Upload");
});
}
});

How to hide toolbar in CKEditor inline

I have an application that needs the inline CKEditor but without toolbar. For the inline CKEditor part I have:
CKEDITOR.disableAutoInline = true;
var editor = CKEDITOR.inline('editable', {on: {
instanceReady: function() {periodic();}
}});
var periodic = (function() {
var data, oldData;
return function() {
if ((data = editor.getData()) !== oldData) {
oldData = data;
$.post("update.php", {txt:data});
}
setTimeout(periodic, 1000);
};
})();
Then for the toolbar hiding part I found this: CKEditor 4 Inline: How to hide toolbar on demand?
//Whenever CKEditor loses focus, We will hide the corresponding toolbar DIV.
function hideToolBarDiv(event) {
// Select the correct toolbar DIV and hide it.
//'event.editor.name' returns the name of the DIV receiving focus.
$('#'+event.editor.name+'TBdiv').hide();
}
Problem is that I have no clue how to combine these two together :) I appreciate for any hint. Thank you.
I found another link that seems to solve my problem: Can I use CKEditor without a toolbar?
The script seems to be working alright though I am still not sure if it is a correct way to do it:
CKEDITOR.disableAutoInline = true;
var editor = CKEDITOR.inline('editable', {
removePlugins: 'toolbar',
allowedContent: 'p h1 h2 strong em; a[!href]; img[!src,width,height]'
on: {instanceReady: function() {periodic();}}
});
var periodic = (function() {
var data, oldData;
return function() {
if ((data = editor.getData()) !== oldData) {
oldData = data;
$.post("update.php", {txt:data});
}
setTimeout(periodic, 1000);
};
})();
In my eyes, I have done it in two ways:
1) Using the removePlugins option and just remove the toolbar:
CKEDITOR.inline( 'textarea', {
removePlugins: 'toolbar'
} );
here, you can also use allowedContent to allow the contents for ACF
CKEDITOR.inline( 'editable', {
removePlugins: 'toolbar',
allowedContent: 'p h1 h2 strong em; a[!href]; img[!src,width,height];'
} );
2) Using CSS - Not the standard approach: (little tricky)
Just make css to display:none the toolbar, like
.cke_inner {
display: none;
}
Hope it will help someone.

How to remove the "link to anchor" from the "Link" editor in CKeditor

Basically I removed the anchor button so there should not be a link to anchor option in my link window.
Any way to remove that dropdown option
?
Figured it out
if ( dialogName == 'link' )
{
var infoTab = dialogDefinition.getContents( 'info' );
var linktypeField = infoTab.get( 'linkType' );
/* Remove it from the array of items */
linktypeField['items'].splice(1, 1);
}
dialogDefinition allows you to completely redesign dialog boxes.
I did it this way, based on the example at http://nightly.ckeditor.com/7156/_samples/api_dialog.html
CKEDITOR.on( 'dialogDefinition', function( ev )
{
// Take the dialog name and its definition from the event
// data.
var dialogName = ev.data.name;
var dialogDefinition = ev.data.definition;
// Check if the definition is from the dialog we're
// interested on (the "Link" dialog).
if ( dialogName == 'link' )
{
// Get a reference to the "Link Info" tab.
var infoTab = dialogDefinition.getContents( 'info' );
infoTab.remove( 'linkType' );
}
});
$("#mydiv").ckeditor(function(){}, {
removeDialogTabs: 'link:advanced;link:target'
// any other customizations go here.
});
This's my solution:
CKEDITOR.on('dialogDefinition', function (ev) {
// Take the dialog name and its definition from the event data.
var dialogName = ev.data.name;
var dialogDefinition = ev.data.definition;
// Check if the definition is from the dialog we're
// interested in (the 'link' dialog).
if (dialogName == 'link') {
// Remove the 'Target' and 'Advanced' tabs from the 'Link' dialog.
//dialogDefinition.removeContents('target');
//dialogDefinition.removeContents('advanced');
// Get a reference to the 'Link Info' tab.
var infoTab = dialogDefinition.getContents('info');
infoTab.remove('protocol');
infoTab.get('linkType').style = 'display: none';
}
});
If you get rid of Link Type using infoTab.remove('linkType'); it will fails to create the link

ckeditor dialog positioning

Dialog windows for CKEditor by default appear in the middle of the page but if the page is an iframe with a big height the dialogs appear way down the page.
Is it possible to configure CKEditor to position the dialogs in a different quadrant of the page? For example top middle?
Yes, the link MDaubs gives will guide you to do what you want.
I've had to do this in the past and the following snippet will demonstrate a solution for your problem:
CKEDITOR.on('dialogDefinition', function(e) {
var dialogName = e.data.name;
var dialogDefinition = e.data.definition;
dialogDefinition.onShow = function() {
this.move(this.getPosition().x,0); // Top center
}
})
You can place this in the config file or the ready function for jQuery.
The solution by zaf works in that it helps to position the dialog, but I've found it to produce a bunch of side effects as to how the dialog functions (failing to display the URL of the image in the image dialog is one example).
It turned out that the original onShow() method that is being overridden returns a meaningful value that we should keep. This could be due to a plugin or something, but here's the code that ultimately worked for me:
CKEDITOR.on('dialogDefinition', function(e) {
var dialogName = e.data.name;
var dialogDefinition = e.data.definition;
var onShow = dialogDefinition.onShow;
dialogDefinition.onShow = function() {
var result = onShow.call(this);
this.move(this.getPosition().x, $(e.editor.container.$).position().top);
return result;
}
});
This may be way you're looking for:
Programatically set the position of CKEditor's dialogs
I just had the same issue as Yehonatan and found this question really fast via Google. But after using the answer provided by zaf I still didn't get a dialog to appear in the proper position when the editor is loaded within an iframe.
In stead of the position() method I used the offset() method to place a dialog right under the toolbar. Together with the response of jonespm I came to this code that seems to work very good, also with existing dialogs.
CKEDITOR.on('dialogDefinition', function(e) {
var dialogName = e.data.name;
var dialogDefinition = e.data.definition;
var onShow = dialogDefinition.onShow;
dialogDefinition.onShow = function() {
this.move(this.getPosition().x, jQuery(this.getParentEditor().container.$).offset().top);
if (typeof onShow !== 'undefined' && typeof onShow.call === 'function')
{
return onShow.call(this);
}
}
});
Hopefully this code can help others with the same issue as me.
Correct syntax is:
CKEDITOR.on('dialogDefinition', function(ev) {
var dialogName = ev.data.name;
var dialogDefinition = ev.data.definition;
var dialog = dialogDefinition.dialog;
if (dialogName == 'image2') {
dialogDefinition.onShow = CKEDITOR.tools.override(dialogDefinition.onShow, function(original) {
return function() {
original.call(this);
CKEDITOR.tools.setTimeout( function() {
/*do anything: this.move(this.getPosition().x, $(e.editor.container.$).position().top);*/
}, 0);
}
});
}
});

Resources