syncronized scroll does not work in div based ckeditor - ckeditor

Basically i have 2 instances of ckeditor on a single page. One on the left and another in the right side of the page.
The left editor uses a div rather than traditional iframe. I've done this by removing the iframe plugin and including the div editing area plugin.
The right editor loads in an iframe and but is also div based(i can use the iframe editor as well on the right if required, not an issue).
Now if the cursor/focus is on the right editor's content area then the left editor should scroll along with it. I've tried to use the code as provied by Reinmar in below url but it seems to work only with editors based on iframe on both sides. Also please note that i'm using jquery adapter for initializing the editors.
CKEDITOR how to Identify scroll event
I initialized the editor on left as below:
var editor_left = $( '#editor_left' ).ckeditor();
And below is my code for the right editor:-
var editor_right = $( '#editor_right' ).ckeditor();
editor_right.editor.on( 'contentDom', function() {
var editable = editor_right.editor.editable();
editable.attachListener( editable.getDocument(), 'scroll', function() {
alert('scroll detected');
parent.$('#left_editor_content_area').scrollTop($(this).scrollTop())
});
});
If i use the iframe based editor on the right then i'm able to get the "scroll detected" alert. But the scrollTop() function still does not work as expected
Any help will be appreciated.

The code that you mentioned is right. You got to update scrollTop property of the div-based editable with the scroll position of the window inside the iframe-based editor (JSFiddle).
var editor_div = CKEDITOR.replace( 'editor_div', {
extraPlugins: 'divarea'
} );
CKEDITOR.replace( 'editor_iframe', {
on: {
contentDom: function() {
var editable = this.editable(),
win = this.document.getWindow(),
doc = editable.getDocument();
editable.attachListener( doc, 'scroll', function( evt ) {
// Get scroll position of iframe-based editor.
var scroll = win.getScrollPosition();
// Update scroll position in the div-based editor.
editor_div.editable().$.scrollTop = scroll.y;
} );
}
}
} );

Related

Scrolling to elements in CKEditor via JavaScript

My CKEditor fields often contain lots of content with h1, h2, h3, etc headings, and I've written a script that presents all the headings in a sidebar for quick reference. I'd also like to use this sidebar as a navigation menu for the editor content, so clicking a heading in the sidebar scrolls the editor to the related heading, but I can't figure out how to wire it all up.
This post at https://davidwalsh.name/scroll-element-ckeditor leads me to believe that it should be possible, but I can't figure out how to get to the "editor" element described in the post.
My sidebar is built with jQuery from a CKEditor textarea with id="content" like this...
var content = $('<div/>').append($('#content').val());
var sidebar = "";
$(content).find('h1,h2,h3,h4,h5,h6').addClass('heading');
$(content).find('.heading').each(function () {
sidebar += this.outerHTML;
});
$('#sidebar').html(sidebar);
I imagine using jQuery :contains() to identify heading elements in the editor based on the text they contain, but I can't figure out how to hook back into the CKEditor instance in a way that enables this kind of DOM activity.
I am using CKEditor 4 but am happy to upgrade to version 5 if it offers a better solution to my problem.
Thanks!
This is what wound up working for me:
var content = $('<div/>').append($('#content').val());
var sidebar = "";
$(content).find('h1,h2,h3,h4,h5,h6').addClass('heading');
$(content).find('.heading').each(function () {
sidebar += this.outerHTML;
});
$('#sidebar').html(sidebar);
$('#sidebar .heading').click(function() {
var element = $('#cke_content iframe').contents().find(':contains(' + $(this).text() + ')')[2];
if (element) {
element.scrollIntoView();
}
});

Kendo Window positioning

