CKEditor: strip element without attributes? - ckeditor

I've configured extraAllowedContent in "config.js" to allow div elements with specific classes, per the Advanced Content Filtering guide, which is working.
However, I need to strip any div elements that have no attributes. Is this possible?

You can specify config.disallowedContent with match function:
CKEDITOR.replace( 'editor1', {
disallowedContent: {
div: {
match: function( el ) {
return CKEDITOR.tools.isEmpty( el.attributes );
}
}
}
} );
While it correctly filters out the contents, for some reason (a bug), it also disables the Div plugin and its dialog. Thus I'd rather suggest something like this at the moment:
CKEDITOR.replace( 'editor1', {
on: {
pluginsLoaded: function( evt ) {
var editor = evt.editor,
rules = {
elements: {
div: function( el ) {
if ( CKEDITOR.tools.isEmpty( el.attributes ) ) {
// Return false to get rid of an element with children.
return false;
// The element can also be removed preserving children.
// el.replaceWithChildren();
}
},
}
};
// Filter what comes out of an editor.
editor.dataProcessor.htmlFilter.addRules( rules );
// Filter what comes into an editor.
editor.dataProcessor.dataFilter.addRules( rules );
}
}
} );

Related

How to make a context menu in a custom ckeditor5 widget?

I made a inline widget similar a placeholder (ckeditor4), but now I want to render a dropdown when the widget is selected to show options values to replace the placeholder. I trying use BalloonPanelView but no success until now, someone have a idea about how to make it?
this.editor.editing.view.document.on('click', (evt, data) => {
evt.stop();
const element = data.target;
if (element && element.hasClass('placeholder')) {
if (!element.getAttribute('data-is-fixed')) {
const balloonPanelView = new BalloonPanelView();
balloonPanelView.render();
['option1', 'option2', 'option3'].forEach((value) => {
const view = new View();
view.set({
label: value,
withText: true
});
balloonPanelView.content.add(view);
});
balloonPanelView.pin({
target: element
});
}
}
});
I found the solution using ContextualBalloon class:
import ContextualBalloon from "#ckeditor/ckeditor5-ui/src/panel/balloon/contextualballoon";
// Define ballon
const balloon = editor.plugins.get(ContextualBalloon);
const placeholderOptions = // Here I defined list with buttons '<li><button></li>'
// Finnaly render ballon
balloon.add({
view: placeholderOptions,
singleViewMode: true,
position: {
target: data.domTarget
}
});

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

CKEditor - add in custom attribute

In CKEditor I'm having issues adding some dataProcessor rules.
I have a custom plugin defined in ckeditor/plugsin/x
I have added the plugin name to config.js extraPlugins
My plugin looks like
CKEDITOR.plugins.add('x',
{
init:function(editor)
{
editor.dataProcessor.htmlFilter.addRules(
{
elements :
{
div : function( element )
{
element.setAttribute("x","y");
}
}
});
editor.dataProcessor.dataFilter.addRules(
{
elements :
{
div : function( element )
{
element.setAttribute("x","y");
}
}
});
});
However it doesn't insert the attribute.
Am I doing something wrong here?
CKEDITOR.dataProcessor works with CKEDITOR.htmlParser.element instead of CKEDITOR.dom.element. CKEDITOR.htmlParser.element is not a real DOM element but an abstract object to make parsing and filtering much easier. It has own set of methods and attributes.
Also note that dataFilter works on input data (what comes to the editor) while htmlFilter deals with output data (content produced by the editor).
You should also get used to Allowed Content Filter because quite likely you need to configure it to have the editor working properly, i.e. config.extraAllowedContent = 'div[x]'.
See the fiddle.
CKEDITOR.replace( 'editor', {
extraAllowedContent: 'div',
on: {
pluginsLoaded: function() {
this.dataProcessor.dataFilter.addRules( {
elements: {
div: function( el ) {
console.log( 'processing', el, 'in dataFilter' );
el.attributes.datafilter = 'x';
}
}
} );
this.dataProcessor.htmlFilter.addRules( {
elements: {
div: function( el ) {
console.log( 'processing', el, 'in htmlFilter' );
el.attributes.htmlfilter = 'y';
}
}
} );
}
}
} );

TypeScript kendoTreeView change / select class method callback

I've been searching around to find why Kendo callbacks are not working with TypeScript but I couldn't find something useful.
So my question is how can kendo use class method as a callback?
Ex:
$("#ipt_tree").kendoTreeView(
{
dataSpriteCssClassField: "sprite",
dataSource: data,
template: "<span data-oid='#= item.oid#'>#= item.text#</span>",
//change: this.Tree_Item_Selected, //doens't get even called
//change: ( item: any ): void => //'this' is not 'this' of the class
//{
// this.Tree_Item_Selected( item );
//}
change: function( item: any ) //using compiler this variable
{
_this.Tree_Item_Selected( item );
}
});
The only solution I've found is to use the _this variable that compiler makes.
Now for the jquery the method callbacks work perfectly.
$( "#ipb_aci_button_edit" ).show().on( "click", this.Info_OnClick_Edit );
private Info_OnClick_Edit = (): void =>
{
//'this' is correct
}
If using the current version of TypeScript (1.0RC), you create a class:
class Demo {
private Tree_Item_Selected(item:any) { }
public Create_Tree(data:any) {
var kendoSettings = {
dataSpriteCssClassField: "sprite",
dataSource: data,
template: "<span>#= item.text#</span>",
change: ( item: any ): void =>
{
this.Tree_Item_Selected( item );
},
change2: function( item: any )
{
_this.Tree_Item_Selected( item );
}
};
}
}
And compile that to JavaScript, the functions change and change2 in the example code both produce the exact same code block:
change: function (item) {
_this.Tree_Item_Selected(item);
},
change2: function (item) {
_this.Tree_Item_Selected(item);
}
The only difference is that the second one produces an error that _this is not found.
In the second example, it's working because it's captured the this correctly. However, you might want to consider a different syntax:
$( "#ipb_aci_button_edit" ).show().on( "click",
(e:JQueryEventObject) => { this.Handle_Info_OnClick_Edit(e) } );
private Handle_Info_OnClick_Edit(e:JQueryEventObject): void
{
// 'this' is correct as it was captured by the event handler code
}

How to default p element to "Normal" style in CKEditor

If I add a paragraph style to the CKEdtior
eg:
format_p: { element : 'p', attributes : { 'style' : 'FONT-SIZE:16px;color:#000000;FONT-STYLE:normal;FONT-FAMILY:Arial, Helvetica, sans-serif;font-weight:normal;' } }
The default style when pressing the enter key is blank. However, if I set the style to "Normal" the style is applied and subsequent p's created by clicking the enter key include the style above.
What I want is for all paragraphs (tag 'p') to use the "Normal" style by default. Is there a way to achieve this?
I think you have use 'contentsCss', have you try 'dataProcessor' like this :
CKEDITOR.on('pluginsLoaded', function (event) {
event.editor.dataProcessor.dataFilter.addRules({
elements: {
p: function (element) {
// element.attributes
}
}
});
event.editor.dataProcessor.htmlFilter.addRules ({
elements: {
p: function (element) {
// element.attributes ...
}
}
});
});

Resources