An extension of Julien's answer above. This copes with multiple lines. Needs a little tweaking, but seems to work. It finds the number of lines by getting the height of a start to end selection, and the height of a single-letter selection, dividing the two, and rounding. There are probably situations where that won't work, but for most purposes...
function getLineCount(node, range) {
    if ((node) && (range)) {
        range.setStart(node, 0);
        range.setEnd(node, 1);
        var r = range.getBoundingClientRect();
        var h1 = r.bottom - r.top;
        range.setEnd(node, node.length);
        r = range.getBoundingClientRect();
        return Math.round((r.bottom - r.top) / h1);
    }
};
Here's a tweaked version of the above code, using the line-count routine above. It also copes a little better with selections within the node, but off to the right of the actual text. None of this is optimized, but we're in user-time here, so milliseconds likely aren't too important.
function getSelectionNodeInfo(x, y) {
    var startRange = document.createRange();
    window.getSelection().removeAllRanges();
    window.getSelection().addRange(startRange);
    // Implementation note: range.setStart offset is
    // counted in number of child elements if any or
    // in characters if there is no childs. Since we
    // want to compute in number of chars, we need to
    // get the node which has no child.
    var elem = document.elementFromPoint(x, y);
    console.log("ElementFromPoint: " + $(elem).attr('class'));
    var startNode = (elem.childNodes.length>0?elem.childNodes[0]:elem);
    var lines = getLineCount(startNode, startRange);
    console.log("Lines: " + lines);
    var startCharIndexCharacter = 0;
    startRange.setStart(startNode, 0);
    startRange.setEnd(startNode, 1);
    var letterCount = startNode.length;
    var rangeRect = startRange.getBoundingClientRect();
    var rangeWidth = 0
    if (lines>1) {
        while ((rangeRect.bottom < y) && (startCharIndexCharacter < (letterCount-1))) {
            startCharIndexCharacter++;
            startRange.setStart(startNode, startCharIndexCharacter);
            startRange.setEnd(startNode, startCharIndexCharacter + 1);
            rangeRect = startRange.getBoundingClientRect();
            rangeWidth = rangeRect.right - rangeRect.left
        }
    }
    while (rangeRect.left < (x-(rangeWidth/2)) && (startCharIndexCharacter < (letterCount))) {
        startCharIndexCharacter++;
        startRange.setStart(startNode, startCharIndexCharacter);
        startRange.setEnd(startNode, startCharIndexCharacter + ((startCharIndexCharacter<letterCount) ? 1 : 0));
        rangeRect = startRange.getBoundingClientRect();
        rangeWidth = rangeRect.right - rangeRect.left
    }
    return {node:startNode, offsetInsideNode:startCharIndexCharacter};
}