I am using bootstrap template and Kendo Window and so far positioning of modal kendo windows wasn't too hard.
But now as I a use a different layout for a certain area, I find myself having problems with that matter.
following code is expected to create a centered (x-axis) modal kendo window:
#(Html.Kendo().Window()
.Name("Window1")
.Visible(false)
.Position(builder => builder.Top(100))
.Draggable()
.Content(#<div class="kendoWindowContent"><p>Please wait...</p><div class="k-loading-image"></div></div>)
.Width(1000)
.Title("Title1")
.Actions(actions => actions.Close())
.Modal(true)
.Resizable())
..and displaying via:
var wnd = $("#ownerVoucherCreateWindow").data("kendoWindow");
wnd.refresh({
url: '#Url.Action("Voucher_Create", "OwnerVoucher")'
});
wnd.open();
The window is not beeing displayed in the middle of the x axis.
Are there any constraints in order to have the kendo window beeing centered.
Window centering requires the usage of the center() method. Since the Window content is loaded via Ajax, you need to center the widget in its refresh event.
var wnd = $("#ownerVoucherCreateWindow").data("kendoWindow");
wnd.one("refresh", function(e) {
e.sender.center();
});
wnd.refresh({
url: '#Url.Action("Voucher_Create", "OwnerVoucher")'
});
wnd.open();
It is also possible to trigger centering in every refresh event, instead of just once.
Another option is to set explicit width and height. In this case you can center the Window at any time, because the widget dimensions will not change after changing (loading) the content.
ok I guess I was just lucky that all my kendo windows happened to be displayed centered although specifying an explicit offset to top like described.
I assumed, that the window would automatically center on y-axis when having only an x-axis position set.
As it seems this is not the case. I don't really know why this has been working in the past.
Anyway, I figured out a way to center the window depending on the browsers' viewport and window width:
just in case anybodes cares...
function displayWindowCenteredOnYAxis(kendoWindow) {
var windowOptions = kendoWindow.options;
var pos = kendoWindow.wrapper.position();
var viewPortWidth = $(window).width();
var wndWidth = windowOptions.width;
pos.left = viewPortWidth / 2 - wndWidth/2;
kendoWindow.wrapper.css({
left: pos.left
});
kendoWindow.open();
}
Usage:
var wnd = $("#id").data("kendoWindow");
wnd.refresh({
url: '#Url.Action("Action", "Controller")'
});
displayWindowCenteredOnYAxis(wnd);

How to disable automatic style to img in ckeditor?

There is one problem when I work with ckeditor. I press on icon for adding images, then modal window appears and I put direct image link in the special field, in this moment width and height detected automatically and it writes to style for ex:
<img src='#some images direct link' style='width:SOME WIDTH;height:SOME HEIGHT'>
Can I disable this automatic function? Or I have to delete this style by myself every time?
You could write a rule for your config that would remove the style tag on elements.
var editorRulesObject = {
elements: {
img: function( el ) {
if(el.attributes.style) {
el.attributes.style = '';
}
}
}
};
CKEDITOR.on('instanceReady', function( e ) {
// Ensures that any non-styled text, or text input without any tags will be correctly styled.
CKEDITOR.instances[e.editor.name].dataProcessor.dataFilter.addRules( editorRulesObject );
CKEDITOR.instances[e.editor.name].dataProcessor.htmlFilter.addRules( editorRulesObject );
});
In 2020, since version 4.5.0, it is much easier to switch off the annoying automatic filling in of height and readiness.
New there is the config option "image_prefillDimensions".
config.image_prefillDimensions = false;
Documentation: http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-image_prefillDimensions
The user still has the possibility to fill in the height and width automatically by clicking on the button [Reset size (circle arrow)].

CKEDITOR - how to add permanent onclick event?

