Reputation: 21
I'm writing a small notetaking webapp which uses CKEditor as a HTML editor. I've been able to integrate it well, with one exception - drag and drop functionality. It mostly works - but there are two quirks I haven't been able to solve despite trying a whole lot of different approaches.
My requirements are simple - I drag any file into CKEditor from outside the app, upon which it should insert a simple anchor tag in the editable text with a local link to the file. Currently, I am implementing drag and drop in my own CKEditor plugin called filemanager:
CKEDITOR.plugins.add( 'filemanager', {
init: function( editor ) {
editor.on('contentDom', function(contentDom) {
//Runs upon dropping a file into the editor
contentDom.editor.document.on('drop', function(e) {
//prevents default behavior as long as it's an actual file drag and drop (if it's dragging text etc. inside the editor, keep default behavior)
if(e.data.$.dataTransfer.files.length)
{
e.data.preventDefault();
}
//Goes through all the dropped files and insert HTML with links
for (var i = 0; i < e.data.$.dataTransfer.files.length; ++i) {
CopyData(e.data.$.dataTransfer.files[i].path);
}
});
});
//Creates the link in the editor
function CopyData(path, range)
{
//Link HTML
var fileHTML;
/* Code to parse the path into HTML to be inserted */
editor.insertHtml(fileHTML);
}
}
});
I have omitted most of the CopyData function as the formatting isn't relevant, but it generates a div such as this:
fileHTML = "<a href=\""+path+"\" class=\"fileLink\" target=\"_blank\">"+fileName+"</a>";
This does work perfectly fine, so the basic implementation of the feature seems to work. However, here are my two issues:
Does anyone have ideas of how to solve the two above issues? If it helps, I am using nw.js so the webapp will always run in Chromium. Grateful for any responses!
Upvotes: 1
Views: 3093
Reputation: 21
I eventually managed to solve these issues.
To prevent default behavior outside the DOM elements, I added this code to my CKEditor plugin:
var iframeWin = window.document.getElementsByTagName('iframe')[0].contentWindow;
iframeWin.addEventListener("dragover",function(e){
e = e || iframeWin.event;
if(e.dataTransfer.files.length)
{
e.preventDefault();
}
},false);
iframeWin.addEventListener("drop",function(e){
e = e || iframeWin.event;
if(e.dataTransfer.files.length)
{
e.preventDefault();
}
},false);
This correctly identified the iframe I needed to disable default behavior in. The if(e.dataTransfer.files.lenght) condition makes sure this only applies to files dragged into the app, and not dragging things within the editor.
Inserting the HTML at the mouse cursor was trickier! This cannot be credited to me - I found a function doing most of the work on the CKEditor bug tracker, and edited it to work in this scenario:
function moveSelectionToDropPosition( editor, dropEvt )
{
var $evt = dropEvt.data.$,
$range,
range = editor.createRange();
// Make testing possible.
if ( dropEvt.data.testRange ) {
dropEvt.data.testRange.select();
return;
}
// Webkits.
if ( document.caretRangeFromPoint ) {
$range = editor.document.$.caretRangeFromPoint( $evt.clientX, $evt.clientY );
range.setStart( CKEDITOR.dom.node( $range.startContainer ), $range.startOffset );
range.collapse( true );
}
// FF.
else if ( $evt.rangeParent ) {
range.setStart( CKEDITOR.dom.node( $evt.rangeParent ), $evt.rangeOffset );
range.collapse( true );
}
// IEs.
else if ( document.body.createTextRange ) {
$range = editor.document.getBody().$.createTextRange();
$range.moveToPoint( $evt.clientX, $evt.clientY );
var id = 'cke-temp-' + ( new Date() ).getTime();
$range.pasteHTML( '<span id="' + id + '">\u200b</span>' );
var span = editor.document.getById( id );
range.moveToPosition( span, CKEDITOR.POSITION_BEFORE_START );
span.remove();
}
range.select();
}
In the drop event I then simply added
moveSelectionToDropPosition(editor, e);
And it all worked as intended!
Hopefully this may help anyone else having similar issues in the future :)
Upvotes: 1