Add rel="noopener" to links/anchors in ckeditor - ckeditor

When a user creates an anchor element in WYSIWYG view, I want to automatically insert rel="noopener" to the HTML.
How can I achieve that?

You can use the dataProcessor to force the way the elements are build:
dataProcessor.htmlFilter.addRules( {
elements: {
a: function( el ) {
if ( !el.attributes.rel) {
el.attributes['rel'] = 'noopener';
}
}
}
});
Here is a working jsfiddle:
https://jsfiddle.net/25x37LgL/

Related

Changing dropdown selected item in jquery not changing text?

I have a dropdownlist and I am trying to change the selected item but the text is not changing. I am assuming it's because I am doing it client side but I once I've changed the selected item I am un sure how to change the text in the dropdownlist.
JQuery Code: -
function setPhotoStage(photoStageId) {
$("[id*=drpPhotoStages] option").each(function () {
if ($(this).val() == photoStageId) {
$(this).attr('selected', 'selected');
alert($(this).val());
} else {
$(this).removeAttr('selected');
}
});
}
The code above is selecting the correct item in the list if I click on it but not changing it on the form: -
Here is the code when I inspect the dropdown. Some how I need to change the filter-option-inner-inner to the newly selected text: -
Any help please, I've looked everywhere and cannot find a solution.
I managed to sort it by adding refresh to the dropdown: -
function setPhotoStage(photoStageId) {
$("[id*=drpPhotoStages] option").each(function () {
if ($(this).val() == photoStageId) {
$(this).attr('selected', 'selected');
} else {
$(this).removeAttr('selected');
}
});
$("#drpPhotoStages").selectpicker('refresh');
}

ag grid Passing KeyPress/Enter event to apply filter

I want to use the same logic when I press the APPLY button but with the ENTER key.
How do I do that?
filterParams: {
closeOnApply:true,
buttons: ['reset', 'apply'],
values: parentCampaignAndNodes.PaymentGroups
},
As you might have found out by now, you can remove the Apply button (by setting filterParams: { buttons: [] } in your column definitions) and then values are submitted onchange.
The solution you ask for is indeed still not available through the AgGrid API. I do have a workaround, but beware it uses direct DOM bindings which is not recommended when working with React.
const applyFilterOnEnter: AgGridReactProps['onFilterOpened'] = ev => {
const inputElem = ev.eGui.querySelector('.ag-filter-body .ag-input-field');
const applyButtonElem = ev.eGui.querySelector('.ag-filter-apply-panel button[ref=applyFilterButton]');
if (inputElem && applyButtonElem) {
inputElem.addEventListener('keydown', keyEv => {
if ((keyEv as KeyboardEvent).key === 'Enter') {
(applyButtonElem as HTMLButtonElement).click();
}
});
}
};
return <AgGridReact
onFilterOpened={applyFilterOnEnter}
/>

How to force CKEditor to process widgets when setting the HTML of an element

I have create a simple CKEditor widget that highlights the elements that have the class "pink".
I have also added a "Pinkify" button to the toolbar, which replaces the HTML of the selected element with some other elements that have the class "pink".
What I observe when I click the button is that widgets are not created for the freshly inserted elements. However, when I toggle between Source mode and WYSISYG mode, the widgets get created.
See the jsfiddle and its code:
CKEDITOR.replace('ck', {
allowedContent: true,
extraPlugins: 'pink'
});
CKEDITOR.plugins.add('pink', {
requires: 'widget',
init: function(editor) {
editor.widgets.add('pinkwidget', {
upcast: function(element) {
return element.hasClass('pink');
}
});
editor.addCommand('pinkify', {
editorFocus: 1,
exec: function(editor) {
var selection = editor.getSelection(),
selectedElement = selection.getStartElement();
if (selectedElement) {
selectedElement.setHtml("Let's have some <span class=\"pink\">pink</span> widget here!");
editor.widgets.checkWidgets(); // needed?
}
}
});
editor.ui.addButton('pinkify', {
label: 'Pinkify',
command: 'pinkify'
});
},
onLoad: function() {
CKEDITOR.addCss('.cke_widget_pinkwidget { background: pink; }');
}
});
I am aware of this question on Stackoverflow, but I can't get it to work with setHtml called on an element. Can you suggest how to modify the code so that widgets get created as soon as the HTML is updated?
According to the CKEditor team, it is normal that CKEDITOR.dom.element.setHtml does not instanciate widgets (see Widgets not initialised after calling setHtml on an element).
So the workaround they gave me was to rewrite the code that insert HTML in place of the selected element to:
if (selectedElement) {
selectedElement.setHtml("");
editor.insertHtml("Let's have some <span class=\"pink\">pink</span> widget here!");
}
For those like me who didn't know, editor.insertHTML inserts HTML code into the currently selected position in the editor in WYSIWYG mode.
Updated jsFiddle here.