I am looking for a way to make the CKEDITOR wysiwyg content interactive. This means for example adding some onclick events to the specific elements. I can do something like this:
CKEDITOR.instances['editor1'].document.getById('someid').setAttribute('onclick','alert("blabla")');
After processing this action it works nice. But consequently if I change the mode to source-mode and then return to wysiwyg-mode, the javascript won't run. The onclick action is still visible in the source-mode, but is not rendered inside the textarea element.
However, it is interesting, that this works fine everytime:
CKEDITOR.instances['editor1'].document.getById('id1').setAttribute('style','cursor: pointer;');
And it is also not rendered inside the textarea element! How is it possible? What is the best way to work with onclick and onmouse events of CKEDITOR content elements?
I tried manually write this by the source-mode:
<html>
<head>
<title></title>
</head>
<body>
<p>
This is some <strong id="id1" onclick="alert('blabla');" style="cursor: pointer;">sample text</strong>. You are using CKEditor.</p>
</body>
</html>
And the Javascript (onclick action) does not work. Any ideas?
Thanks a lot!
My final solution:
editor.on('contentDom', function() {
var elements = editor.document.getElementsByTag('span');
for (var i = 0, c = elements.count(); i < c; i++) {
var e = new CKEDITOR.dom.element(elements.$.item(i));
if (hasSemanticAttribute(e)) {
// leve tlacitko mysi - obsluha
e.on('click', function() {
if (this.getAttribute('class') === marked) {
if (editor.document.$.getElementsByClassName(marked_unique).length > 0) {
this.removeAttribute('class');
} else {
this.setAttribute('class', marked_unique);
}
} else if (this.getAttribute('class') === marked_unique) {
this.removeAttribute('class');
} else {
this.setAttribute('class', marked);
}
});
}
}
});
Filtering (only CKEditor >=4.1)
This attribute is removed because CKEditor does not allow it. This filtering system is called Advanced Content Filter and you can read about it here:
http://ckeditor.com/blog/Upgrading-to-CKEditor-4.1
http://ckeditor.com/blog/CKEditor-4.1-Released
Advanced Content Filter guide
In short - you need to configure editor to allow onclick attributes on some elements. For example:
CKEDITOR.replace( 'editor1', {
extraAllowedContent: 'strong[onclick]'
} );
Read more here: config.extraAllowedContent.
on* attributes inside CKEditor
CKEditor encodes on* attributes when loading content so they are not breaking editing features. For example, onmouseout becomes data-cke-pa-onmouseout inside editor and then, when getting data from editor, this attributes is decoded.
There's no configuration option for this, because it wouldn't make sense.
Note: As you're setting attribute for element inside editor's editable element, you should set it to the protected format:
element.setAttribute( 'data-cke-pa-onclick', 'alert("blabla")' );
Clickable elements in CKEditor
If you want to observe click events in editor, then this is the correct solution:
editor.on( 'contentDom', function() {
var element = editor.document.getById( 'foo' );
editor.editable().attachListener( element, 'click', function( evt ) {
// ...
// Get the event target (needed for events delegation).
evt.data.getTarget();
} );
} );
Check the documentation for editor#contentDom event which is very important in such cases.

CKEditor dialog input field above tab elements

I'm building a simple dialog plugin to replace the default link tool. The design calls for a particular layout that is difficult to achieve with the CKEdit dialog definition: We want a single field to appear above the tab elements in the dialog (see illustration).
Can anyone suggest a way that this might be implemented? Thanks!
As far as I can tell it is not possible to achieve this using the built-in dialog definition.
I was able to get around this limitation by building my dialog plugin using the iframedialog plugin. This basically pops up a CKEditor dialog window and loads an external URL into it. You can do anything you want in that iframe, and then return the text to CKEditor when the user presses the OK button.
A simple example:
// plugins/iframelink/plugin.js
CKEDITOR.plugins.add('iframelink', {
requires: ['iframedialog'],
init: function(editor){
CKEDITOR.dialog.addIframe('iframelinkDialog',
// title
'Insert a Link',
// src
this.path + 'dialogs/link.html',
// minWidth
500,
// minHeight
250,
// onContentLoad
);
var cmd = editor.addCommand('iframelink', {exec: iframelinkOnclick});
editor.ui.addButton('iframelink', {
label: 'Insert a Link (Special Link Tool)',
command: 'iframelink',
icon: this.path + 'images/world_link.png'
});
}
});
function iframelinkOnclick(editor){
dialog = editor.openDialog('msiteslinkDialog');
};
// plugins/iframelink/dialogs/iframelink.js
$(function() {
if (typeof(window.parent.CKEDITOR) != 'undefined') {
CKEDITOR = window.parent.CKEDITOR;
var dialog = CKEDITOR.dialog.getCurrent();
var editor = dialog.getParentEditor();
// Get value of the selected text:
var selection = editor.getSelection().getSelectedText();
// Do something when the user presses the OK button:
var okListener = function(ev) {
link = yourFunctionToDoSomethingClever();
this._.editor.insertHtml(link);
dialog.removeListener("ok", okListener);
};
// Bind the OK button to your okListener method:
dialog.on("ok", okListener);
};
}
So you can make the dialog look any way you want:

Resources