Share Quill toolbar across multiple editors - javascript

With the great Quill rich text editor for Javascript I'm trying to make two or more editors share the same toolbar.
I suppose (from documentation) that this is not possible right away at the moment, so I'm trying to "simulate" this by adding the toolbar module through API on the editor that has been clicked as the latter:
// this uses jQuery
$editorTag.click(function(e){
var tag = e.target;
var editor = getEditorByTag(tag);
if( editor )
editor.addModule('toolbar',{container:'#toolbar'});
});
It seems to work, but I suspect that Quill doesn't like adding many times the same module over and over on the same object since eventually it spits:
(node) warning: possible EventEmitter memory leak detected. 11
listeners added. Use emitter.setMaxListeners() to increase limit.
quill.js (row 4727)
So is there a way to remove a previously added module? Something like:
// installs editor
var editor = new Quill('#editor');
// adds toolbar module
editor.addModule('toolbar',{container:'#toolbar'});
// removes just added toolbar module
editor.removeModule('toolbar');

I encountered the same problem the other day, and have found a solution that works for our usecase. This is basically what I've done:
I'm creating one instance of Quill, using a custom toolbar positioned
at the top. The editor element is placed in a temporary, hidden,
container. When the user double clicks any of the three text
containers (Editables), the editor element will be transplanted form
the temporary container to a new location inside the Editable. If a
user hits the escape key, the Editable will be deactivated, moving the
editor element back to the temporary container.
You can test it here: https://codesandbox.io/s/hungry-pine-o8oh9?file=/src/App.js
GitHub repo: https://github.com/maxfahl/Quill-Edit-Multiple
Feel free to use the code however you'd like.

A solution that keeps the history of your Quills is to extends Toolbar and register it has a module
class ToolbarAlt extends Toolbar {
resetToolbar () {
this.container.childNodes.forEach(el => {
const clone = el.cloneNode(true);
el.parentNode.replaceChild(clone, el);
});
this.container.childNodes.forEach((input) => {
this.attach(input);
}, this);
}
}
Quill.register('modules/toolbar', ToolbarAlt, true);
Then call your Toolbar with the quill.getModule('toolbar') when you go focusin one editor

Related

Detecting text input to VSCode activeTextEditor

I'm working on a VScode extension for my school open source project.
I was wondering there was a way to detect text input to the activeTextEditor window?
For example if someone were to paste a string in, could I grab detect that string similar to an OnInput in JavaScript? A setup would be spell checking or doing a replacement for commands, similar to Visual Studios' intellisense you type prop +tab +tab it auto generates code.
Let me know if you've heard of something that might work. Thanks
The api you are looking for is vscode.workspace.onDidChangeTextDocument. This event is fired whenever a document that VS Code knows about changes. It is fired with a TextDocumentChangeEvent object which includes all the text changes:
import * as vscode from 'vscode'
export function activate() {
vscode.workspace.onDidChangeTextDocument(changeEvent => {
console.log(`Did change: ${changeEvent.document.uri}`);
for (const change of changeEvent.contentChanges) {
console.log(change.range); // range of text being replaced
console.log(change.text); // text replacement
}
});
}
If you only care about changes to the active editor's text, just check to see if changeEvent.document matches the active editor's document.

Removing tool from contenttools

I'm trying to remove the insert video option (icon) built into contenttools from the menu.
I tried following this https://github.com/GetmeUK/ContentTools/issues/173 but getting a toolbox().tools() Cannot read property '_toolsUIs' of null error. I'm guessing tools is no longer a method for toolbox. Doe's anyone know how to remove icons from the editor menu?
window.addEventListener('load', function(){
var editor;
editor = ContentTools.EditorApp.get();
editor.toolbox()._toolsUIs['video'].disabled(true);
}

fillEmptyBlocks config in CKEditor 4.6.2 does not help manipulate insertion of <br> tags in empty DIVs

Problem 1: I consume the following plugins as part of a customized build of the basic version of CKEditor 4.6.2 - basicstyles, dialogui, dialog, clipboard, button, toolbar, enterkey, floatingspace, undo, divarea. The empty DIV container blocks have <br> elements added by CKEditor which I am trying to prevent.
The config file looks like this -
CKEDITOR.editorConfig = function(config) {
config.title = false;
config.allowedContent = true;
config.fillEmptyBlocks = false;
// Toolbar groups configuration.
config.toolbar = ...
};
fillEmptyBlocks = false; did not prevent CKEditor 4.6.2 from inserting the <br> elements. I tried doing what https://stackoverflow.com/a/34849579 suggests by having all the line-break rules set to FALSE and I could not get around the problem.
I tried it with the whole basic version of CKEditor 4.6.2 and the released version of 4.5.11 and this still occurs in them. However, this problem does not exist in CKEditor 4.1.3 and 4.4.0. Am I missing something as part of a config or should I have any additional plugin to not have this issue with 4.6.2?
Problem 2:
Pressing backspace in an empty div block removes the div block from the DOM. This again happens with 4.6.2 and 4.5.11 and not on 4.1.3 or 4.4.0, is there a config that I could be missing?
Edited: <br> tags are being inserted despite having fillEmptyBlocks set to false in all the versions post CKEditor 4.4.7, it does not happen with 4.4.6. Looking at the release notes, http://dev.ckeditor.com/ticket/12735 is the change that has gone into 4.4.7 Is there another way to fix this then?
I have developed similar kind of WYSIWYG editor using CKEDITOR 4 where everything is working fine although I used ckeditor.replace
However, I uploaded it in the github. So, you can refer there and compare and fix your code if you want.
Click here for the github project.
Please ask me if you face any problems!!
Please try this code it is working for me:
For removing br on load
$('your_id').ckeditor();
CKEDITOR.on('instanceReady', function() {
$('div.application').find('br').remove(); // remove br tag added on empty divs
});
for removing &nbsp use this in destroy function:
for(CKname in CKEDITOR.instances) // delete multiple instances of ckeditor
{
CKEDITOR.instances[CKname].destroy(function(){
$("your_id").html(function (i, html) {
return html.replace(/ /g, ''); // remove
});
})
}

CKEditor, custom object with children

I am trying to create a plugin for CKEditor that adds a custom object with children.
Example:
<div>
<img src="someimage.jpg" />
<p>
Some text
<span>Some subtext</span>
</p>
<img src="someStaticImage.jpg" />
</div>
In the onOk function i have:
---snip---
this.imgElement.setAttribute('src',path + data.imageSrc);
this.staticImgElement.setAttribute('src',path + 'images/staticimg.jpg');
this.imgElement.appendTo(this.element);
this.imgElement.appendTo(this.element);
this.staticImgElement.appendTo(this.element);
---snip---
I would like for this block to behave as a single element, meaning that pressing backspace deletes the whole block, double clicking on it opens the edit dialog...
Any idea how i could do that?
I came close with setting
this.element.setAttribute('contenteditable','false');
However this doesn't allow content to be inserted before "it", if "it" was the first element in the ckedit window.
EDIT:
More info:
I'm using CKEditor 4.0, inline version
I wish for my "object" to be like the "image" plugin, where when you double click on the image, a dialog opens, the same one as when you create the object (where you set the src, width...).
I managed to make it similar to it, but because it is a div with child elements, CKEditor treats each part as seperate, which makes the deleting of the object (with backspace) behave in a wierd way, only parts of it get deleted, backspace needs to be pressed multiple times to delete the entire object.
I'm a CKEditor core developer and I think that I've got an interesting news for you :). Coincidentally, right now we're working on Widgets feature which is exactly what you mean.
Making some fragments of the page non-editable by setting contenteditable=false make them unusable. Selecting, copying&pasting, right clicking, using arrow keys - all this is at least partially broken. And it's even worse if you try to add a nested editable element (editable inside non-editable), because e.g. it can be deleted from inside.
That's why we decided to implement a nice API for creating widgets and fix all these bugs under the hood. Maybe not all bugs and in all browsers at the beginning, because there's huge (really... I mean huuuuge :P) amount of them and of course no standard behaviour between browsers at all. But it will be a good start. First version of widgets that will be released in upcoming CKEditor 4.2 should be usable - this is our goal. Then we'll focus on stabilizing the implementation.
PS. CKEditor Roadmap says that CKE 4.2 will be ready in 11 days and this is not true, unfortunately. We're delayed, but I don't want to estimate now how much.
You indicated that you've created the plugin that handles the object, but that the problem you want to solve is the inability to insert content before the object when it's the first item.
It looks like this bug report is about this issue:
Can't move cursor behind non-editable element (or placeholder) in CKEditor
I used my plugin to insert this block of code into the editor:
<div contenteditable="false">
<img src="someimage.jpg" />
<p>
Some text
<span>Some subtext</span>
</p>
<img src="someStaticImage.jpg" />
</div>
You can also add the display: inline-block; style if you want (see discussion below):
From my tests, it seems like you can't put content before the object using the back arrow, but if you back arrow to that row of content and press the home key, you can type before the object. It also seems that if you click with your mouse in the upper left corner, you can add content before the object.
Both approaches push the object onto the next line because it's a <div>, you can change the style of the div to display: inline-block; if you want the object to stay on the first row. I tried just making the object <div> a <span> instead, but then it becomes possible to edit parts of the object.
You can't use backspace to delete the object, but you can click the object to select it and then delete it.
I checked the info discussed above with Firefox 20 and IE 9 on Win 7. Google Chrome has a bunch of problems:
When the block of HTML is inserted with the plugin, the contenteditable="false" attribute is stripped out.
So I tried to see how it worked if I just pasted that block of code into CkEditor while in source mode. The contenteditable="false" attribute wasn't stripped out, but the whole content area became uneditable.
My tests were using CkEditor 3.6.1, so this may not be a problem in CkEditor 4.X.
This bug report seems to be about the problem I encountered with being unable to do anything in the content area using Chrome, the report indicates version 3.X:
ContentEditable, Image and Chrome
Additional Info
Here's a plugin from CKSource that might be helpful:
Magic Line
The description:
With this plugin you can create new paragraphs into spaces where normally would be impossible to reach. For example, above a table at the beginning of the document.
Here's my plugin that inserts content into the editor, it doesn't solve the problem you have, but you might use it to add functionality to your plugin. I'll write the full instructions in case someone who hasn't created a plugin finds this and wants to give it a try.
Here's the code that goes in the ckeditor/plugins/cwmarinsertsnippet/plugin.js file:
/**
* Plugin to insert the contents of an element into the editor content area.
*/
// Register the plugin with the editor. cwmarinsertsnippet
// http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.plugins.html
CKEDITOR.plugins.add( 'cwmarinsertsnippet',
{
// The plugin initialization logic goes inside this method.
// http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.pluginDefinition.html#init
init: function( editor )
{
// Define an editor command that grabs the content of an element and inserts it into the content area.
// http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.editor.html#addCommand
editor.addCommand( 'cwMarInsertSnippet',
{
// Define a function that will be fired when the command is executed.
// http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.commandDefinition.html#exec
exec : function( editor )
{
// Create an element based on a native DOM element.
var codesnippet = new CKEDITOR.dom.element( document.getElementById( 'resmar' ) );
//alert( codesnippet.getHtml() );
// Insert the element content into the document.
// http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.editor.html#insertHtml
editor.insertHtml( codesnippet.getHtml() );
}
});
// Create a toolbar button that executes the plugin command.
// http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.ui.html#addButton
editor.ui.addButton( 'CwMarInsertSnippet',
{
// Toolbar button tooltip.
label: 'Insert Search Box',
// Reference to the plugin command name.
command: 'cwMarInsertSnippet',
// Button's icon file path.
icon: this.path + 'images/buttonicon.gif'
});
}
});
It needs to be added to the config file with:
config.extraPlugins = 'cwmarinsertsnippet';
and to make the button visible, the button name "CwMarInsertSnippet" needs to be added to the config toolbar entry:
CKEDITOR.config.toolbar_XXXX
[
Snip...
{ name: 'tools', items : [ 'About','CwMarInsertSnippet' ] },
... End Snip
];
The button for the plugin should be 13px X 13px (for CkEditor 3.X, not sure about version 4.X). It gets placed here:
ckeditor/plugins/cwmarinsertsnippet/images
The name should match the one used in the editor.ui.addButton function at the end of the plugin code (this path can really be anywhere you want).
Here's an example of the code to be added to the page where CkEditor is being used:
<div id ="resmar">
<div contenteditable="false">
<img src="someimage.jpg" />
<p>
Some text
<span>Some subtext</span>
</p>
<img src="someStaticImage.jpg" />
</div>
</div>
You can also add the display: inline-block; style if you want:
<div style="display:inline-block" contenteditable="false">
The container element can be hidden with styling if it shouldn't appear on the page.
Basically, just put the content you want to insert inside the element with the target ID
<div id ="resmar">
Content to be inserted.
</div>
Of course the plugin name and the element ID can be anything you like.

