// Based on the code from http://bililite.com/blog/2012/08/05/line-numbering-in-pre-elements/

function wrapLines (el){
    var text = el.textContent.split('\n');
    var range = document.createRange();
    var pointer = 0; 
    el.textContent.split('\n').forEach(function(line, i){
            var len = line.length;
            setBounds (pointer, pointer+len); 
            var wrapper = document.createElement('span');
            wrapper.setAttribute('class', 'line');
            wrapper.appendChild(range.extractContents()); 
            range.insertNode(wrapper); 
            pointer += len+1; 
        });
    for (var node = el.firstChild; node; node = node.nextSibling){
        if (node.nodeType != 3 && node.getAttribute('class') != 'line'){
            var replacement = document.createTextNode(node.textContent);
            el.replaceChild(replacement, node);
            node = replacement;
        }
    }
    
    function setBounds (start, end){
        range.selectNodeContents(el);
        moveBoundary (start, 'start');
        range.collapse (true);
        moveBoundary (end-start, 'end');
    }
    function moveBoundary (n, start){
        if (n <= 0) return;
        var startNode = range[start+'Container'];
        
        if (startNode.nodeType == 3) n += range[start+'Offset'];
        
        var iter = document.createNodeIterator(el, 4 /* SHOW_TEXT */), node;
        while (node = iter.nextNode()){
            if (startNode.compareDocumentPosition(node) & 2 /* DOCUMENT_POSITION_PRECEDING */ ) continue;
            if (n <= node.nodeValue.length){
                
                range[start == 'start' ? 'setStart' :'setEnd'](node, n);
                return;
            }else{
                n -= node.nodeValue.length; 
            }
        }
    }
}