gaAllowedSingleton = new Array(
	"C", 4,
	"O", 2,
	"N", 3,
	"H", 1,
	"OH", 1,
	"NO2", 1,
	"NH2", 1,
	"Cl", 1,
	"Br", 1,
	"CH2OH", 1
);
//////////////////////////////////////////////////////////////////
// Характеристики атома
//////////////////////////////////////////////////////////////////
function CAtomDescription()
{
	this.freeLinks = 4;
}

//////////////////////////////////////////////////////////////////
// Один атом в молекуле
//////////////////////////////////////////////////////////////////
function CAtom()
{
	this.children=new Array();
	this.type="C";
	this.parent=null;
	this.drawType=1;
	this.x=0;
	this.y=0;
	this.rootDistance=0;
	this.linkType=0;
	this.freeLinks=4;
	this.firstChildDirection="down";
	this.molecule=null;
	this.oText=null;
	this.oLink=null;
	this.longestChild=null;
	this.longestChildCount=0;
	this.isOnMainLine=false;
	this.subtreeLength=0;
	this.index=0;
	this.isFlipped=false;	// атом повернут на 180 град.?
}
// Строковой тип атома
CAtom.prototype.type="C";
// Ссылка на атом-родитель в дереве молекулы
CAtom.prototype.parent=null;
// Текущее направление добавления атомов, для всех "ответвлений"
CAtom.prototype.firstChildDirection="right";
// Относительные координаты на рабочей плоскости
CAtom.prototype.x=0;
CAtom.prototype.y=0;
// "Расстояние" до первого атома
CAtom.prototype.rootDistance=0;
// Массив ссылок на дочерние атомы
CAtom.prototype.children=null;
// Ссылка на потомка, который имеет наибольшую длину ветки
CAtom.prototype.longestChild=null;
// Длина максимального поддерева у потомка longestChild
CAtom.prototype.longestChildCount=0;
// Тип связи с родителем
// 1. 1 - одинарная
// 2. 2 - двойная
// 3. 3 - тройная
CAtom.prototype.linkType=1;
// Изначальное количество свободных электронов
CAtom.prototype.freeLinks=4;
// Ссылка на молекулу, которой принадлежит этот атом
CAtom.prototype.molecule=null;
// Текст для названия элемента
CAtom.prototype.oText=null;
// Картинка для связи элемента
CAtom.prototype.oLink=null;
// Атом располагается в наидлиннейшей цепочке
CAtom.prototype.isOnMainLine=false;
// Длина всего поддерева, начиная с текущего атома
CAtom.prototype.subtreeLength=0;
// Тип атома для отрисовки
// 1. Обычный атом
// 2. Атом на главной линии
// 3. Атом на главной линии
// 16. Выделенный для удаления атом
// 32. Подсвеченный красным атом
// 64. Подсвеченный серым атом
CAtom.prototype.drawType=1;
// Флаг выделенности
CAtom.prototype.isMarked=false;
// Флаг транс[true] или цис[false] изомера
CAtom.prototype.isTransIsomer=false;
// Получение абсолютных номеров ячеек на экране
CAtom.prototype.getType = function()
{
	if( this.type.indexOf(":")!=-1)	return this.type;
	if( this.type.indexOf("_")!=-1)	return this.type;

	var str = this.type;
	str = str.replace(/(<.?sub>)/g,"");
	for( var i=0; i<gaAllowedSingleton.length; i++)
	{
		if( gaAllowedSingleton[i] == str)
		{
			return this.type;
		}
	}
	return "C";
}
// Получение абсолютных номеров ячеек на экране
CAtom.prototype.getAbsXY = function()
{
	var pnt = new Object();
	pnt.x = x;
	pnt.y = y;
	if( this.molecule!=null)
	{
		pnt.x += this.molecule.x;
		pnt.y += this.molecule.y;
	}
	return pnt;
}
// Является ли атом первым в родительском списке
CAtom.prototype.isFirstSon = function()
{
	if( this.parent == null || this.parent.children.length==0)	return false;
	return this.parent.children[0]==this?true:false;
}
// Добавляет дочерний элемент к атому
CAtom.prototype.addChild = function(atom)
{
	this.children.push( atom);
	atom.parent = this;
	atom.linkType = 1;	// по умолчанию устанавливаем одинарную связь
}
// Удаляет дочерний элемент
CAtom.prototype.removeChild = function(atom)
{
	for( var i=0; i<this.children.length; i++)
	{
		if( this.children[i] == atom)
		{
			this.children.splice( i, 1);
			atom.parent = null;
			atom.linkType = 0;
			return true;
		}
	}
	return false;
}
// Функция возвращает характеристики атома
CAtom.prototype.getDescription = function(atomType)
{
	var desc = new CAtomDescription();
	for( var i=0; i<gaAllowedSingleton.length; i++)
	{
		if( gaAllowedSingleton[i] == atomType)
		{
			desc.freeLinks = Number(gaAllowedSingleton[i+1]);
			return desc;
		}
	}
	trace("mc2D description error: incorrect atom type["+atomType+"]");
	return desc;
}
// Функция обработки нажатия на атом
CAtom.prototype.handleOnPress = function()
{
trace("#FF0000 Atom pressed!!");
	this.molecule.xPressMouse = scene.xMouse;
	this.molecule.yPressMouse = scene.yMouse;
	this.molecule.markSmallestSubtree( this);

this.molecule.temp = 16;
this.molecule.calcTopDownMarked( this.molecule.wfAddDrawStyle);

	this.molecule.grabMarkedAtomsToArray();
	this.molecule.board.handleOnAtomPressed( this);

	this.molecule.setStyles();
}
// Функция обработки отпускания кнопки мыши
CAtom.prototype.handleOnRelease = function()
{
trace("#FF0000 Atom released!!");
	this.molecule.board.handleOnAtomReleased( this);
	if( this.molecule.xPressMouse != scene.xMouse ||
		this.molecule.yPressMouse != scene.yMouse)
	{
		return;
	}
	if( this.molecule.board.workspace.mode == "add")
	{
		trace("#FF0000 ADD ELEMENT:"+this.molecule.board.workspace.activeElement);
		this.addElement( this.molecule.board.workspace.activeElement);
	}
	else	if( this.molecule.board.workspace.mode == "delete")
	{
		this.molecule.deleteSmallestSubtree(this);
	}
}
// Функция обработки нажатия на связь
CAtom.prototype.handleLinkPress = function()
{
trace("link:this.type="+this.type);
	this.molecule.board.stick( true);
}
// Функция отпускания на связи
CAtom.prototype.handleLinkRelease = function()
{
	this.molecule.board.stick( false);
}
// Функция наведения на атом
CAtom.prototype.handleOnTextRollOver = function()
{
	this.drawType |= 32;
	this.molecule.wfSetStyles( this);
	if( this.molecule.board.workspace.mode == "delete")
	{
		this.molecule.markSmallestSubtree( this, this.molecule.board.stlBold);
		this.molecule.temp = 16;
		this.molecule.calcTopDownMarked( this.molecule.wfAddDrawStyle);
		this.molecule.setStyles();
	}
	this.molecule.board.handleOnTextRollOver( this);
}
// Функция убирания мыши с атома
CAtom.prototype.handleOnTextRollOut = function()
{
	this.drawType &= ~32;
	this.molecule.wfSetStyles( this);
	if( this.molecule.board.workspace.mode == "delete")
	{
		this.molecule.markSmallestSubtree( this, this.molecule.board.stlNormal);
		this.molecule.temp = 16;
		this.molecule.calcTopDownMarked( this.molecule.wfRemoveDrawStyle);
		this.molecule.setStyles();
	}
	this.molecule.board.handleOnTextRollOut( this);
}
// Функция нажатия правой кнопкой на атоме
CAtom.prototype.handleOnRButtonDown = function()
{
trace("2. #00FF00 ******************************");
	this.molecule.board.handleOnAtomRButtonDown( this);
}
// Функция возвращает количество "занятых" связей
CAtom.prototype.getLockedLinks = function()
{
	var lockedLinks=0;
//trace("this.children.length="+this.children.length);
	for( var i=0; i<this.children.length; i++)
		lockedLinks += this.children[i].linkType;
	if( this.parent != null)
		lockedLinks += this.linkType;
	return lockedLinks;
}
// Функция добавления нового элемента
CAtom.prototype.addElement = function(atomType)
{
	this.molecule.addElement( this, atomType);
}
// Удаление графического представления молекулы
CAtom.prototype.destroy=function()
{
//traceRefs(this);
trace("CAtom - destroy");
	if( this.oText!=null)
	{
		this.oText.parent = null;
		this.oText = null;
	}
	if( this.oLink!=null)
	{
		this.oLink.parent = null;
		this.oLink = null;
	}
	if( this.molecule!=null && this.molecule.board!=null)
	{
		this.molecule.board.markCellAsEmpty(this.x+this.molecule.x, this.y+this.molecule.y);
	}
	this.children.length=0;
var xxx=Mouse.target;
}
// Смена типа связи
CAtom.prototype.setLink=function(alinkType)
{
	if( this.parent==null)
	{
		trace("mc2d setLink error: this.parent is null");
		return;
	}

	var thisDesc = CAtom.prototype.getDescription(this.getType());
	var thisBusyLinks = this.getLockedLinks() - this.linkType;

	var parentDesc = CAtom.prototype.getDescription(this.parent.getType());
	var parentBusyLinks = this.parent.getLockedLinks() - this.linkType;

	if( (thisBusyLinks+alinkType <= thisDesc.freeLinks) &&
		(parentBusyLinks+alinkType <= parentDesc.freeLinks))
	{
		this.linkType = alinkType;
		this.loadLinkImage();
		this.molecule.optimize();
		this.molecule.visualize();
	}
	else
	{
		this.molecule.board.onWarning(10);
	}

	this.molecule.onStructureChanged(null);
}
// Загружает нужную картинку связи
CAtom.prototype.loadLinkImage=function()
{
//trace("1...");
	if( this.oLink == null)	return;
//trace("oLink._class.name = "+this.oLink._class.name);
//trace("2...");
	var xy = this.molecule.board.getCellXY(this.getLinkAbsX(), this.getLinkAbsY());
	this.oLink.x = xy.x;
	this.oLink.y = xy.y;
//trace("this.parent = "+this.parent);
//trace("3...");
	if( this.parent != null)
	{
//trace("4...");
		var prefix = "h";
		this.oLink.visible=true;
		if( this.firstChildDirection == "down")
		{
			prefix = "v";
		}
		else	if( this.firstChildDirection == "up")
		{
			prefix = "v";
		}
		else	if( this.firstChildDirection == "left-up" || this.firstChildDirection == "right-down")
		{
			prefix = "rl";
		}
		else	if( this.firstChildDirection == "left-down" || this.firstChildDirection == "right-up")
		{
			prefix = "lr";
		}
		this.oLink.setSrc( this.molecule.rmmlParent.getImagePath("link"+this.linkType+prefix+".png"));
		// Затычка!!!
		if( this.parent!=null && (this.parent instanceof CSpecCycleAtom) && this.parent.oText!=null)
		{
			this.oLink.x = this.parent.oText.x;
			this.oLink.x -= (this.oLink.getMediaSize().width/2 - this.parent.oText.getMediaSize().width)/2;
		}
		else
		{
			this.oLink.x += (this.molecule.board.cellSizeX - this.oLink.getMediaSize().width/2)/2;
			this.oLink.y += (this.molecule.board.cellSizeY - this.oLink.getMediaSize().height)/2;
		}
		this.oLink.visible = true;

		var margins = 2;
		var	pos = 0;
		var width = 0;
		var height = 0;
		var myParent = this.parent;
		if( this instanceof CCycleAtom && this.cycleIndex==0)
		{
			myParent = this.myCycle.parent;
		}
//trace("5...");
		if( myParent.oText!=null)
		{
//trace("6..."+prefix);
			if( prefix == "h")
			{
				var thisWidth = this.oText.getMediaSize().width;
				var parentWidth = myParent.oText.getMediaSize().width;
				if(this.oText.x < myParent.oText.x)
				{
					pos = this.oText.x+thisWidth+margins;
					width = myParent.oText.x-pos-margins;
				}
				else
				{
					pos = myParent.oText.x+parentWidth+margins;
					width = this.oText.x-pos-margins;
				}
//trace("...pos="+pos+", width="+width);
				if( pos!=0 && width!=0)
				{
					this.oLink.x = pos;
					this.oLink.width = width;
//trace("...width="+width);
					this.oLink.setCropWidth(width);
				}
			}
			else if( prefix == "v")
			{
				var thisHeight = this.oText.getMediaSize().height;
				var parentHeight = myParent.oText.getMediaSize().height;
				if(this.oText.y < myParent.oText.y)
				{
					pos = this.oText.y+thisHeight+margins;
					height = myParent.oText.y-pos-margins;
				}
				else
				{
					pos = myParent.oText.y+parentHeight+margins;
					height = this.oText.y-pos-margins;
				}
				if( pos!=0 && height!=0)
				{
					this.oLink.y = pos;
					this.oLink.height = height;
					this.oLink.setCropHeight(height);
				}
			}
		}
	}
	else
	{
		this.oLink.visible=false;
	}
}
// Возвращает "вес" атома, который используется при оптимизации
CAtom.prototype.getOptimizeWeight=function()
{
	if( this.getType()=="C" &&
		this.parent!=null &&
		this.parent.getType()=="C")
	{
		return (this.linkType > 1)?100:50;
	}
	//return this.linkType;
	return 1;
}
// Содержит ли атом этого потомка
CAtom.prototype.hasChild=function(atom)
{
	for(var i=0; i<this.children.length;i++)
	{
		if( this.children[i] == atom)	return true;
	}
	return false;
}
// Возвращает абсолютные координаты атома на "виртуальной" доске
CAtom.prototype.getAbsX=function()
{
	return this.molecule.x + this.x;
}
CAtom.prototype.getAbsY=function()
{
	return this.molecule.y + this.y;
}
// Устанавливает координату X
CAtom.prototype.setX=function(ax)
{
	this.x = ax;
}
// Устанавливает координату Y
CAtom.prototype.setY=function(ay)
{
	this.y = ay;
}
// Возвращает абсолютные координаты связи на "виртуальной" доске
CAtom.prototype.getLinkAbsX=function()
{
	var absX = this.getAbsX();
	if( this.parent == null)	return absX;
	if( this.firstChildDirection.indexOf("right")!=-1)
		absX--;
	else	if( this.firstChildDirection.indexOf("left")!=-1)
		absX++
	return absX;
}
CAtom.prototype.getLinkAbsY=function()
{
	var absY = this.getAbsY();
	if( this.parent == null)	return absY;
	if( this.firstChildDirection.indexOf("up")!=-1)
		absY++;
	else	if( this.firstChildDirection.indexOf("down")!=-1)
		absY--;
	return absY;
}
// Устанавливает новый тип атома
CAtom.prototype.setType = function( atype)
{
	var lockedLinks = this.getLockedLinks();
	var desc = CAtom.prototype.getDescription(atype);
	if( lockedLinks <= desc.freeLinks)
	{
		this.type = atype;
		this.molecule.wfCreate(this);
		this.molecule.onStructureChanged(null);
	}
	else
	{
		this.molecule.board.onWarning(10);
	}
}
// Возвращает границы объекта
CAtom.prototype.getBounds = function()
{
	var bounds=new Object();
	bounds.left = this.x;	bounds.top = this.y;
	bounds.right = this.x;	bounds.bottom = this.y;
	return bounds;
}
// Поворачивает атом на 180 градусов
CAtom.prototype.flip = function()
{
trace("CAtom.prototype.flipCAtom.prototype.flipCAtom.prototype.flipCAtom.prototype.flip");
	this.isFlipped = !this.isFlipped;
	this.molecule.calcPositions();
	this.molecule.create();
	this.molecule.onStructureChanged(null);
}
// Копирует параметры с другого атома
CAtom.prototype.copyFrom = function(atom)
{
	this.type = atom.type;
	this.linkType = atom.linkType;
	this.isFlipped = atom.isFlipped;
}
// Создает объект - атом
CAtom.prototype.create = function(atomType)
{
	var newAtom = new CAtom();
	newAtom.type = atomType;
	newAtom.linkType = 1;
	return newAtom;
}
// Создает визуальное представление атома
CAtom.prototype.createVisual = function(atomType)
{
//trace("This atomtype="+this.type+":"+atomType);
	if( this.oText == null)
	{
		this.oText = new Text();
		this.oText._class="CMC2Text";
		this.oText.parent = this.molecule.rmmlParent;
		this.oText.atom = this;
	}

	this.showH(this);

	// Затычка!!!
	if( this.parent!=null && (this.parent instanceof CSpecCycleAtom) && this.parent.oText!=null)
	{
		this.oText.x = this.parent.oText.x;
		this.oText.x -= (this.oText.getMediaSize().width - this.parent.oText.getMediaSize().width)/2;
		if( this.firstChildDirection == "up")
		{
			this.oText.y = this.parent.oText.y - 2*gvCellSize;
		}
		else	if( this.firstChildDirection == "down")
		{
			this.oText.y = this.parent.oText.y + 2*gvCellSize;
		}
		else
		{
			trace("#F00000 mc2D CAtom.prototype.createVisual error: incorrect this.firstChildDirection["+this.firstChildDirection+"]");
		}
	}
	else
	{
		// Нормальный алгоритм
		var xy = this.molecule.board.getCellXY(this.getAbsX(), this.getAbsY());
		this.oText.x = xy.x;
		this.oText.y = xy.y;
		this.oText.x += (this.molecule.board.cellSizeX - this.oText.getMediaSize().width)/2;
		this.oText.y += (this.molecule.board.cellSizeY - 14/*this.oText.getMediaSize().height*/)/2;
	}

//trace("this.oText.x="+this.oText.x);

	if( this.oLink == null && this.parent!=null)
	{
		this.oLink = new Image();
		this.oLink._class = "CMC2Image";
		this.oLink.visible = false;
		this.oLink.parent = this.molecule.rmmlParent;
		this.oLink.atom = this;
	}
	if( this.oLink != null)
	{
		this.loadLinkImage();
	}
}
// Создает визуальное представление атома
CAtom.prototype.showH = function()
{
	var str = this.type;
	if( this.molecule.isShowH && this.getType()!="H")
	{
		var desc = CAtom.prototype.getDescription(this.getType());
		if( desc == null)	return;
		var busyLinks = this.getLockedLinks();
		if( busyLinks < desc.freeLinks)
		{
			if( desc.freeLinks - busyLinks != 1)
				//str += "H<font size=\"10px\">" + (desc.freeLinks - busyLinks) + "</font>";
				str += "H" + (desc.freeLinks - busyLinks);
			else
				str += "H";
		}
	}
	if( this.oText != null)
	{
		var nowW = this.oText.getMediaSize().width;
		if( str == "OH" && this.firstChildDirection=="left")
			str = "HO";
		else	if( str == "HO" && this.firstChildDirection=="right")
			str = "OH";
		str = str.replace(/(<.?sub>)/g,"").replace(/(\d+)/g,"<sub>$1</sub>");
		this.oText.value = str;
		this.oText.x += (nowW - this.oText.getMediaSize().width)/2;
	}

}
// проверяет, является ли передаваемый тип - типом цикла
CAtom.prototype.isCycleType = function(atomType)
{
	return atomType.indexOf(":")==-1?false:true;
}
// проверяет, является ли передаваемый тип - типом цикла
CAtom.prototype.isSpecialCycleType = function(atomType)
{
	return atomType.indexOf("_")==-1?false:true;
}
// устанавливает флаг расположения первого атома
CAtom.prototype.setFirstChildDirection = function(aDir)
{
	this.firstChildDirection = aDir;
}
// считает расстояние от атома до корня молекулы
CAtom.prototype.calculateDistances = function()
{
	if( this.parent != null)
		this.rootDistance = this.parent.rootDistance + 1;
	else
		this.rootDistance = 1;
}
// устанавливает стиль текста
CAtom.prototype.setTextStyle = function(stl)
{
	if( this.oText != null)
	{
		this.oText.style = stl;
	}
}
// возвращает количество хим элементов этого атома
CAtom.prototype.getElementCount = function(elem)
{
//trace("CAtom.getElementCount="+elem + ":" + this.type);
	if( elem == "H")
	{
		var desc = CAtom.prototype.getDescription(this.getType());
//trace("desc.freeLinks="+desc.freeLinks + ",this.getLockedLinks()=" + this.getLockedLinks());
 		return desc.freeLinks - this.getLockedLinks();
	}
	return this.getType()==elem?1:0;
}
CAtom.prototype.getVisualInstance = function()
// Возвращает ссылку на атом, который представляет this в дереве молекулы
{
	return this;
}
// Добавляет флаг видимого типа
CAtom.prototype.addDrawType = function( atype)
{
	this.drawType |= atype;
}
// Снимает флаг видимого типа
CAtom.prototype.removeDrawType = function( atype)
{
	this.drawType &= ~atype;
}
// Устанавливает основное значение видимого типа
CAtom.prototype.setDrawStyle = function( atype)
{
	// Получение флагов > 15
	var currentAddons = this.drawType&240;
	this.drawType = atype;
	this.drawType |= currentAddons;
}
// Устанавливает дополнительное значение видимого типа
CAtom.prototype.setExtendedDrawStyle = function( atype)
{
	// Получение флагов <= 15
	var currentAddons = this.drawType&15;
	this.drawType = currentAddons + atype;
}
// Можно ли к данному атому добавлять потомков
CAtom.prototype.allowAddition = function(atomType)
{
trace("#00A000 atomType="+atomType);
	if( CAtom.prototype.isSpecialCycleType( atomType))
	{
		this.molecule.board.onWarning(15);
		return false;
	}
	if( this.parent!=null && this.parent instanceof CSpecCycleAtom)
	{
		this.molecule.board.onWarning(12);
		return false;
	}
	return true;
}
//////////////////////////////////////////////////////////////////
// Вся молекула
//////////////////////////////////////////////////////////////////
function CMolecule()
{
	this.root=null;
	this.x=0;
	this.y=0;
	this.board=null;
	this.rmmlParent=null;
	this.maxSubtreeLength=0;
	this.maxSubtreeItem=null;
	this.bounds = new Object();
		this.bounds.left = 0;
		this.bounds.right = 0;
		this.bounds.top = 0;
		this.bounds.bottom = 0;
	this.stopNode=null;
	this.optimizationDelay=true;
	this.isShowH=false;
	this.name = "";
}
// Ссылка на объект-родитель для видимых элементов
CMolecule.prototype.rmmlParent=null;
// Ссылка на рабочее поле
CMolecule.prototype.board=null;
// Ссылка на первый элемент
CMolecule.prototype.root=null;
// Логические координаты на рабочей плоскости
CMolecule.prototype.x=0;
CMolecule.prototype.y=0;
// Флаг ошибки в расчетах при отрисовке модули
CMolecule.prototype.calculationError=false;
// Значение максимальной длины двух или одного поддерева в одном из узлов
CMolecule.prototype.maxSubtreeLength=0;
// Ссылка на узел, в котором значение maxSubtreeLength - максимально
CMolecule.prototype.maxSubtreeItem=null;
// Границы молекулы - в индексах ячеек рабочего поля
CMolecule.prototype.bounds=null;
// Переменная для ограниченных обходов дерева
CMolecule.prototype.stopNode=null;
// Переменная для "медленной" визуализации дерева
CMolecule.prototype.optimizationDelay=true;
// Переменная хранят координаты мыши при нажатии на атом
CMolecule.prototype.xPressMouse=0;
CMolecule.prototype.yPressMouse=0;
// Флаг заполнения свободных связей атомами H
CMolecule.prototype.isShowH=false;
// Расчет расстояний от атомов до начального атома
CMolecule.prototype.calculateDistances=function()
{
	if( this.root==null)
	{
		return;
	}
	this.root.rootDistance = 1;
	this.calcTopDown( this.wfCalculateDistances);
};
CMolecule.prototype.wfCalculateDistances=function(atom)
{
	atom.calculateDistances();
};
// Вывод названий элементов в trace
CMolecule.prototype.print=function()
{
	this.calcTopDown( this.wfPrint);
	trace("// ----- Tree printed -----");
};
CMolecule.prototype.wfPrint=function(atom)
{
	var i=0;
	var s="";
	for( ;i<atom.rootDistance;i++)
	{
		s += "_";
	}
	/*if(atom.isOnMainLine)
		s += atom.type + "["+atom.rootDistance+","+atom.longestChildCount+"] is on main line";
	else
		s += atom.type + "["+atom.rootDistance+","+atom.longestChildCount+"]";*/

	s += atom.type + "["+atom.drawType+"] is on main line " + atom.children.length;
	/*s += "atom.type"+atom.type;
	if( atom.parent != null)
		s += " parent is " + atom.parent.type;
	*/
	trace(s);
	/*
	if( atom.oLink == null)
		trace("this.oLink == null");
	else
		trace("this.oLink="+atom.oLink.src+":"+atom.oLink.visible);
	*/

	if( atom instanceof CCycle)
	{
		//for( var i=0; i<atom.cycleChildren.length; i++)
			//this.wfPrint(atom.cycleChildren[i]);
	}
	else	if( atom instanceof CCycleAtom)
	{
		/*trace("Childrens are");
		for( var i=0; i<atom.children.length; i++)
			trace("atom.children[i]="+atom.children[i].type);*/
	}
};
// Вывод длин поддеревьев элементов в trace
CMolecule.prototype.printLengths=function()
{
	this.calcTopDown( this.wfPrintLengths);
};
CMolecule.prototype.wfPrintLengths=function(atom)
{
	var i=0;
	var s="";
	for( ;i<atom.rootDistance;i++)
	{
		s += "_";
	}
	s += atom.type + "["+atom.longestChildCount+"]";
	trace(s);
};
// Создание графического представления молекулы
CMolecule.prototype.create=function()
{
	this.calcTopDown( this.wfCreate);
};
CMolecule.prototype.wfCreate=function(atom)
{
	atom.createVisual();
};
// Рабочая функция обходов
CMolecule.prototype.calcFunction=null;
// Функция обсчета сверху вниз
CMolecule.prototype.calcTopDown=function(acalcFunction, startAtom)
{
	if( acalcFunction==null || typeof(acalcFunction)=="undefined")
	{
		trace("mc2D calcTopDown error: incorrect work-function");
		return;
	}
	if( this.root==null && typeof(startAtom)=="undefined")
	{
		return;
	}
	this.calcFunction = acalcFunction;
	if( typeof(startAtom)!="undefined")
		this.wfCalcTopDown( startAtom);
	else
		this.wfCalcTopDown( this.root);
};
CMolecule.prototype.wfCalcTopDown=function(atom)
{
	if( atom==null || typeof(atom)=="undefined")
	{
		return;
	}
	this.calcFunction(atom);
	var cc = atom.children.length;
	for( var i=0 ; i<cc; i++)
	{
		this.wfCalcTopDown(atom.children[i]);
	}
};
// Функция обсчета сверху вниз
CMolecule.prototype.calcTopDownMarked=function(acalcFunction, startAtom)
{
	if( acalcFunction==null || typeof(acalcFunction)=="undefined")
	{
		trace("mc2D calcTopDown error: incorrect work-function");
		return;
	}
	if( this.root == null)
	{
		return;
	}
	this.calcFunction = acalcFunction;
	if( typeof(startAtom)!="undefined")
		this.wfCalcTopDownMarked( startAtom);
	else
		this.wfCalcTopDownMarked( this.root);
};
CMolecule.prototype.wfCalcTopDownMarked=function(atom)
{
	if( atom==null || typeof(atom)=="undefined")
	{
		return;
	}
	if( atom.isMarked)
	{
		//trace("Atom is marked");
		this.calcFunction(atom);
	}
	else
	{
		//trace("Atom is not marked");
	}
	var cc = atom.children.length;
	for( var i=0 ; i<cc; i++)
	{
		this.wfCalcTopDownMarked(atom.children[i]);
	}
};
// Функция обсчета снизу вверх
CMolecule.prototype.calcBottomUp=function(acalcFunction, startAtom)
{
	if( acalcFunction==null || typeof(acalcFunction)=="undefined")
	{
		trace("mc2D calcBottomUp error: incorrect work-function");
		return;
	}
	if( this.root == null)
	{
		return;
	}
	this.calcFunction = acalcFunction;
	if( typeof(startAtom)!="undefined")
		this.wfCalcBottomUp( startAtom);
	else
		this.wfCalcBottomUp( this.root);
};
CMolecule.prototype.wfCalcBottomUp=function(atom)
{
	if( atom==null || typeof(atom)=="undefined")
	{
		return;
	}
	var cc = atom.children.length;
	for( var i=0 ; i<cc; i++)
	{
		this.wfCalcBottomUp(atom.children[i]);
	}
	this.calcFunction(atom);
};
// Функция обсчета сверху вниз с ограничением
CMolecule.prototype.calcTopDownWithStop=function(acalcFunction)
{
	if( acalcFunction==null || typeof(acalcFunction)=="undefined")
	{
		trace("mc2D calcTopDownWithStop error: incorrect work-function");
		return;
	}
	if( this.root == null)
	{
		return;
	}
	this.calcFunction = acalcFunction;
	this.wfCalcTopDownWithStop( this.root);
};
CMolecule.prototype.wfCalcTopDownWithStop=function(atom)
{
	//trace("atom="+atom.type);
	//trace("stopNode="+this.stopNode.type);
	if( atom==null || typeof(atom)=="undefined" || atom==this.stopNode)
	{
		return;
	}
	this.calcFunction(atom);
	var cc = atom.children.length;
	for( var i=0 ; i<cc; i++)
	{
		this.wfCalcTopDownWithStop(atom.children[i]);
	}
};
// Определение текущей длины горизонтальной цепочки
CMolecule.prototype.getHChainLength=function()
{
	var	length=0;
	if( this.root==null)	return;
};
// Расчет границы молекулы - в индексах ячеек рабочего поля
CMolecule.prototype.getBounds=function()
{
	if( this.root != null)
	{
		this.bounds.left = 10000;
		this.bounds.right = -10000;
		this.bounds.top = 10000;
		this.bounds.bottom = -10000;
		this.calcTopDown( this.wfCalculateBounds);
	}
	else
        {
		this.bounds.left = 0;
		this.bounds.right = 0;
		this.bounds.top = 0;
		this.bounds.bottom = 0;
        }
	return this.bounds;
};
// Расчет границы молекулы - в индексах ячеек рабочего поля
CMolecule.prototype.getMarkedBounds=function()
{
	if( this.root != null)
	{
		this.bounds.left = 10000;
		this.bounds.right = -10000;
		this.bounds.top = 10000;
		this.bounds.bottom = -10000;
		this.calcTopDownMarked( this.wfCalculateBounds);
	}
	else
        {
		this.bounds.left = 0;
		this.bounds.right = 0;
		this.bounds.top = 0;
		this.bounds.bottom = 0;
        }
	return this.bounds;
};
CMolecule.prototype.wfCalculateBounds=function(atom)
{
	var atomBounds = atom.getBounds();
//trace("atomBounds="+atomBounds.left+":"+atomBounds.top+":"+atomBounds.right+":"+atomBounds.bottom);
	if( this.bounds.left>atomBounds.left)		this.bounds.left=atomBounds.left;
	if( this.bounds.top>atomBounds.top)			this.bounds.top=atomBounds.top;
	if( this.bounds.right<atomBounds.right)		this.bounds.right=atomBounds.right;
	if( this.bounds.bottom<atomBounds.bottom)	this.bounds.bottom=atomBounds.bottom;
};

