Search code examples
ckeditorselectionclient-sideserver-sideoffset

CKEDITOR - identify the selected html


I have discovered some usefull attributes of CKEDITOR selection object (get via editor.getSelection()):

e.getSelection().getRanges()[0].startOffset
e.getSelection().getRanges()[0].endOffset

This returns the start and end position of the selected text - but only in relation to startContainer (element) and endContainer (element). I would like to get the absolute position in relation to the whole document or something else to identify the selected html (some intern id of the start or end element?). I would like to read this attributes on the server side and modify the selected html there. Is there some universal way to move the information about position of the selected text from the client side to the server side?

Thanks a lot.


Solution

  • I found a good and elegant solution:

    textselector.js (marks selection - inserts bookmars into CKEDITOR)

     function selectText(irtId, startIdSelectionId, endIdSelectionId) {
        var editor = CKEDITOR.instances[irtId];
        if (editor.getSelection().getRanges()[0].collapsed) {
            document.getElementById(startIdSelectionId).value = "";
            document.getElementById(endIdSelectionId).value = "";
        } else {
            var bookmarks = editor.getSelection().createBookmarks(true);
            var startId = bookmarks[0].startNode;
            var endId = bookmarks[0].endNode;
            document.getElementById(startIdSelectionId).value = startId;
            document.getElementById(endIdSelectionId).value = endId;
        }
        return true;
    }
    

    Action on the serverside (identifying conflicts - tag-crossing, repairs elements if necessary - divides into 2 parts and insert span):

    /**
     * prida span na oznacene html osestreni nezadoucich pripadu: prazdny
     * select, kolize s jinym spanem
     */
    public void spanSelectedHtml() {
        // parsovani celeho dokumentu
        Document doc = Jsoup.parse(value);
        // osetreni chybovych stavu
        // - prazdny select
        if (startIdSelection.isEmpty() || endIdSelection.isEmpty()) {
            return;
        }
        // nalezeni znacek
        Element es = doc.getElementById(startIdSelection);
        Element ee = doc.getElementById(endIdSelection);
        // - konflikt s jinou znackou
        // bude doplneno
    
        // oprava okoli znacek v pripade nutnosti
        repairIfNecessary(es, ee);
        // vytvoreni span tagu s nalezitymi atributy
        Element span = doc.createElement("span");
        span.attr("property", clicked.getUrl());
        span.attr("class", clicked.getStyleClass());
        // nahrazeni prvni znacky span tagem
        es.replaceWith(span);
        // pripojeni vsech nasledujicich uzlu az do koncove znacky
        while (span.nextSibling() != ee) {
            span.appendChild(span.nextSibling());
        }
        // odstraneni koncove znacky
        ee.remove();
        // aktualizace hodnoty textove komponenty
        value = doc.toString();
    }
    
    /**
     * oprava okoli elementu v pripade nutnosti - pri zjisteni unikatniho rodice
     *
     * @param e1 prvni element
     * @param e2 druhy element
     */
    private void repairIfNecessary(Element e1, Element e2) {
        while (hasUniqueParent(e1, e2)) { // unikatni rodice e1?
            repairElement(e1);
        }
        while (hasUniqueParent(e2, e1)) { // unikatni rodice e2?
            repairElement(e2);
        }
    }
    
    /**
     * oprava okoli elementu: rozdeleni na dve casti a vymazani rodice,
     * zachovani atributu
     *
     * @param e element s okolim na opravu
     */
    private void repairElement(Element e) {
        // "problemovy rodic", ktereho je treba rozdelit
        Element p = e.parent();
        // 1. cast - pred znackou
        if (e.previousSibling() != null) { // osetreni null
            Element n = p.clone().empty(); // vkladany element musi byt formalne stejny
            p.prependChild(n); // umisteni elementu na zacatek - jako 1. dite
            while (n.nextSibling() != e) { // dokud se nedostanu ke znacce, pridavam uzly
                n.appendChild(n.nextSibling());
            }
        }
        // 2. cast - za znackou
        if (e.nextSibling() != null) { // osetreni null
            Element n = p.clone().empty(); // vkladany element musi byt formalne stejny
            p.appendChild(n); // umisteni elementu na konec - jako posledni dite
            while (n.previousSibling() != e) { // dokud se nedostanu ke znacce, pridavam uzly
                n.prependChild(n.previousSibling());
            }
        }
        p.unwrap(); // vymazani puvodniho "problemoveho rodice"
    }
    
    /**
     * ma testovaci element rodice, ktereho kontrolni element nema?
     *
     * @param e testovaci element
     * @param c kontrolni element
     * @return testovaci element ma unikatniho rodice (takoveho, ktery kontrolni
     * element nema)
     */
    private boolean hasUniqueParent(Element e, Element c) {
        if (e.parents().isEmpty() || c.parents().isEmpty()) { // test na null
            return false;
        }
        for (Element pe : e.parents()) {
            if (!c.parents().contains(pe)) {
                return true; // unikatni rodic
            }
        }
        return false; // bez unikatniho rodice
    }