kekule
Version:
Open source JavaScript toolkit for chemoinformatics
1,044 lines (1,005 loc) • 35.1 kB
JavaScript
/**
* @fileoverview
* This file contains basic classes about chemical reactions.
* @author Partridge Jiang
*/
/*
* requires /lan/classes.js
* requires /core/kekule.common.js
* requires /core/kekule.structures.js
*/
/**
* Enumeration of type of molecules or conditions in reaction.
* @class
*/
Kekule.ChemReactionComponent = {
REACTANT: 'reactant',
PRODUCT: 'product',
CATALYST: 'catalyst',
SOLVENT: 'solvent',
REAGENT: 'reagent',
};
/**
* Enumeration of reaction direction.
*/
Kekule.ChemReactionDirection = {
/** Reaction equilibrium which is (almost) fully on the product side. Often denoted with a forward arrow. */
FORWARD: 1,
/** Reaction equilibrium state. Often denoted by a double arrow. */
BIDIRECTION: 0,
/** Actually not a real reaction, just mark two resonances. */
RESONANCE: 99
};
/**
* Enumeration of reaction condition component.
* @enum
*/
Kekule.ReactionConditionComponent = {
TEMPERATURE: 'temperature',
PRESSURE: 'pressure',
WAVELENGTH: 'wavelength',
TIME: 'time',
VOLUME: 'volume',
UNKNOWN: 'unknown'
// OTHER: 'other'
}
/**
* Enumeration of reaction qualitative conditions.
* @enum
*/
Kekule.ReactionQualitativeCondition = {
HEAT: 'heat',
HIGH_TEMP: 'hitemp',
LOW_TEMP: 'lowtemp',
PRESSURE: 'pressure',
LIGHT: 'light',
MICROWAVE: 'microwave'
};
/**
* Represent an omitted intermediate product of two continuous reactions.
* E.g. CH3COOEt (NaOEt) -> * -> (H+) CH3COCH2COOEt, where the salt of ethyl acetoacetate is omitted in the reaction.
* @type {Class}
*/
Kekule.ImplicitReactionIntermediate = Class.create(Kekule.Molecule,
/** @lends Kekule.ImplicitReactionIntermediate# */
{
/** @private */
CLASS_NAME: 'Kekule.ImplicitReactionIntermediate',
/** @private */
initialize: function(id)
{
this.tryApplySuper('initialize', [id, '*']);
},
/** @private */
getAutoIdPrefix: function()
{
return 'im';
}
});
/**
* A chemical reaction. Including information on reactants, products, reagents and conditions.
* @class
* @augments Kekule.ChemObject
*
* @property {String} name Name of reaction.
* @property {String} title Title of reaction.
* @property {String} reactionType Type of reaction.
* @property {Float} yield Yield of reaction. 0 <= Value <= 1.
* @property {Kekule.Molecule[]} reactants Reactants of reaction.
* @property {Kekule.Molecule[]} products Products of reaction.
* @property {Kekule.Molecule[]} catalysts Catalysts of reaction.
* @property {Kekule.Molecule[]} reagents Other reagents (e.g. solvent) of reaction.
* @property {Kekule.Molecule[]} inputs Inputted molecules in reaction, including reactants, reagents, catalysts and solvents.
* @property {Kekule.Molecule[]} outputs Outputted molecules in reaction, alias of products.
*
*/
Kekule.ChemReaction = Class.create(Kekule.ChemObject,
/** @lends Kekule.ChemReaction# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemReaction',
/** @private */
initialize: function(id)
{
this.tryApplySuper('initialize', [id]);
this._initSubstanceListsManipulationMethods();
},
doFinalize: function(/*$super*/)
{
var lists = this._getAllSubstanceLists();
for (var i = 0, l = lists.length; i < l; ++i)
{
var list = lists[i];
if (list)
list.finalize();
}
this.tryApplySuper('doFinalize');
},
/** @private */
initProperties: function()
{
this.defineProp('name', {'dataType': DataType.STRING});
this.defineProp('title', {'dataType': DataType.STRING});
this.defineProp('reactionType', {'dataType': DataType.STRING});
this.defineProp('yield', {'dataType': DataType.FLOAT});
this.defineProp('direction', {
'dataType': DataType.INT, 'defaultValue': Kekule.ChemReactionDirection.FORWARD,
'enumSource': Kekule.ChemReactionDirection
});
this.defineProp('reactants', {
'dataType': DataType.ARRAY,
'serializable': false,
'setter': null,
'getter': function()
{
return this.getSubstancesOfType(Kekule.ChemReactionComponent.REACTANT);
}
});
this.defineProp('products', {
'dataType': DataType.ARRAY,
'serializable': false,
'setter': null,
'getter': function()
{
return this.getSubstancesOfType(Kekule.ChemReactionComponent.PRODUCT);
}
});
this.defineProp('catalysts', {
'dataType': DataType.ARRAY,
'serializable': false,
'setter': null,
'getter': function()
{
return this.getSubstancesOfType(Kekule.ChemReactionComponent.CATALYST);
}
});
this.defineProp('solvents', {
'dataType': DataType.ARRAY,
'serializable': false,
'setter': null,
'getter': function()
{
return this.getSubstancesOfType(Kekule.ChemReactionComponent.SOLVENT);
}
});
this.defineProp('reagents', {
'dataType': DataType.ARRAY,
'serializable': false,
'setter': null,
'getter': function()
{
return this.getSubstancesOfType(Kekule.ChemReactionComponent.REAGENT);
}
});
this.defineProp('outputs', {
'dataType': DataType.ARRAY,
'serializable': false,
'setter': null,
'getter': function()
{
return this.getProducts();
}
});
this.defineProp('inputs', {
'dataType': DataType.ARRAY,
'serializable': false,
'setter': null,
'getter': function()
{
var result = [].concat(this.getReactants() || []).concat(this.getReagents() || []).concat(this.getCatalysts() || []).concat(this.getSolvents() || []);
return result;
}
});
// reaction related molecules, including reactants, products, catalysts and so on
this.defineProp('substances', {
'dataType': DataType.HASH,
'scope': Class.PropertyScope.PRIVATE,
'setter': null,
'getter': function(canCreate)
{
var r = this.getPropStoreFieldValue('substances');
if (!r && canCreate)
{
r = {};
this.setPropStoreFieldValue('substances', r);
}
return r;
}
});
// conditions map of reaction
this.defineProp('conditions', {
'dataType': DataType.HASH,
'setter': null,
'getter': function(canCreate)
{
var r = this.getPropStoreFieldValue('conditions');
if (!r && canCreate)
{
r = {};
this.setPropStoreFieldValue('conditions', r);
}
return r;
}
});
},
/** @ignore */
initPropValues: function()
{
this.tryApplySuper('initPropValues');
this.setPropStoreFieldValue('direction', Kekule.ChemReactionDirection.FORWARD);
},
/** @ignore */
ownerChanged: function(newOwner, oldOwner)
{
var lists = this._getAllSubstanceLists();
for (var i = 0, l = lists.length; i < l; ++i)
lists[i].setOwner(newOwner);
this.tryApplySuper('ownerChanged', [newOwner, oldOwner]);
},
/** @private */
_initSubstanceListsManipulationMethods()
{
var names = this.getDefaultSubstanceGroupNames();
for (var i = 0, l = names.length; i < l; ++i)
{
var name = names[i];
var nameUpper = name.upperFirst();
this['get' + nameUpper + 'Count'] = this.getSubstanceCount.bind(this, name);
this['get' + nameUpper + 'At'] = this.getSubstanceAt.bind(this, name);
this['indexOf' + nameUpper] = this.indexOfSubstance.bind(this, name);
this['remove' + nameUpper] = this.removeSubstance.bind(this, name);
this['remove' + nameUpper + 'At'] = this.removeSubstanceAt.bind(this, name);
this['append' + nameUpper] = this.appendSubstance.bind(this, name);
this['insert' + nameUpper + 'Before'] = this.insertSubstanceBefore.bind(this, name);
this['insert' + nameUpper + 'At'] = this.insertSubstanceAt.bind(this, name);
}
},
/** @private */
_getAllSubstanceLists: function()
{
var result = [];
var names = this.getChildSubgroupNames();
for (var i = 0, l = names.length; i < l; ++i) {
var list = this._doGetSubstanceListOfType(names[i], false);
if (list)
result.push(list);
}
return result;
},
/** @ignore */
getDefaultSubstanceGroupNames: function()
{
var CC = Kekule.ChemReactionComponent;
return [CC.REACTANT, CC.PRODUCT, CC.CATALYST, CC.SOLVENT, CC.REAGENT];
},
/** @ignore */
getChildSubgroupNames: function()
{
/*
var result = this.__$childSubgroupNames$__;
var compKeys = Object.getOwnPropertyNames(Kekule.ChemReactionComponent);
for (var i = 0, l = compKeys.length; i < l; ++i)
{
result.push(Kekule.ChemReactionComponent[compKeys[i]]);
}
return result;
*/
// return ['reactant', 'product', 'catalyst', 'solvent', 'reagent'];
return Object.getOwnPropertyNames(this.getSubstances() || {});
},
/** @ignore */
getBelongChildSubGroupName: function(obj)
{
// default insert child to the reactant group
return Kekule.ChemReactionComponent.REACTANT;
},
/** @private */
assertAddingMolecule: function(obj)
{
if (!(obj instanceof Kekule.Molecule))
{
Kekule.chemError(Kekule.$L('ErrorMsg.UNABLE_ADD_NONMOLECULE_AS_REACTION_SUBSTANCE'));
}
},
/**
* Notify the substances property has been changed.
* @private
*/
notifySubstanceChanged: function(substanceType)
{
this.notifyPropSet('substances', this.getPropStoreFieldValue('substances'));
var propName = Kekule.ChemReactionComponent[substanceType] + 's';
this.notifyPropSet(propName, this.getSubstancesOfType(substanceType));
},
/** @private */
_doSetSubstanceParentAndOwner: function(molecule, parent, owner)
{
molecule.setParent(parent);
molecule.setOwner(owner);
},
/** @private */
_doGetSubstanceListOfType: function(substanceType, canCreate) {
var substances = this.getSubstances(canCreate);
var result = substances && substances[substanceType];
if (!result && canCreate)
{
result = new Kekule.ChemObjList(null, Kekule.Molecule, true);
result.setParent(this);
result.addEventListener('change', function()
{
this.objectChange(['substances', substanceType + 's']);
this.notifySubstanceChanged(substanceType);
}, this);
substances[substanceType] = result;
}
return result;
},
/**
* Returns substances of specified type.
* @param {String} substanceType
* @returns {Kekule.ChemObjList}
*/
getSubstanceListOfType: function(substanceType)
{
return this._doGetSubstanceListOfType(substanceType, false);
},
/**
* Returns substances list of specified type.
* @param {String} substanceType
* @returns {Kekule.Molecule[]}
*/
getSubstancesOfType: function(substanceType)
{
var list = this._doGetSubstanceListOfType(substanceType, false);
return list? list.getItems(): [];
},
/**
* Returns all substance types in this reaction.
* @returns {String[]}
*/
getSubstanceTypes: function()
{
var substances = this.getSubstances(false);
return substances? Object.getOwnPropertyNames(substances): [];
},
/**
* Returns count of substances of specified type.
* If substanceType is not set, substance countof all types will be returned.
* @param {String} substanceType
* @returns {Int}
*/
getSubstanceCount: function(substanceType) {
var types = (!substanceType)? this.getSubstanceTypes(): [substanceType];
var result = 0;
for (var i = 0, l = types.length; i < l; ++i)
{
var subType = types[i];
var substances = this.getSubstanceListOfType(subType);
result += substances? substances.getItemCount(): 0;
}
return result;
},
indexOfSubstance(substanceType, molecule)
{
var substances = this.getSubstanceListOfType(substanceType);
if(substances)
{
return substances.indexOf(molecule);
}
else
return -1;
},
getSubstanceAt(substanceType, index)
{
var substances = this.getSubstanceListOfType(substanceType);
if(substances)
{
return substances.getItemAt(index);
}
else
return null;
},
/**
* Append a molecule to the substance list of reaction.
* @param {String} substanceType
* @param {Kekule.Molecule} molecule
*/
appendSubstance(substanceType, molecule)
{
this.assertAddingMolecule(molecule);
var substances = this._doGetSubstanceListOfType(substanceType, true);
substances.append(molecule);
return this;
},
/**
* Insert a molecule to the substance list at index.
* @param {String} substanceType
* @param {Kekule.Molecule} molecule
* @param {Int} index
*/
insertSubstanceAt(substanceType, molecule, index)
{
this.assertAddingMolecule(molecule);
var substances = this._doGetSubstanceListOfType(substanceType, true);
substances.insertItemAt(molecule, index);
return this;
},
/**
* Insert a molecule before refMolecule in the substance list.
* @param {String} substanceType
* @param {Kekule.Molecule} molecule
* @param {Kekule.Molecule} refMolecule
*/
insertSubstanceBefore(substanceType, molecule, refMolecule)
{
this.assertAddingMolecule(molecule);
var substances = this._doGetSubstanceListOfType(substanceType, true);
substances.insertItemBefore(molecule, refMolecule);
return this;
},
/**
* Remove a molecule from the substance list at index.
* @param {String} substanceType
* @param {int} index
*/
removeSubstanceAt(substanceType, index)
{
var substances = this.getSubstanceListOfType(substanceType);
if (substances)
{
substances.removeAt(index);
}
return this;
},
/**
* Remove a molecule from the substance list.
* @param {String} substanceType
* @param {Kekule.Molecule} molecule
*/
removeSubstance(substanceType, molecule)
{
var substances = this.getSubstanceListOfType(substanceType);
if (substances)
{
substances.remove(molecule);
}
return this;
},
/**
* Returns the explicitly set condition components of reaction.
* @returns {Array}
*/
getConditionComponents: function()
{
var conditions = this.getConditions(false);
return Kekule.ObjUtils.getOwnedFieldNames(conditions);
},
/**
* Get the value of a condition.
* @param {String} name Condition name.
* @returns {Variant} Usually a {Kekule.Scalar}, but other objects are also allowed.
*/
getCondition: function(name)
{
var conditions = this.getConditions(false);
return conditions && conditions[name];
},
/**
* Set the value of a condition.
* @param {String} name Condition name.
* @param {Variant} value Usually a {Kekule.Scalar}, but other objects are also allowed.
*/
setCondition: function(name, value)
{
var conditions = this.getConditions(true);
conditions[name] = value;
return this;
},
/**
* Unset a condition.
* @param {String} name Condition name.
*/
unsetCondition: function(name)
{
var conditions = this.getConditions(false);
if (conditions && conditions[name])
delete conditions[name];
return this;
},
/**
* Clear all conditions of reaction.
*/
clearConditions: function()
{
this.setPropStoreFieldValue('conditions', null);
return this;
},
setQualitativeCondition: function(qualitiveCondition)
{
var QC = Kekule.ReactionQualitativeCondition;
var CC = Kekule.ReactionConditionComponent;
if (qualitiveCondition === QC.HEAT || qualitiveCondition === QC.LOW_TEMP || qualitiveCondition === QC.HIGH_TEMP)
this.setCondition(CC.TEMPERATURE, qualitiveCondition);
else if (qualitiveCondition === QC.PRESSURE)
this.setCondition(CC.PRESSURE, qualitiveCondition);
else if (qualitiveCondition === QC.LIGHT || qualitiveCondition === QC.MICROWAVE)
this.setCondition(CC.WAVELENGTH, qualitiveCondition);
else
this.setCondition(CC.UNKNOWN, qualitiveCondition);
return this;
},
/**
* Clear all structure stereo features (e.g., bond wedge, node zIndex2D) of all molecules in reaction.
*/
clearStereo: function()
{
this.beginUpdate();
try
{
for (var i = 0, l = this.getChildCount(); i < l; ++i)
{
var obj = this.getChildAt(i);
if (obj && obj.clearStereo)
obj.clearStereo();
}
}
finally
{
this.endUpdate();
}
},
// /**
// * Returns the index of a condition object in condition list of reaction.
// * @param {Kekule.Scalar} condition
// */
// indexOfCondition: function(condition)
// {
// var conditions = this.getPropStoreFieldValue('conditions');
// if (!conditions)
// return -1;
// else
// return conditions.indexOf(condition);
// },
// /**
// * Append a condition to condition list of reaction.
// * @param {Kekule.Scalar} condition
// */
// appendCondition: function(condition)
// {
// this.getConditions().push(condition);
// return this;
// },
// /**
// * Insert a condition to condition list of reaction.
// * @param {Kekule.Scalar} condition
// * @param {Int} index
// */
// insertConditionAt: function(condition, index)
// {
// this.getConditions().splice(index, 0, condition);
// return this;
// },
// /**
// * Insert a condition to condition list of reaction.
// * @param {Kekule.Scalar} condition
// * @param {Kekule.Scalar} refCondition
// */
// insertConditionBefore: function(condition, refCondition)
// {
// if (!refCondition)
// return this.appendCondition(condition);
// else
// {
// var refIndex = this.indexOfCondition(refCondition);
// return (refIndex >= 0)? this.insertConditionAt(condition, refIndex): this.appendCondition(condition);
// }
// },
// /**
// * Remove a condition at the index of condition list.
// * @param {Int} index
// */
// removeConditionAt: function(index)
// {
// var conditions = this.getPropStoreFieldValue('conditions');
// if (conditions)
// {
// conditions.splice(index, 1);
// }
// return this;
// },
// /**
// * Remove a condition from condition list.
// * @param {Kekule.Scalar} condition
// */
// removeCondition: function(condition)
// {
// var index = this.indexOfCondition(condition);
// if (index >= 0)
// this.removeConditionAt(index);
// return this;
// },
// method for comparing two reaction
/** @private */
_getComparisonOptionFlagValue: function(options, flagName)
{
var compatibleName = 'compare' + flagName.capitalizeFirst();
var result = options[compatibleName];
if (Kekule.ObjUtils.isUnset(result))
result = options[flagName];
return result;
},
/** @private */
_compareChildArrayItems: function(arr1, arr2, options) // compare substance array regardless of order
{
var result = arr1.length - arr2.length;
if (!result)
{
var a1 = Kekule.ArrayUtils.clone(arr1);
var a2 = Kekule.ArrayUtils.clone(arr2);
var self = this;
// sort these two arrays, then begin the comparison
a1.sort(function (item1, item2) {
return Kekule.ObjComparer.compare(item1, item2, options);
});
a2.sort(function (item1, item2) {
return Kekule.ObjComparer.compare(item1, item2, options);
});
result = Kekule.ObjComparer.compare(a1, a2, options);
// console.log('compare', a1, a2, result);
}
return result;
},
/** @ignore */
doGetActualCompareOptions: function(options)
{
var result = this.tryApplySuper('doGetActualCompareOptions', [options]);
/*
// by default we will compare substances
result = Object.extend({
'compareSubstances': true
}, result);
*/
return result;
},
/** @ignore */
doGetComparisonPropNames: function(options)
{
var result = this.tryApplySuper('doGetComparisonPropNames', [options]) || [];
if (this._getComparisonOptionFlagValue(options, 'direction'))
result.push('direction');
if (this._getComparisonOptionFlagValue(options, 'yield'))
result.push('yield');
return result;
},
/** @ignore */
doCompare: function(targetObj, options)
{
var result = this.tryApplySuper('doCompare', [targetObj, options]);
// compare substances and conditions
if (!result && this._getComparisonOptionFlagValue(options, 'conditions'))
result = this._compareChildArrayItems(this.getConditions(), targetObj.getConditions(), options);
if (!result)
{
if (this._getComparisonOptionFlagValue(options, 'inputsOutputs'))
result = this._compareChildArrayItems(this.getInputs(), targetObj.getInputs(), options)
|| this._compareChildArrayItems(this.getOutputs(), targetObj.getOutputs(), options);
else if (this._getComparisonOptionFlagValue(options, 'substances') !== false)
{
var substanceNames = this.getDefaultSubstanceGroupNames();
for (var i = 0, l = substanceNames.length; i < l; ++i)
{
var name = substanceNames[i];
var doCompare = Kekule.oneOf(this._getComparisonOptionFlagValue(options, name), this._getComparisonOptionFlagValue(options, name + 's'));
if (doCompare !== false)
// result = Kekule.ObjComparer._compareValue(this.getSubstancesOfType(name), targetObj.getSubstancesOfType(name), options);
result = this._compareChildArrayItems(this.getSubstancesOfType(name), targetObj.getSubstancesOfType(name), options);
if (!!result)
break;
}
}
}
return result;
},
/**
* Explicit set compare method to chem structure and compare to target reaction.
* @param {Kekule.ChemObject} targetObj
* @param {Hash} options
* @returns {Int}
*/
compareStructure: function(targetObj, options)
{
var ops = Object.create(options || {});
ops.method = Kekule.ComparisonMethod.CHEM_STRUCTURE;
return this.compare(targetObj, ops);
},
/**
* Check if this reaction and target reaction are same with chem structure.
* @param {Kekule.ChemObject} targetObj
* @param {Hash} options
* @returns {Bool}
*/
equalStructure: function(targetObj, options)
{
return this.compareStructure(targetObj, options) === 0;
},
});
/**
* A reaction step inside a {@link Kekule.ConsecutiveReactions}.
* The multistep reaction may in form A + B -> C -> D,
* where the reactant of second embedded reaction is actually the product of the first step reaction.
* Thus, the reactant of embeddedReaction is consisted of two parts: the explicit reactant in substances list of this object,
* and the implicit reactant, actually the product of the previous step reaction.
* @class
* @augments Kekule.ChemObject
*
* @property {Kekule.Molecule[]} implicitReactants The implicit reactants of this reaction, usually the product of previous step reaction.
*/
Kekule.EmbeddedReaction = Class.create(Kekule.ChemReaction,
/** @lends Kekule.EmbeddedReaction# */
{
/** @private */
CLASS_NAME: 'Kekule.EmbeddedReaction',
/** @private */
initialize: function(id)
{
this.tryApplySuper('initialize', [id]);
this._initSubstanceListsManipulationMethods();
},
/** @private */
initProperties: function()
{
this.defineProp('implicitReactants', {
'dataType': DataType.ARRAY,
'serializable': false,
'setter': null,
'getter': function()
{
var prev = this.getPrevSibling();
return (prev && prev.getProducts && prev.getProducts()) || [];
}
});
this.defineProp('explicitReactants', {
'dataType': DataType.ARRAY,
'serializable': false,
'setter': null,
'getter': function()
{
return this.getSubstancesOfType('explicitReactant');
}
});
},
/** @ignore */
getDefaultSubstanceGroupNames: function()
{
// replace the 'reactant' subgroup to 'explicitReactant'
var result = this.tryApplySuper('getDefaultSubstanceGroupNames');
var reactantIndex = result.indexOf(Kekule.ChemReactionComponent.REACTANT);
if (reactantIndex >= 0)
result.splice(reactantIndex, 1, 'explicitReactant');
return result;
},
/** @ignore */
doGetReactants()
{
var result = [];
result = result.concat(this.getImplicitReactants());
result = result.concat(this.getExplicitReactants());
return result;
},
getReactantCount()
{
return this.getImplicitReactantCount() + this.getExplicitReactantCount();
},
getReactantAt(index)
{
var implicitCount = this.getImplicitReactantCount();
if (index < implicitCount)
return this.getImplicitReactantAt(index);
else
return this.getExplicitReactantAt(index - implicitCount);
},
indexOfReactant: function(molecule) {
var result = this.indexOfImplicitReactant(molecule);
if (result < 0) {
result = this.indexOfExplicitReactant(molecule);
if (result >= 0)
result = result + this.getImplicitReactantCount();
}
return result;
},
removeReactant: function(molecule) {
var implicitCount = this.getImplicitReactantCount();
var index = this.indexOfReactant(molecule);
if (index >= 0)
return this.removeReactantAt(index);
else
return this;
},
removeReactantAt: function(index)
{
var implicitCount = this.getImplicitReactantCount();
if (index < implicitCount)
Kekule.chemError(Kekule.$L('ErrorMsg.UNABLE_TO_REMOVE_IMPLICIT_REACTANT'));
else
this.removeExplicitReactantAt(index - implicitCount);
return this;
},
appendReactant(molecule)
{
return this.appendExplicitReactant(molecule);
},
insertReactantAt(molecule, index)
{
var implicitCount = this.getImplicitReactantCount();
if (index < implicitCount)
Kekule.chemError(Kekule.$L('ErrorMsg.UNABLE_TO_INSERT_IMPLICIT_REACTANT'));
else
return this.insertExplicitReactantAt(molecule, index - implicitCount);
},
insertReactantBefore(molecule, refMolecule)
{
var refIndex = this.indexOfReactant(refMolecule);
if (refIndex >= 0)
return this.insertReactantAt(molecule, refIndex);
else
return this.appendReactant(molecule);
},
/**
* Return the count of implicit reactants.
* @returns {Int}
*/
getImplicitReactantCount()
{
var prev = this.getPrevSibling();
return (prev && prev.getProductCount && prev.getProductCount()) || 0;
},
/**
* Get the implicit product at index.
* @param {Int} index
* @returns {Kekule.Molecule}
*/
getImplicitReactantAt(index)
{
var prev = this.getPrevSibling();
return prev && prev.getProductAt && prev.getProductAt(index);
},
/**
* Returns the index of molecule in the implicit reactant list.
* @param {Kekule.Molecule} molecule
* @returns {Int}
*/
indexOfImplicitReactant(molecule)
{
var prev = this.getPrevSibling();
return (prev && prev.indexOfProduct && prev.indexOfProduct(molecule)) || -1;
},
/** @ignore */
doGetActualCompareOptions: function(options)
{
var result = this.tryApplySuper('doGetActualCompareOptions', [options]);
result = Object.extend({
'compareExplicitReactants': true
}, result);
return result;
},
});
/**
* A multistep chemical reaction, composited of a series of {@link Kekule.EmbeddedReaction}.
* The multistep reaction may in form A + B -> C -> D,
* where the reactant of second embedded reaction is actually the product of the first step reaction.
* @class
* @augments Kekule.ChemObject
*
* @property {String} name Name of reaction.
* @property {String} title Title of reaction.
* @property {Kekule.EmbeddedReaction[]} reactions Child embedded reactions.
*/
Kekule.ConsecutiveReactions = Class.create(Kekule.ChemObject,
/** @lends Kekule.ConsecutiveReactions# */
{
/** @private */
CLASS_NAME: 'Kekule.ConsecutiveReactions',
/** @private */
initialize: function(id)
{
this.tryApplySuper('initialize', [id]);
},
doFinalize()
{
this.getEmbeddedReactions().finalize();
this.tryApplySuper('finalize');
},
/** @private */
initProperties: function()
{
this.defineProp('name', {'dataType': DataType.STRING});
this.defineProp('title', {'dataType': DataType.STRING});
this.defineProp('reactions', {'dataType': DataType.ARRAY, 'setter': null, 'serializable': false,
'getter': function(){
return this.getEmbeddedReactions().getItems();
}
});
this.defineProp('embeddedReactions', {'dataType': 'Kekule.ChemObjList', 'setter': null, 'scope': Class.PropertyScope.PRIVATE});
},
initPropValues: function()
{
this.tryApplySuper('initPropValues');
var list = new Kekule.ChemObjList(null, Kekule.EmbeddedReaction, true);
list.setParent(this);
list.addEventListener('change', function()
{
this.objectChange(['embeddedReactions']);
}, this);
this.setPropStoreFieldValue('embeddedReactions', list);
},
/** @ignore */
ownerChanged: function(newOwner, oldOwner)
{
// change owners of children
this.getEmbeddedReactions().setOwner(newOwner);
this.tryApplySuper('ownerChanged', [newOwner, oldOwner]);
},
/** @ignore */
getChildHolder: function()
{
return this.getEmbeddedReactions();
},
/**
* Returns the child reaction step count.
* @returns {Int}
*/
getStepCount() {
return this.getChildCount();
},
/**
* Returns the child reaction step at index.
* @param {Int} index
* @returns {Kekule.EmbeddedReaction}
*/
getStepAt(index) {
return this.getChildAt(index);
},
/**
* Create and insert a new reaction step.
* @param {String} id
* @param {Int} index
* @returns {Kekule.EmbeddedReaction}
*/
newStep: function(id, index) {
var reactionList = this.getEmbeddedReactions();
var step = new Kekule.EmbeddedReaction(id);
if (index === undefined || index < 0)
reactionList.append(step);
else if (index >= 0) {
reactionList.insert(step, index);
}
return step;
},
/**
* Clear all structure stereo features (e.g., bond wedge, node zIndex2D) of all molecules in reaction.
*/
clearStereo: function()
{
this.beginUpdate();
try
{
for (var i = 0, l = this.getStepCount(); i < l; ++i)
{
var step = this.getStepAt(i);
if (step && step.clearStereo)
step.clearStereo();
}
}
finally
{
this.endUpdate();
}
},
/** @ignore */
doGetComparisonPropNames: function(options)
{
var result = this.tryApplySuper('doGetComparisonPropNames', [options]) || [];
result.push('reactions');
return result;
}
});