domingo, 21 de julho de 2013

HTMLEDitor em JavaScript



   Tenho feito meus estudos em javaScript e hoje gostaria de apresentar o resultado de um código estruturado (não OO) em javaScript: Um HTMLEDitor. 
Eu ví um exemplo de como se navegar entre os DOMs e reimplementei os metodos getTextNodesIn e setSelectionRange. Fonte original:
http://stackoverflow.com/questions/6240139/highlight-text-range-using-javascript

Dicas:

1) Pra pegar a seleção do range, é necessário 4 coisas:
o nó onde começa a seleção: startContainer
a posição dentro do nó ende começa a seleção: startOffset
o nó onde termina a seleção: endContainer
e a posição dentro do no onde termina a seleção.: endOffset

2) Prorpiedades da Div
contentEditable = true
Isso determina que a DIV pode ser edivavel.

style.whiteSpace = "pre";
Isso determina que a DIV trabalhe com espaços e enteres no formato "\n" ao invez de "<BR>"

3) o metodo mais complicado é setProperty.
Notem que ele é um método recursivo e seu pseudo código é esse:
setProperty = function (div, node, atributo, valor)
{
 verificar se é a primeira chamada do metodo
  se for, cria um marcador para sabermos 
  se foi encontrado os nos iniciais e finais 
  e pega o range que armazena os dados da
  seleção

 se não existir seleção, apesar do cursor estar na div
  retorna a mesma coisa. OU seja, não vai ser feito 
  nenhuma alteração.

 Verifica se o nó é um textNode.
 {
  se for, é necessario saber que:
  os nós possiveis serão de 5 tipos:
  no sem nenhuma seleção (parte inicial e sem seleção de todo o texto)
  no com seleção parcial, onde o final é o selecionado
  no com seleção total (todo o nó)
  no com seleção parcial, onde o inicio é o selecionado
  no sem nenhuma seleção (parte final e sem seleção de todo o texto)
  

  A primeira condição é a mais fácil
  se o no é igual ao inicial e ao final do range
  {
   marca que encontrou o no inicial e final
   existe a possibilidade desse texto ser dividida em 3 partes:
   texto inicial, que não tem seleção
   texto da seleção
   texto final, sem seleção nenhuma.
  }
  se não, se o nó é igual ao inicial do range
  {
   marca que encontrou o no inicial
   existe a possibilidade desse texto ser dividida em 2 partes:
   texto inicial, que não tem seleção
   texto da seleção
  }
  se não, se o nó é igual ao final do range
  {
   marca que encontrou o no final
   existe a possibilidade desse texto ser dividida em 2 partes:
   texto da seleção
   texto final, que não tem seleção
  }

  aqui, é o tratamento para os nos que não fazem parte do range. 
  nossos marcadores serão analizados aqui. 

  os que vem antes e depois da seleção não precisam ser alterados
  já o que está no meio da seleção, irá receber as novas propriedades

  antes da seleção : não achou inicio da seleção
  no meio da seleção : achou inicio mas não achou o fim da seleção. 
   recebetratamento.
  ou depois da seleção: achou inicio e fim da seleção

  
 }
 se não for
 {
  realiza a recursividade nos nós filhos
 }
}
    Esboço:
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">  
    <script type="text/javascript">
        Selector = function()
        { 
            if (window.getSelection) 
            {
                return window.getSelection();
            } 

            else if (document.getSelection)
            {
                return document.getSelection();
            }  
        }

        createRange = function (cell)
        {
            var rng=null;
            if(cell.createRange) 
            {
                rng=cell.createRange();
            } 
            else if(cell.getRangeAt) 
            {
                rng=cell.getRangeAt(0);
                if(rng.toString()=="") rng=null;
            }
            return rng;
        }



        getTextNodesIn = function (node) 
        {  
            var textNodes = [];
            if (node!=undefined && node.nodeType !=undefined && node.nodeType == Node.TEXT_NODE ) 
            {
                textNodes.push(node); 
            }
            else 
            {
                var children = node.childNodes;
                for (var i = 0, len = children.length; i < len; ++i) 
                {
                    textNodes = textNodes.concat( getTextNodesIn(children[i]) );
                }
            } 

            return textNodes;
        }

        getCursorPosition = function (div) 
        { 
            var se = Selector ();  
            var range = se.getRangeAt(0);   
            textNodes = getTextNodesIn(div);
            countChar=0;
            for (var i = 0; i<textNodes.length; i++ ) 
            { 
                if ( range.startContainer==textNodes[i] ) 
                {
                    return countChar+range.startOffset 
                }  
                countChar += textNodes[i].textContent.length
            } 
            return null;
        }

        getStartContainer = function (div) 
        {
            var se = Selector ();
            var range = se.getRangeAt(0);   
            textNodes = getTextNodesIn(div); 
            return range.startContainer;
        }    

        getSelectionPosition = function (div) 
        { 
            var se = Selector ();  
            var range = se.getRangeAt(0);   
            textNodes = getTextNodesIn(div); 
            endCharCount = 0;
            Int=null
            end=null
            for (var i = 0; i<textNodes.length; i++ ) 
            { 
                if ( range.startContainer==textNodes[i] ) 
                {
                    Int = endCharCount+range.startOffset
                }  

                if ( range.endContainer==textNodes[i] ) 
                {
                    end = endCharCount+range.endOffset
                    break;
                } 
                endCharCount += textNodes[i].textContent.length
            }  
            return [Int, end];
        }


        setSelectionPosition = function (div, start, end) 
        {
            range = createRange(document);
            range.selectNodeContents(div);
            var foundStart = false;
            var charCount=0;
            textNodes = getTextNodesIn(div);
            var aa=0
            var bb=0
            for (var i = 0; i<textNodes.length; i++ ) 
            {   
                velhoCount = charCount;
                charCount += textNodes[i].textContent.length 
                if (!foundStart && velhoCount <= start && start <= charCount   ) 
                { 
                    aa = textNodes[i].textContent.length  - (charCount - start );
                    range.setStart(textNodes[i], aa );
                    foundStart = true;
                }

                if (foundStart && end <= charCount) 
                { 
                    bb = textNodes[i].textContent.length - (charCount - end) ;
                    range.setEnd(textNodes[i], bb );
                    break;
                }

            }
            var sel = Selector();
            sel.removeAllRanges();
            sel.addRange(range);
            div.focus();
        }


        setCursorPosition = function (div, position) 
        { 
            range = createRange(document);
            range.selectNodeContents(div);
            var charCount = 0
            var endCharCount;
            textNodes = getTextNodesIn(div);
            for (var i = 0; i<textNodes.length; i++ ) 
            {
                textNode = textNodes[i];
                endCharCount = charCount + textNode.length;
                if (position >= charCount && position <= endCharCount ) 
                {
                    range.setStart(textNode, position - charCount);
                    range.setEnd(textNode, position - charCount);
                    range.collapse(false);
                    break;
                } 
                charCount = endCharCount;
            }
            var sel = Selector();
            sel.removeAllRanges();
            sel.addRange(range);
            div.focus();
        }

        enterEvent = function (event) 
        {
            if (event.keyCode == 13) 
            {
                event.preventDefault();
                var position = getCursorPosition (this) 
                var se = Selector (); 
                var range = se.getRangeAt(0);
                var posA = 0 ; 
                var posB = range.endOffset ; 
                var posC = range.endContainer.textContent.length ; 
                var a = range.endContainer.textContent.substring( posA, posB ) ; 
                var b = range.endContainer.textContent.substring( posB, posC ) ;  
                range.endContainer.parentNode.innerHTML = (a + '\n' + b)   ;  
                setCursorPosition (this, position+1)
            }

        }

        setStyle = function (tag, atributo, valor)
        {
            tag.style[atributo] = valor;
        }



        setProperty = function (div, node, atributo, valor)
        {
            if(node==undefined || node==null ) 
            {
                div.achouEndContainer = false;
                div.achouStartContainer = false;        
                var se = Selector (); 
                div.range = se.getRangeAt(0);
                node = div
            }

            if(  div.range.startContainer == div.range.endContainer && div.range.startOffset==div.range.endOffset  )
            {
                return;
            }
 

            if( node.nodeType == Node.TEXT_NODE    )
            {
                var conteudo = node.textContent ; 
                var posA = 0 ; 
                var posB = div.range.startOffset ; 
                var posC = div.range.endOffset ; 
                var posD = conteudo.length ; 
                if(node == div.range.startContainer && node == div.range.endContainer  )
                { 
                    div.achouEndContainer = true;  
                    div.achouStartContainer = true; 

                    var span  = document.createElement("span"); 

                    var s1 = document.createElement("span");
                    s1.textContent = conteudo.substring( posA, posB )  
                    span.appendChild(s1) 

                    var s2 = document.createElement("span"); 
                    setStyle(s2, atributo, valor)

                    s2.textContent = conteudo.substring( posB, posC )  
                    span.appendChild(s2) 

                    var s3 = document.createElement("span");
                    s3.textContent = conteudo.substring( posC, posD)  
                    span.appendChild(s3) 

                    var rec = node.parentNode;
                    rec.removeChild(node); 
                    rec.appendChild( span ) ; 
                }

                else if( node == div.range.startContainer)
                {   
                    div.achouStartContainer = true; 
                    var span  = document.createElement("span"); 
                    var s1 = document.createElement("span");
                    s1.textContent = conteudo.substring( posA, posB ) 
                    span.appendChild(s1)

                    var s2 = document.createElement("span"); 
                    setStyle(s2, atributo, valor)
                    s2.textContent = conteudo.substring( posB, posD )
                    span.appendChild(s2) 

                    var rec = node.parentNode;
                    rec.removeChild(node); 
                    rec.appendChild( span ) ; 
                }
                else if( node == div.range.endContainer)
                {   
                    div.achouEndContainer = true; 
                    var span  = document.createElement("span");

                    var s1 = document.createElement("span");
                    setStyle(s1, atributo, valor);
                    s1.textContent = conteudo.substring( posA, posC )
                    span.appendChild(s1)

                    var s2 = document.createElement("span");
                    s2.textContent = conteudo.substring( posC, posD )
                    span.appendChild(s2) 

                    var rec = node.parentNode;
                    rec.removeChild(node); 
                    rec.appendChild( span ) ; 
                } 

                else if( !div.achouStartContainer )
                {  
                }

                else if( div.achouStartContainer && !div.achouEndContainer )
                { 
                    var span = document.createElement("span");
                    setStyle(span, atributo, valor);
                    var s1  = document.createTextNode(conteudo);
                    span.appendChild(s1); 
 

                    var rec = node.parentNode;
                    rec.removeChild(node); 
                    rec.appendChild( span ) ; 
                } 

                else if( div.achouEndContainer )
                { 
                }
            }

            else
            {
                var children = node.childNodes;
                for (var i = 0; i<children.length; i++)
                {
                    setProperty( div, children[i], atributo, valor);
                }
            }
        }

    load = function()
    {
        var divA        = document.getElementById("div");  
        divA.style.whiteSpace   = "pre";
        divA.contentEditable    = true;
        divA.style.position = "absolute";
        divA.style.border   = "1px solid #000000";
        divA.style.position = "absolute"; 
        divA.style.left     = "30px"; 
        divA.style.top      = "30px"; 
        divA.style.width    = "450"; 
        divA.style.height   = "150"; 
        divA.style.border   = "1px solid #000000";
        divA.innerHTML      = "<span>abcdefghij 1234567890a\n1234567890</span>" ; 
        divA.onkeydown      = enterEvent;
    }

    teste1 = function()
    {   
        var rec = document.getElementById("div")
        obj = getSelectionPosition(rec)
        setProperty  (rec, null, 'color', 'red');
        setSelectionPosition(rec, obj[0], obj[1] ) 
    }

    teste2 = function()
    { 
        var rec = document.getElementById("div")
        obj = getSelectionPosition(rec)
        setProperty  (rec, null, 'color', 'blue');
        setSelectionPosition(rec, obj[0], obj[1] ) 
    }

    teste3 = function()
    {  
        var rec = document.getElementById("div")
        obj = getSelectionPosition(rec)
        setProperty  (rec, null, 'fontWeight', 'bold'); 
        setSelectionPosition(rec, obj[0], obj[1] ) 
    }

    teste4 = function()
    {  
        var rec = document.getElementById("div")
        obj = getSelectionPosition(rec)
        setProperty  (rec, null, 'fontWeight', 'normal');
        setSelectionPosition(rec, obj[0], obj[1] ) 
    }

    teste5 = function()
    { 
        var rec = document.getElementById("div")
        obj = getSelectionPosition(rec)
        setProperty  (rec, null, 'fontStyle', 'italic');
        setSelectionPosition(rec, obj[0], obj[1] ) 
    }

    teste6 = function()
    {  
        var rec = document.getElementById("div")
        obj = getSelectionPosition(rec)
        setProperty  (rec, null, 'fontStyle', 'normal');
        setSelectionPosition(rec, obj[0], obj[1] ) 
    }
    </script>
</head>
<body onload="load()">
    <div id="div">sdf</div>
    <button type="button" onclick="teste1()">vermelho</button> 
    <button type="button" onclick="teste2()">azul</button> 
    <button type="button" onclick="teste3()">bold</button> 
    <button type="button" onclick="teste4()">unbold</button> 
    <button type="button" onclick="teste5()">italic</button> 
    <button type="button" onclick="teste6()">normal text</button> 
</body>
</html>

Nenhum comentário: