/**
* @fileoverview
* Object modifier to change property of atoms (and other similar chem nodes) in chem editor.
* @author Partridge Jiang
*/
/*
* requires /lan/classes.js
* requires /utils/kekule.utils.js
* requires /core/kekule.structures.js
* requires /widgets/kekule.widget.base.js
* requires /widgets/commonCtrls/kekule.widget.buttons.js
* requires /widgets/chem/editor/kekule.chemEditor.editorUtils.js
* requires /widgets/chem/editor/kekule.chemEditor.baseEditor.js
* requires /widgets/chem/editor/kekule.chemEditor.objModifiers.js
* requires /widgets/chem/editor/kekule.chemEditor.utilWidgets.js
*/
(function(){
"use strict";
var AU = Kekule.ArrayUtils;
var DU = Kekule.DomUtils;
var EU = Kekule.HtmlElementUtils;
var CNS = Kekule.Widget.HtmlClassNames;
var CCNS = Kekule.ChemWidget.HtmlClassNames;
var BNS = Kekule.ChemWidget.ComponentWidgetNames;
Kekule.ChemWidget.HtmlClassNames = Object.extend(Kekule.ChemWidget.HtmlClassNames, {
COMPOSER_ATOM_MODIFIER_BUTTON: 'K-Chem-Composer-AtomModifier-Button',
COMPOSER_ATOM_MODIFIER_DROPDOWN: 'K-Chem-Composer-AtomModifier-DropDown',
COMPOSER_BOND_MODIFIER_BUTTON: 'K-Chem-Composer-BondModifier-Button',
COMPOSER_BOND_MODIFIER_DROPDOWN: 'K-Chem-Composer-BondModifier-DropDown',
COMPOSER_CHARGE_MODIFIER_BUTTON: 'K-Chem-Composer-ChargeModifier-Button',
COMPOSER_CHARGE_MODIFIER_DROPDOWN: 'K-Chem-Composer-ChargeModifier-DropDown'
});
/**
* Base modifier class to change the chem structure in editor.
* @class
* @augments Kekule.Editor.ObjModifier.Base
*/
Kekule.Editor.ObjModifier.ChemStructureModifier = Class.create(Kekule.Editor.ObjModifier.Base,
/** @lends Kekule.Editor.ObjModifier.ChemStructureModifier# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.ObjModifier.ChemStructureModifier',
/** @ignore */
installValueChangeEventHandler: function(widget)
{
// do nothing, use our own value change event handlers
}
});
/** @ignore */
Kekule.Editor.ObjModifier.ChemStructureModifier.getCategories = function()
{
return [Kekule.Editor.ObjModifier.Category.CHEM_STRUCTURE];
};
/**
* A atom modifier to change the atom property of chem nodes.
* @class
* @augments Kekule.Editor.ObjModifier.ChemStructureModifier
*/
Kekule.Editor.ObjModifier.Atom = Class.create(Kekule.Editor.ObjModifier.ChemStructureModifier,
/** @lends Kekule.Editor.ObjModifier.Atom# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.ObjModifier.Atom',
/** @construct */
initialize: function($super, editor)
{
$super(editor);
this._valueStorage = {};
},
/** @private */
initProperties: function()
{
// private
this.defineProp('atomSetter', {
'dataType': 'Kekule.Widget.BaseWidget', 'serializable': false, 'setter': null
});
},
/** @ignore */
doCreateWidget: function()
{
var result = new Kekule.Widget.DropDownButton(this.getEditor());
result.setHint(Kekule.$L('ChemWidgetTexts.HINT_ATOM_MODIFIER'));
result.setText(Kekule.$L('ChemWidgetTexts.CAPTION_ATOM_MODIFIER'));
result.setShowText(true);
result.setButtonKind(Kekule.Widget.Button.Kinds.DROPDOWN);
result.addClassName(CCNS.COMPOSER_ATOM_MODIFIER_BUTTON);
//var atomSetter = this._createAtomSetter(this.getEditor());
//result.setDropDownWidget(atomSetter);
result.setDropDownWidgetGetter(this._createAtomSetter.bind(this));
return result;
},
/** @private */
_createAtomSetter: function(parentWidget)
{
if (!parentWidget)
parentWidget = this.getEditor();
var editor = this.getEditor();
var result = new Kekule.ChemWidget.StructureNodeSetter(parentWidget);
//result.setUseDropDownSelectPanel(true);
//result.setCaption(Kekule.$L('ChemWidgetTexts.CAPTION_ATOM_MODIFIER'));
result.addClassName([CNS.PANEL, CCNS.COMPOSER_ATOM_MODIFIER_DROPDOWN, CNS.CORNER_ALL]); // simulate panel outlook
result.setLabelConfigs(this.getEditor().getRenderConfigs().getDisplayLabelConfigs());
// simulate a panel caption
var captionElem = parentWidget.getDocument().createElement('div');
captionElem.className = CNS.PANEL_CAPTION;
DU.setElementText(captionElem, Kekule.$L('ChemWidgetTexts.CAPTION_ATOM_MODIFIER'));
var parentElem = result.getElement();
parentElem.insertBefore(captionElem, Kekule.DomUtils.getFirstChildElem(parentElem));
/*
if (result.setResizable)
result.setResizable(true);
*/
var listAtoms = AU.clone(this.getEditor().getEditorConfigs().getStructureConfigs().getPrimaryOrgChemAtoms());
listAtoms.push('...'); // add periodic table item
// non-atom nodes
var nonAtomLabelInfos = editor && editor.getEnabledNonAtomInputData && editor.getEnabledNonAtomInputData();
result.setSelectableInfos({
'elementSymbols': listAtoms,
'nonElementInfos': nonAtomLabelInfos,
'subGroupRepItems': Kekule.Editor.StoredSubgroupRepositoryItem2D.getAllRepItems()
});
// react to value change of setter
var self = this;
result.addEventListener('valueChange', function(e){
self.applyToTargets();
result.dismiss();
});
/*
var self = this;
result.addEventListener('keyup', function(e)
{
var ev = e.htmlEvent;
if (ev.getKeyCode() === Kekule.X.Event.KeyCode.ENTER)
{
self.applySetter(result);
result.dismiss(); // avoid call apply setter twice
}
}
);
result.addEventListener('valueSelect', function(e){
//var data = e.value;
//console.log(e.target, e.currentTarget);
if (self.getAtomSetter() && self.getAtomSetter().isShown())
{
self.applySetter(result);
result.dismiss(); // avoid call apply setter twice
}
});
result.addEventListener('showStateChange', function(e)
{
if (e.target === result && !e.byDomChange)
{
//console.log('show state change', e);
if (!e.isShown && !e.isDismissed) // widget hidden, feedback the edited value
{
if (self.getAtomSetter() && self.getAtomSetter().isShown())
self.applySetter(result);
}
}
}
);
*/
this.setPropStoreFieldValue('atomSetter', result);
if (this._valueStorage.nodes)
result.setNodes(this._valueStorage.nodes);
return result;
},
/** @private */
_filterStructureNodes: function(targets)
{
var nodes = [];
for (var i = 0, l = targets.length; i < l; ++i)
{
var target = targets[i];
if (target instanceof Kekule.ChemStructureNode)
{
/*
if (target instanceof Kekule.StructureFragment && target.isExpanded()) // expanded group can not be modified
;
else
*/
nodes.push(target);
}
}
return nodes;
},
/** @private */
_getActualModificationNodes: function(nodes, byPassfilter)
{
var result = [];
for (var i = 0, l = nodes.length; i < l; ++i)
{
var node = nodes[i];
if (node instanceof Kekule.StructureFragment && node.isExpanded()) // actually modify children node in expanded group
{
var children = this._getActualModificationNodes(node.getNodes(), true);
AU.pushUnique(result, children);
}
else
result.push(node);
}
if (!byPassfilter)
result = this._filterStructureNodes(result);
return result;
},
/** @ignore */
doLoadFromTargets: function(editor, targets)
{
// filter chem nodes from targets
//var nodes = this._filterStructureNodes(targets);
var nodeLabel;
var nodes = this._getActualModificationNodes(targets);
this._valueStorage.nodes = nodes;
if (this.getAtomSetter())
{
this.getAtomSetter().setLabelConfigs(this.getEditor().getRenderConfigs().getDisplayLabelConfigs());
this.getAtomSetter().setNodes(nodes);
/*
if (nodes.length)
{
nodeLabel = this.getAtomSetter().getNodeLabel();
//console.log('update node label', nodeLabel);
}
*/
}
/*
else
{
nodeLabel = Kekule.Editor.StructureUtils.getAllChemStructureNodesLabel(nodes, this.getEditor().getRenderConfigs().getDisplayLabelConfigs());
}
*/
nodeLabel = Kekule.Editor.StructureUtils.getAllChemStructureNodesHtmlCode(nodes, null, null, this.getEditor().getRenderConfigs().getDisplayLabelConfigs());
this.getWidget().setText(nodeLabel || Kekule.$L('ChemWidgetTexts.CAPTION_ATOM_MODIFIER_MIXED'))
.setShowText(true).setDisplayed(!!nodes.length);
},
/** @ignore */
doApplyToTargets: function($super, editor, targets)
{
var data = this.getAtomSetter().getValue();
var opers = [];
//var nodes = this._filterStructureNodes(targets);
var nodes = this._getActualModificationNodes(targets);
for (var i = 0, l = nodes.length; i < l; ++i)
{
var target = nodes[i];
if (target instanceof Kekule.ChemStructureNode)
{
var op = this._createNewDataToAtomOperation(data, target);
if (op)
opers.push(op);
}
}
var operation;
if (opers.length > 1)
operation = new Kekule.MacroOperation(opers);
else
operation = opers[0];
if (operation) // only execute when there is real modification
{
var editor = this.getEditor();
editor.execOperation(operation);
}
},
/** @private */
_createNewDataToAtomOperation: function(newData, atom)
{
if (!newData)
return;
//console.log('apply setter', newData);
var nodeClass = newData.nodeClass;
var modifiedProps = newData.props;
var repItem = newData.repositoryItem;
var newNode;
if (repItem) // need to apply structure repository item
{
var repResult = repItem.createObjects(atom) || {};
var repObjects = repResult.objects;
var transformParams = Kekule.Editor.RepositoryStructureUtils.calcRepObjInitialTransformParams(this.getEditor(), repItem, repResult, atom, null);
this.getEditor().transformCoordAndSizeOfObjects(repObjects, transformParams);
newNode = repObjects[0];
nodeClass = newNode.getClass();
}
if (newData.isUnknownPseudoatom && !this.getEditorConfigs().getInteractionConfigs().getAllowUnknownAtomSymbol())
nodeClass = null;
if (!nodeClass)
{
Kekule.error(Kekule.$L('ErrorMsg.INVALID_ATOM_SYMBOL'));
return null;
}
else
{
return this._createModificationOperation(atom, newNode, nodeClass, modifiedProps);
}
},
/**
* @private
*/
_createModificationOperation: function(node, newNode, newNodeClass, modifiedProps)
{
var newNode;
var operGroup, oper;
var oldNodeClass = node.getClass();
if (newNode && !newNodeClass)
newNodeClass = newNode.getClass();
if (newNode || newNodeClass !== oldNodeClass) // need to replace node
{
operGroup = new Kekule.MacroOperation();
if (!newNode)
newNode = new newNodeClass();
var tempNode = new Kekule.ChemStructureNode();
tempNode.assign(node);
newNode.assign(tempNode); // copy some basic info of old node
var operReplace = new Kekule.ChemStructOperation.ReplaceNode(node, newNode, null, this.getEditor());
operGroup.add(operReplace);
}
else // no need to replace
newNode = node;
if (modifiedProps)
{
oper = new Kekule.ChemObjOperation.Modify(newNode, modifiedProps, this.getEditor());
if (operGroup)
operGroup.add(oper);
}
var operation = operGroup || oper;
return operation;
}
});
/**
* A modifier to change the bond property of chem connectors.
* @class
* @augments Kekule.Editor.ObjModifier.ChemStructureModifier
*/
Kekule.Editor.ObjModifier.Bond = Class.create(Kekule.Editor.ObjModifier.ChemStructureModifier,
/** @lends Kekule.Editor.ObjModifier.Bond# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.ObjModifier.Bond',
/** @construct */
initialize: function($super, editor)
{
$super(editor);
this._activeSelBtnHtmlClass = null;
this._defaultBondTypeData = null;
this._valueStorage = {};
var bondTypeData = this.getEditor().getEnabledBondFormData();
this._defaultBondTypeData = bondTypeData[0]; // regard the first one as default
},
/** @private */
initProperties: function()
{
// private
this.defineProp('bondSelector', {
'dataType': 'Kekule.Widget.BaseWidget', 'serializable': false, 'setter': null
});
},
/** @ignore */
doCreateWidget: function()
{
var result = new Kekule.Widget.DropDownButton(this.getEditor());
result.setHint(Kekule.$L('ChemWidgetTexts.HINT_BOND_MODIFIER'));
result.setText(Kekule.$L('ChemWidgetTexts.CAPTION_BOND_MODIFIER'));
result.setShowText(false);
result.setButtonKind(Kekule.Widget.Button.Kinds.DROPDOWN);
result.addClassName(CCNS.COMPOSER_BOND_MODIFIER_BUTTON);
//var bondSelector = this._createBondSelector(this.getEditor());
//result.setDropDownWidget(bondSelector);
result.setDropDownWidgetGetter(this._createBondSelector.bind(this));
if (this._defaultBondTypeData.htmlClass)
result.addClassName(this._defaultBondTypeData.htmlClass); // initialize default bond html class name, for button icon
return result;
},
/** @private */
_filterBond: function(targets)
{
var result = [];
for (var i = 0, l = targets.length; i < l; ++i)
{
var target = targets[i];
if (target instanceof Kekule.Bond)
{
result.push(target);
}
}
return result;
},
/** @private */
_getActualModificationBonds: function(targets, byPassfilter)
{
var result = [];
for (var i = 0, l = targets.length; i < l; ++i)
{
var target = targets[i];
if (target instanceof Kekule.StructureFragment && target.isExpanded()) // actually modify children bond in expanded group
{
var children = this._getActualModificationBonds(target.getNodes(), true); // cascade child expanded groups
AU.pushUnique(result, children);
AU.pushUnique(result, target.getConnectors())
}
else if (target instanceof Kekule.Bond)
result.push(target);
}
if (!byPassfilter)
result = this._filterBond(result);
return result;
},
/** @private */
_createBondSelector: function(parentWidget)
{
if (!parentWidget)
parentWidget = this.getEditor();
var result = new Kekule.ChemWidget.StructureConnectorSelectPanel(parentWidget);
result.setCaption(Kekule.$L('ChemWidgetTexts.CAPTION_BOND_MODIFIER'));
result.addClassName(CCNS.COMPOSER_BOND_MODIFIER_DROPDOWN);
if (this.getEditor().getEnabledBondFormData)
{
var bondTypeData = this.getEditor().getEnabledBondFormData();
result.setBondPropNames(this._getBondComparePropNames(bondTypeData));
result.setBondData(bondTypeData);
}
if (this._valueStorage.bondsPropValues)
{
result.setActiveBondPropValues(this._valueStorage.bondsPropValues);
}
this.setPropStoreFieldValue('bondSelector', result);
// react to value change of setter
var self = this;
result.addEventListener('valueChange', function(e){
self.applyToTargets();
result.dismiss();
});
return result;
},
/** @private */
_setActiveBondHtmlClass: function(className)
{
var oldClassName = this._activeSelBtnHtmlClass;
if (oldClassName)
this.getWidget().removeClassName(oldClassName);
this._activeSelBtnHtmlClass = className;
if (!className)
className = this._defaultBondTypeData.htmlClass;
if (className)
this.getWidget().addClassName(className);
},
/** @private */
_extractBondPropValues: function(bond, propNames)
{
return bond.getPropValues(propNames);
},
/** @private */
_extractBondsPropValues: function(bonds, propNames)
{
var result;
var currPropValues;
for (var i = 0, l = bonds.length; i < l; ++i)
{
currPropValues = this._extractBondPropValues(bonds[i], propNames);
if (!result)
result = currPropValues;
else if (!Kekule.Editor.StructureUtils.isBondPropsMatch(result, currPropValues, propNames))
{
result = null;
break;
}
}
return result;
},
/** @private */
_getBondComparePropNames: function(bondData)
{
var result = [];
for (var i = 0, l = bondData.length; i < l; ++i)
{
var data = bondData[i];
var propData = data.bondProps;
if (propData)
AU.pushUnique(result, Kekule.ObjUtils.getOwnedFieldNames(propData));
}
return result;
},
/** @private */
_getMatchedBondData: function(bondPropValues, bondData, bondPropNames)
{
if (!bondPropValues)
return null;
for (var i = 0, l = bondData.length; i < l; ++i)
{
var data = bondData[i];
if (data && Kekule.Editor.StructureUtils.isBondPropsMatch(bondPropValues, data.bondProps, bondPropNames))
{
return data;
}
}
return null;
},
/** @ignore */
doLoadFromTargets: function(editor, targets)
{
// filter chem connectors from targets
/*
var connectors = [];
for (var i = 0, l = targets.length; i < l; ++i)
{
var target = targets[i];
if (target instanceof Kekule.ChemStructureConnector)
connectors.push(target);
}
*/
// update bond data retrieved from editor
var bondTypeData = this.getEditor().getEnabledBondFormData();
this._defaultBondTypeData = bondTypeData[0]; // regard the first one as default
var connectors = this._getActualModificationBonds(targets);
this._valueStorage.connectors = connectors;
if (this.getBondSelector())
{
this.getBondSelector().setBondData(bondTypeData);
var comparedPropNames = this.getBondSelector().getBondPropNames();
var bondPropValues = this._extractBondsPropValues(connectors, comparedPropNames);
//console.log('set value', bondPropValues);
this.getBondSelector().setActiveBondPropValues(bondPropValues);
// copy html class
this._setActiveBondHtmlClass(this.getBondSelector().getActiveBondHtmlClass());
}
else
{
this._valueStorage.bondPropNames = this._getBondComparePropNames(bondTypeData);
this._valueStorage.bondsPropValues = this._extractBondsPropValues(connectors, this._valueStorage.bondPropNames);
var matchedBondData = this._getMatchedBondData(this._valueStorage.bondsPropValues, bondTypeData, this._valueStorage.bondPropNames);
this._setActiveBondHtmlClass(matchedBondData && matchedBondData.htmlClass);
}
this.getWidget().setDisplayed(!!connectors.length);
},
/** @ignore */
doApplyToTargets: function($super, editor, targets)
{
var data = this.getBondSelector().getActiveBondPropValues();
//console.log('modify data', data);
var opers = [];
/*
for (var i = 0, l = targets.length; i < l; ++i)
{
var target = targets[i];
if (target instanceof Kekule.ChemStructureConnector)
{
var op = this._createBondModificationOperation(data, target);
if (op)
opers.push(op);
}
}
*/
var bonds = this._getActualModificationBonds(targets);
for (var i = 0, l = bonds.length; i < l; ++i)
{
var bond = bonds[i];
var op = this._createBondModificationOperation(data, bond);
if (op)
opers.push(op);
}
var operation;
if (opers.length > 1)
operation = new Kekule.MacroOperation(opers);
else
operation = opers[0];
if (operation) // only execute when there is real modification
{
var editor = this.getEditor();
editor.execOperation(operation);
}
//console.log('set class name', this.getBondSelector().getActiveBondHtmlClass());
this._setActiveBondHtmlClass(this.getBondSelector().getActiveBondHtmlClass());
},
/** @private */
_createBondModificationOperation: function(data, bond)
{
var op = new Kekule.ChemObjOperation.Modify(bond, data, this.getEditor());
return op;
}
});
/**
* A modifier to change the charge property of chem nodes.
* @class
* @augments Kekule.Editor.ObjModifier.ChemStructureModifier
*/
Kekule.Editor.ObjModifier.Charge = Class.create(Kekule.Editor.ObjModifier.ChemStructureModifier,
/** @lends Kekule.Editor.ObjModifier.Charge# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.ObjModifier.Charge',
/** @construct */
initialize: function($super, editor)
{
$super(editor);
this._valueStorage = {};
},
/** @private */
initProperties: function()
{
// private
this.defineProp('chargeSelector', {
'dataType': 'Kekule.Widget.BaseWidget', 'serializable': false, 'setter': null
});
},
/** @ignore */
doCreateWidget: function()
{
var result = new Kekule.Widget.DropDownButton(this.getEditor());
result.setHint(Kekule.$L('ChemWidgetTexts.HINT_CHARGE_MODIFIER'))
.setText(Kekule.$L('ChemWidgetTexts.TEXT_CHARGE_UNKNOWN'))
.setShowText(true)
.setButtonKind(Kekule.Widget.Button.Kinds.DROPDOWN);
result.addClassName(CCNS.COMPOSER_CHARGE_MODIFIER_BUTTON);
result.setDropDownWidgetGetter(this._createChargeSelector.bind(this));
return result;
},
/** @private */
_createChargeSelector: function(parentWidget)
{
if (!parentWidget)
parentWidget = this.getEditor();
var result = new Kekule.ChemWidget.ChargeSelectPanel(parentWidget);
result.setCaption(Kekule.$L('ChemWidgetTexts.CAPTION_CHARGE_MODIFIER'));
result.addClassName(CCNS.COMPOSER_CHARGE_MODIFIER_DROPDOWN);
if (Kekule.ObjUtils.notUnset(this._valueStorage.charge))
{
result.setValue(this._valueStorage.charge);
}
this.setPropStoreFieldValue('chargeSelector', result);
// react to value change of setter
var self = this;
result.addEventListener('valueChange', function(e){
self.applyToTargets();
result.dismiss();
});
return result;
},
/** @private */
_filterStructureNodes: function(targets)
{
var nodes = [];
for (var i = 0, l = targets.length; i < l; ++i)
{
var target = targets[i];
if (target instanceof Kekule.ChemStructureNode && target.getCharge)
{
nodes.push(target);
}
}
return nodes;
},
/** @private */
_getActualModificationNodes: function(nodes, byPassfilter)
{
var result = [];
for (var i = 0, l = nodes.length; i < l; ++i)
{
var node = nodes[i];
if (node instanceof Kekule.StructureFragment && node.isExpanded()) // actually modify children node in expanded group
{
var children = this._getActualModificationNodes(node.getNodes(), true);
AU.pushUnique(result, children);
}
else
result.push(node);
}
if (!byPassfilter)
result = this._filterStructureNodes(result);
return result;
},
/** @private */
_getChargeOfNodes: function(nodes)
{
var result = null;
for (var i = 0, l = nodes.length; i < l; ++i)
{
var n = nodes[i];
if (n.getCharge)
{
var c = n.getCharge() || 0;
if (result === null)
result = c;
else if (result !== c)
return null;
}
}
return result;
},
/** @ignore */
doLoadFromTargets: function(editor, targets)
{
// filter chem nodes from targets
//var nodes = this._filterStructureNodes(targets);
var nodeLabel;
var nodes = this._getActualModificationNodes(targets);
var showModifier = (nodes.length >= 0) && (nodes.length <= 1); // display modifier when only one node is selected
if (showModifier)
{
var charge = this._getChargeOfNodes(nodes);
this._valueStorage.charge = charge;
if (this.getChargeSelector())
{
this.getChargeSelector().setValue(charge);
}
var caption;
if (charge)
{
var sSign = (charge > 0) ? Kekule.$L('ChemWidgetTexts.TEXT_CHARGE_POSITIVE') : Kekule.$L('ChemWidgetTexts.TEXT_CHARGE_NEGATIVE');
var c = Kekule.NumUtils.toDecimals(charge, 1); // reserve 1 digit after point if it is not a integer charge
caption = Math.abs(c) + sSign;
}
else
caption = Kekule.$L('ChemWidgetTexts.TEXT_CHARGE_UNKNOWN');
this.getWidget().setText(caption).setShowText(true);
}
this.getWidget().setDisplayed(showModifier);
},
/** @ignore */
doApplyToTargets: function($super, editor, targets)
{
var charge = this.getChargeSelector().getValue();
var opers = [];
var nodes = this._getActualModificationNodes(targets);
var editor = this.getEditor();
for (var i = 0, l = nodes.length; i < l; ++i)
{
var target = nodes[i];
if (target instanceof Kekule.ChemStructureNode)
{
var op = new Kekule.ChemObjOperation.Modify(target, {'charge': charge}, editor);
if (op)
opers.push(op);
}
}
var operation;
if (opers.length > 1)
operation = new Kekule.MacroOperation(opers);
else
operation = opers[0];
if (operation) // only execute when there is real modification
{
var editor = this.getEditor();
editor.execOperation(operation);
}
}
});
var OMM = Kekule.Editor.ObjModifierManager;
OMM.register(Kekule.ChemStructureNode, [Kekule.Editor.ObjModifier.Atom]);
OMM.register(Kekule.ChemStructureNode, [Kekule.Editor.ObjModifier.Charge]);
OMM.register([Kekule.Bond, Kekule.StructureFragment], [Kekule.Editor.ObjModifier.Bond]); // can change child bonds of structure fragment
})();