// Расчет границы молекулы - в пикселях
CMolecule.prototype.getScreenBounds=function()
{
	var bnds = this.getBounds();
//trace("************************************************************");
//trace(bnds.left+":"+bnds.top+","+bnds.right+":"+bnds.bottom);
	var screenBounds = new Object();
	var lt = this.board.getCellXY(this.x + bnds.left, this.y + bnds.top);
	var rb = this.board.getCellXY(this.x + bnds.right + 1, this.y + bnds.bottom + 1);
	var border = 4;
	screenBounds.left = lt.x-border;	screenBounds.top = lt.y-border;
	screenBounds.right = rb.x+border;	screenBounds.bottom = rb.y+border;
//trace("************************************************************");
//trace(screenBounds.left+":"+screenBounds.top+","+screenBounds.right+":"+screenBounds.bottom);
	return screenBounds;
};

// Расчет границы молекулы - в пикселях
CMolecule.prototype.getMarkedScreenBounds=function()
{
	var bnds = this.getMarkedBounds();
	var screenBounds = new Object();
	var lt = this.board.getCellXY(this.x + bnds.left, this.y + bnds.top);
	var rb = this.board.getCellXY(this.x + bnds.right + 0.5, this.y + bnds.bottom + 0.5);
	var border = 4;
	screenBounds.left = lt.x-border;	screenBounds.top = lt.y-border;
	screenBounds.right = rb.x+border;	screenBounds.bottom = rb.y+border;
	return screenBounds;
};

