Any suggestions or workarounds for this bug?
http://dev.ckeditor.com/ticket/6436
i really need to get around this bug as i need to delete whole divs with one backspace in the editor area in CKEditor. but on insertion the divs get nested due to the bug. So, deletion of individual divs become impossible.
Here is the workaround i finally designed--
http://thecamelcase.com/2011/06/reining-in-the-cursor-in-ckeditor/
I encountered this same issue and unfortunately the link to the fix made by ghostCoder isn't available anymore.
In addition to nested divs issue, we have forced paste as plain text (so data is pasted as plain text unless "paste from word" is used) and we are using div as our enterMode. In the pasted data line breaks were replaced with <br /> tags whereas we wanted each line to be wrapper inside element.
This our how I eventually solved the issue. You should know that we have not changed autoParagraph config option so it defaults to true and I don't recommend disabling it in config level. I'll try to code comment this solution well so that you get a good idea what is actually done here.
config.on.instanceReady = function(e) {
var enterMode = e.editor.config.enterMode, elem = 'div';
if(enterMode === CKEDITOR.ENTER_P) {
elem = 'p';
}
// We didn't encounter any issues when using br as enterMode so
// continue only if enterMode is div or p element
if(enterMode === CKEDITOR.ENTER_DIV || enterMode === CKEDITOR.ENTER_P) {
// Disable autoParagraph in initialization to avoid nested div issue.
// When autoParagraph is manipulated this way it will still be active
// later on so if you write some inline content in source mode it
// will be properly wrapped inside <p> or <div> element.
e.editor.config.autoParagraph = false;
// Handle paste event to get rid of <br /> line breaks issue
e.editor.on('paste', function(evt) {
var data = '';
// Stop event, handle everything here manually
evt.stop();
// Get data either from event's text or html property
if(evt.data.text) {
// Remove HTML markup from pasted data
data = evt.data.text.replace(/(<([^>]+)>)/ig, '');
// Replace all line breaks with </div><div> or </p><p> markup
// And wrap the whole content inside <div> or <p> element
data = '<' + elem + '>'
+ data.replace(/\r\n|\r|\n/g, '</' + elem + '><' + elem + '>')
+ '</' + elem + '>';
} else {
// If data was not pasted as plain text just
// get data as is from event's html property
data = evt.data.html;
}
// Insert HTML data to editor
evt.editor.insertHtml(data);
// Fire afterPaste manually as we have stopped the event
// and afterPaste wouldn't get triggered otherwise
evt.editor.fire( 'afterPaste' );
}, e.editor.element.$);
}
};
Related
I am trying to replace html links (and eventually other elements) with bbcode when a user does a paste from a document (like gdocs or libre office). So we are dealing with rich html already formatted (which is why it needs to copy HTML and not text).
Essentially, I want to be able to copy stuff pre-written from a document into a textarea on my website without having to manually write BBCode tags in the original document (as it's messy for proof-reading).
Thanks to the help here Adjust regex to ignore anything else inside link HTML tags I have gotten mostly there, but I am stuck on replacing the found tags with the original text.
Here's what I have:
function fragmentFromString(strHTML) {
return document.createRange().createContextualFragment(strHTML);
}
$('textarea').on('paste',function(e) {
e.preventDefault();
var text = (e.originalEvent || e).clipboardData.getData('text/html') || prompt('Paste something..');
var fragment = fragmentFromString(text);
var aTags = Array.from(fragment.querySelectorAll('a'));
aTags.forEach(a => {
text = text.replace(a, "[url="+a.href+"]"+a.textContent+"[/url]");
});
window.document.execCommand('insertText', false, text);
});
You can see it loops over the found a tags and I am essentially trying to replace them from the original text with the new stuff.
Here's an example of the type of content that could be pasted (this is a single link from google docs):
<span style="font-size:14.666666666666666px;font-family:Arial;color:#1155cc;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre-wrap;">Link test</span>
Expected to be replaced with:
[url=https://www.test.com]Link test[/url]
So I want that HTML replaced, with the BBCode within the original text that's then sent to the textarea from the paste.
The aTags foreach currently does nothing. You need to create a new text node, and replace the existing anchor tag with it.
aTags.forEach(a => {
var new_text = document.createTextNode("[url=" + a.href + "]" + a.textContent + "[/url]");
a.parentNode.insertBefore(new_text, a);
a.parentNode.removeChild(a);
});
window.document.execCommand('insertText', false, text.innerText);
This will replace every a tag into the given text.
Both the $("<test") & document.createElement(",test") throws error due to < character associated to the text. I do not want to replace the character & wanted to see if there is option to create dom or jquery object using such text. I know replace will work but since the code is pre-existing & also since code is written such that it assume it can either have the simple text (textnode) or html tag (like span) hence this error is occuring as it fails to check if it is proper self closing html tag.
I am thinking of creating it to xml node & then check if the childnode is textNode or not before trying to create jquery object,however I am looking for suggestion & best approach to tackle such issue. I know replace of < will work & also there is no need to check for attributes of plain text but since the code is dynamic it sometimes retrieves plain text & some time it gives valid html tag that why this issue appears
I am not sure what your exact end goal is, but basically you need to do something like this:
function makeElemHack( str ) {
var div = $("<div>").html(str); //create a div and add the html
var html = div.html(); //read the html
if (!html.length) { //if the html has no length the str was invalid
div.html(str.replace(/</g,"<")); //escape the < like text should be
//div.text(str); //or you can just add it as plain text
}
return div; //with the div wraper
//return div.contents(); //without the div wrapper
}
var bd = $("body");
bd.append( makeElemHack("<p>Hello</p>") );
bd.append( makeElemHack("1<0") );
bd.append( makeElemHack("<booo") );
I am working on an HTML5/javascript/php project where I have a which is set to be editable.
I know the user can be press ctrl + b to make the text bold, but I also want to allow the user to be able to click on the appropriate button on the web page to perform the same action.
I.e. when the user types it will be in normal text, they click on the bold button on the web page and it appends <strong> to the html of the section, and whatever they type now will be hold until they press the button again and it then appends </strong>.
However, at the moment when I append <strong> it seems to automatically add </strong> and I want to stop that from happening. Am I going about this the right way or is there a better way that this can be achieved.
Below is the javascript that I am trying to use
function enableDisableBold(section)
{
var boldEnabled = $("#boldEnabled").val();
var content = $("#" + section).html();
var newContent;
if (boldEnabled == "true")
{
$("#btnBold").removeClass("formatButtonsActivated");
$("#boldEnabled").val("false");
//newContent = content + "</strong>";
//$("#" + section).html(newContent);
}
else
{
$("#btnBold").addClass("formatButtonsActivated");
$("#boldEnabled").val("true");
newContent = content + "<strong>";
$("#" + section).html(newContent.replace("</strong>", ""));
}
alert($("#" + section).html());
}
Thanks for any help you can provide.
UPDATE
I've just come across another solution which does what I want to achieve, but there is a slight issue. At the end of the function call, I perform the following bit of code.
var article = document.getElementById(section);
article.focus();
document.execCommand("Bold", false, null);
The problem is this is working fine in Internet Explorer, but in Chrome is where I am having the problem. When I set the focus back to the <section> tag, it puts the cursor back to the beginning of the text. I read somewhere that putting onfocus="this.value = this.value;" on the control but this doesn't help.
I was trying to find a way of putting the cursor back to the end of the character, but I would actually need to put the cursor back to where it originally was. Is this something that can be done.
create a strong element and append your content to it
$('<strong />').append(content);
Firefox inserts a <br /> tag on press enter whereas the other browsers are adding either a <p> or <div>. I know that chrome and safari are inserting the same tag of the firstchild of the contentEditable div. So do Firefox, however, if the first tag's innerHTML is empty firefox is just ignoring the tag and creating a new line by pushing the default node in the second line and writes directly inside the editor instead of inside a child node. So basically, I want Firefox to write inside the given tag and continue to insert that kind of node on each press on enter. How can it be done? Any suggestions?
I've found the solution :) In order to make this work, you've to give an id to the caret's parent element. Then you can use the following code. Please note that I get the browsernName from c# and put it into a hidden field. That's why I equaled it to "firefox". The following code is tested with FF 3.6 and it works perfectly. The only thing is that you'll have to check the caret's parent element and if it is not equal to the current row's id, then you'll have to place the caret inside the current row by using a selection function. Also, perform this code on the keyup event and make sure that if you perform some other codes on the keyup event, put this code at the end of it! Anways, enjoy :)
// The code works good in the following cases:
// 1) If there is text inside the editor and the user selects the whole text
// and replace it with a single character, the <p> tag will be placed and the
// text will place inside the p tag
// 2) If the user selects the whole code and deletes it and begins to type again
// 3) If the user types normally and press enter
// NB: Please note me if you find any bug
if (browserName == "firefox") {
//remove all br tags
var brs = txteditor.getElementsByTagName("br");
for (var i = 0; i < brs.length; i++) { brs[i].parentNode.removeChild(brs[i]); }
//check whether there is a p tag inside
var para = txteditor.getElementsByTagName("p");
if (para.length == 0) {
var inner = txteditor.innerHTML.replace(/^\s+|\s+$/g, '');
var str = (inner == "") ? "" : txteditor.innerHTML;
var nText = "<p id=\"" + cRow + "\">" + str + "</p>";
// in order to prevent a dublicate row clear inside the text editor
txteditor.innerHTML = "";
document.execCommand('insertHTML', false, nText);
} else {
// always make sure that the current row's innerHTML is never empty
if (document.getElementById(cRow).innerHTML == "")
document.getElementById(cRow).innerHTML = "";
}
}
Try inserting a <p></p> inside your element. Then almost certainly every newline will be a new paragraph.
If you change the contenteditable element to be a <span> instead of a <div> the new line will be made with a <br>. I haven't tested this with other elements, but it would be interesting to see how different elements would behave.
A <span> element can be styled with display: block to make it look like a <div> element.
I have a form in which I want to edit a HTML template. It has 2 textareas, one for the HTML and another one for the CSS.
I'd like to use either TinyMCE or CKEditor for the HTML textarea.
Is there any way to change the content CSS in either of them to match the CSS in the CSS textarea on the run, so when I change the CSS it is automatically loaded into the editor?
Thanks.
I have no experience with CKEditor, but i know that it is possible with TinyMce. What you need to do is to write an own plugin which will provide the necessary functionality.
OnNodeChange in the 2nd textarea (the one with your css) you need to update the head of the first editors iframe. This code snippet to be executed on a special action (for example onNodeChange) should point you into the right direction:
var DEBUG = false;
var css_code = tinymce.editors[1].getContent(); // get content of 2nd editorinstance on page (your css)
iframe_id = tinymce.editors[0].id+'_ifr';
with(document.getElementById(iframe_id).contentWindow){
var h=document.getElementsByTagName("head");
if (!h.length) {
if (DEBUG) console.log('length of h is null');
return;
}
var newStyleSheet=document.createElement("style");
newStyleSheet.type="text/css";
h[0].appendChild(newStyleSheet);
try{
if (typeof newStyleSheet.styleSheet !== "undefined") {
newStyleSheet.styleSheet.cssText = css_code;
}
else {
newStyleSheet.appendChild(document.createTextNode(css_code));
newStyleSheet.innerHTML=css_code;
}
}
Be aware that this code will add a new style sheet everytime it is called - yielding in increasing the editor iframes head. So i think best practice is to clean up the last inserted style before appliing the new one. Removing the last Node of the head shozld be sufficient.