CKEDITOR: can I create a widget that uses a different plugin - ckeditor

I have a plugin that inserts a chunk of HTML in the form of a div with various attributes but not other content. I have a 'doubleclick' handler that opens my plugin dialog and populates it when the user doubleclicks. It would be nice if my inserted DIV was a "widget" so I could drag it around and select it. I see in the widget docs how to create a widget, but does that mean I have to recreate my entire plugin in the widget framework? Can I just somehow turn it into a widget "as is?"

I added this code to the init section of my plugin:
editor.widgets.add( 'anyName', {
upcast: function( element ) {
//indicate the thing you want to be a widget:
//e.g., <div class=subbox>:
return element.name == 'div' && element.hasClass( 'subbox' );
}
});
And this seems to work. Did I do it right? Any unforeseen consequences here?

Related

How to disable widget content nesting in CKEditor?

I have an editable custom widget that can be placed in CKEditor 4 by
clicking a button in the toolbar - works fine = does not allow nesting
drag & drop from outside of the editor - allows nesting
I do not want to let user to have nested content of the widget. On the other hand I do want users to able to edit the content of the widget.
NOTE
click the button to insert widget's content. Click inserted text and the button becomes not available for clicking. Click some other text and you would be able to insert again. This is desired behavior.
the widget button will not be present in the final version of the CKEditor application / widget. Only drag&drop way of inserting text will be available
Insert two widgets in the editor and try to drag&drop one inside the other one. It will not work. This is desired behavior.
Now try to insert a widget and then insert another one by drag&drop of the text "master or editable" into existing widget. It will be possible.
Could someone help me to set CKEditor a way so nesting is NOT possible?
Working jsfiddle.
init: function( editor ) {
editor.widgets.add( 'simplebox', {
button: 'Create a simple box',
template:
'<div class="simplebox">' +
'<h2 class="simplebox-title">Title</h2>' +
'<div class="simplebox-content"><p>Content<br>.<br>.<br>.</p></div>' +
'</div>',
editables: {
title: {
selector: '.simplebox-title',
allowedContent: 'br strong'
},
content: {
selector: '.simplebox-content',
allowedContent: 'p br ul ol li strong em'
}
},
allowedContent:
'div(!simplebox); div(!simplebox-content); h2(!simplebox-title)',
requiredContent: 'div(simplebox)',
upcast: function( element ) {
return element.name == 'div' && element.hasClass( 'simplebox' );
}
} );
}
} );

Making focus works inside a CK Editor 5 createUIElement

So I've a custom widget which renders a custom component.
conversion.for('editingDowncast').elementToElement({
model: 'modelName',
view: (modelElement, viewWriter) => {
const modelName = modelElement.getAttribute('modelName');
const modelNameView = viewWriter.createContainerElement('span', {
class: 'modelName',
'data-modelName': modelName,
});
const reactWrapper = viewWriter.createUIElement(
'span',
{
class: 'modelName__react-wrapper',
},
function (this, domDocument) {
const domElement = this.toDomElement(domDocument);
rendermodelName(modelName, domElement);
return domElement;
},
);
viewWriter.insert(
viewWriter.createPositionAt(modelNameView, 0),
reactWrapper,
);
return toWidgetEditable(modelNameView, viewWriter);
},
});
Where rendermodelName will give back a React component with a simple input box as
return (
<div>
<input type="text" />
</div>
);
https://ckeditor.com/docs/ckeditor5/latest/builds/guides/integration/frameworks/react.html.
But the problem is, whenever I tried to add some content inside the input, the focus is lost from the field and automatically moved to the surrounding editor. What am I missing. Tried creating a focushandler and adding the modelNameView to it.
Should I go with the new createRawElement? My current CK5 is 20.0.0 So I don't want any breaking changes coming now.
EDIT:
I researched a little bit more. seems like createRawElement may not work here. I think this doesn't have a simple solution. I tried with allowContentOf: '$block' which also not letting me focus. But these values are explicitly for normal CK widget, not for a react component.
I had the same issue and solved it by adding this tag to the parent div that wraps my Vue component.
https://ckeditor.com/docs/ckeditor5/latest/framework/guides/deep-dive/ui/widget-internals.html#exclude-dom-events-from-default-handlers
Adding from CKE Docs:
Sometimes it can be useful to prevent processing of events by default handlers, for example using React component inside an UIElement in the widget where, by default, widget itself wants to control everything. To make it possible the only thing to do is to add a data-cke-ignore-events attribute to an element or to its ancestor and then all events triggered by any of children from that element will be ignored in default handlers.
Let’s see it in an short example:
<div data-cke-ignore-events="true">
<button>Click!</button>
</div>
In the above template events dispatched from the button, which is placed inside containing data-cke-ignore-events attribute, will be ignored by default event handlers.
I faced the similar issue.
CKEditor will takes all the events on React component which you hosted on Widget.
The work around is to stop propagation of events to CKEditor which are fired from your DOM element(domElement) where your React component hosted.
Here is the sample code:
https://github.com/ckeditor/ckeditor5-core/compare/proto/input-widget#diff-44ca1561ce575490eac0d660407d5144R239
You should stop all required events. Also you can't paste any content inside the input field of React component. That will also listened by clipboardInput event of CKEditor.

