kekule
Version:
Open source JavaScript toolkit for chemoinformatics
1,522 lines (1,470 loc) • 114 kB
JavaScript
/**
* @fileoverview
* Implements of actions related with chem editor.
* @author Partridge Jiang
*/
/*
* requires /lan/classes.js
* requires /utils/kekule.utils.js
* requires /io/kekule.io.js
* requires /xbrowsers/kekule.x.js
* requires /widgets/kekule.widget.base.js
* requires /widgets/kekule.widget.clipboards.js
* requires /widgets/chem/kekule.chemWidget.chemObjDisplayers.js
* requires /widgets/chem/editor/kekule.chemEditor.baseEditors.js
* requires /widgets/chem/editor/kekule.chemEditor.composers.js
* requires /widgets/operation/kekule.actions.js
*
* requires /localization/kekule.localize.widget.js
*/
(function(){
"use strict";
var AU = Kekule.ArrayUtils;
var BNS = Kekule.ChemWidget.ComponentWidgetNames;
var CCNS = Kekule.ChemWidget.HtmlClassNames;
//var CWT = Kekule.ChemWidgetTexts;
var AM = Kekule.ActionManager;
var _editorActionRegInfo = [];
/** @ignore */
Kekule.ChemWidget.HtmlClassNames = Object.extend(Kekule.ChemWidget.HtmlClassNames, {
// predefined actions
ACTION_UNDO: 'K-Chem-Undo',
ACTION_REDO: 'K-Chem-Redo',
ACTION_NEWDOC: 'K-Chem-NewDoc',
ACTION_SELECT_ALL: 'K-Chem-SelectAll',
ACTION_CLONE_SELECTION: 'K-Chem-Clone-Selection',
ACTION_COPY: 'K-Chem-Copy',
ACTION_CUT: 'K-Chem-Cut',
ACTION_PASTE: 'K-Chem-Paste',
ACTION_TOGGLE_SELECT: 'K-Chem-Toggle-Select-State',
ACTION_ERASE_SELECTION: 'K-Chem-Erase-Selection',
ACTION_RECHECK_ISSUES: 'K-Chem-Recheck-Issues',
ACTION_TOGGLE_OBJ_INSPECTOR: 'K-Chem-Toggle-ObjInspector',
ACTION_TOGGLE_ISSUE_INSPECTOR: 'K-Chem-Toggle-IssueInspector',
ACTION_TOGGLE_SHOW_ISSUES: 'K-Chem-Toggle-ShowIssues',
});
Object.extend(Kekule.ChemWidget.ComponentWidgetNames, {
manipulateMarquee: 'manipulateMarquee',
manipulateLasso: 'manipulateLasso',
manipulateBrush: 'manipulateBrush',
manipulateAncestor: 'manipulateAncestor',
molBondSingle: 'bondSingle',
molBondDouble: 'bondDouble',
molBondTriple: 'bondTriple',
molBondCloser: 'bondCloser',
molBondWedgeUp: 'bondWedgeUp',
molBondWedgeDown: 'bondWedgeDown',
molBondWedgeUpOrDown: 'bondWedgeUpOrDown',
molBondDoubleEither: 'bondDoubleEither',
molChargeClear: 'chargeClear',
molChargePositive: 'chargePositive',
molChargeNegative: 'chargeNegative',
molElectronicBiasPositive: 'electronicBiasPositive',
molElectronicBiasNegative: 'electronicBiasNegative',
molRadicalSinglet: 'radicalSinglet',
molRadicalTriplet: 'radicalTriplet',
molRadicalDoublet: 'radicalDoublet',
molElectronLonePair: 'electronLonePair',
molChain: 'chain',
molRing3: 'ring3',
molRing4: 'ring4',
molRing5: 'ring5',
molRing6: 'ring6',
molRing7: 'ring7',
molRing8: 'ring8',
molRingAr6: 'ringAr6',
molRingAr5: 'ringAr5',
molFlexRing: 'flexRing',
molRepCyclopentaneHaworth1: 'repCyclopentaneHaworth1',
molRepCyclopentaneHaworth2: 'repCyclopentaneHaworth2',
molRepCyclohexaneHaworth1: 'repCyclohexaneHaworth1',
molRepCyclohexaneHaworth2: 'repCyclohexaneHaworth2',
molRepCyclohexaneChair1: 'repCyclohexaneChair1',
molRepCyclohexaneChair2: 'repCyclohexaneChair2',
molRepSubBondMark: 'subBondMark',
molRepMethane: 'methane',
molRepFischer1: 'repFischer1',
molRepFischer2: 'repFischer2',
molRepFischer3: 'repFischer3',
molRepSawhorseStaggered: 'repSawhorseStaggered',
molRepSawhorseEclipsed: 'repSawhorseEclipsed',
glyphRepLine: 'repLine',
glyphRepOpenArrowLine: 'repOpenArrowLine',
glyphRepTriangleArrowLine: 'repTriangleArrowLine',
glyphRepDiOpenArrowLine: 'repDiOpenArrowLine',
glyphRepDiTriangleArrowLine: 'repDiTriangleArrowLine',
glyphRepReversibleArrowLine: 'repReversibleArrowLine',
glyphRepOpenArrowDiLine: 'repOpenArrowDiLine',
glyphRepOpenArrowArc: 'repOpenArrowArc',
glyphRepSingleSideOpenArrowArc: 'repSingleSideOpenArrowArc',
glyphRepHeatSymbol: 'repHeatSymbol',
glyphRepAddSymbol: 'repAddSymbol',
glyphElectronPushingArrow: 'repElectronPushingArrow',
glyphElectronPushingArrowDouble: 'repElectronPushingArrowDouble',
glyphElectronPushingArrowSingle: 'repElectronPushingArrowSingle',
glyphElectronPushingArrowBondForming: 'repElectronPushingArrowBondForming',
glyphRepSegment: 'repGlyphSegment',
glyphReactionArrowNormal: 'repGlyphReactionArrowNormal',
glyphReactionArrowReversible: 'glyphReactionArrowReversible',
glyphReactionArrowResonance: 'glyphReactionArrowResonance',
glyphReactionArrowRetrosynthesis: 'glyphReactionArrowRetrosynthesis'
});
/**
* A helper class for editor actions.
* @class
*/
Kekule.Editor.ActionOperUtils = {
/** @private */
getObjsCenterScreenCoord: function(editor, objects)
{
var BU = Kekule.BoxUtils;
var CU = Kekule.CoordUtils;
var containerBox = null;
for (var i = 0, l = objects.length; i < l; ++i)
{
var box = Kekule.Render.ObjUtils.getContainerBox(objects[i], editor.getCoordMode(), editor.getAllowCoordBorrow());
if (!containerBox)
containerBox = box;
else
containerBox = BU.getContainerBox(box, containerBox);
}
//var centerCoord = BU.getCenterCoord(containerBox);
var coords = BU.getMinMaxCoords(containerBox);
var screenCoords = {
'min': editor.objCoordToScreen(coords.min),
'max': editor.objCoordToScreen(coords.max)
};
var result = CU.add(screenCoords.min, screenCoords.max);
var result = CU.divide(result, 2);
//return editor.objCoordToScreen(centerCoord);
return result;
},
/**
* Add standalone objects to a chem space editor, with operation support.
* @param {Kekule.Editor.ChemSpaceEditor} editor
* @param {Array} objs
* @param {Hash} options May including fields:
* {
* screenCoordOffset: Coords of new added objects will be added with this value.
* autoAdjustPosition: Whether the newly added object will be put at the center of editor screen.
* This option takes no effect when screenCoordOffset is true.
* autoSelect: Whether automatically select the newly added objects.
* }
*/
addObjectsToChemSpaceEditor: function(editor, objs, options)
{
var ops = Object.extend({autoAdjustPosition: true}, options); // default options
var _getAppendableObjs = function(srcObj, rootSpace)
{
var result = [];
var rootObj = rootSpace;
if (rootObj && srcObj)
{
if (srcObj.getClass() === rootObj.getClass() || srcObj instanceof Kekule.ChemSpace) // class is same (chemspace)
{
result = AU.clone(srcObj.getChildren());
}
else
result = [srcObj];
}
return result;
};
var chemSpace = editor.getChemSpace && editor.getChemSpace();
if (editor && chemSpace && editor.canAddNewStandaloneObject && editor.canAddNewStandaloneObject())
{
var actualObjs = [];
for (var i = 0, l = objs.length; i < l; ++i)
{
var appendableObjs = _getAppendableObjs(objs[i], chemSpace);
if (appendableObjs && appendableObjs.length)
AU.pushUnique(actualObjs, appendableObjs);
}
//editor.beginUpdateObject();
editor.beginManipulateAndUpdateObject();
try
{
var marcoOper = new Kekule.MacroOperation();
for (var i = 0, l = actualObjs.length; i < l; ++i)
{
var obj = actualObjs[i];
var oper = new Kekule.ChemObjOperation.Add(obj, chemSpace, null, editor);
marcoOper.add(oper);
}
marcoOper.execute();
var screenCoordOffset = ops.screenCoordOffset;
if (!screenCoordOffset && ops.autoAdjustPosition) // auto adjust position
{
//var originCenterCoord = this.getObjsCenterScreenCoord(editor, originalSelectedObjs);
var editorClientRect = editor.getClientVisibleRect();
var editorCenterScreenCoord = {
'x': editorClientRect.left + editorClientRect.width / 2,
'y': editorClientRect.top + editorClientRect.height / 2
};
var targetCenterCoord = this.getObjsCenterScreenCoord(editor, actualObjs);
var deltaCoord = Kekule.CoordUtils.substract(editorCenterScreenCoord, targetCenterCoord);
screenCoordOffset = deltaCoord;
}
if (screenCoordOffset)
{
for (var i = 0, l = actualObjs.length; i < l; ++i)
{
var obj = actualObjs[i];
var coord = editor.getObjectScreenCoord(obj);
var newCoord = Kekule.CoordUtils.add(coord, screenCoordOffset);
editor.setObjectScreenCoord(obj, newCoord);
}
}
editor.pushOperation(marcoOper);
if (ops.autoSelect)
editor.select(actualObjs);
}
finally
{
//editor.endUpdateObject();
editor.endManipulateAndUpdateObject();
}
}
}
};
/**
* Base class for actions for chem editor only.
* @class
* @augments Kekule.ChemWidget.ActionOnDisplayer
*
* @param {Kekule.Editor.BaseEditor} editor Target editor object.
* @param {String} caption
* @param {String} hint
* @param {String} explicitGroup Use this property to explicitly set child actions to different group.
*/
Kekule.Editor.ActionOnEditor = Class.create(Kekule.ChemWidget.ActionOnDisplayer,
/** @lends Kekule.Editor.ActionOnEditor# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.ActionOnEditor',
/** @constructs */
initialize: function(/*$super, */editor, caption, hint)
{
this.tryApplySuper('initialize', [editor, caption, hint]) /* $super(editor, caption, hint) */;
},
/** @private */
initProperties: function()
{
this.defineProp('explicitGroup', {'dataType': DataType.STRING});
},
/**
* Returns the widget class that best fit this action.
* Descendants may override this method.
* @returns {null}
*/
getPreferredWidgetClass: function()
{
return null;
},
/** @private */
doUpdate: function()
{
var displayer = this.getDisplayer();
this.setEnabled(displayer && displayer.getChemObj() && displayer.getChemObjLoaded() && displayer.getEnabled());
},
/**
* Returns target chem editor object.
* @returns {Kekule.Editor.BaseEditor}
*/
getEditor: function()
{
var result = this.getDisplayer();
return (result instanceof Kekule.Editor.BaseEditor)? result: null;
}
});
/**
* An undo action on editor.
* @class
* @augments Kekule.Editor.ActionOnEditor
*
* @param {Kekule.Editor.BaseEditor} editor Target editor object.
*/
Kekule.Editor.ActionEditorUndo = Class.create(Kekule.Editor.ActionOnEditor,
/** @lends Kekule.Editor.ActionEditorUndo# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.ActionEditorUndo',
/** @private */
HTML_CLASSNAME: CCNS.ACTION_UNDO,
/** @constructs */
initialize: function(/*$super, */editor)
{
this.tryApplySuper('initialize', [editor, /*CWT.CAPTION_UNDO, CWT.HINT_UNDO*/Kekule.$L('ChemWidgetTexts.CAPTION_UNDO'), Kekule.$L('ChemWidgetTexts.HINT_UNDO')]) /* $super(editor, \*CWT.CAPTION_UNDO, CWT.HINT_UNDO*\Kekule.$L('ChemWidgetTexts.CAPTION_UNDO'), Kekule.$L('ChemWidgetTexts.HINT_UNDO')) */;
},
/** @private */
doUpdate: function(/*$super*/)
{
this.tryApplySuper('doUpdate') /* $super() */;
if (this.getEnabled())
this.setEnabled(this.getEditor().getEnableOperHistory() && this.getEditor().canUndo());
},
/** @private */
doExecute: function()
{
var editor = this.getEditor();
if (editor)
editor.undo();
}
});
/**
* A redo action on editor.
* @class
* @augments Kekule.Editor.ActionOnEditor
*
* @param {Kekule.Editor.BaseEditor} editor Target editor object.
*/
Kekule.Editor.ActionEditorRedo = Class.create(Kekule.Editor.ActionOnEditor,
/** @lends Kekule.Editor.ActionEditorRedo# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.ActionEditorRedo',
/** @private */
HTML_CLASSNAME: CCNS.ACTION_REDO,
/** @constructs */
initialize: function(/*$super, */editor)
{
this.tryApplySuper('initialize', [editor, /*CWT.CAPTION_REDO, CWT.HINT_REDO*/Kekule.$L('ChemWidgetTexts.CAPTION_REDO'), Kekule.$L('ChemWidgetTexts.HINT_REDO')]) /* $super(editor, \*CWT.CAPTION_REDO, CWT.HINT_REDO*\Kekule.$L('ChemWidgetTexts.CAPTION_REDO'), Kekule.$L('ChemWidgetTexts.HINT_REDO')) */;
},
/** @private */
doUpdate: function(/*$super*/)
{
this.tryApplySuper('doUpdate') /* $super() */;
if (this.getEnabled())
this.setEnabled(this.getEditor().getEnableOperHistory() && this.getEditor().canRedo());
},
/** @private */
doExecute: function()
{
var editor = this.getEditor();
if (editor)
editor.redo();
}
});
/**
* A new document action on editor.
* @class
* @augments Kekule.Editor.ActionOnEditor
*
* @param {Kekule.Editor.BaseEditor} editor Target editor object.
*/
Kekule.Editor.ActionEditorNewDoc = Class.create(Kekule.Editor.ActionOnEditor,
/** @lends Kekule.Editor.ActionEditorNewDoc# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.ActionEditorNewDoc',
/** @private */
HTML_CLASSNAME: CCNS.ACTION_NEWDOC,
/** @constructs */
initialize: function(/*$super, */editor)
{
this.tryApplySuper('initialize', [editor, /*CWT.CAPTION_NEWDOC, CWT.HINT_NEWDOC*/Kekule.$L('ChemWidgetTexts.CAPTION_NEWDOC'), Kekule.$L('ChemWidgetTexts.HINT_NEWDOC')]) /* $super(editor, \*CWT.CAPTION_NEWDOC, CWT.HINT_NEWDOC*\Kekule.$L('ChemWidgetTexts.CAPTION_NEWDOC'), Kekule.$L('ChemWidgetTexts.HINT_NEWDOC')) */;
},
/** @private */
doUpdate: function()
{
var editor = this.getDisplayer();
this.setEnabled(editor && editor.getEnabled() && editor.getEnableCreateNewDoc());
},
/** @private */
doExecute: function()
{
var editor = this.getEditor();
if (editor)
editor.newDoc();
}
});
/**
* Action for loading or appending new data into editor.
* @class
* @augments Kekule.ChemWidget.ActionDisplayerLoadData
*
* @property {Bool} enableAppend Whether appending data into editor is enabled.
*/
Kekule.Editor.ActionEditorLoadData = Class.create(Kekule.ChemWidget.ActionDisplayerLoadData,
/** @lends Kekule.Editor.ActionEditorLoadData# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.ActionEditorLoadData',
/* @private */
// HTML_CLASSNAME: CCNS.ACTION_LOADFILE,
/** @constructs */
initialize: function(/*$super, */editor)
{
this.tryApplySuper('initialize', [editor]) /* $super(editor) */;
},
/** @private */
initProperties: function()
{
this.defineProp('enableAppend', {'dataType': DataType.BOOL});
},
/** @private */
doExecute: function(/*$super, */target)
{
var dialog = this.getDataDialog();
if (dialog && dialog.setDisplayAppendCheckBox)
dialog.setDisplayAppendCheckBox(this._isEditorRootObjAppendable() && !this._isEditorEmpty());
return this.tryApplySuper('doExecute', [target]) /* $super(target) */;
},
/** @private */
_getEditorRootObj: function()
{
var editor = this.getDisplayer();
var rootObj = editor.getChemObj();
return rootObj;
},
/** @private */
_isEditorRootObjAppendable: function()
{
// check if the root object of editor can append child
var editor = this.getDisplayer();
var rootObj = this._getEditorRootObj();
return rootObj && (rootObj instanceof Kekule.ChemSpace) && (editor.canAddNewStandaloneObject && editor.canAddNewStandaloneObject())
&& (editor.getAllowAppendDataToCurr && editor.getAllowAppendDataToCurr());
},
/** @private */
_isEditorEmpty: function()
{
var rootObj = this._getEditorRootObj();
return (!rootObj || (rootObj.getChildCount && rootObj.getChildCount() === 0));
},
/* @private */
/*
_getAppendableObjs: function(srcObj)
{
var result = [];
var rootObj = this._getEditorRootObj();
if (rootObj && srcObj)
{
if (srcObj.getClass() === rootObj.getClass() || srcObj instanceof Kekule.ChemSpace) // class is same (chemspace)
{
result = AU.clone(srcObj.getChildren());
}
else
result = [srcObj];
}
return result;
},
*/
/** @private */
createDataDialog: function()
{
var doc = this.getDisplayer().getDocument();
var result = new Kekule.ChemWidget.LoadOrAppendDataDialog(doc);
return result;
},
/** @ignore */
doLoadInDisplayer: function(displayer, dataDetails, dialog)
{
var editor = displayer || this.getDisplayer();
var isAppending = dialog.getIsAppending();
//console.log('is appending', isAppending);
if (isAppending && !this._isEditorEmpty() && this._isEditorRootObjAppendable())
{
var rootObj = this._getEditorRootObj();
//var appendableObjs = this._getAppendableObjs(chemObj);
//Kekule.Editor.ActionOperUtils.addObjectsToChemSpaceEditor(editor, appendableObjs);
editor.loadFromData(dataDetails.data, dataDetails.mimeType, dataDetails.fileName, dataDetails.formatId, function(chemObj){
if (chemObj)
{
editor.beginUpdateObject();
try
{
rootObj.beginUpdate();
try
{
Kekule.Editor.ActionOperUtils.addObjectsToChemSpaceEditor(editor, [chemObj], {'autoSelect': true});
} finally
{
rootObj.endUpdate();
}
}
finally
{
editor.endUpdateObject();
}
}
});
}
else
//return this.tryApplySuper('doLoadToDisplayer', [chemObj, dialog]);
return this.tryApplySuper('doLoadInDisplayer', [displayer, dataDetails, dialog]);
}
/* @ignore */
/*
doLoadToDisplayer: function(chemObj, dialog)
{
var editor = this.getDisplayer();
var isAppending = dialog.getIsAppending();
console.log('is appending', isAppending);
if (isAppending && !this._isEditorEmpty() && this._isEditorRootObjAppendable())
{
editor.beginUpdateObject();
try
{
var rootObj = this._getEditorRootObj();
rootObj.beginUpdate();
try
{
//var appendableObjs = this._getAppendableObjs(chemObj);
//Kekule.Editor.ActionOperUtils.addObjectsToChemSpaceEditor(editor, appendableObjs);
Kekule.Editor.ActionOperUtils.addObjectsToChemSpaceEditor(editor, [chemObj], {'autoSelect': true});
}
finally
{
rootObj.endUpdate();
}
}
finally
{
editor.endUpdateObject();
}
}
else
return this.tryApplySuper('doLoadToDisplayer', [chemObj, dialog]);
}
*/
});
/**
* A select-all action for editor.
* @class
* @augments Kekule.Editor.ActionOnEditor
*
* @param {Kekule.Editor.BaseEditor} editor Target editor object.
*/
Kekule.Editor.ActionSelectAll = Class.create(Kekule.Editor.ActionOnEditor,
/** @lends Kekule.Editor.ActionSelectAll# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.ActionSelectAll',
/** @private */
HTML_CLASSNAME: CCNS.ACTION_SELECT_ALL,
/** @constructs */
initialize: function(editor)
{
this.tryApplySuper('initialize', [editor, Kekule.$L('ChemWidgetTexts.CAPTION_SELECT_ALL'), Kekule.$L('ChemWidgetTexts.HINT_SELECT_ALL')]);
},
/** @private */
doUpdate: function(/*$super*/)
{
this.tryApplySuper('doUpdate') /* $super() */;
if (this.getEnabled())
this.setEnabled(this.getEditor().getChemObj());
},
/** @private */
doExecute: function()
{
this.getEditor().selectAll();
}
});
/**
* A clone selection action on editor.
* @class
* @augments Kekule.Editor.ActionOnEditor
*
* @param {Kekule.Editor.BaseEditor} editor Target editor object.
*/
Kekule.Editor.ActionCloneSelection = Class.create(Kekule.Editor.ActionOnEditor,
/** @lends Kekule.Editor.ActionCloneSelection# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.ActionCloneSelection',
/** @private */
HTML_CLASSNAME: CCNS.ACTION_CLONE_SELECTION,
/** @constructs */
initialize: function(/*$super, */editor)
{
this.tryApplySuper('initialize', [editor, /*CWT.CAPTION_CLONE_SELECTION, CWT.HINT_CLONE_SELECTION*/Kekule.$L('ChemWidgetTexts.CAPTION_CLONE_SELECTION'), Kekule.$L('ChemWidgetTexts.HINT_CLONE_SELECTION')]) /* $super(editor, \*CWT.CAPTION_CLONE_SELECTION, CWT.HINT_CLONE_SELECTION*\Kekule.$L('ChemWidgetTexts.CAPTION_CLONE_SELECTION'), Kekule.$L('ChemWidgetTexts.HINT_CLONE_SELECTION')) */;
},
/** @private */
_hasCloneMethod: function()
{
var editor = this.getEditor();
return editor && editor.cloneSelection;
},
/** @private */
doUpdate: function(/*$super*/)
{
this.tryApplySuper('doUpdate') /* $super() */;
if (this.getEnabled())
this.setEnabled(this._hasCloneMethod() && this.getEditor().hasSelection());
this.setDisplayed(this._hasCloneMethod() && this.getEditor().canCloneObjects());
},
/** @private */
doExecute: function()
{
var editor = this.getEditor();
if (editor && editor.getChemSpace)
{
var coordOffset = editor.getDefaultCloneScreenCoordOffset && editor.getDefaultCloneScreenCoordOffset();
var objs = editor.cloneSelection();
Kekule.Editor.ActionOperUtils.addObjectsToChemSpaceEditor(editor, objs, {
'screenCoordOffset': coordOffset,
'autoSelect': true
});
}
/*
var chemSpace = editor.getChemSpace && editor.getChemSpace();
if (editor && chemSpace)
{
var coordOffset = editor.getDefaultCloneScreenCoordOffset();
editor.beginUpdateObject();
try
{
var objs = editor.cloneSelection();
var marcoOper = new Kekule.MacroOperation();
for (var i = 0, l = objs.length; i < l; ++i)
{
var obj = objs[i];
var oper = new Kekule.ChemObjOperation.Add(objs[i], chemSpace);
marcoOper.add(oper);
}
marcoOper.execute();
if (coordOffset)
{
for (var i = 0, l = objs.length; i < l; ++i)
{
var obj = objs[i];
var coord = editor.getObjectScreenCoord(obj);
var newCoord = Kekule.CoordUtils.add(coord, coordOffset);
editor.setObjectScreenCoord(obj, newCoord);
}
}
editor.pushOperation(marcoOper);
editor.select(objs);
}
finally
{
editor.endUpdateObject();
}
}
*/
}
});
/**
* A copying selection action on editor.
* @class
* @augments Kekule.Editor.ActionOnEditor
*
* @param {Kekule.Editor.BaseEditor} editor Target editor object.
*/
Kekule.Editor.ActionCopySelection = Class.create(Kekule.Editor.ActionOnEditor,
/** @lends Kekule.Editor.ActionCopySelection# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.ActionCopySelection',
/** @private */
HTML_CLASSNAME: CCNS.ACTION_COPY,
/** @constructs */
initialize: function(/*$super, */editor)
{
this.tryApplySuper('initialize', [editor, /*CWT.CAPTION_COPY, CWT.HINT_COPY*/Kekule.$L('ChemWidgetTexts.CAPTION_COPY'), Kekule.$L('ChemWidgetTexts.HINT_COPY')]) /* $super(editor, \*CWT.CAPTION_COPY, CWT.HINT_COPY*\Kekule.$L('ChemWidgetTexts.CAPTION_COPY'), Kekule.$L('ChemWidgetTexts.HINT_COPY')) */;
},
/** @private */
doUpdate: function(/*$super*/)
{
this.tryApplySuper('doUpdate') /* $super() */;
if (this.getEnabled())
this.setEnabled(this.getEditor().hasSelection());
},
/** @private */
doExecute: function()
{
var editor = this.getEditor();
var chemSpace = editor.getChemSpace && editor.getChemSpace();
if (editor && chemSpace)
{
var objs = editor.cloneSelection();
/*
Kekule.Widget.clipboard.setObjects(Kekule.IO.MimeType.JSON, objs);
//console.log(Kekule.Widget.Clipboard.getData('text/json'));
*/
var space = new Kekule.IntermediateChemSpace();
try
{
space.appendChildren(objs); // use a space to keep all objs, to keep the relations
Kekule.Widget.clipboard.setObjects(Kekule.IO.MimeType.JSON, [space]);
}
finally
{
space.finalize();
}
}
}
});
/**
* A cutting selection action on editor.
* @class
* @augments Kekule.Editor.ActionOnEditor
*
* @param {Kekule.Editor.BaseEditor} editor Target editor object.
*/
Kekule.Editor.ActionCutSelection = Class.create(Kekule.Editor.ActionOnEditor,
/** @lends Kekule.Editor.ActionCutSelection# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.ActionCutSelection',
/** @private */
HTML_CLASSNAME: CCNS.ACTION_CUT,
/** @constructs */
initialize: function(/*$super, */editor)
{
this.tryApplySuper('initialize', [editor, /*CWT.CAPTION_CUT, CWT.HINT_CUT*/Kekule.$L('ChemWidgetTexts.CAPTION_CUT'), Kekule.$L('ChemWidgetTexts.HINT_CUT')]) /* $super(editor, \*CWT.CAPTION_CUT, CWT.HINT_CUT*\Kekule.$L('ChemWidgetTexts.CAPTION_CUT'), Kekule.$L('ChemWidgetTexts.HINT_CUT')) */;
},
/** @private */
doUpdate: function(/*$super*/)
{
this.tryApplySuper('doUpdate') /* $super() */;
if (this.getEnabled())
this.setEnabled(this.getEditor().hasSelection());
},
/** @private */
doExecute: function()
{
var editor = this.getEditor();
var chemSpace = editor.getChemSpace && editor.getChemSpace();
if (editor && chemSpace)
{
var objs = editor.cloneSelection();
//Kekule.Widget.clipboard.setObjects(Kekule.IO.MimeType.JSON, objs);
var space = new Kekule.IntermediateChemSpace();
try
{
space.appendChildren(objs); // use a space to keep all objs, to keep the relations
Kekule.Widget.clipboard.setObjects(Kekule.IO.MimeType.JSON, [space]);
}
finally
{
space.finalize();
}
// TODO: this is not a good approach
var controller = editor.getIaController('BasicMolEraserIaController');
if (controller)
controller.removeSelection();
}
}
});
/**
* A copying selection action on editor.
* @class
* @augments Kekule.Editor.ActionOnEditor
*
* @param {Kekule.Editor.BaseEditor} editor Target editor object.
*/
Kekule.Editor.ActionPaste = Class.create(Kekule.Editor.ActionOnEditor,
/** @lends Kekule.Editor.ActionPaste# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.ActionPaste',
/** @private */
HTML_CLASSNAME: CCNS.ACTION_PASTE,
/** @constructs */
initialize: function(/*$super, */editor)
{
this.tryApplySuper('initialize', [editor, /*CWT.CAPTION_PASTE, CWT.HINT_PASTE*/Kekule.$L('ChemWidgetTexts.CAPTION_PASTE'), Kekule.$L('ChemWidgetTexts.HINT_PASTE')]) /* $super(editor, \*CWT.CAPTION_PASTE, CWT.HINT_PASTE*\Kekule.$L('ChemWidgetTexts.CAPTION_PASTE'), Kekule.$L('ChemWidgetTexts.HINT_PASTE')) */;
Kekule.Widget.clipboard.addEventListener('setData', this._reactClipboardChange, this);
},
/** @ignore */
finalize: function(/*$super*/)
{
Kekule.Widget.clipboard.removeEventListener('setData', this._reactClipboardChange, this);
this.tryApplySuper('finalize') /* $super() */;
},
/** @private */
_reactClipboardChange: function()
{
this.update();
},
/** @private */
doUpdate: function(/*$super*/)
{
this.tryApplySuper('doUpdate') /* $super() */;
if (this.getEnabled())
this.setEnabled(Kekule.Widget.clipboard.hasData(Kekule.IO.MimeType.JSON) && this.getEditor().canAddNewStandaloneObject());
},
/** @private */
getObjsCenterScreenCoord: function(editor, objects)
{
/*
var BU = Kekule.BoxUtils;
var CU = Kekule.CoordUtils;
var containerBox = null;
for (var i = 0, l = objects.length; i < l; ++i)
{
var box = Kekule.Render.ObjUtils.getContainerBox(objects[i], editor.getCoordMode(), editor.getAllowCoordBorrow());
if (!containerBox)
containerBox = box;
else
containerBox = BU.getContainerBox(box, containerBox);
}
//var centerCoord = BU.getCenterCoord(containerBox);
var coords = BU.getMinMaxCoords(containerBox);
var screenCoords = {
'min': editor.objCoordToScreen(coords.min),
'max': editor.objCoordToScreen(coords.max)
};
var result = CU.add(screenCoords.min, screenCoords.max);
var result = CU.divide(result, 2);
//return editor.objCoordToScreen(centerCoord);
return result;
*/
return Kekule.Editor.ActionOperUtils.getObjsCenterScreenCoord(editor, objects);
},
/** @private */
doExecute: function()
{
var editor = this.getEditor();
if (editor && editor.getChemSpace)
{
//var objs = Kekule.Widget.clipboard.getObjects(Kekule.IO.MimeType.JSON);
var space, objs;
var clipboardObjs = Kekule.Widget.clipboard.getObjects(Kekule.IO.MimeType.JSON);
if (clipboardObjs.length === 1 && clipboardObjs[0] instanceof Kekule.IntermediateChemSpace)
{
space = clipboardObjs[0];
objs = AU.clone(space.getChildren());
// remove objs from space first
space.removeChildren(objs);
}
else
objs = clipboardObjs;
if (space)
space.finalize();
// calc coord offset
var coordOffset = null;
var selObjs = editor.getSelection();
if (selObjs && selObjs.length)
{
coordOffset = editor.getDefaultCloneScreenCoordOffset? editor.getDefaultCloneScreenCoordOffset(): null;
var originalSelectedObjs = AU.clone(selObjs);
if (originalSelectedObjs && originalSelectedObjs.length && coordOffset)
{
var originCenterCoord = this.getObjsCenterScreenCoord(editor, originalSelectedObjs);
var targetCenterCoord = this.getObjsCenterScreenCoord(editor, objs);
var deltaCoord = Kekule.CoordUtils.substract(originCenterCoord, targetCenterCoord);
coordOffset = Kekule.CoordUtils.add(coordOffset, deltaCoord);
}
}
Kekule.Editor.ActionOperUtils.addObjectsToChemSpaceEditor(editor, objs, {
'screenCoordOffset': coordOffset,
'autoSelect': true
});
}
/*
var chemSpace = editor.getChemSpace && editor.getChemSpace();
if (editor && chemSpace)
{
var objs = Kekule.Widget.Clipboard.getObjects('text/json');
editor.beginUpdateObject();
try
{
var marcoOper = new Kekule.MacroOperation();
for (var i = 0, l = objs.length; i < l; ++i)
{
var obj = objs[i];
var oper = new Kekule.ChemObjOperation.Add(objs[i], chemSpace);
marcoOper.add(oper);
}
marcoOper.execute();
var coordOffset = editor.getDefaultCloneScreenCoordOffset();
if (coordOffset)
{
for (var i = 0, l = objs.length; i < l; ++i)
{
var obj = objs[i];
var coord = editor.getObjectScreenCoord(obj);
var newCoord = Kekule.CoordUtils.add(coord, coordOffset);
editor.setObjectScreenCoord(obj, newCoord);
}
}
editor.pushOperation(marcoOper);
editor.select(objs);
}
finally
{
editor.endUpdateObject();
}
}
*/
}
});
/**
* Set isToggleSelectionOn property to editor.
* @class
* @augments Kekule.Editor.ActionOnEditor
*
* @param {Kekule.Editor.BaseEditor} editor Target editor object.
*/
Kekule.Editor.ActionToggleSelectState = Class.create(Kekule.Editor.ActionOnEditor,
/** @lends Kekule.Editor.ActionToggleSelectState# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.ActionToggleSelectState',
/** @private */
HTML_CLASSNAME: CCNS.ACTION_TOGGLE_SELECT,
/** @constructs */
initialize: function(/*$super, */editor)
{
this.tryApplySuper('initialize', [editor, Kekule.$L('ChemWidgetTexts.CAPTION_TOGGLE_SELECT'), Kekule.$L('ChemWidgetTexts.HINT_TOGGLE_SELECT')]) /* $super(editor, Kekule.$L('ChemWidgetTexts.CAPTION_TOGGLE_SELECT'), Kekule.$L('ChemWidgetTexts.HINT_TOGGLE_SELECT')) */;
this.setExplicitGroup(''); // force no check group
},
/** @ignore */
getPreferredWidgetClass: function()
{
return Kekule.Widget.CheckButton;
},
/** @private */
doUpdate: function(/*$super*/)
{
this.tryApplySuper('doUpdate') /* $super() */;
this.setChecked(this.getEditor().getIsToggleSelectOn());
},
/** @ignore */
checkedChanged: function(/*$super*/)
{
this.tryApplySuper('checkedChanged') /* $super() */;
},
/** @ignore */
doExecute: function(/*$super, */target, htmlEvent)
{
this.tryApplySuper('doExecute', [target, htmlEvent]) /* $super(target, htmlEvent) */;
var oldChecked = this.getChecked();
var editor = this.getEditor();
editor.setIsToggleSelectOn(!oldChecked);
this.setChecked(!oldChecked);
}
});
/**
* A simple action to delete all selected objects in editor.
* @class
* @augments Kekule.Editor.ActionOnEditor
*
* @param {Kekule.Editor.BaseEditor} editor Target editor object.
*/
Kekule.Editor.ActionEraseSelection = Class.create(Kekule.Editor.ActionOnEditor,
/** @lends Kekule.Editor.ActionEraseSelection# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.ActionEraseSelection',
/** @private */
HTML_CLASSNAME: CCNS.ACTION_ERASE_SELECTION,
/** @constructs */
initialize: function(editor)
{
this.tryApplySuper('initialize', [editor, Kekule.$L('ChemWidgetTexts.CAPTION_ERASE_SELECTION'), Kekule.$L('ChemWidgetTexts.HINT_ERASE_SELECTION')]);
},
/** @private */
doUpdate: function(/*$super*/)
{
this.tryApplySuper('doUpdate') /* $super() */;
if (this.getEnabled())
this.setEnabled(this.getEditor().hasSelection());
},
/** @private */
doExecute: function()
{
var editor = this.getEditor();
if (editor)
{
// TODO: this is not a good approach, need to refactor it later
var controller = editor.getIaController('BasicMolEraserIaController');
if (controller)
controller.removeSelection();
}
}
});
/**
* Recheck issues for chem objects in editor.
* @class
* @augments Kekule.Editor.ActionOnEditor
*
* @param {Kekule.Editor.BaseEditor} editor Target editor object.
*/
Kekule.Editor.ActionRecheckIssues = Class.create(Kekule.Editor.ActionOnEditor,
/** @lends Kekule.Editor.ActionRecheckIssues# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.ActionRecheckIssues',
/** @private */
HTML_CLASSNAME: CCNS.ACTION_RECHECK_ISSUES,
/** @constructs */
initialize: function(editor)
{
this.tryApplySuper('initialize', [editor, Kekule.$L('ChemWidgetTexts.CAPTION_RECHECK_ISSUES'), Kekule.$L('ChemWidgetTexts.HINT_RECHECK_ISSUES')]);
},
/** @private */
doUpdate: function(/*$super*/)
{
this.tryApplySuper('doUpdate') /* $super() */;
var editor = this.getEditor();
this.setEnabled(editor.getEnableIssueCheck()).setDisplayed(editor.getEnableIssueCheck());
},
/** @ignore */
doExecute: function(target, htmlEvent)
{
this.tryApplySuper('doExecute', [target, htmlEvent]) /* $super(target, htmlEvent) */;
var editor = this.getEditor();
if (editor)
editor.checkIssues();
}
});
/**
* Set {@link Kekule.Editor.BaseEditor.showAllIssueMarkers} property of editor.
* @class
* @augments Kekule.Editor.ActionOnEditor
*
* @param {Kekule.Editor.BaseEditor} editor Target editor object.
*/
Kekule.Editor.ActionToggleShowIssueMarkers = Class.create(Kekule.Editor.ActionOnEditor,
/** @lends Kekule.Editor.ActionToggleShowIssueMarkers# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.ActionToggleShowIssueMarkers',
/** @private */
HTML_CLASSNAME: CCNS.ACTION_TOGGLE_SHOW_ISSUES,
/** @constructs */
initialize: function(editor)
{
this.tryApplySuper('initialize', [editor, Kekule.$L('ChemWidgetTexts.CAPTION_TOGGLE_SHOW_ISSUE_MARKERS'), Kekule.$L('ChemWidgetTexts.HINT_TOGGLE_SHOW_ISSUE_MARKERS')]);
this.setExplicitGroup(''); // force no check group
},
/** @ignore */
getPreferredWidgetClass: function()
{
return Kekule.Widget.CheckButton;
},
/** @private */
doUpdate: function(/*$super*/)
{
this.tryApplySuper('doUpdate') /* $super() */;
var editor = this.getEditor();
this.setChecked(editor.getShowAllIssueMarkers());
this.setEnabled(editor.getEnableIssueCheck()).setDisplayed(editor.getEnableIssueCheck());
},
/** @ignore */
doExecute: function(target, htmlEvent)
{
this.tryApplySuper('doExecute', [target, htmlEvent]) /* $super(target, htmlEvent) */;
var oldChecked = this.getChecked();
var editor = this.getEditor();
editor.setShowAllIssueMarkers(!oldChecked);
this.setChecked(!oldChecked);
}
});
/**
* Namespace of all operation creation actions for editor.
* @namespace
*/
Kekule.Editor.ActionOperationCreate = {};
/**
* Base operation creation action for editor.
* This type of action is a special action, rather then run execute() directly,
* it's main propers is to create one or multiple operations that need to be performed by the editor.
* Usually, this type of actions should not be bound to UI directly.
* @class
* @augments Kekule.Editor.ActionOnEditor
*
* @param {Kekule.Editor.BaseEditor} editor Target editor object.
*/
Kekule.Editor.ActionOperationCreate.Base = Class.create(Kekule.Editor.ActionOnEditor,
/** @lends Kekule.Editor.ActionOperationCreate.Base# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.ActionOperationCreate.Base',
/** @constructs */
initialize: function(editor)
{
this.tryApplySuper('initialize', [editor]);
},
/**
* Check whether this action can be applied to editor.
* Descendants should override this method.
* @param {Kekule.Editor.BaseEditor} editor
*/
applicable: function(editor)
{
var targets = this.getOperationTargets();
if (targets && targets.length)
{
var data = this.getData();
for (var i = 0, l = targets.length; i < l; ++i)
{
if (this.applicableOnTarget(targets[i], data, editor))
{
return true;
}
}
return false;
}
return false;
},
/**
* Check if an operation can be created on target object in editor.
* Descendants should override this method.
* @param {Object} target
* @param {Hash} data
* @param {Kekule.Editor.BaseEditor} editor
* @returns {Bool}
*/
applicableOnTarget: function(target, data, editor)
{
return false;
},
/**
* Returns objects in editor that act as operation targets.
* Descendants should override this method.
* @param {Kekule.Editor.BaseEditor} editor
* @returns {Array}
*/
getOperationTargets: function(editor)
{
return [];
},
/**
* Returns the associated data to run the action.
* @returns {Hash}
* @private
*/
getData: function()
{
return this.ACTION_DATA || {};
},
/**
* If action can be applied to target chem objects, this method create the concrete operation.
* @param {Kekule.Editor.BaseEditor} editor
* @returns {Kekule.Operation}
* @private
*/
createOperations: function(editor)
{
var chemObjs = this.getOperationTargets(editor);
var data = this.getData();
var opers = [];
for (var i = 0, l = chemObjs.length; i < l; ++i)
{
var target = chemObjs[i];
if (this.applicableOnTarget(target, data, editor))
{
var oper = this.doCreateOperationOnTarget(target, data, editor);
if (oper)
{
opers.push(oper);
}
}
}
return opers;
},
/**
* Do concrete work of creating operation on one target.
* Descendants should override this method.
* @param {Object} target
* @param {Hash} data Data of the modification.
* @param {Kekule.Editor.BaseEditor} editor
* @returns {Kekule.Operation}
* @private
*/
doCreateOperationOnTarget: function(target, data, editor)
{
return null;
},
/** @private */
doExecute: function()
{
var editor = this.getEditor();
var targets = this.getModificationTargets(editor);
var opers = this.createOperations(targets, data, editor);
if (opers && opers.length)
{
editor.execOperations(opers);
return true; // indicating something actually be done
}
else
return false;
}
});
/**
* Base operation creation action for editor.
* This type of action is a special action, rather then run execute() directly,
* it's main propers is to create one or multiple operations that need to be performed by the editor.
* Usually, this type of actions should not be bound to UI directly.
* @class
* @augments Kekule.Editor.ActionOperationCreate.Base
*
* @param {Kekule.Editor.BaseEditor} editor Target editor object.
*/
Kekule.Editor.ActionOperationCreate.ChemObjModify = Class.create(Kekule.Editor.ActionOperationCreate.Base,
/** @lends Kekule.Editor.ActionOperationCreate.ChemObjModify# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.ActionOperationCreate.ChemObjModify',
/** @ignore */
getOperationTargets: function(editor)
{
var result = editor.getHotTrackedObjs(); // first try to apply modification to hot trackeed object
if (!result || !result.length) // then the selection
{
result = editor.getSelection();
}
return result;
},
});
/**
* Modify or replace a chem node in editor.
* @class
* @augments Kekule.Editor.ActionOperationCreate.ChemObjModify
*/
Kekule.Editor.ActionOperationCreate.ChemNodeModify = Class.create(Kekule.Editor.ActionOperationCreate.ChemObjModify,
/** @lends Kekule.Editor.ActionOperationCreate.ChemNodeModify# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.ActionOperationCreate.ChemNodeModify',
/** @ignore */
applicableOnTarget: function(target, data, editor)
{
// at least there should be a bond connected to target, avoid modifier standalone molecule in editor
return (target instanceof Kekule.ChemStructureNode) && !(target instanceof Kekule.Molecule) && (target.getLinkedBonds().length);
},
/** @ignore */
doCreateOperationOnTarget: function(target, data, editor)
{
return Kekule.Editor.OperationUtils.createNodeModificationOperationFromData(target, data, editor);
}
});
/**
* Modify or replace a chem connector in editor.
* @class
* @augments Kekule.Editor.ActionOperationCreate.ChemObjModify
*/
Kekule.Editor.ActionOperationCreate.ChemConnectorModify = Class.create(Kekule.Editor.ActionOperationCreate.ChemObjModify,
/** @lends Kekule.Editor.ActionOperationCreate.ChemConnectorModify# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.ActionOperationCreate.ChemConnectorModify',
/** @ignore */
applicableOnTarget: function(target, data, editor)
{
return (target instanceof Kekule.ChemStructureConnector);
},
/** @ignore */
doCreateOperationOnTarget: function(target, data, editor)
{
// data is simply a prop-value pair hash
if (Kekule.ObjUtils.match(target, data)) // no actual modified props
return null;
else
return new Kekule.ChemObjOperation.Modify(target, data, editor);
}
});
/** @ignore */
Kekule.Editor.createEditorOperationCreateActionClass = function(classShortName, actionRegName, superClass, actionData)
{
var definition = {};
if (classShortName)
definition.CLASS_NAME = 'Kekule.Editor.ActionOperationCreate.' + classShortName;
if (actionData)
definition.ACTION_DATA = actionData;
var result = Class.create(superClass, definition);
if (actionRegName)
{
_editorActionRegInfo.push({'name': actionRegName, 'actionClass': result});
}
return result;
};
// create and register some default operation create action classes
function _createAndRegisterNodeModifyActions()
{
var atomIsotopeIds = ['C', 'H', 'O', 'N', 'P', 'S', 'Si', 'F', 'Cl', 'Br', 'I', 'B', 'K', 'Na', 'D'];
var repSubgroupNames = ['methyl', 'ethyl', 'phenyl', 'Ac', 'TMS', 'COOCH3', 'OTs'];
var superClass = Kekule.Editor.ActionOperationCreate.ChemNodeModify;
var create = Kekule.Editor.createEditorOperationCreateActionClass;
for (var i = 0, l = atomIsotopeIds.length; i < l; ++i)
{
var actionId = 'atom_' + atomIsotopeIds[i];
create(null, actionId, superClass, {'nodeClass': Kekule.Atom, props: {'isotopeId': atomIsotopeIds[i]}});
}
for (var i = 0, l = repSubgroupNames.length; i < l; ++i)
{
var repName = repSubgroupNames[i];
var actionId = 'subgroup_' + repName;
var repItem = Kekule.Editor.RepositoryItemManager.getItem(repName);
var structFragment = repItem.getStructureFragment && repItem.getStructureFragment();
if (structFragment)
create(null, actionId, superClass, {'nodeClass': structFragment.getClass(), 'repositoryItem': repItem});
}
// special nodes
create(null, 'subgroup_R', superClass, {'nodeClass': Kekule.SubGroup});
create(null, 'atom_variable', superClass, {'nodeClass': Kekule.VariableAtom});
create(null, 'atom_dummy', superClass, {'nodeClass': Kekule.Pseudoatom, 'props': {'atomType': Kekule.PseudoatomType.DUMMY}});
create(null, 'atom_hetero', superClass, {'nodeClass': Kekule.Pseudoatom, 'props': {'atomType': Kekule.PseudoatomType.HETERO}});
create(null, 'atom_any', superClass, {'nodeClass': Kekule.Pseudoatom, 'props': {'atomType': Kekule.PseudoatomType.ANY}});
}
function _createAndRegisterConnectorModifyActions()
{
var BT = Kekule.BondType;
var BO = Kekule.BondOrder;
var BS = Kekule.BondStereo;
var bondMap = {
'single': {'bondType': BT.COVALENT, 'bondOrder': BO.SINGLE, 'stereo': BS.NONE},
'double': {'bondType': BT.COVALENT, 'bondOrder': BO.DOUBLE, 'stereo': BS.NONE},
'triple': {'bondType': BT.COVALENT, 'bondOrder': BO.TRIPLE, 'stereo': BS.NONE},
'quad': {'bondType': BT.COVALENT, 'bondOrder': BO.QUAD, 'stereo': BS.NONE},
'up': {'bondType': BT.COVALENT, 'bondOrder': BO.SINGLE, 'stereo': BS.UP},
'down': {'bondType': BT.COVALENT, 'bondOrder': BO.SINGLE, 'stereo': BS.DOWN},
'closer': {'bondType': BT.COVALENT, 'bondOrder': BO.SINGLE, 'stereo': BS.CLOSER},
'upOrDown': {'bondType': BT.COVALENT, 'bondOrder': BO.SINGLE, 'stereo': BS.UP_OR_DOWN}
};
var superClass = Kekule.Editor.ActionOperationCreate.ChemConnectorModify;
var create = Kekule.Editor.createEditorOperationCreateActionClass;
var names = Kekule.ObjUtils.getOwnedFieldNames(bondMap);
for (var i = 0, l = names.length; i < l; ++i)
{
create(null, 'bond_' + names[i], superClass, bondMap[names[i]]);
}
}
function _createAndRegisterPredefinedOperationCreateActions()
{
_createAndRegisterNodeModifyActions();
_createAndRegisterConnectorModifyActions();
}
/**
* Base class for actions for chem composer.
* @class
* @augments Kekule.Action
*
* @param {Kekule.Editor.Composer} composer Target composer widget.
* @param {String} caption
* @param {String} hint
*/
Kekule.Editor.ActionOnComposer = Class.create(Kekule.Action,
/** @lends Kekule.Editor.ActionOnComposer# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.ActionOnComposer',
/** @constructs */
initialize: function(/*$super, */composer, caption, hint)
{
this.tryApplySuper('initialize') /* $super() */;
this.setText(caption);
this.setHint(hint);
this.setComposer(composer);
},
/** @private */
initProperties: function()
{
this.defineProp('composer', {'dataType': 'Kekule.Editor.Composer', 'serializable': false});
},
/** @private */
doUpdate: function()
{
var composer = this.getComposer();
var editor = this.getEditor();
this.setEnabled(composer && composer.getEnabled() && editor && editor.getChemObj() && editor.getChemObjLoaded() && editor.getEnabled());
},
/**
* Returns chem editor object inside composer.
* @returns {Kekule.Editor.BaseEditor}
*/
getEditor: function()
{
var composer = this.getComposer();
return composer? composer.getEditor(): null;
}
});
/**
* Actions that has a series of child attached actions on composer (such as bond action
* may has single, double, triple bond variations).
* @class
* @augments Kekule.Editor.ActionOnComposer
*
* @property {Kekule.ActionList} attachedActions
* @property {Kekule.Action} defaultAttachedAction If this property is set, when check on the parent action,
* if no attached action is checked, default one will be checked on automatically.
* @property {Class} defaultAttachedActionClass If this property is set, when creating child attached actions,
* {@link Kekule.Editor.ActionOnComposerAdv.defaultAttachedAction} will be set automatically.
*/
Kekule.Editor.ActionOnComposerAdv = Class.create(Kekule.Editor.ActionOnComposer,
/** @lends Kekule.Editor.ActionOnComposerAdv# */
{
/** @private */
CLASS_NAME: 'Kekule.Editor.ActionOnComposerAdv',
/** @constructs */
initialize: function(/*$super, */composer, caption, hint)
{
var actions = new Kekule.ActionList();
actions.setOwnActions(true);
this.setPropStoreFieldValue('attachedActions', actions);
this.tryApplySuper('initialize', [composer, caption, hint]) /* $super(composer, caption, hint) */;
},
finalize: function(/*$super*/)
{
this.getAttachedActions().finalize();
this.tryApplySuper('finalize') /* $super() */;
},
/** @private */
initProperties: function()
{
this.defineProp('attachedActionClasses', {'dataType': DataType.ARRAY, 'serializable': false, 'setter': null});
this.defineProp('attachedActions', {'dataType': 'Kekule.ActionList', 'serializable': false, 'setter': null});
this.defineProp('defaultAttachedAction', {'dataType': 'Kekule.Action', 'serializable': false});
this.defineProp('defaultAttachedActionClass', {'dataType': DataType.CLASS, 'serializable': false});
},
/** @private */
checkedChanged: function(/*$super*/)
{
this.tryApplySuper('checkedChanged') /* $super() */;
var checked = this.getChecked();
//console.log('self checked change', this.getClassName(), checked);
if (this.hasAttachedActions())
{
var composer = this.getComposer();
if (composer)
{
if (checked)
{
var attachedActions = this.getAttachedActions();
composer.bindAssocActions(attachedActions);
composer.showAssocToolbar();
var checkedChild = attachedActions.getCheckedAction(this.getClassName());
//console.log('child checked change', this.getClassName(), checkedChild.getClassName(), attachedActions.getActions());
/*
if (checkedChild)
{
console.log('execute child', checkedChild.getClassName());
checkedChild.execute();
}
else
{
var defAction = this.getDefaultAttachedAction();
if (defAction)
{
if (defAction.getCheckGroup() && !this.getAttachedActions().hasActionChecked(defAction.getCheckGroup()))
defAction.execute();
}
}
*/
if (!checkedChild)
checkedChild = this.getDefaultAttachedAction();
// check and execute child
if (checkedChild)
{
checkedChild.setChecked(false); // important, force execute again
checkedChild.execute();
}
}
else
{
composer.hideAssocToolbar();
}
}
}
},
doSetDefaultAttachedActionClass: function(value) {
this.setPropStoreFieldValue('defaultAttachedActionClass', value);
if (value && this.hasAttachedActions())
{
for (var i = 0, l = this.getAttachedActions().getActionCount(); i < l; ++i)
{
var attachedActio