Javascript.RU

Создать новую тему Ответ
 
Опции темы Искать в теме
  #1 (permalink)  
Старый 02.02.2011, 22:41
Новичок на форуме
Отправить личное сообщение для killer8080 Посмотреть профиль Найти все сообщения от killer8080
 
Регистрация: 01.02.2011
Сообщений: 6

Зацикливание рекурсивного вызова в Firefox
Столкнулся с непонятным поведением лисички. Суть проблемы следующая: пишу скрипт генерирующий 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>

Последний раз редактировалось killer8080, 02.02.2011 в 22:53.
Ответить с цитированием
  #2 (permalink)  
Старый 04.02.2011, 03:11
Интересующийся
Отправить личное сообщение для Michael83 Посмотреть профиль Найти все сообщения от Michael83
 
Регистрация: 05.01.2010
Сообщений: 28

замените
reg= /#([a-z0-9]+)#/ig;

на
reg= new RegExp("#([a-z0-9]+)#", 'ig');


в чем различие можно прочитать тут
Ответить с цитированием
  #3 (permalink)  
Старый 04.02.2011, 09:13
Новичок на форуме
Отправить личное сообщение для killer8080 Посмотреть профиль Найти все сообщения от killer8080
 
Регистрация: 01.02.2011
Сообщений: 6

+1 в карму за помощь
Одно неясно: почему в остальных браузерах работало?
Выходит ФФ единственный строго следовал стандартам.
Ответить с цитированием
  #4 (permalink)  
Старый 04.02.2011, 20:25
Интересующийся
Отправить личное сообщение для Michael83 Посмотреть профиль Найти все сообщения от Michael83
 
Регистрация: 05.01.2010
Сообщений: 28

возможно проблемы нет в FF 4.0 , я не проверял, остальные браузеры это либо исправили (хром), либо вообще так не делали.
Ответить с цитированием
  #5 (permalink)  
Старый 04.02.2011, 22:46
Новичок на форуме
Отправить личное сообщение для killer8080 Посмотреть профиль Найти все сообщения от killer8080
 
Регистрация: 01.02.2011
Сообщений: 6

В Opera, IE, Safari, Chrome всё прекрасно работает, проблема возникала только в Firefox. В той статье на хабре regexp объект создавался в глобальной области видимости, поэтому естественно повторный вызов функции натыкался на предыдущий результат, хранимый в объекте. Но у меня он создавался внутри функции, по идее повторный вызов функции должен инициировать по новой все внутренние переменные, так и происходит со всеми переменными кроме regexp. Баг ФФ в том, что если функция вызвана рекурсивно до завершения предыдущей переменная ссылается на один и тот же объект, и она не только наследует lastIndex, но и возвращает тот же массив с результатами, от предыдущего вызова. Если вызывать её не напрямую, а через setTimeout проблем нет.
Тем не менее объявление через new RegExp решает эту проблему.
Ответить с цитированием
Ответ



Опции темы Искать в теме
Искать в теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
тег <audio> в Opera, Chrome, Firefox Magneto Javascript под браузер 12 25.11.2010 15:12
Peppy и Firefox Cr@ZyBoY Библиотеки/Тулкиты/Фреймворки 13 14.02.2010 20:38
Проблемы с MouseMove в FireFox slim-v Events/DOM/Window 5 05.06.2009 02:44
В Mozilla Firefox 3.0 проблемма с Javascript меню artmedia Элементы интерфейса 1 07.09.2008 10:43
FireFox: onmouseover не работает при зажатой кнопке мыши no. Общие вопросы Javascript 4 19.08.2008 13:43