// Функция добавления нового элемента
CMolecule.prototype.addElement = function(atom, atomType)
{
	if( this.getAtomsCount() >= gvMaxAtomsCount)
	{
		trace("mc2D addElement error: max atoms count exceed");
		this.board.onWarning(11);
		return;
	}
	if( atom!=null && !atom.allowAddition(atomType))
	{
		trace("mc2D addElement error: !atom.allowAddition");
		return;
	}
	var desc = CAtom.prototype.getDescription(atom==null?"C" : atom.getType());
	var busyLinks = 0;

	if( atom!=null)
	{
		busyLinks = atom.getLockedLinks();
		if( busyLinks >= desc.freeLinks)
		{
			trace("mc2D addElement error: no free links["+busyLinks+","+desc.freeLinks+"]");
			this.board.onWarning(10);
			return;
		}
	}
	var newAtom = null;
	if(atomType.indexOf("?xml") != -1)
	{
		var molecule = scene.mcEditor.loadMoleculeFromString(atomType);
		if( molecule == null)
		{
			return;
		}
		newAtom = molecule.root;
		this.calcTopDown( this.wfAttach, newAtom);
	}
	else
	{
		if( CAtom.prototype.isCycleType(atomType))
			newAtom = CCycle.prototype.create( atomType);
		else	if( CAtom.prototype.isSpecialCycleType(atomType))
			newAtom = CSpecCycle.prototype.create( atomType);
		else
			newAtom = CAtom.prototype.create( atomType);
	}

	newAtom.molecule = this;
	if( atom != null)
	{
		atom.addChild(newAtom);
	}
	else
	{
		this.root = newAtom;
	}
	this.wfCalculateDistances(newAtom);
	this.calculateLocations(newAtom);
	//this.wfCreate(newAtom);
	this.calcTopDown( this.wfCreate, newAtom);
	if( atom!=null)
		this.wfCreate(atom);
	this.onStructureChanged( atom);
//this.print();
}

