Upload Image: Cannot read property 'setCustomData' of undefined - ckeditor

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");
});
}
});

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!

CKEditor 4 + Enhanced Image, center by default. FYI

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');
}
};
}
});

CKeditor no Image preview when displaying upload Tab first

I use this code to display Upload Tab first when the Image dialog box is opened
CKEDITOR.on('dialogDefinition', function( ev ) {
var dialogName = ev.data.name;
var dialogDefinition = ev.data.definition;
if ( dialogName == 'image' ) {
dialogDefinition.onShow = function() {
this.selectPage( 'Upload' );
};
}
});
After uploading image to the server it was not displayed in Tab Preview page and I get this javascript error
Uncaught TypeError: Cannot call method 'removeStyle' of undefined image.js?t=DBAA:467
contents.elements.children.children.onChange image.js?t=DBAA:467

Preload ajax loaded content

On my site I use one core/frame PHP file. If user hit one of my link (like contact, our about..etc..) the content loaded via ajax. I use the following snippet to achieve this:
var AjaxContent = function(){
var container_div = '';
var content_div = '';
return {
getContent : function(url){
$(container_div).animate({opacity:0},
function(){ // the callback, loads the content with ajax
$(container_div).load(url, //only loads the selected portion
function(){
$(container_div).animate({opacity:1});
}
);
});
},
ajaxify_links: function(elements){
$(elements).click(function(){
AjaxContent.getContent(this.href);
return false;
});
},
init: function(params){
container_div = params.containerDiv;
content_div = params.contentDiv;
return this;
}
}
}();
I need help how to integrate a preloading, so if visitors hit one of my link (for example the gallery menu) will see a little loading image, because now they see the big white nothing for long - long seconds.
Add loading image beforing ajax call and after you get response from server simply replace that image with data like the one below
function(){ // the callback, loads the content with ajax
$(container_div).html("<img src='loading.gif' />");//add image before ajax call
$(container_div).load(url, //only loads the selected portion
function(){
$(container_div).html(data);//replace image with server response data
$(container_div).animate({opacity:1});
}
Try this
var AjaxContent = function(){
var container_div = '';
var content_div = '';
return {
getContent : function(url){
$(container_div).html('Loading...'); //replace with your loading img html code
$(container_div).load(url, //only loads the selected portion
function(){
$(container_div).css({opacity:0});
$(container_div).animate({opacity:1});
});
},
ajaxify_links: function(elements){
$(elements).click(function(){
AjaxContent.getContent(this.href);
return false;
});
},
init: function(params){
container_div = params.containerDiv;
content_div = params.contentDiv;
return this;
}
}
}();

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