Most of our customers complain about formatting carried across from Word to our redactor.js rich text editor fields. We upgraded to use the pastePlainText setting, which seems to work well.
However SOME customers still need to paste html into the rich text boxes. We've added a "paste as html" button to the toolbar using a plugin but we can't work out what code to add to the plugin to paste the clipboard content as-is into the editor. Help!
We'd be almost as happy to remove the pastePlainText option and have a "paste as plain text" button on the toolbar instead, but we also can't work out how to do that.
RedactorPlugins.pasteAsHtml = {
init: function () {
this.buttonAdd('pasteAsHtml', 'Paste as HTML', this.testButton);
},
testButton: function (buttonName, buttonDOM, buttonObj, e) {
// What do we do here?
};
$(".richText").redactor({
plugins: ['pasteAsHtml'],
pastePlainText: true
});
We now have a solution to this.
We were barking up the wrong tree here: for security reasons it's difficult to read from the clipboard. We had assumed that redactor.js has the ability to do this, but in fact it appears to read from the rich text editor only after the user has initiated the paste themselves via Ctrl+v or the context menu. That means clicking a button to trigger a "paste" isn't easy. I believe there's at least one jquery plugin that attempts to solve that problem, and a bunch of solutions involving Flash, but we're after a more lightweight fix.
Instead, we did the following.
Set redactor to accept html (ie we didn't set the pastePlainText option).
Caused our button to show a modal dialog containing a textarea, into which the user pastes their html content. Once the content is pasted we process it to strip out html and retain line breaks.
So users wanting to retain formatting just paste into the RTE, and users who want to paste as plain text click the new button. Here's the code for the plugin.
if (!RedactorPlugins) var RedactorPlugins = {};
RedactorPlugins.pasteAsPlainText = {
init: function () {
// add a button to the toolbar that calls the showMyModal method when clicked
this.buttonAdd('pasteAsPlainText', 'Paste as plain text', this.showMyModal);
},
// pasteAsPlainText button handler
showMyModal: function () {
// add a modal to the DOM
var $modalHtml = $('<div id="mymodal" style="display:none;"><section><label>Paste content here to remove formatting</label><textarea id="mymodal-textarea" style="width: 100%; height: 150px;"></textarea></section><footer><button class="btn btn-primary" id="mymodal-insert" style="color:#fff;">Insert</button></footer></div>');
$("body").append($modalHtml);
// callback executed when modal is shown
var callback = $.proxy(function () {
this.selectionSave();
$('#mymodal-insert')
.css("width", "100px")
.click($.proxy(this.insertFromMyModal, this));
$("#mymodal-textarea").focus();
}, this);
// initialize modal with callback
this.modalInit('Paste as plain text', '#mymodal', 500, callback);
},
insertFromMyModal: function (html) {
this.selectionRestore();
// remove formatting from the text pasted into the textarea
html = $('#mymodal-textarea').val();
var tmp = this.document.createElement('div');
html = html.replace(/<br>|<\/H[1-6]>|<\/p>|<\/div>/gi, '\n');
tmp.innerHTML = html;
html = tmp.textContent || tmp.innerText;
html = $.trim(html);
html = html.replace('\n', '<br>');
html = this.cleanParagraphy(html);
// insert the text we pulled out of the textarea into the rich text editor
this.insertHtml(html);
// close the modal and remove from the DOM
this.modalClose();
$("#mymodal").remove();
}
};
$(".richText").redactor({
plugins: ['pasteAsPlainText']
});
By the way, if Internet Explorer had a "paste as plain text" option available via Ctrl+shift+v or on the context menu like Firefox and Chrome we would just have told customers to do that!
If you've just recently upgraded from Redactor v9 to v10, you will find that the above code does not work since Redactor has now updated most of its existing APIs and added new modules. For example, .modalInit(), .selectionRestore(), .selectionSave(), .insertHtml() from v9 is now .modal.load(), selection.restore(), .selection.save(), etc in v10.
I've modified the above code slightly and am adding it here if anybody's interested. Feel free to edit/ optimize it if needed.
Reference - http://imperavi.com/redactor/docs/how-to-create-modal/
if (!RedactorPlugins) var RedactorPlugins = {};
RedactorPlugins.pasteasplaintext = function()
{
return {
init: function()
{
// add a button to the toolbar that calls the showMyModal method when clicked
var button = this.button.add('pasteasplaintext', 'Paste As Plain Text');
this.button.setAwesome('pasteasplaintext', 'fa-paste');
this.button.addCallback(button, this.pasteasplaintext.showMyModal);
},
getTemplate: function()
{
// this function creates template for modal that is to be added
return String()
+ '<div id="mymodal">'
+ ' <section>'
+ ' <label>Paste content here to remove formatting</label>'
+ ' <textarea id="mymodal-textarea" style="width: 100%; height: 150px;"></textarea>'
+ ' </section>'
+ '</div>';
},
showMyModal: function () {
// fetch and load template
this.modal.addTemplate('pasteasplaintext', this.pasteasplaintext.getTemplate());
this.modal.load('pasteasplaintext', 'Paste As Plain Text', 500);
// create cancel and insert buttons
this.modal.createCancelButton();
var buttonPaste = this.modal.createActionButton('Paste');
buttonPaste.on('click',this.pasteasplaintext.insertFromMyModal);
// save current content, show modal and add focus to textarea
this.selection.save();
this.modal.show();
$("#mymodal-textarea").focus();
},
insertFromMyModal: function (html) {
// remove formatting from the text pasted into the textarea
html = $('#mymodal-textarea').val();
var tmp = document.createElement('div');
html = html.replace(/<br>|<\/H[1-6]>|<\/p>|<\/div>/gi, '\n');
tmp.innerHTML = html;
html = tmp.textContent || tmp.innerText;
html = $.trim(html);
html = html.replace('\n', '<br>');
// close modal, restore content and insert 'plain' text
this.modal.close();
this.selection.restore();
this.insert.html(html);
$("#mymodal").remove();
}
};
};
If you, like me, were looking for a plugin to paste simple text into Redactor and found this question only to learn that the answers are very old, you'd be happy to find out that the answer is actually in the Redactor docs.
It turns out that their example plugin with modal window is exactly that, a "paste clean text" plugin.
You can find it here:
https://imperavi.com/redactor/examples/creating-plugins/sample-plugin-with-modal-window/
Related
I am using PrimeFaces 7 and its TextEditor component, which uses internally the free and open-source editor Quill
I need to add a custom HTML button, which, when selected, inserts the word selected on the current position of the cursor in the TextEditor - (in the Quill)
It is possible to add custom buttons in the Quill Editor and to attach EventListeners to them, as shown here:
https://quilljs.com/docs/modules/toolbar/
(Please look at this part of the above page):
var customButton = document.querySelector('#custom-button');
customButton.addEventListener('click', function() {
console.log('Clicked!');
});
There is also an API provided by the Quill editor to insert text at a given position, see here:
https://quilljs.com/docs/api/#content
have a look at this mehod:
`quill.insertText(0, 'Hello', 'bold', true);`
However, I do miss a couple of things:
1.) The definition of the custom-button shall be done in the toolbar div, as specified in the following code:
`<div id="toolbar">
<!-- But you can also add your own -->
<button id="custom-button"></button>`
However: how could I do that, when using ready PrimeFaces Text Editor component?
I locally tried this:
jQuery(document).ready(function () {
jQuery(document).ready(function () {
var customButton = document.getElementById("resultsFormId:quillToolbarId_toolbar:custButId");
if (customButton!=null){
return;
}
customButton = document.createElement("button");
customButton.innerHTML = 'Params';
customButton.id="resultsFormId:quillToolbarId_toolbar:custButId";
var qlTollbar = document.getElementById("resultsFormId:quillToolbarId_toolbar");
qlTollbar.appendChild(customButton);
customButton.addEventListener('click', function() {
Quill.insertText(0, 'Hello', 'bold', true);
console.log('Clicked!');
});
});
});
and could say the following:
1.1) The custom button gets inserted on the Quill toolbar (is not nice and styled, but it is there)
1.2) When I click the custom button, first the EventListener gets executed. This is OK,but here:
Quill.insertText(0, 'Hello', 'bold', true);
instead of Quill.insertText()., I need a reference to the js-object representing the Quill editor. === > Could you help?
1.3) after the EventListener from Point (1.2) gets executed, my whole code under
jQuery(document).ready(function () {
jQuery(document).ready(function () {
gets executed once again, the id of the customButton is not found, and it is recreated once again. ===> Could I avoid that?
2.) In the code for the insertion of a text on a specific position, I need to get the last position the cursor was, before the user clicked (selected the option on the) custom button.
How can I do that?
I never heard of PrimeFaces, but maybe I can answer this part of you question:
Quill.insertText(0, 'Hello', 'bold', true);
instead of Quill.insertText()., I need a reference to the js-object representing the Quill editor. === > Could you help?
According to the code on GitHub it should be
PrimeFaces.widget.TextEditor.editor.insertText(0, 'Hello', 'bold', true)
can you confirm? Then you should also be able to get the cursor location and selection (if any) with
PrimeFaces.widget.TextEditor.editor.getSelection();
My current Chrome extension is supposed to programmatically insert text on user input. This works great, however, it mangles the undo/redo behavior of all text editors I've tried it on so far. So, the following routine:
type some text
insert some text programmatically
type more text
press ctrl-z thrice
does not always revert to a blank state. Very often it will get stuck somewhere in the middle. Its behaviour is mostly inconsistent.
Here's an MCVE of the content script:
function init() {
var $input = document.getElementsByTagName("textarea")[0];
if (!$input) {
console.log("No input element found.");
return true;
}
var $btn = document.createElement("button");
$btn.innerHTML = "Click";
$btn.addEventListener("click", function() {
var caretPos = $input.selectionStart;
$input.value = $input.value.substring(0, caretPos) + " test string " + $input.value.substring(caretPos);
});
$input.parentElement.insertBefore($btn, $input);
return true;
}
window.addEventListener("load", init);
<div><textarea></textarea></div>
(I also bundled it as a Chrome extension in case you'd like that.)
I want the undo/redo to function perfectly in both textarea as well as contenteditable nodes. I also tried document.execCommand in both insertText and insertHTML modes without any success. I've looked at the other two related questions but they do not answer my query. (q1, q2)
What else can be a possible solution to this problem?
Thanks to the helpful comments above, my problem was doing execCommand on pre-programmed weird editors like Facebook messenger box, tinymce, etc. They might have their own customizations interfering.
In normal text editors, document.execCommand should work fine, and support undo/redo. Use it like so:
document.execCommand('insertText', false, "text");
document.execCommand('insertHTML', false, "html");
When using tinyMCE in a jqueryUI modal dialog, I can't use the hyperlink or 'insert image' features.
Basically, after lots of searching, I've found this:
http://www.tinymce.com/develop/bugtracker_view.php?id=5917
The weird thing is that to me it seams less of a tinyMCE issue and more of a jqueryUI issue since the problem is not present when jqueryUI's modal property is set to false.
With a richer form I saw that what happens is that whenever the tinyMCE loses focus, the first element in the form gets focus even if it's not the one focused / clicked.
Does some JavaScript guru have any idea how I might be able to keep the dialog modal and make tinyMCE work?
This fixed it for me when overriding _allowInteraction would not:
$(document).on('focusin', function(e) {
if ($(event.target).closest(".mce-window").length) {
e.stopImmediatePropagation();
}
});
I can't really take credit for it. I got it from this thread on the TinyMCE forums.
(They have moved their bugtracker to github. tinymce/issues/703 is the corresponding github issue.)
It seems there are no propper solution for this issue yet. This is kind of a hack but it really worked for me.
Every time you open the Dialog remove the text area and re add it like following,
var myDialog = $('#myDialog');
var myTextarea = myDialog.find('textarea');
var clonedTextArea = myTextarea.clone(); // create a copy before deleting from the DOM
var myTextAreaParent = myTextarea.parent(); // get the parent to add the created copy later
myTextarea.remove(); // remove the textarea
myDialog.find('.mce-container').remove(); // remove existing mce control if exists
myTextAreaParent.append(clonedTextArea); // re-add the copy
myDialog.dialog({
open: function(e1,e2){
setTimeout(function () {
// Add your tinymce creation code here
},50);
}
});
myDialog.dialog('open');
This seems to fix it for me, or at least work around it (put it somewhere in your $(document).ready()):
$.widget('ui.dialog', $.ui.dialog, {
_allowInteraction: function(event) {
return ($('.mce-panel:visible').length > 0);
}
});
I am using bootstrap-popover to show a message beside an element.
If I want to show different text in the popover after the first time, the text does not change. Re instantiating the popover with new text does not overwrite.
See this js fiddle for a live example:
http://jsfiddle.net/RFzvp/1/
(The message in the alert and the message in the dom is inconsistent after the first click)
The documentation is a bit light on how to unbind: http://twitter.github.com/bootstrap/javascript.html#popovers
Am I using this wrong? The Any suggestions on how to work around?
Thanks
You can access the options directly using the jquery data closure dictionary like this:
$('a#test').data('bs.popover').options.content = 'new content';
This code should work fine even after first initializing the popover.
Hiya please see working demo here: http://jsfiddle.net/4g3Py/1/
I have made the changes to get your desired outcome. :)
I reckon you already know what you are doing but some example recommendations from my end as follows for sample: http://dl.dropbox.com/u/74874/test_scripts/popover/index.html# - sharing this link to give you idea for different link with different pop-over if you will see the source notice attribute data-content but what you wanted is working by the following changes.
Have a nice one and hope this helps. D'uh don't forget to up vote and accept the answer :)
Jquery Code
var i = 0;
$('a#test').click(function() {
i += 1;
$('a#test').popover({
trigger: 'manual',
placement: 'right',
content: function() {
var message = "Count is" + i;
return message;
}
});
$('a#test').popover("show");
});
HTML
<a id="test">Click me</a>
just in-case anyone's looking for a solution that doesn't involve re-instantiating the popover and just want to change the content html, have a look at this:
$('a#test').data('popover').$tip.find(".popover-content").html("<div>some new content yo</div>")
Update: At some point between this answer being written and Bootstrap 3.2.0 (I suspect at 3.0?) this changed a little, to:
$('a#test').data('bs.popover').tip().find ............
Old question, but since I notice that the no answer provides the correct way and this is a common question, I'd like to update it.
Use the $("a#test").popover("destroy");-method. Fiddle here.
This will destroy the old popover and enable you to connect a new one again the regular way.
Here's an example where you can click a button to set a new popover on an object that already has a popover attached. See fiddle for more detail.
$("button.setNewPopoverContent").on("click", function(e) {
e.preventDefault();
$(".popoverObject").popover("destroy").popover({
title: "New title"
content: "New content"
);
});
The question is more than one year old, but maybe this would be usefull for others.
If the content is only changed while the popover is hidden, the easiest way I've found is using a function and a bit of JS code.
Specifically, my HTML looks like:
<input id="test" data-toggle="popover"
data-placement="bottom" data-trigger="focus" />
<div id="popover-content" style="display: none">
<!-- Hidden div with the popover content -->
<p>This is the popover content</p>
</div>
Please note no data-content is specified. In JS, when the popover is created, a function is used for the content:
$('test').popover({
html: true,
content: function() { return $('#popover-content').html(); }
});
And now you can change anywhere the popover-content div and the popover will be updated the next time is shown:
$('#popover-content').html("<p>New content</p>");
I guess this idea will also work using plain text instead of HTML.
On Boostrap 4 it is just one line:
$("#your-element").attr("data-content", "your new popover content")
You can always directly modify the DOM:
$('a#test').next(".popover").find(".popover-content").html("Content");
For example, if you want a popover that will load some data from an API and display that in the popover's content on hover:
$("#myPopover").popover({
trigger: 'hover'
}).on('shown.bs.popover', function () {
var popover = $(this);
var contentEl = popover.next(".popover").find(".popover-content");
// Show spinner while waiting for data to be fetched
contentEl.html("<i class='fa fa-spinner fa-pulse fa-2x fa-fw'></i>");
var myParameter = popover.data('api-parameter');
$.getJSON("http://ipinfo.io/" + myParameter)
.done(function (data) {
var result = '';
if (data.org) {
result += data.org + '<br>';
}
if (data.city) {
result += data.city + ', ';
}
if (data.region) {
result += data.region + ' ';
}
if (data.country) {
result += data.country;
}
if (result == '') {
result = "No info found.";
}
contentEl.html(result);
}).fail(function (data) {
result = "No info found.";
contentEl.html(result);
});
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.min.css" rel="stylesheet"/>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
Hover here for details on IP 151.101.1.69
This assumes that you trust the data supplied by the API. If not, you will need to escape the data returned to mitigate XSS attacks.
Learn't from previous answers
let popOverOptions = {
trigger: 'click',
...
};
// save popOver instance
let popOver = $(`#popover-unique-id`).popover(popOverOptions);
// get its data
let popOverData = popOver.data('bs.popover');
// load data dynamically (may be with AJAX call)
$(`#popover-unique-id`).on('shown.bs.popover', () => {
setTimeout(() => {
// set content, title etc...
popOverData.config.content = 'content/////////';
// updata the popup in realtime or else this will be shown next time opens
popOverData.setContent();
// Can add this if necessary for position correction:
popOver._popper.update();
}, 2000);
});
This way we can update popover content easily.
There's another way using destroy method.
http://jsfiddle.net/bj5ryvop/5/
Bootstrap 5.0 update
let popoverInstance = new bootstrap.Popover($('#element'));
And then:
popoverInstance._config.content = "Hello world";
popoverInstance.setContent();
(Caution: it will update popover content globally, so if you have multiple open popovers then they all will be updated with "Hello world")
I found Bootstrap popover content cannot changed dynamically which introduces the setContent function. My code (hopefully helpful to someone) is therefore:
(Noting that jquery data() isn't so good at setting as it is getting)
// Update basket
current = $('#basketPopover').data('content');
newbasket = current.replace(/\d+/i,parseInt(data));
$('#basketPopover').attr('data-content',newbasket);
$('#basketPopover').setContent();
$('#basketPopover').$tip.addClass(popover.options.placement);
if jQuery > 4.1 use
$("#popoverId").popover("dispose").popover({
title: "Your new title"
content: "Your new content"
);
Bootstrap 5.1
I tried about 8 different ways to change the content for my Bootstrap 5.1 project, but none worked. I could see the values in the underlying popover object and could change them, but they didn't show on the page.
I got it going by first using the Bootstrap Popover's selector option, which the docs don't explain that well, but basically amounts to putting a watch on the page, so if new popover elements are added to the page (with the selector) they will become popovers automatically.
$(function() {
// set up all popovers
new bootstrap.Popover(document.body, {selector: 'has-popover');
})
then in my ajax call where some different content has been fetched, I remove the existing popover div, change the attribute with the text, and add it again:
var $pop = $('#pop_id1234')
var html = $pop[0].outerHTML // save the HTML
$pop.remove()
var $new = $(html).attr('data-bs-content',popoverText) // data('bs-content') becomes bsContent which won't work
$('#pop-container').append($new)
I am looking for a very minimal jQuery rich textbox plugin for a web app I am working on.
The user will only need to see the 'textbox', and not any toolbars as all of the rich formatting will be coded depending on what they type.
I have attempted to create my own with an iframe, but there are problems. One of them being when wrapping strings in divs, the caret is moved to the beginning and it can't be moved inside the div without clicking. http://jsfiddle.net/DVjYa/
This is a problem because I need it to behave like a normal textbox. In a normal textbox, you would be able to navigate with the arrow keys without having to click. Hence why I am looking for a plugin which has already overcome these problems.
You can use CLEDITOR which is very lightweight. You can disable all the toolbar buttons and hide the toolbar as well. In addition to this, it lets you make the selection bold/italic using keyboard shortcuts (CTRL+B/CTRL+I) even though the toolbar does not exist.
Demo: http://jsfiddle.net/Rft3A/
var editorDoc;
$(function() {
var editor = document.getElementById ("editable");
if (editor.contentDocument) {
editorDoc = editor.contentDocument;
} else {
editorDoc = editor.contentWindow.document;
}
var editorBody = editorDoc.body;
if ('contentEditable' in editorBody) {
// allow contentEditable
editorBody.contentEditable = true;
}
else { // Firefox earlier than version 3
if ('designMode' in editorDoc) {
// turn on designMode
editorDoc.designMode = "on";
}
}
});
will add another answer although post is a little old
Trumbowyg A lightweight and amazing WYSIWYG JavaScript editor - 15kB only (from github page)