ckeditorfckeditor

Capitalize first letter of sentence in CKeditor


I wish to capitalize the first letter of a sentence, on-the-fly, as the user types the text in a CKEditor content instance.

The strategy consists in catching each keystroke and try to replace it when necessary, that is for instance, when the inserted character follows a dot and a space. I'm fine with catching the event, but can't find a way to parse characters surrounding the caret position:

var instance = CKEDITOR.instances.htmlarea
instance.document.getBody().on('keyup', function(event) {
    console.log(event);
    // Would like to parse here from the event object...
    event.data.preventDefault();
});

Any help would be much appreciated including a strategy alternative.


Solution

  • You should use keydown event (close to what you proposed):

    var editor = CKEDITOR.instances.editor1;
    
    editor.document.getBody().on('keydown', function(event) {
        if (event.data.getKeystroke() === 65 /*a*/ && isFirstLetter()) {
            // insert 'A' instead of 'a'
            editor.insertText('A');
            event.data.preventDefault();
        }
    });
    

    Now - how should isFirstLetter() look like?

    1. You have to start from editor.getSelection().getRanges() to get caret position.
    2. You're interested only in the first range from the collection.
    3. To extract text content from before the caret use small trick:
      • move start of the range to the beginning of document: range.setStartAt( editor.document.getBody(), CKEDITOR.POSITION_AFTER_START ),
      • use CKEDITOR.dom.walker to traverse through DOM tree in source order,
      • collect text nodes and find out what's before caret (is it /\. $/) - remember that you have to skip inline tags and stop on block tags - hint: return false from guard function to stop traversing.

    Example of how you can use walker on range:

    var range, walker, node;
    
    range = editor.getSelection().getRanges()[0];
    range.setStartAt(editor.document.getBody(), CKEDITOR.POSITION_AFTER_START);
    walker = new CKEDITOR.dom.walker(range);
    walker.guard = function(node) {
        console.log(node);
    };
    
    while (node = walker.previous()) {}
    

    And now few sad things.

    So this approach isn't realistic. If you still want to implement this feature I think that you should set timer and by traversing DOM (you can use walker on range containing entire document, or recently typed text (hard to find out where it is)) find all sentences starting from lower letter and fix them.