Pour ceux qui voudraient savoir dans l'avenir, c'est ainsi que je l'ai fait:
jQuery.fn.highlight = function(startOffset,endOffset,type) {
function innerHighlight(node, startOffset,endOffset) {
var calledStartOffset = parseInt(startOffset);
var startOffsetNode=getChildNodeForOffset(node,parseInt(startOffset));
var endOffsetNode=getChildNodeForOffset(node,parseInt(endOffset));
startOffset = resizeOffsetForNode(startOffsetNode,parseInt(startOffset));
if (startOffsetNode == endOffsetNode){
endOffset = resizeOffsetForNode(endOffsetNode,parseInt(endOffset));
highlightSameNode(startOffsetNode, parseInt(startOffset),parseInt(endOffset),type,calledStartOffset);
} else {
highlightDifferentNode(startOffsetNode,endOffsetNode,parseInt(startOffset),parseInt(endOffset),type,calledStartOffset);
}
}
return this.each(function() {
innerHighlight(this, startOffset,endOffset);
});
};
function resizeOffsetForNode(offsetNode,offset){
if (offsetNode.id >= 0){
offset = parseInt(offset)-parseInt(offsetNode.id);
} else if (offsetNode.previousSibling != null && offsetNode.previousSibling.id > 0){
offset = parseInt(offset)-parseInt(offsetNode.previousSibling.id)-parseInt(offsetNode.previousSibling.textContent.length);
}
return offset;
}
function getChildNodeForOffset(testNode,offset) {
if (testNode.nodeType == 1 && testNode.childNodes && !/(script|style)/i.test(testNode.tagName)) {
var offsetNode=null;
var currentNode;
for (var i = 0; i < testNode.childNodes.length; ++i) {
currentNode=testNode.childNodes[i];
if (currentNode.id >= 0 && parseInt(currentNode.id) <= parseInt(offset) && ((parseInt(currentNode.id) + parseInt(currentNode.textContent.length)) >= parseInt(offset))){
offsetNode = currentNode;
break;
} else if (currentNode.id >= 0 && parseInt(currentNode.id) > parseInt(offset)){
offsetNode = currentNode.previousSibling;
break;
}
}
if (offsetNode==null){
offsetNode = testNode.childNodes[testNode.childNodes.length-1];
}
return offsetNode;
}
}
function highlightSameNode(node, startOffset,endOffset,type,calledStartOffset) {
var skip = 0;
if (node.nodeType == 3) {
if (startOffset >= 0) {
var spannode = document.createElement('span');
spannode.className = 'entity '+ type;
spannode.id=calledStartOffset;
var middlebit = node.splitText(startOffset);
var endbit = middlebit.splitText(endOffset-startOffset);
var middleclone = middlebit.cloneNode(true);
spannode.appendChild(middleclone);
middlebit.parentNode.replaceChild(spannode, middlebit);
}
} else if (node.nodeType == 1 && node.childNodes && !/(script|style)/i.test(node.tagName)) {
var childnode = node.childNodes[0];
highlightSameNode(childnode, startOffset,endOffset,type,calledStartOffset);
}
}
function highlightDifferentNode(startnode, endnode, startOffset,endOffset,type,calledStartOffset) {
var skip = 0;
if (startnode.nodeName == "#text") {
if (startOffset >= 0) {
var spannode = document.createElement('span');
spannode.className = 'entity '+ type;
spannode.id=calledStartOffset;
var endbit = node.splitText(startOffset);
var endclone = endbit.cloneNode(true);
spannode.appendChild(endclone);
endbit.parentNode.replaceChild(spannode, endbit);
}
} else if (startnode.nodeName == "SPAN") {
if (startOffset >= 0) {
var spannode = document.createElement('span');
spannode.className = 'entity '+ type;
spannode.id=calledStartOffset;
var endTextbit = startnode.childNodes[0].splitText(startOffset);
spannode.appendChild(endTextbit);
startnode.parentNode.insertBefore(spannode, startnode.nextSibling);
}
}
var currentTestNode=startnode.nextSibling;
while (currentTestNode!=endnode){
if (currentTestNode.nodeName == "#text") {
var spannode = document.createElement('span');
spannode.className = 'entity '+ type;
spannode.id=parseInt(currentTestNode.previousSibling.id)+parseInt(currentTestNode.previousSibling.textContent.length);
var currentNodeClone=currentTestNode.cloneNode(true);
spannode.appendChild(currentNodeClone);
endbit.parentNode.replaceChild(spannode, currentTestNode);
} else if (currentTestNode.nodeName == "SPAN") {
currentTestNode.className = 'entity overlap';
}
currentTestNode=currentTestNode.nextSibling;
}
var previousNodeEnd = parseInt(endnode.previousSibling.id)+parseInt(endnode.previousSibling.textContent.length);
var spannode = document.createElement('span');
spannode.className = 'entity '+ type;
spannode.id=previousNodeEnd;
if (endnode.nodeName == "#text") {
if (endOffset >= 0) {
//end offset here is the original end offset from the beginning of the text, not node
var unwantedbit = endnode.splitText(parseInt(endOffset)-parseInt(previousNodeEnd));
var endclone = endnode.cloneNode(true);
spannode.appendChild(endclone);
endnode.parentNode.replaceChild(spannode, endnode);
}
} else if (endnode.nodeName == "SPAN") {
if (endOffset >= 0) {
var wantTextbit = endnode.childNodes[0].splitText(parseInt(endOffset)-parseInt(previousNodeEnd));
spannode.appendChild(wantTextbit);
wantTextbit.parentNode.parentNode.insertBefore(spannode, endnode);
}
}
if (startnode.textContent.length < 1){
startnode.parentNode.removeChild(startnode);
}
if (endnode.textContent.length < 1){
endnode.parentNode.removeChild(endnode);
}
}
jQuery.fn.removeHighlight = function() {
return this.find("span.entity").each(function() {
this.parentNode.firstChild.nodeName;
with (this.parentNode) {
replaceChild(this.firstChild, this);
normalize();
}
}).end();
};
function contains(a, b){
return a.contains ? a != b && a.contains(b) : !!(a.compareDocumentPosition(b) & 16);
}
C'est grand, pourriez-vous donner un aperçu de ce que fait chaque bit de code et montrer un exemple en cours d'utilisation (jsfiddle, codepen, tout ce que vous préférez)? Une petite explication des points critiques serait appréciée. – Deviljho