How-to clear custom style applied to element? - ckeditor

With Ckeditor 4x, when you add custom styles, you cannot use the "Remove format" button it not remove the style (class).
It seem to be working good only when use the default styles.

There are a couple of things to explain here because of design decisions taken in the past it is not as easy as it seems.
By default only inline styles are removed with RemoveFormat.
In order to remove block styles applied by the Styles drop-down, you need to unselect them in that drop-down (it works in toggle mode). You will than stay with a raw tag like H1 which you can change to another tag like P with the help of the Format drop-down. It has to be done in that order.
It seems the best solution would be to overwrite the addRemoveFormatFilter method. Let's say for example that you want to remove format only from h1-h3:
var editor = CKEDITOR.replace('textarea_id');
editor.on( 'pluginsLoaded', function( ev ) {
editor.addRemoveFormatFilter( function( element ) {
return element.is( 'h3' ) || element.is( 'h1' ) || element.is( 'h2' );
});
});
Of course you can use more advanced code here and not just a simple check if something is a particular element or not. You could for example use the checkElementRemovable method to check if a particular element matches a particular style.
Please note that it is necessary to select the whole block element to remove its format. In some cases e.g. a header holding a floated image it might be necessary to select this element using the element's path because it is hard to select it using the mouse.

Related

create and show one use only dialog, constructed based on global state.

I have a plugin which need to show a (Modal) dialog each time the user double click on a word.
Detecting double click is no problem, but the exact fields/values in the dialog depends on exactly which word the user clicked on, and some mutable global state. So I can't create the dialog until the moment before I need to show it. And here is the problem: How do I do that?
Right now I use this code:
var dialogName="uniqueDialog" + counter++;
CKEDITOR.dialog.add(dialogName,function(editor) {
// Creating dialog here.
});
CKEDITOR.instances.editor.openDialog(dialogName);
This works, but having to add a uniquely named dialog, just to show it once and then newer use it again seems really really wrong. Also I fear this will keep using resources since the dialogs are newer removed(I could not find any remove method).
So my question is: Is there a better way to dynamical create and show a "one use" dialog?
Update:
If bootstrap is not allowed then maybe an addFrame version of the dialog is acceptable. This could then refer to a html file that can load from parameters.
NB: The plunkr only works, if you fork and edit it, otherwise it will give you a 404 for the template.
Here is a quick plunkr:
plunky
And here is the plugin in question:
CKEDITOR.plugins.add( 'insertVariable', {
requires: ['iframedialog'],
icons: 'insertvariable',
init: function( editor ) {
editor.addCommand( 'varDialog', new CKEDITOR.dialogCommand( 'varDialog' ) );
CKEDITOR.dialog.addIframe('varDialog','varDialog','sample.html?var='+item,500,400);
editor.ui.addButton( 'insertVariable', {
label: 'Insert Variable',
command: 'varDialog',
icon: this.path + '<insert gif>'
});
}
});
Obviously you are not creating dialogs anymore with different content, but you are referring to another piece of html, that can change. I've kept the bootstrap thing in there as well for reference.
I made one final edit, that will show the current contents. So I think that is roughly what you want. Check it out.
Previous Answer
If you are prepared to use bootstrap, then you can do no worse than check out their modal dialog, which can be just be shown and hidden at will.
http://getbootstrap.com/javascript/#modals
It's simple and cuts down any need to keep creating your own dialog. The dialog won't be one use type, but you will set the defaults as necessary. The varying content link is here:
http://getbootstrap.com/javascript/#modals-related-target
That would be the quickest way to get this going. It all depends on whether you want to use this framework. As CKEDITOR is already using JQuery it is an option worth considering.

Setting default paragraph style without user interaction

