23 Dec

rich text editors for christmas

I’ve been working on getting FCKEditor working in Konqueror. This is a very difficult task. I got around the problem of script elements not supporting onload through the use of a brute-force hack, but that is nothing compared to the obstacles that face me.
In order to get the thing working, I need to emulate contenteditable and execCommand.
contenteditable is not a w3c-sanctioned CSS attribute, but is incredibly useful, so both Mozilla and IE have written code for it. Konqueror does not yet support this.
execCommand is a javascript function which allows you to run a pre-defined command on a selection. execCommand exists for both IE and Firefox.
I will need to emulate contenteditable, which allows you to make a page editable (duh). As there is no existing ability for this, I will need to write this from the ground up – trapping key-presses and interpretting them – a bloody nuisance, but there you go…
As for execCommand – that should be a bit easier to emulate – there is a list of preset commands which I will need to emulate. I believe this will not be too difficult – it will be drudge-work, but not difficult.
If anyone knows of any existing KHTML code for any of this, please do tell me.

6 thoughts on “rich text editors for christmas

  1. Maybe this can help:

    if( !document.execCommand ) {
    document.execCommand = function(command,ui,value) {
    var UnlinkCommand = function(doc,ui,value) {
    var i = 0;
    do {
    var l = doc.getElementsByTagName(‘A’)[0];
    if( l ) {
    for( var j = 0; l.childNodes[j]; j++ )
    l.parentNode.insertBefore(l.childNodes[j].cloneNode(true),l);
    l.parentNode.removeChild(l);
    }
    } while( l )
    }

    var DeleteCommand = function(doc,ui,value) {
    var body = doc.getElementsByTagName(‘BODY’)[0];
    while( body.hasChildNodes() )
    body.removeChild(body.firstChild);
    }

    var PrintCommand = function(doc,ui,value) {
    window.print();
    }

    var wrapBodyCommand = function(tagName,doc,ui,value) {
    var body = doc.getElementsByTagName(‘BODY’)[0];
    var t = doc.createElement(tagName);
    for( var i = 0; body.childNodes[i]; i++ )
    t.appendChild(body.childNodes[i].cloneNode(true));
    while( body.hasChildNodes() )
    body.removeChild(body.firstChild);
    body.appendChild(t);
    }

    var ItalicCommand = function(doc,ui,value) { wrapBodyCommand(‘i’,doc,ui,value); }
    var BoldCommand = function(doc,ui,value) { wrapBodyCommand(‘b’,doc,ui,value); }
    var UnderlineCommand = function(doc,ui,value) { wrapBodyCommand(‘u’,doc,ui,value); }

    switch(command) {
    case ‘Unlink’: UnlinkCommand(this,ui,value); break;
    case ‘Italic’: ItalicCommand(this,ui,value); break;
    case ‘Bold’: BoldCommand(this,ui,value); break;
    case ‘Underline’: UnderlineCommand(this,ui,value); break;
    case ‘Delete’: DeleteCommand(this,ui,value); break;
    case ‘Print’: PrintCommand(this,ui,value); break;
    }
    };
    document.queryCommandSupported = function(command) {
    switch(command) {
    case ‘Unlink’:
    case ‘Italic’:
    case ‘Bold’:
    case ‘Delete’:
    case ‘Print’:
    return true;
    }
    return false;
    }
    }

    if( document.getSelection ) {
    document._origGetSelection = document.getSelection;

    document.getSelection = function() {
    var selection = this._origGetSelection();

    if( selection.execCommand ) {
    selection.execCommand = function(command,ui,value) {
    // FIXME need to simulate execCommand here
    }
    selection.queryCommandSupported = function(command) {
    return false;
    }
    }
    return selection;
    };
    }

    It’s far from complete – let alone perfect, but maybe a good start.

  2. Thanks, Christof. I don’t know why WordPress does that – too busy with other stuff to fix that. I guess it will be taken care of at some point, as they have quite a few hackers working on that project.
    I’ll use that function as a starting point, after I’ve studied it.
    On a progress point, I’ve hit yet another snag, where I’ve found that khtml’s window.getSelection() function returns plain text, instead of a Range object, which adds yet another layer of complexity to this project (without the Range, it’s very difficult to figure out where to place a caret, or exactly what section of HTML to affect).

  3. I thought about your problem for a while, but didn’t find a really good solution. The best thing konqueror gives you is document.createRange() and mouse and keyboard events. Seems like it’s left to you to create and fill a range while the mouse is moving with the left mouse button down. At least you don’t have to create your own range object.

    The only way I see to find out where the mouse button was pressed is to put every single character inside a span tag which can be removed when the Data is saved.

  4. I have figured out how to find the selection. The click locations can be tracked as you say to an x/y coordinate. It is not necessary to encase everything in spans, though. Once the x/y coordinates are trapped, the corresponding text can be figured out by inserting a span element containing a space (or something thinner) at various points using a binary tree method to figure out which position would be optimal for the mouse actions.
    The deeper I dig into this problem, the deeper the problem seems to be. It’s frustrating, but I’m sure the satisfaction, once I’ve finished it, will be immense!

  5. Hi Kae,

    I’m in the throes of building an open source GUI toolkit and application framework with JavaScript (called Plex) and am on a mission to get every component working on all browsers including Konqueror.

    I’ve done a bit of hacking on Mozile which implements contentEditable for Mozzy without the use of Midas. I’d like to help you with this; if you’re okay with that, could you please send me any code that you’ve got so far and I’ll see if I can get some patches to you?

    Cheers,
    -R

Comments are closed.