Wordpress menu - ajax loading

Is it possible to dynamically load Wordpress menu via Ajax?
Best solution would be using wp_nav_menu().
If your theme needs to dynamically initialize menus with JavaScript, the pattern for the initialization code should be:
jQuery(function($) {
function initMainNavigation( container ) {
/* set up container... */
}
initMainNavigation( $( '.main-navigation' ) );
$( document ).on( 'customize-preview-menu-refreshed', function( e, params ) {
if ( 'primary' === params.wpNavMenuArgs.theme_location ) {
initMainNavigation( params.newContainer );
/* optionally sync a previous menu state from params.oldContainer... */
}
});
});
The params being passed to the event handler consists of the following properties:
newContainer: jQuery object containing the new menu container element retrieved from Ajax; this is what you would manipulate to initialize.
oldContainer: the previous jQuery object holding the element for the replaced menu container; this is useful if there is any state in the old menu that should persist in the new menu, such as which submenus are expanded (as in Twenty Fifteen).
wpNavMenuArgs: The array of arguments passed to wp_nav_menu() in the template, such as template_location.
instanceNumber: The index for which wp_nav_menu() call being updated.
You can create a custom file to handle ajax requests in your theme, returning the HTML output of wp_nav_menu(); and call that file.
wp-content/themes/your-theme/ajax.php:
<?php wp_nav_menu(); ?>
It's simple, but efficient. Be careful with security though. Make sure to validate input and dont eval() any input!

Loading a hidden div into an AJAX jQuery UI tab (future DOM element)

I have been trying to manipulate content that is loaded into jQuery UI tabs via AJAX.
As you can imagine, these elements are "future" DOM elements and aren't manipulated by normal $(".someClass")functions.
I've read using .live() for event handling is now deprecated using jQuery 1.7+ and is replaced by the new .on() method.
My issue is that the div I want to hide, when it loads in an AJAX tab, must be manipulated after the initial DOM load and is not bound to a click event at first.
My functions, which are currently wrapped in $() are below.
I think I have the syntax correct for links that use a click handler, but I'm not sure of the correct way to ".hide()" my "hiddenStory" div at load.
I also think that the functions themselves shouldn't be wrapped in an overall $()?
Any help or advice would be greatly appreciated.
$(function(){
// this .hiddenStory div below is what I want to hide on AJAX load
// need syntax and/or new methods for writing this function
$(".hiddenStory").hide();
// this is a function that allows me to toggle a "read more/read less" area
// on the "hiddenStory" div
$(".showMoreOrLess").on('click', (function() {
if (this.className.indexOf('clicked') != -1 ) {
$(this).removeClass('clicked');
$(this).prev().slideUp(500);
$(this).html("Read More" + "<span class='moreUiIcon'></span>");
}
else {
$(this).addClass('clicked');
$(this).prev().slideDown(500);
$(this).html("See Less" + "<span class='lessUiIcon'></span>");
}
}));
});
// prevents default link behavior
// on BBQ history stated tab panes with
// "showMoreOrLess" links
$('.showMoreOrLess').click(function (event)
{
event.preventDefault();
// here you can also do all sort of things
});
// /prevents default behavior on "showMoreOrLess" links
Could you set the display: none via CSS and override it when you wanted to show the element's content? Another option, if you have to do it this way would be to add the `$(".hiddenStory").hide() in the callback from the AJAX load that is populating the element. For example:
$(".hiddenStory").load("http://myurl.com", function(){
$(".hiddenStory").hide();
}
);
If you aren't using the .load method, you should have some sort of call back to tie into (e.g. success if using $.ajax...)

Watermark text in CKEditor

Do you know how to add watermark in CKEditor (visual word processor)?
I want the behavior like this: When the CKEditor is loaded, it has text by default. When I click on it, text must disappear.
Below are the steps to add water mark in CKEditor
Generally when you set default text in Ck Editor through java script on page load. JavaScript event get fired before the control actually load so if possible set the text for code behind.
Attaching Events in Javascript for OnFocus and OnBlur
$(document).ready(function() {
var myeditor = CKEDITOR.instances['EditorId'];
myeditor.on("focus", function(e) {
RemoveDefaultText();
});
myeditor.on("blur", function(e) {
setDefaultText();
});
});
Define your default text in this function
function setDefaultText() {
if (CKEDITOR.instances['EditorId'].getData() == '') {
CKEDITOR.instances['EditorId'].setData('Your Message Here');
}
}
function RemoveDefaultText() {
if (CKEDITOR.instances['EditorId'].getData().indexOf('Your Mesage Here') >= 0) {
CKEDITOR.instances['EditorId'].setData('');
CKEDITOR.instances['EditorId'].focus();
}
}
You can also define styles to the water mark by adding classes to the default text and place the class into you ck editor content css otherwise it will not work
A ready to use plugin can be tested here. It allows to customize the default text and it takes into acount, dialogs as well as reading the data from an external script.
You might want to try this jQuery plugin:
https://github.com/antoineleclair/ckwatermark

Resources