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.
Related
By RTE tags I mean <b>, <i>, <u>, or <s> tags. I've been working on a RTE, and using jquery I can get the entire area to bold, but I want to be able to bold only a specific portion (think google docs, word, or any other text editor).
The mozilla site only had deprecated information, and inspecting elements on other sites (including this one) were no help to me.
I am trying to edit a content-editable <div> currently, although I'm open to switching to a text area if that works better.
//my jquery for bolding the entire thing
var bolded = false;
$("#bold").on('click', function(){
//access css of editor div, change status using a ternary
$("#editor").css("font-weight", bolded ? this.value : 'bold');
bolded = !bolded;
//log for debugging
console.log('clicked bold: ' + bolded);
});
my HTML5 for the editor. Sectioned off for formatting purposes.
<section>
<div id="editor" contenteditable="TRUE"></div>
</section>
My buttons are id'd as "bold", "itallic", "strike", and "underl", but I really just want to get one of them working so I can work from there.
EDIT
I realized that this question isn't as straightforward as I'd hoped. I have a <div>, and I would like to have multiple different formats inside of this <div>. The way I would do it logially is by inserting a <b> tag on the click of a button / keyboard command and then continuing to type from there, but I can't find any resources for it. Hope this clears that up.
EDIT 2
So as far as I can tell, the document.execCommand() still works but is predicted to be replaced by Input Events Level 2. I can't find any readable documentation for implementing this. Does anybody know how to do this?
Answer for current methodology (document.execCommand('command')):
Attaching a simple onclick() to the buttons works, where that onclick is a function that runs the aforementioned command with no particular focus:
function format(command, value){
//In use, "value" is left blank in order to do the current selection / no selection
document.execCommand(command, false, value);
}
<button onclick="format('bold')"><strong>B</strong></button>
<button onclick="format('italic')"><em>I</em></button>
Please note that this functionality WILL be deprecated, but no replacement has come up yet. When I know more, I will come back and edit this answer.
Basically, I want to place an anchor element as the top level element, but TinyMCE enforces it's own ideas every time the source code panel is closed.
Disclaimer: TinyMCE is an amazing free wysiwyg editor tool. I am just having a really bad day with it.
It pains me that TinyMCE, supposedly "the most advanced wysiwyg html editor", often can't even let you enter the html you want without screwing it all up for you the second you save or close the source code view.
All I want to do, is to be able to close the editor, leaving my code (or even just anchor elements) intact. But no matter what options or configuration I pass to TinyMCE, it repeatedly tears my code asunder; carelessly scattering elements about the document like shards of broken dreams.
<!-- Casually transforming this -->
<a class="box" href="#">
<div class="title">Box Title</div>
<p>This is the box content!</p>
</a>
<!-- into this -->
<p><a class="box" href="#"></a></p>
<div class="title"><a class="box" href="#">Box Title</a></div>
<p><a class="box" href="#">This is the box content!</a></p>
<p></p>
For some reason best known to themselves they have removed the life-saving cleanup option allowing you to disable this abomination entirely, and I cannot get any of the valid_elements/children options to work correctly. (a[*], etc)
I have spent the last 3 hours scouring their documentation, forums and stack overflow, but to no avail whatsoever. My only solace was finally being able to put block-level elements within anchor tags, but this only solves part of my problem.
The main issue I am facing is that TinyMCE will not allow me to place an anchor tag, containing block-level elements, at the top level. Anybody know how to configure TinyMCE to allow this?
TinyMCE Code (stripped down to relevant):
tinymce.init({
selector: 'textarea.wysiwyg',
schema: "html5",
element_format : 'html',
plugins: ['codemirror'],
convert_urls: false,
valid_elements: "*[*]",
valid_children: "+body[a],+a[div|h1|h2|h3|h4|h5|h6|p|#text]",
//apply_source_formatting: false, // removed I think
//verify_html: false, // removed :'(
codemirror: { indentOnInit: true },
content_css: '/Content/Styles/Sass/main.css'
});
The solution is to disable forced_root_block.
tinymce.init({
...
forced_root_block : false, // default = 'p' >= 3.0a1
});
This feature automatically ensures that any non block elements or text nodes are wrapped in block elements.
For example <strong>something</strong> will result in output like <p><strong>something</strong></p>.
Documentation Reference
I have a webpage div with contentEditable=true. After the user modifies the content of the div, the system uses document.getElementById(id).innerHTML to get the content and send it to server. This is all working:
<div id='editBox'; contentEditable='true'></div>
<script>
document.getElementById('editBox').onkeydown=function(e){
clearTimeout ( backupTimeoutID );
backupTimeoutID = setTimeout(function(){sendDivContentToServer()},3000);
}
<script>
the reason I'm using the a div with contentEditable=true instead of text area is that I need to allow displaying and formatting of background-colored text in the div. If I'm unaware of any better way, please, let me know.
My problem is with the inconsistence with which line breaks are displayed. If the user presses return inside the div, it creates another div for the line break. So, when the function gets the innerHtml, it looks like this
first line of text<div>second line of text</div>
Sometimes, when pasting text from other sources in the edit box (from internet, word, etc), line breaks appear as <p>.
I want all line breaks to look like this:
first line of text<br>second line of text
I have tried changing the behavior of the <div> whenever the user presses return; I know the code is working, for if I try to insert a word instead of return it works. But, when I set the code to substitute return for <br> it acts erradically. This is the code I'm using for this:
<script>
document.getElementById('editBox').onkeydown=function(e){
clearTimeout ( backupTimeoutID );
backupTimeoutID = setTimeout(function(){sendDivContentToServer()},3000);
}
} else if ( pressedKeyCode==13 ) {
e.preventDefault();
document.execCommand("InsertHTML",false,"a"); // this works
document.execCommand("InsertHTML",false,"<br>"); // I don't see the effects I want with this
}
<script>
Converting the multiple line breaks – <div>, <p> and <br> – seem to be a hard task. Using <br> for line breaks seems less error prone.
I'm developing for a web viewer in FileMaker for use in Mac OSX. So, so far, I care more about Safari than any other browser.
Thanks, in advance, for the help.
Reining in contentEditable is not an easy task. Unfortunately it's not really standardized and every browser has its quirks.
I would suggest you have a look at the many well written HTML rich text editors that are around.
For example, CKEditor only creates sensible, valid HTML, it allows you to configure what happens, when the user presses return, it can remove or replace any unwanted HTML and you can disable any features that the user shouldn't use.
I have this function:
function main() {
var bottomArea = document.getElementsByClassName("bottom-area");
for (var i = 0; i < bottomArea.length; i++) {
var showDialogLink = document.createElement("a");
showDialogLink.innerHTML = "link";
showDialogLink.onclick = function(){showSelect(this);return false;};
bottomArea[i].insertBefore(showDialogLink, bottomArea[i].childNodes[3]);
}
}
So far the code works just fine. When I click the newly created link, it calls showSelect(this) function just fine.
The problem is there is another userscript/browser extension (which I don't have access to - it's not mine), which basically clones whole another div in which 'bottom-area' div is nested. This is all right too, but the problem is that it doesn't clone my function trigger and those newly cloned instances (I'm not really sure what is their nature) of that link do no longer trigger showSelect(this) function. Only the first one created by my userscript does.
Is there some way in which I should add my function trigger on my link, that will stay even after cloning/copying?
EDIT: I'll just edit to show html tree:
This is at the beginning:
<div>
<div class="bottom-area"></div>
</div>
My userscript adds a link with an onclick eventlistener on the 'a' tag:
<div>
<div class="bottom-area"><a>link</a></div>
</div>
The other userscript basically clones it (there is a textarea inside the div and its value gets cloned too), but without the eventlistener, so clicking on the cloned links no longer triggers my function.
EDIT2: If it helps, the userscript I'm creating is a reddit userscript. I'm adding small functionality to commenting and adding a link right next to the 'reddiquette' link under the comment text field. That works with the pre-generated text field. However when I click 'reply' down the comment tree, the whole div together with text field, submit button and my link gets cloned under the comment I'm replying too, but my link no longer has the function trigger on itself.
The easiest solution may be to simply use HTML event attributes (instead of addEventListener), such as <a onclick="dostuff();">link</a>, because the attribute should be preserved during 'cloning'. See this fiddle (tested in Firefox 40) for an example.
Letting the code in the onclick attribute interact with your userscript may be be a little difficult because it runs in a different JavaScript environment. Luckily there's plenty of possible workarounds, depending on your exact needs.
I have my own custom non-jQuery ajax which I use for programming web applications. I recently ran into problems with IE9 using TinyMCE, so am trying to switch to CKeditor
The editable text is being wrapped in a div, like so:
<div id='content'>
<div id='editable' contenteditable='true'>
page of inline text filled with ajax when links throughout the site are clicked
</div>
</div>
When I try to getData on the editable content using the examples in the documentation, I get an error.
I do this:
CKEDITOR.instances.editable.getData();
And get this:
Uncaught TypeError: Cannot call method 'getData' of undefined
So I figure that it doesn't know where the editor is in the dom... I've tried working through all editors to get the editor name, but that doesn't work-- no name appears to be found.
I've tried this:
for(var i in CKEDITOR.instances) {
alert(CKEDITOR.instances[i].name);
}
The alert is just blank-- so there's no name associated with it apparently.
I should also mention, that despite my best efforts, I cannot seem to get the editable text to have a menu appear above it like it does in the Massive Inline Editing Example
Thanks for any assistance you can bring.
Jason Silver
UPDATE:
I'm showing off my lack of knowledge here, but I had never come across "contenteditable='true'" before, so thought that because I was able to type inline, therefore the editor was instantiated somehow... but now I'm wondering if the editor is even being applied to my div.
UPDATE 2:
When the page is loaded and the script is initially called, the div does not exist. The editable div is sent into the DOM using AJAX. #Zee left a comment below that made me wonder if there is some other command that should be called in order to apply the editor to that div, so I created a button in the page with the following onclick as a way to test this approach: (adapted from the ajax example)
var editor,html='';config = {};editor=CKEDITOR.appendTo('editable',config, html );
That gives the following error in Chrome:
> Uncaught TypeError: Cannot call method 'equals' of undefined
> + CKEDITOR.tools.extend.getEditor ckeditor.js:101
> b ckeditor.js:252
> CKEDITOR.appendTo ckeditor.js:257
> onclick www.pediatricjunction.com:410
Am I headed in the right direction? Is there another way to programmatically tell CKEditor to apply the editor to a div?
UPDATE 3:
Thanks to #Reinmar I had something new to try. The most obvious way for me to test to see if this was the solution was to put a button above the content editable div that called CKEDITOR.inlineAll() and inline('editable') respectively:
<input type='button' onclick=\"CKEDITOR.inlineAll();\" value='InlineAll'/>
<input type='button' onclick=\"CKEDITOR.inline('editable');\" value='Inline'/>
<input type='button' onclick=\"var editor = CKEDITOR.inline( document.getElementById( 'editable' ) );\" value='getElementById'/>
This returned the same type of error in Chrome for all three buttons, namely:
Uncaught TypeError: Cannot call method 'equals' of undefined ckeditor.js:101
+ CKEDITOR.tools.extend.getEditor ckeditor.js:101
CKEDITOR.inline ckeditor.js:249
CKEDITOR.inlineAll ckeditor.js:250
onclick
UPDATE 4:
Upon further fiddling, I've tracked down the problem being related to json2007.js, which is a script I use which works with Real Simple History (RSH.js). These scripts have the purpose of tracking ajax history, so as I move forward and back through the browser, the AJAX page views is not lost.
Here's the fiddle page: http://jsfiddle.net/jasonsilver/3CqPv/2/
When you want to initialize inline editor there are two ways:
If element which is editable (has contenteditable attribute) exists when page is loaded CKEditor will automatically initialize an instance for it. Its name will be taken from that element's id or it will be editor<number>. You can find editors initialized automatically on this sample.
If this element is created dynamically, then you need to initialize editor on your own.
E.g. after appending <div id="editor" contenteditable="true">X</div> to the document you should call:
CKEDITOR.inline( 'editor' )
or
CKEDITOR.inlineAll()
See docs and docs.
You can find editor initialized this way on this sample.
The appendTo method has different use. You can initialize themed (not inline) editor inside specified element. This method also accepts data of editor (as 3rd arg), when all other methods (CKEDITOR.inline, CKEDITOR.replace, CKEDITOR.inlineAll) take data from the element they are replacing/using.
Update
I checked that libraries you use together with CKEditor are poorly written and cause errors you mentioned. Remove json2007.js and rsh.js and CKEditor works fine.
OK, so I have tracked down the problem.
The library I was using for tracking Ajax history and remembering commands for the back button, called Real Simple History, was using a script called json2007 which was intrusive and extended native prototypes to the point where things broke.
RSH.js is kind of old, and I wasn't using it to it's full potential anyway, so my final solution was to rewrite the essential code I needed for that, namely, a listener that watched for anchor (hash) changes in the URL, then parsed those changes and resubmitted the ajax command.
var current_hash = window.location.hash;
function check_hash() {
if ( window.location.hash != current_hash ) {
current_hash = window.location.hash;
refreshAjax();
}
}
hashCheck = setInterval( "check_hash()", 50 );
'refreshAjax()' was an existing function anyway, so this is actually a more elegant solution than I was using with Real Simple History.
After stripping out the json2007.js script, everything else just worked, and CKEditor is beautiful.
Thanks so much for your help, #Reinmar... I appreciate your patience and effort.