Ostatnio zgłaszałem BUGa:
http://4programmers.net/coyote/bug.php?id=262
Chodziło o złe zamykanie tagów w JavaScript w Coyote (funkcja closeTags(str)). Zamiast czekać, aż ktoś (nie koniecznie Ktoś ;) ) ją poprawi, postanowiłem samemu zaproponować jakieś rozwiązanie. Jest obszerne, ale działa szybko, poprawnie i ma kilka udogodnień.
Publikuję je tu w celach testowych.. Jak będą jakieś błędy, proszę zgłaszać w tym temacie. (Kod HTML pliku nie jest poprawny, ale to był tylko szkielet do testów). Wszystkie tagi w kodzie w części HTML poprzedzone są spacją, by udało się to bez problemów wrzucić na forum - przed testem usuńcie te spacje.
< html>
< body>
< script type="text/javascript">
// ['TAG' (1), czy_zamykać (2), czy_usuwać_puste (3), czy_analizowac_tagi_wewnętrzne (4)]
//
// (1) TAGi nie wyszczególnione w tablicy, są zamieniane na encje
// (2) jeśli true, konieczne będzie < /TAG>, w przeciwnym wypadku < TAG> będzie bez zamknięcia
// (3) jeśli true, pusty zapis < TAG>< /TAG> lub < TAG=parametr>< /TAG> zostanie usunięty
// (4) jeśli true, wewnętrzne TAGi są analizowane, jeśli nie - zamieniane na encje
var tags = [
['b', true, true, true],
['hr', false, true, true],
['a', true, false, true],
['code', true, true, false]
];
function ZamknijTagi(tekst)
{
var tagstack=new Array();
//Otwarte TAGi wrzuca na stos, zamykane pobiera ze stosu
//PARAMETRY: całość, /, tag, właściwość
var tekst=tekst.replace(/<(\/)?([\w]+)([\s=][^>]*)?>/g, function(a, b, c, d)
{
if (typeof(b) == "undefined")
b = '';
if (typeof(c) == "undefined")
c = '';
if (typeof(d) == "undefined")
d = '';
if (b!='/')
{
var analizuj = true;
if (tagstack.length>0)
{
for (i=0; i<tags.length; i++)
{
if ((tags[i][0] == tagstack[tagstack.length-1]) && (!tags[i][3]))
analizuj = false;
}
}
if (analizuj)
{
for (i=0; i<tags.length; i++)
{
if (tags[i][0] == c)
{
if (tags[i][1])
tagstack.push(c);
return '<'+c+d+'>';
}
}
}
//nieznane TAGi: zwraca encje
return '<'+c+d+'>';
}
else
{
var analizuj = true;
var obecny = false;
if (tagstack.length>0)
{
for (i=0; i<tags.length; i++)
{
if ((tags[i][0] == tagstack[tagstack.length-1]) && (!tags[i][3]))
{
analizuj = false;
break;
}
}
for (i=0; i<tagstack.length; i++)
{
if (tagstack[i] == c)
{
obecny = true;
break;
}
}
}
if (((analizuj) || (tagstack[tagstack.length-1] == c)) && obecny)
{
var s='';
while (tagstack.length>0)
{
var tag = tagstack.pop();
s+='</'+tag+'>';
if (tag == c)
break;
}
return s;
}
return '</'+c+d+'>';
}
}
);
//Uzupełnia zamknięcie TAGów na końcu tekstu
while (tagstack.length>0)
{
tekst+='</'+tagstack.pop()+'>';
}
//Usuwa puste TAGi, które na to pozwalają
var l=0;
do {
l=tekst.length;
//PARAMETRY: całość, TAG1, właściwość, TAG2
tekst=tekst.replace(/<([\w]+)([\s=][^>]*)?><\/([\w]*)>/g, function(a, b, c, d)
{
if (typeof(b) == "undefined")
b = '';
if (typeof(c) == "undefined")
c = '';
if (typeof(d) == "undefined")
d = '';
if (b == d)
{
for (i=0; i<tags.length; i++)
{
if (tags[i][0] == b)
{
if (!tags[i][2])
return '<'+b+c+'></'+d+'>';
break;
}
}
return '';
}
else
return '<'+b+c+'></'+d+'>';
}
);
} while (l>tekst.length);
return tekst;
}
//TEST
alert(ZamknijTagi("< b=par1>A< b>< /b>< /b>< b>< /n1>< code>< n2>A< /n2>< n3>< /code>< hr>< a>< /a>< a>A"));
< /script>
< /body>
< /html>
Dodałem zabezpieczenie przed wypisywaniem undefined w Internet Explorer.
//Nie jest to w tagach plain, by zostawić kolorowanie (o wiele bardziej czytelne), ani w code, to znacznik ten jest użyty w przykładzie.
//Dodane: zmieniłem kod, by nie głupiał przy zamykaniu nieotwartych tagów, wtedy zamykając wszystkie otwarte.