Source: chemdoc/kekule.commonChemMarkers.js

/**
 * @fileoverview
 * Implementation of some concrete property markers (e.g. charge mark, lone pair, etc.) binding to the chem object.
 * @author Partridge Jiang
 */

/*
 * requires /lan/classes.js
 * requires /core/kekule.common.js
 * requires /core/kekule.structures.js
 * requires /chemdoc/kekule.attachedMarkers.js
 * requires /chemdoc/kekule.contentBlocks.js
 */

(function(){

"use strict";

/**
 * Base class of chem marker representing chemical concepts (e.g. charge, lone pair...) of parent chem object.
 * @class
 * @augments Kekule.ChemObject
 * @param {String} id Id of this marker.
 * @param {Hash} coord2D The 2D coordinates of marker, {x, y}, can be null.
 * @param {Hash} coord3D The 3D coordinates of marker, {x, y, z}, can be null.
 *
 * @property {Variant} value Value of the property value of parent chem object.
 */
Kekule.ChemMarker.BaseMarker = Class.create(Kekule.ChemObject,
/** @lends Kekule.ChemMarker.BaseMarker# */
{
	/** @private */
	CLASS_NAME: 'Kekule.ChemMarker.BaseMarker',
	/**
	 * @constructs
	 */
	initialize: function init($super, id, coord2D, coord3D)
	{
		$super(id, coord2D, coord3D);
	},
	/** @ignore */
	getAutoIdPrefix: function()
	{
		return 'marker';
	}
});
Kekule.ClassDefineUtils.addStandardCoordSupport(Kekule.ChemMarker.BaseMarker);

/**
 * Represent a lone pair marker in rendering parent chem object.
 * @class
 * @augments ChemMarker.BaseMarker
 */
Kekule.ChemMarker.UnbondedElectronSet = Class.create(Kekule.ChemMarker.BaseMarker,
/** @lends Kekule.ChemMarker.UnbondedElectronSet# */
{
	/** @private */
	CLASS_NAME: 'Kekule.ChemMarker.UnbondedElectronSet',
	/** @private */
	initProperties: function()
	{
		this.defineProp('electronCount', {'dataType': DataType.VARIANT});
	},
	/** @ignore */
	initPropValues: function($super)
	{
		$super();
		this.setElectronCount(2);  // default is lone pair
	}
});

/**
 * Represent a marker represents property (e.g. charge, radical...) of parent chem object.
 * @class
 * @augments Kekule.ChemMarker.BaseMarker
 * @param {String} id Id of this node.
 * @param {Hash} coord2D The 2D coordinates of marker, {x, y}, can be null.
 * @param {Hash} coord3D The 3D coordinates of marker, {x, y, z}, can be null.
 *
 * @property {Variant} value Value of the property value of parent chem object.
 */
Kekule.ChemMarker.ChemPropertyMarker = Class.create(Kekule.ChemMarker.BaseMarker,
/** @lends Kekule.ChemMarker.ChemPropertyMarker# */
{
	/** @private */
	CLASS_NAME: 'Kekule.ChemMarker.ChemPropertyMarker',
	/** @private */
	initProperties: function()
	{
		this.defineProp('value', {'dataType': DataType.VARIANT, 'serializable': false,
			'getter': function()
			{
				var result;
				var p = this.getParent();
				var propName = this.getPropertyName();
				if (p && propName)
				{
					result = p.getPropValue(propName);
					this.setPropStoreFieldValue('value', result);  // update cached value when possible
				}
				else
				{
					result = this.getPropStoreFieldValue('value');
				}
				return result;
			},
			'setter': function(value)
			{
				this.setPropStoreFieldValue('value', value);
				this.setParentPropValue(value);
			}
		});
	},
	/** @ignore */
	initPropValues: function($super)
	{
		$super();
		/*
		this.setNeedRecalcSize(true);
		this.setSize2D({'x': 0, 'y': 0});
		*/
	},
	/** @private */
	setParentPropValue: function(value)
	{
		var p = this.getParent();
		if (p)
		{
			var propName = this.getPropertyName();
			if (propName && p)
				p.setPropValue(propName, value);
		}
		return this;
	},
	/** @private */
	resetParentPropValue: function()
	{
		return this.setParentPropValue(undefined);
	},
	/* @ignore */
	/*
	doObjectChange: function($super, modifiedPropNames)
	{
		// when text block changed, size may need to be recalculated
		if (Kekule.ArrayUtils.intersect(['text', 'renderOptions'], modifiedPropNames).length)
			this.setNeedRecalcSize(true);
	},
	*/
	/**
	 * Returns the mapped property name of parent chem object.
	 * Descendants should override this method.
	 * @returns {Variant}
	 */
	getPropertyName: function()
	{
		return null;
	},

	// editor related methods
	/** @ignore */
	beforAddingByEditor: function(newParent, refSibling)
	{
		if (newParent) // when adding to new parent, update value of parent
		{
			var value = this.getPropStoreFieldValue('value');
			if (Kekule.ObjUtils.notUnset(value))
				this.setParentPropValue(value);  // update to parent
		}
		$super();
	},
	/** @ignore */
	beforeRemovingByEditor: function(parent)
	{
		// when removing from editor, usually we should reset the value of property
		// ensure cached the property value
		this.setPropStoreFieldValue('value', this.getValue());
		this.resetParentPropValue();
	}
});

/**
 * Represent a charge marker (+/-) in rendering parent chem object.
 * @class
 * @augments Kekule.ChemMarker.ChemPropertyMarker
 * @param {String} id Id of this node.
 * @param {Hash} coord2D The 2D coordinates of marker, {x, y}, can be null.
 * @param {Hash} coord3D The 3D coordinates of marker, {x, y, z}, can be null.
 */
Kekule.ChemMarker.Charge = Class.create(Kekule.ChemMarker.ChemPropertyMarker,
/** @lends Kekule.ChemMarker.Charge# */
{
	/** @private */
	CLASS_NAME: 'Kekule.ChemMarker.Charge',
	/** @ignore */
	getPropertyName: function()
	{
		return 'charge';
	}
});

/**
 * Represent a radical marker in rendering parent chem object.
 * @class
 * @augments Kekule.ChemMarker.ChemPropertyMarker
 * @param {String} id Id of this node.
 * @param {Hash} coord2D The 2D coordinates of marker, {x, y}, can be null.
 * @param {Hash} coord3D The 3D coordinates of marker, {x, y, z}, can be null.
 */
Kekule.ChemMarker.Radical = Class.create(Kekule.ChemMarker.ChemPropertyMarker,
/** @lends Kekule.ChemMarker.Radical# */
{
	/** @private */
	CLASS_NAME: 'Kekule.ChemMarker.Radical',
	/** @ignore */
	getPropertyName: function()
	{
		return 'radical';
	}
});


ClassEx.extend(Kekule.ChemStructureNode,
/** @lends Kekule.ChemStructureNode# */
{
	/**
	 * Get charge marker of node. If no such a marker currently and canCreate is true, a new marker will be created.
	 * @returns {Kekule.ChemMarker.ChemPropertyMarker}
	 */
	fetchChargeMarker: function(canCreate, defProps)
	{
		return this.fetchMarkerOfType(Kekule.ChemMarker.Charge, canCreate, false, defProps);
	},
	/**
	 * Get radical marker of node. If no such a marker currently and canCreate is true, a new marker will be created.
	 * @returns {Kekule.ChemMarker.ChemPropertyMarker}
	 */
	fetchRadicalMarker: function(canCreate, defProps)
	{
		return this.fetchMarkerOfType(Kekule.ChemMarker.Radical, canCreate, false, defProps);
	}
});

ClassEx.defineProp(Kekule.AbstractAtom, 'lonePairCount', {
	'dataType': DataType.INT, 'scope': Class.PropertyScope.PUBLISHED, 'serializable': false,
	'setter': null,  // currently disable setter
	'getter': function()
	{
		var result = 0;
		var unbondedESets = this.getMarkersOfType(Kekule.ChemMarker.UnbondedElectronSet);
		for (var i = 0, l = unbondedESets.length; i < l; ++i)
		{
			var eSet = unbondedESets[i];
			if (eSet.getElectronCount() === 2)  // a lone pair
				++result;
		}
		return result;
	}
});
ClassEx.extendMethod(Kekule.AbstractAtom, 'doCompare', function($origin, targetObj, options)
	{
		var result = $origin(targetObj, options);
		if (!result && options.method === Kekule.ComparisonMethod.CHEM_STRUCTURE)  // can not find different in origin method
		{
			if (this._getComparisonOptionFlagValue(options, 'lonePair') ||
					this._getComparisonOptionFlagValue(options, 'unbondedElectron'))  // parity null/0 should be regard as one in comparison
			{
				/*
				 var c1 = this.getLonePairCount() || 0;
				 var c2 = (targetObj.getLonePairCount && targetObj.getLonePairCount()) || 0;
				 result = this.doCompareOnValue(c1, c2, options);
				 */
				var _getUnbondedElectronArray = function(atom)
				{
					var result = [];
					if (atom.getMarkersOfType)
					{
						var unbondedESets = atom.getMarkersOfType(Kekule.ChemMarker.UnbondedElectronSet);
						for (var i = 0, l = unbondedESets.length; i < l; ++i)
						{
							var eSet = unbondedESets[i];
							result.push(eSet.getElectronCount())
						}
					}
					result.sort();
					return result;
				};
				var a1 = _getUnbondedElectronArray(this);
				var a2 = _getUnbondedElectronArray(targetObj);
				return Kekule.ArrayUtils.compare(a1, a2);
			}
		}
		return result;
	});

})();