// Функция вызывает оптимизацию и графическую визуализацию молекулы
CMolecule.prototype.onStructureChanged = function(atom)
{
	var flag = this.optimizationDelay;
	if( typeof(atom)!="undefined" && atom != null && atom.isOnMainLine && atom.children.length<=2)
		this.optimizationDelay = false;
	this.calculateDistances();
//trace("**********CMolecule.prototype.onStructureChanged***********:" + this.optimizationDelay);
	this.optimize();
	if( this.optimizationDelay)
	{
		this.board.delayOptimization();
	}
	else
	{
		this.visualize();
	}
	this.board.onMoleculeChanged();
	this.optimizationDelay = flag;
}

// Функция удаляет наименьшее поддерево атома
CMolecule.prototype.deleteSmallestSubtree = function(atom)
{
	var biggestRoot = this.getBiggestSubtreeRoot( atom);
	if( biggestRoot == null)
	{
		if( atom.parent == null)
		{
			this.calcBottomUp( this.fwDeleteSubtree);
			this.root = null;
		}
	}
	else
	{
		if( biggestRoot == atom.parent)
		{
			atom.parent.removeChild( atom);
			this.calcBottomUp( this.fwDeleteSubtree, atom);
		}
		else
		{
			atom.removeChild( biggestRoot);
			this.calcBottomUp( this.fwDeleteSubtree);
			this.root = biggestRoot;
		}
	}
	this.optimize();
	this.visualize();
	this.board.onMoleculeChanged();
}
// Функция удаляет наименьшее поддерево атома
CMolecule.prototype.fwDeleteSubtree = function(atom)
{
	atom.destroy();
}
// Функция назначает стили для названий атомов
CMolecule.prototype.setStyles = function()
{
	this.calcTopDown(this.wfSetStyles);
}
CMolecule.prototype.wfSetStyles = function(atom)
{
	var stl = this.board.stlNormal;
	if( (atom.drawType & 16) == 16)
	{
		//trace("Atom is bold");
		stl = this.board.stlBold;
	}
	else	if( (atom.drawType & 64) == 64)
	{
		//trace("Atom is grayed");
		stl = this.board.stlGrayed;
	}
	else	if( (atom.drawType & 32) == 32)
	{
		//trace("Atom is highlighted");
		stl = this.board.stlHighlighted;
	}
	else
	{
		if( atom.drawType == 2)
		{
			//trace("Atom is on main line");
			stl = this.board.stlMainLine;
		}
	}
	atom.setTextStyle( stl);
}
CMolecule.prototype.setDefaultStyles = function()
{
	this.temp = 16;	this.calcTopDown( this.wfRemoveDrawStyle);
	this.temp = 32;	this.calcTopDown( this.wfRemoveDrawStyle);
	this.temp = 64;	this.calcTopDown( this.wfRemoveDrawStyle);
	this.setStyles();
}
// Функция возвращает свое XML представление
/*<molecule>
	<atom type="C">
		<atom type="C" link="double" drawlength="true">
			<atom type="H"/>
			<atom type="H"/>
		</atom>
		<atom type="C" drawlength="true">
			<atom type="H"/>
			<atom type="H"/>
			<atom type="H"/>
		</atom>
		<atom type="H"/>
	</atom>
</molecule>*/
CMolecule.prototype.getNoHXML = function(isStrip)
{
	var showH = this.isShowH;
	this.isShowH = false;
	var ret = this.getXML(isStrip);
	this.isShowH = showH;
	return ret;
}
// Возвращает строку для загрузки в 3D. Атомы типа OH - представляются двумя атомами
CMolecule.prototype.getXML = function( isStrip)
{
	if( typeof(isStrip) == "undefined")
		this.stripAtoms = true;
	else
		this.stripAtoms = isStrip;

	this.sXML="<molecule "
	if( this.name != "")
	{
		this.sXML+="name=\""+this.name+"\"";
	}
	this.sXML+=">\n";
	if( this.root != null)
		this.wfGetXML( this.root);
	this.sXML+="</molecule>\n";
	return this.sXML;
}
// Возвращает строку для записи в файл. Атомы типа OH - представляются одним атомом
CMolecule.prototype.getIsomerXML = function()
{
trace("#0000FF BEGIN STRIP ATOMS");
	this.sXML="<?xml version=\"1.0\" encoding=\"Win-1251\"?><isomer>\n";
	this.sXML+=this.getNoHXML( false);
	this.sXML+="</isomer>";
trace("#0000FF " + this.sXML);
	return this.sXML;
}
//
CMolecule.prototype.wfGetXML = function(atom)
{
trace("#00FF00 wfGetXML for "+atom.type);
trace("#00FF00 this.stripAtoms="+this.stripAtoms);
	var type = atom.getType();
	type = type.replace(/(<.?sub>)/g,"");

	if( this.stripAtoms && (type == "OH" || type == "HO"))
	{
		trace("#FF0000 1");
		this.sXML+="<atom type=\"O\"><atom type=\"H\"/>";
	}
	else	if( this.stripAtoms && type == "NO2")
	{
		trace("#FF0000 2");
		this.sXML+="<atom type=\"N\"><atom type=\"O\"/><atom type=\"O\"/>";
	}
	else	if( this.stripAtoms && type == "NH2")
	{
		trace("#FF0000 3");
		this.sXML+="<atom type=\"N\"><atom type=\"H\"/><atom type=\"H\"/>";
	}
	else	if( this.stripAtoms && type == "CH2OH")
	{
		trace("#FF0000 4");
		this.sXML+="<atom type=\"C\"><atom type=\"H\"/><atom type=\"H\"/><atom type=\"O\"><atom type=\"H\"/></atom>";
	}
	else
	{
		trace("#FF0000 5");
		if( atom instanceof CCycleAtom || atom instanceof CSpecCycleAtom)
			this.sXML+="<atom index=\""+atom.cycleIndex+"\" type=\""+type+"\"";
		else
			this.sXML+="<atom type=\""+type+"\"";
		if( atom.linkType == 2)
			this.sXML+=" link=\"double\"";
		else	if( atom.linkType == 3)
			this.sXML+=" link=\"troub\"";
		if( atom.isFlipped)
			this.sXML+=" flipped=\"true\"";
		this.sXML+=">\n";
	}

	if( atom instanceof CCycle || atom instanceof CSpecCycle)
	{
		for(var i=0;i<atom.cycleChildren.length;i++)
		{
			this.wfGetXML(atom.cycleChildren[i]);
		}
	}
	else	if( atom instanceof CCycleAtom || atom instanceof CSpecCycleAtom)
	{
		for(var i=0;i<atom.children.length;i++)
		{
			var child = atom.children[i];
			if( (child instanceof CCycleAtom || atom instanceof CSpecCycleAtom) && child.myCycle==atom.myCycle)
				continue;
			this.wfGetXML(atom.children[i]);
		}
	}
	else
	{
		for(var i=0;i<atom.children.length;i++)
		{
			this.wfGetXML(atom.children[i]);
		}
	}
	if( this.isShowH &&
		!(atom instanceof CCycle) &&
		!(atom instanceof CSpecCycle) &&
		!(atom instanceof CCycleAtom && atom.isTail))
	{
		var desc = CAtom.prototype.getDescription(atom.getType());
		if( desc != null)
		{
			var busyLinks = atom.getLockedLinks();
			while( busyLinks < desc.freeLinks)
			{
				this.sXML+="<atom type=\"H\"/>\n";
				busyLinks++;
			}
		}
	}
	this.sXML+="</atom>\n";
}
// Очищает ресурсы, занимаемые молекулой
CMolecule.prototype.destroy = function()
{
	if( this.root != null)
		this.calcBottomUp( this.fwDeleteSubtree);
	this.root = null;
}
// Копирует данные из другой молекулы
CMolecule.prototype.copyFrom = function( amolecule)
{
	this.destroy();
	amolecule.copyTo( this);
}
// Копирует данные в другую молекулу
CMolecule.prototype.copyTo = function( amolecule)
{
	this.destMolecule = amolecule;
	this.calcTopDown( this.wfCopyTo);
}
CMolecule.prototype.wfCopyTo = function( atom)
{
trace("wfCopyTo="+atom.type+":"+atom.parent);
	var newAtom = null;
	if( CAtom.prototype.isCycleType(atom.type))
		newAtom = new CCycle();
	else	if( CAtom.prototype.isSpecialCycleType(atom.type))
		newAtom = new CSpecCycle();
	else
		newAtom = new CAtom();
	newAtom.copyFrom( atom);
	atom.createdCopy = newAtom;
	newAtom.molecule = this.destMolecule;

	if( atom == this.root)
	{
		this.destMolecule.root = newAtom;
	}
	else
	{
trace("atom.parent = "+atom.parent.type);
		var parent = atom.parent.createdCopy;
		parent.addChild( newAtom);
		newAtom.linkType = atom.linkType;
	}
}
//
CMolecule.prototype.toString = function()
{
	this.countC = 0;
	this.countN = 0;
	this.countO = 0;
	this.countH = 0;

	this.calcTopDown( this.wfCalculateCNOH);
	var value = CMolecule.prototype.getText("C", this.countC);
	value += CMolecule.prototype.getText("N", this.countN);
	value += CMolecule.prototype.getText("O", this.countO);
	value += CMolecule.prototype.getText("H", this.countH);
	return value;
}

CMolecule.prototype.wfCalculateCNOH = function(atom)
{
	this.countC += atom.getElementCount("C");
	this.countN += atom.getElementCount("N");
	this.countO += atom.getElementCount("O");
	this.countH += atom.getElementCount("H");
}