Столкнулся с непонятным поведением лисички. Суть проблемы следующая: пишу скрипт генерирующий html контент для всплывающего блока. Формат входных данных - объект со следующей структурой:
{
element1 : {
tag: "название html тэга",
attrib: {}, хеш с атрибутами (style, class, src и т.п.)
content: "текстовый контент #element2#",
root: 1 // флаг определяющий что тег является корневым для главного блока
},
element2 : {
tag: "название html тэга",
attrib: {}, хеш с атрибутами (style, class, src и т.п.)
content: "текстовый контент"
},
...
}
При построении контента циклом обхожу все элементы с флагом root (метод build), для генерации тега служит метод makeTag(), который так же ищет в тексте ключевые коды, замещаемые вложенными тегами, вот на этом рекурсивном вызове Firefox и виснет, в остальных браузерах всё работает. В чём проблема никак не могу понять
Вот листинг демки
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Демонстрация бага firefox</title>
<style type="text/css">
h1{ color: #33AA44 }
</style>
</head>
<body>
<h1>Демонстрация бага firefox</h1>
<script type="text/javascript">
var infoBox= function(content, settings){
this.init(content, settings);
infoBox.count++;
}
infoBox.default_settings= {
width: 250,
height: 150,
padding: 20,
borderCSS: "4px solid #FFF",
borderRadius: 25,
bgColorCSS: "#C9D6DC",
top: 100,
left: 100,
ovarlapBg: true,
overlapBgColorCSS: "#000",
overlapBgOpacity: 85,
zIndex: 100,
centralize: true,
initVisible: false,
prefix: "__infoBoxId_"
};
infoBox.count= 0;
infoBox.prototype.init= function(content, settings){
if(typeof content!= "object") var content= {};
if(typeof settings!= "object") var settings= {};
this.content= content;
this.settings= {};
for(var k in infoBox.default_settings){
this.settings[k]= settings.hasOwnProperty(k) ? settings[k] : infoBox.default_settings[k];
}
this.prefix= this.settings.prefix + infoBox.count + "_";
this.build();
}
infoBox.prototype.build= function(show){
if(typeof show== "undefined") var show= this.settings.initVisible;
var docSize= main.getDocumentSize();
var overlapBg= document.getElementById(this.prefix + "overlapBg");
if(!overlapBg){
overlapBg= document.body.appendChild(document.createElement("div"));
overlapBg.id= this.prefix + "overlapBg";
with(overlapBg.style){
position= "absolute";
top= left= "0px";
backgroundColor= this.settings.overlapBgColorCSS;
zIndex= this.settings.zIndex;
opacity= "0." + this.settings.overlapBgOpacity;
filter= "alpha(opacity=" + this.settings.overlapBgOpacity + ")";
MozOpacity= "0." + this.settings.overlapBgOpacity;
display= "none";
width= docSize.width+"px";
height= docSize.height+"px";
}
}
var infoboxWindow= document.getElementById(this.prefix + "window");
if(!infoboxWindow){
infoboxWindow= document.body.appendChild(document.createElement("div"));
infoboxWindow.id= this.prefix + "window";
with(infoboxWindow.style){
position= "absolute";
display= "none";
top= this.settings.top + "px";
left= this.settings.left + "px";
backgroundColor= this.settings.bgColorCSS;
zIndex= this.settings.zIndex + 1;
width= this.settings.width + "px";
height= this.settings.height + "px";
padding= this.settings.padding + "px";
border= this.settings.borderCSS;
borderRadius= this.settings.borderRadius + "px";
MozBorderRadius= this.settings.borderRadius + "px";
WebkitBorderRadius= this.settings.borderRadius + "px";
overflow= "auto";
}
}
this.clear();
for(var name in this.content){
if(this.content[name].root) this.makeTag(name, infoboxWindow);
}
if(show) this.show();
}
infoBox.prototype.makeTag= function(name, parent){
alert(name);//выводим имя элемента для отладки
var obj= this.content[name];
var tag= parent.appendChild(document.createElement(obj.tag));
if(obj.attrib){
if(obj.attrib["style"]) tag.style.cssText= obj.attrib["style"];
if(obj.attrib["class"]) tag.className= obj.attrib["class"];
for(var attrName in obj.attrib){
if(attrName== "style" || attrName== "class") continue;
tag.setAttribute(attrName, obj.attrib[attrName]);
}
}
if(obj.content){
var reg= null, found= null, last= 0;
reg= /#([a-z0-9]+)#/ig;
while((found= reg.exec(obj.content))!== null){
if(found.index > last){
tag.appendChild(document.createTextNode(obj.content.substring(last, found.index)));
}
if(found[1]!= name && this.content[found[1]]) {
this.makeTag(found[1], tag);
}
last= reg.lastIndex;
}
if(last < obj.content.length){
tag.appendChild(document.createTextNode(obj.content.substring(last, obj.content.length)));
}
}
}
infoBox.prototype.show= function(){
document.getElementById(this.prefix + "overlapBg").style.display= "block";
document.getElementById(this.prefix + "window").style.display= "block";
this.centralize();
window.onscroll= window.onresize= (function(_this){return function(){_this.centralize()}})(this);
}
infoBox.prototype.hide= function(){
document.getElementById(this.prefix + "overlapBg").style.display= "none";
document.getElementById(this.prefix + "window").style.display= "none";
window.onscroll= window.onresize= null;
}
infoBox.prototype.clear= function(){
document.getElementById(this.prefix + "window").innerHTML= "";
}
infoBox.prototype.centralize= function(){
var docSize= main.getDocumentSize(), center= main.getScreenCenter(),
win= document.getElementById(this.prefix + "window");
with(win.style){
top= Math.floor(center.Y - (win.offsetHeight / 2)) + "px";
left= Math.floor(center.X - (win.offsetWidth / 2)) + "px";
}
with(document.getElementById(this.prefix + "overlapBg").style){
width= docSize.width + "px";
height= docSize.height + "px";
}
}
var main= {
getPos: function(obj){
var width= obj.offsetWidth;
var height= obj.offsetHeight;
var top, left;
top= obj.offsetTop;
left= obj.offsetLeft;
while(obj= obj.offsetParent){
top+= obj.offsetTop;
left+= obj.offsetLeft;
}
return {'top': top, 'left': left, 'width': width, 'height': height};
},
getDocumentSize: function(){
var size= {
height: document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight,
width: document.body.scrollWidth > document.body.offsetWidth ? document.body.scrollWidth : document.body.offsetWidth
};
if(document.documentElement && size.height < document.documentElement.clientHeight) {
size.height= document.documentElement.clientHeight;
}
if(document.documentElement && size.width < document.documentElement.clientWidth) {
size.width= document.documentElement.clientWidth;
}
return size;
},
getScreenCenter: function(){
return {
X: Math.floor(document.documentElement.scrollLeft + document.documentElement.clientWidth / 2),
Y: Math.floor(document.documentElement.scrollTop + document.documentElement.clientHeight / 2)
}
},
};
var box= new infoBox({
title: {
tag: "h1",
attrib: {
style: "text-align: center;"
},
content: "Заголовок h1",
root: 1
},
combotext: {
tag: "p",
content: "#boldtext# обычный текст",
root: 1
},
boldtext: {
tag: "b",
attrib: {
style: "margin-right: 20px"
},
content: "жирный текст"
},
link: {
tag: "a",
attrib: {
href: "javascript:box.hide()"
},
content: "Закрыть",
root: 1
}
});
</script>
<input type="button" value="Демо" onclick="box.show()" />
</body>
</html>