CKEditor: multiple widget templates

I'm currently creating a 'smartobject' widget. In the widgets dialog, the user can choose a 'smartobject', which simply put, generates some html, which should be added to the editor. Here comes the tricky part: the html sometimes div elements and sometimes simply span elements. In the case of the div variant, the widget should be wrapped in a div 'template'. In the case of a span variant, the widget should be wrapped in a span and the html should be added 'inline'.
In the widgets API I see the following way to define a template:
editor.widgets.add('smartobject', {
dialog: 'smartobject',
pathName: lang.pathName,
template: '<div class="cke_smartobject"></div>', // <------
upcast: function(element) {
return element.hasClass('smartObject');
},
init: function() {
this.setData('editorHtml', this.element.getOuterHtml());
},
data: function() {
var editorHtml = this.data.editorHtml;
var newElement = new CKEDITOR.dom.element.createFromHtml(editorHtml);
newElement.copyAttributes(this.element);
this.element.setText(newElement.getText());
}
});
But in my case, the template is more dynamic: sometimes a div and sometimes the span will do the correct thing..
How can I fix this without needing to create two widgets which will do the exact same thing, with only the wrapping element as difference?
I've already tried to replace the entire element in the 'data' method, like:
newElement.replace(this.element);
this.element = newElement;
But this seemed not supported: resulted in undefined errors after calling editor.getData().
I'm using ckeditor v4.5.9
Thanks for your help!
It seems I got it working (with a workaround).
The code:
CKEDITOR.dialog.add('smartobject', this.path + 'dialogs/smartobject.js');
editor.widgets.add('smartobject', {
pathName: lang.pathName,
// This template is needed, to activate the widget logic, but does nothing.
// The entire widgets html is defined and created in the dialog.
template: '<div class="cke_smartobject"></div>',
init: function() {
var widget = this;
widget.on('doubleclick', function(evt) {
editor.execCommand('smartobject');
}, null, null, 5);
},
upcast: function(element) {
return element.hasClass('smartObject');
}
});
// Add a custom command, instead of using the default widget command,
// otherwise multiple smartobject variants (div / span / img) are not supported.
editor.addCommand('smartobject', new CKEDITOR.dialogCommand('smartobject'));
editor.ui.addButton && editor.ui.addButton('CreateSmartobject', {
label: lang.toolbar,
command: 'smartobject',
toolbar: 'insert,5',
icon: 'smartobject'
});
And in the dialog, to insert code looks like:
return {
title: lang.title,
minWidth: 300,
minHeight: 80,
onOk: function() {
var element = CKEDITOR.dom.element.createFromHtml(smartobjectEditorHtml);
editor.insertElement(element);
// Trigge the setData method, so the widget html is transformed,
// to an actual widget!
editor.setData(editor.getData());
},
...etc.
UPDATE
I made the 'onOk' method a little bit better: the smartobject element is now selected after the insertion.
onOk: function() {
var element = CKEDITOR.dom.element.createFromHtml(smartobjectEditorHtml);
var elementId = "ckeditor-element-" + element.getUniqueId();
element.setAttribute("id", elementId);
editor.insertElement(element);
// Trigger the setData method, so the widget html is transformed,
// to an actual widget!
editor.setData(editor.getData());
// Get the element 'fresh' by it's ID, because the setData method,
// makes the element change into a widget, and thats the element which should be selected,
// after adding.
var refreshedElement = CKEDITOR.document.getById(elementId);
var widgetWrapperElement = CKEDITOR.document.getById(elementId).getParent();
// Better safe then sorry: if the fresh element doesn't have a parent, simply select the element itself.
var elementToSelect = widgetWrapperElement != null ? widgetWrapperElement : refreshedElement;
// Normally the 'insertElement' makes sure the inserted element is selected,
// but because we call the setData method (to ensure the element is transformed to a widget)
// the selection is cleared and the cursor points to the start of the editor.
editor.getSelection().selectElement(elementToSelect);
},
So in short, I partially used the widget API for the parts I wanted:
- Make the html of the widget not editable
- Make it moveable
But I created a custom dialog command, which simply bypasses the default widget insertion, so I can entirely decide my own html structure for the widget.
All seems to work like this.
Any suggestions, to make it better are appreciated:)!
As suggested in this ckeditor forum thread, the best approach would be to set the template to include all possible content elements. Then, in the data function, remove the unnecessary parts according to your specific logic.

Remove class from editable area

Im need to remove class from editable area (cke_textarea_inline) in my plugin.
var command = editor.addCommand('command', {
exec: function(editor) {
//remove class... HOW?
}
});
It's as simple as: editor.editable().removeClass( 'cke_textarea_inline' );.

Resources