I am trying to set the default style applied to the P elements that are automatically created when a user enters the blank editing area. I've spent many hours searching for an answer but have not found anything that works. The requirements are:
Style has to be inline, no stylesheet
No user interaction, no format/style plugin to click
When the user clicks in the editing area and starts typing, I want the style to be applied and visible automatically. Surely there is a way to accomplish this?
The closest I have gotten is by using the htmlFilter, like this :
p_rule = {
elements : {
p : function(element) {
if (element.attributes.style === undefined) {
element.attributes.style = "color: #0000ff;";
}
}
}
};
ev.editor.dataProcessor.htmlFilter.addRules(p_rule);
But the new style is not automatically visible.
It does become visible if the user goes into source editing mode and back to WYSIWYG but I want it to be automatic.
I tried using updateElement() in the filter function, but it does not work and creates infinite recursion:
p_rule = {
elements : {
p : function(element) {
if (element.attributes.style === undefined) {
element.attributes.style = "color: #0000ff;";
CKEDITOR.instances['editor1'].updateElement();
}
}
}
};
ev.editor.dataProcessor.htmlFilter.addRules(p_rule);
(I guess updateElement() triggers the filter)
If I use setData(getData()) from an event I can strangely get the textarea to update with the changes the filter applied, for example:
CKEDITOR.instances['editor1'].on('blur', function() {
CKEDITOR.instances['editor1'].setData(CKEDITOR.instances['editor1'].getData());
});
But that too requires user interaction. Using the "change" event creates recursion.
I am new at CKEditor and obviously I'm missing something on how the filter works in relation to what is currently being displayed in the textarea.
Any CKEditor guru out there? Help!
Thanks
I really advise not to go this way. You'll find yourself fighting with countless issues, like what if you copy&paste, what if you change format to h1 and then back, what if you create a list item and then convert that into a paragraph, etc. etc. There are really dozens of those. You'd need to rewrite half of the editor.
The way to handle this in CKEditor 4 is to rethink this:
Style has to be inline, no stylesheet
Inside CKEditor you clearly need to use a stylesheet. I presume though that you want the inline styles in the output. So what I would propose is to:
Write htmlFilter rule which adds this style to every paragraph.
Write dataFilter rule which removes this style from every paragraph.
The second rule is needed so if you save the data and then load it back to the editor, the styles do not pollute it.
PS. CKEditor 5 will separate data model from rendering (the view) so you'll be able to render paragraph as you wish without affecting how other features interact with it. Read more about CKEditor 5 in this article.

How to tell if an element is a Widget? (CKEditor)

Per CKEditor, initialize widget added with insertElement, we are doing an insertElement() and then initializing with initOn(). The problem is that some of the elements we are inserting are not supposed to be widgets and initOn() makes them widgets and the context menu doesn't work right. I am having trouble finding any properties inside the item/element to tell if something is/is not a widget so I can then call initOn().
Cross-posted downstream on Drupal.org here https://www.drupal.org/node/2466297
First of all - which element do you mean?
(Note: In this section I am assuming that a widget was correctly and fully initialised.)
Widget element
A widget can obviously consists of many elements. One of them is called the "widget element" and this is the element which you "upcasted" and which you can later access through widget.element.
Since CKEditor 4.5.0 there will be such method available:
Widget.isDomWidgetElement = function( node ) {
return node.type == CKEDITOR.NODE_ELEMENT && node.hasAttribute( 'data-widget' );
};
You can of course already use this code to check if a given node is a widget element.
Widget wrapper
Second important element is the widget's wrapper. It is created during data processing if a widget element was marked to be upcasted or when initOn() is called if the widget element wasn't wrapped yet. You can access this element through the widget.wrapper property.
Since CKEditor 4.5.0 there will be a following method available:
Widget.isDomWidgetWrapper = function( node ) {
return node.type == CKEDITOR.NODE_ELEMENT && node.hasAttribute( 'data-cke-widget-wrapper' );
};
And again - you can use this code already.
Important note here - since you mention insertElemet() in your question. As I explained in CKEditor, initialize widget added with insertElement editor#insertElement() does not trigger data processing. Therefore, element that you insert is inserted as is. This means that the widget wrapper is not created during insertion and will be created once you call initOn().
Finding widgets by any element
Many times you want to find a widget instance by some element that you have (any element that can be inside a widget). There's a useful method for that: getByElement().
What should become a widget? Aka - how to deal with editor.insertElement()?
You mentioned that you use editor.insertElement() and that you don't know which elements are supposed to be widgets. This should never happen. editor.insertElement() is a quite low level method which will not do all the data processing and upcasting magic which editor.insertHtml() does. It means that it is supposed to be used in a different case - when you want to insert exactly the element that you have.
For instance, your table plugin is building a table structure to be inserted into editor. You know that the table is empty, so you control every bit of it (other plugins should not interfere here). It is also important that it's the table's plugin decision, not e.g. a template's plugin decision. The table's plugin control the table feature, while the template plugin only uses tables. So in such case, when you have a full control, you can use editor.insertElement(). Then you always know what you insert and what is supposed to become a widget.
In all other scenarios you should use editor.insertHtml(), so the whole data processing layer is triggered. Thanks to it other features like the widgets system, the link plugin (which turns empty anchors into fake objects), etc. can prepare the data that you insert to be fully editable and integrated.
Tl;dr
If your plugin knows what it does, it can use editor.insertElement(), but since it knows what it does it will know which inserted element must become a widget.
If your plugin does not fully control the situation, then you should use the editor.isertHtml() method which is far more automated and will turn proper elements into widgets based on the upcast callbacks.

