Reputation: 627
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.
Upvotes: 0
Views: 1788
Reputation: 627
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
}
Upvotes: 0
Reputation: 627
Now I have to solve this issue:
- if someone wants to select (* = selection borders) this: <b>some te*xt </b> aaa <i>bb*b</i>
...the JS puts this bookmarks:
<b>some te
<span id="cke_bm_69S" style="display: none;"> </span>
xt</b>
aaa
<i>bb
<span id="cke_bm_69S" style="display: none;"> </span>
b</i>
... and I have to do something like this:
<b>some te
</b><span property="..."><b>xt</b>
aaa
<i>bb</i></span><i>b</i>
My solution (untested yet): select a Node with id="cke_bm_69S", ensure the tag-crossing problems and if there are some then use before() and after() methods to insert html. But I am afraid, there will be temporary unclosed tags, I dont know, if its ok.
Upvotes: 0
Reputation: 627
The calculation goes strongly against a principles of CKEDITOR. But I think, I have discovered the best solution - it is almost the same as Nenotlep suggested:
var bookmarks = e.getSelection().createBookmarks(true);
var startId = bookmarks[0].startNode;
var endId = bookmarks[0].endNode;
This inserts to the code invisible bookmarks (span with id startId and endId) and then I can process it on the server side. Now I have to solve the multi-selection issue and tag-crossing issue.
Upvotes: 1
Reputation: 13412
If I understood you correctly, you want to identify a specific node or multiple nodes serverside based on where the cursor was or where selection was during a submit/ajax call. And you already know the incantation to get the nodes for the current selection.
I would suggest that before submitting to server, you tag the element(s) manually. Add a custom attribute or class to the intended victims before submitting and then use that tag to identify the node serverside.
For example [
is the start of the selection and ]
is the end and your data is:
<p>foo</p><p>[bar</p><p>baz]</p>
Before submit, get and tag the element with JS so that it looks like
<p>foo</p><p class="chosen">bar</p><p class="chosen">baz</p>
And then you can use whatever XML/HTML weapon you like to find the element tagged chosen
(if you are using C# I recommend CSQuery). Then just remove the tag and command the node(s) to do your bidding. For multiple nodes, just tag multiple nodes.
Upvotes: 2