Needing an ExtJS bug workaround

I have a web application that uses Ext-JS 2.2. In a certain component, we have an empty toolbar that we are trying to add a button to using
myPanel.getTopToolbar().insertButton(0, [...array of buttons...]);
However, in IE6/7 this fails because of lines 20241-20242 in ext-all-debug.js:
var td = document.createElement("td");
this.tr.insertBefore(td, this.tr.childNodes[index]);
Since "this.tr.childNodes([0])" does not yet exist in IE, this fails with "Invalid argument".
THE REAL QUESTION:
Can I, using CSS similar to the below add a child to every toolbar <tr> so that this.tr.childNodes[0] is found:
div.x-toolbar tr:after { content: " "; }
I totally realize this is a hack, but for legal reasons I cannot change any Javascript, not even to add an empty button ({}) to each toolbar. Major kudos to anyone that can figure this out.
What I've had to do in the past was include an empty toolbar in my element config:
tbar:[]
Then (and only after the element has completely rendered) use the .add() method for injecting buttons.
Order of events will get you every time. It takes a while to get a handle on it.
If all you are doing is adding to a empty panel
myPanel.getTopToolbar().add(buttons etc);
Or
myPanel.getTopToolbar().addButton(..);
Either should work. It looks like purpose of insertButton is for putting a button within a non-empty toolbar.
Did you look into adding the button after the panel has been rendered? Maybe something like:
myPanel.on('render', function() {
this.getTopToolbar().insertButton(0, [...array of buttons...]);
}, true);
I didn't think there was a CSS-only solution.
For the record, I ended up injecting javascript into the page that overrides the Ext.Toolbar prototype for the insertButton() function to check for the existance of "this.tr.childNodes([0])" and default to addButton() if it didn't exist.

Categories

Resources