Targeting pseudo elements with d3 - is it possible?

This works just fine:
d3.selectAll('ul li')
.style('background', 'red');
However, this doesn't do anything:
d3.selectAll('ul li:before')
.style('background', 'red');
The selection returned by d3.selectAll('ul li:before') is empty, even though the :before elements do exist, and have some existing CSS styling.
Is it possible to target pseudo elements with d3?
And if it is, a quick follow up question: How would I target all the :before pseudo-elements directly on (ie, not within) a particular selection?
Eg:
var listItems = d3.selectAll('ul li');
var beforeElements = listItems.selectAll('&:before'); // SASS-style selector obviously won't work here
It is not possible the way you're trying to do it.
The querySelector methods, on which D3's select functions are based, never return results for pseudo-element selectors.
Furthermore, the D3 style method works by setting inline style attributes on the selected elements. You cannot set inline styles for pseudo-elements, so setting the style attribute on the parent element won't work either.
What you could do is select the parent elements, give them a class name, and then use CSS stylesheet rules to target the :before/:after pseudo-elements for objects of that class. If you would need to dynamically create the CSS rules, see this Q&A.
However, it is probably easiest to just create empty <span> or <div> child elements, and style those instead.

CKEditor ignores tags which have classes

If the source of something I write in my CKEDITOR looks like this:
This is my text. <strong>This part is bold.</strong> This part isn't.
I can highlight the bolded part and unbold it by pressing CTRL+B. However, if I add a class to that strong tag (due to another plugin I'm working on), I can only unbold clean strong tags - no attributes, styles, or classes. For example, consider this scenario:
This is my text. <strong>This part is bold.</strong> This part isn't. <strong class="whatever">This part is bolded AND has a custom class.</strong>
Only the first bolded segmented will be unbolded - the 2nd is pretty much stuck as is until I remove the ".whatever" class. Is there any way to get it to ignore strong tags with classes, and just do them regardless of what other attributes they have? I'm guessing it has to do with that "Advanced Content Filter" or something, but I can't figure out what.
After much hairpulling, I (think) I have the answer. In the CKEDITOR style definition, an applied style (ex, a strong tag) needs to have all of its attributes parsed through the content filter. If an attribute that is not dealt with by this filter remains when it comes time to actually remove the textNode from the style tags and replace it back into the parent element, the tags (and thus the style) will NOT be removed if there are any attributes remaining on the element. There is a (very poorly) documented workaround to this: the alwaysRemoveElement property can be set to true on the style DEFINITION (why the definition, and not the style itself, I have no idea).
Long story short, a little snippet of code that will force the removal of all style tags, even if their attributes don't match exactly with the filter. Hopefully it doesn't cause bugs somewhere else...
//this = Your Editor Instance
this.data.editor.on( 'instanceReady', function(){
//Filter through the existing contentRules, looking for styleCommands
$.each(this.activeFilter.allowedContent, function(i,v) {
var name = v.featureName, command = this.commands[v.featureName];
if (name && command && command.contentForms && command.style) {
command.style._.definition.alwaysRemoveElement = true;
}
}.bind(this));
}.bind(this));
As the previous answer, just add this in the config:
CKEDITOR.config.coreStyles_bold : { element: 'strong', overrides: 'b' ,alwaysRemoveElement: true},
It was also difficult for me to find this workaround, in my case I was adding an id to the strong element.

Resources