kekule
Version:
Open source JavaScript toolkit for chemoinformatics
3,758 lines • 126 kB
JavaScript
/**
* @fileoverview
* Implemetation of base class involves in displaying chem object (such as viewer or editor).
* @author Partridge Jiang
*/
/*
* requires /lan/classes.js
* requires /utils/kekule.utils.js
* requires /xbrowsers/kekule.x.js
* requires /render/kekule.render.configs.js
* requires /render/kekule.render.utils.js
* requires /widgets/operation/kekule.actions.js
* requires /widgets/kekule.widget.base.js
* requires /widgets/kekule.widget.helpers.js
* requires /widgets/kekule.widget.styleResources.js
* requires /widgets/commonCtrls/kekule.widget.buttons.js
* requires /widgets/commonCtrls/kekule.widget.tabViews.js
* requires /widgets/commonCtrls/kekule.widget.dialogs.js
* requires /widgets/commonCtrls/kekule.widget.formControls.js
* requires /widgets/chem/kekule.chemWidget.base.js
* requires /widgets/chem/kekule.chemWidget.dialogs.js
*/
(function(){
"use strict";
var PS = Class.PropertyScope;
var AU = Kekule.ArrayUtils;
var DU = Kekule.DomUtils;
var ZU = Kekule.ZoomUtils;
var CW = Kekule.ChemWidget;
//var CWT = Kekule.ChemWidgetTexts;
var CNS = Kekule.Widget.HtmlClassNames;
var CCNS = Kekule.ChemWidget.HtmlClassNames;
/** @ignore */
Kekule.ChemWidget.HtmlClassNames = Object.extend(Kekule.ChemWidget.HtmlClassNames, {
DISPLAYER_DRAWCONTEXT_PARENT: 'K-Chem-Displayer-DrawContext-Parent',
ACTION_MOL_DISPLAY_TYPE: 'K-Chem-MolDisplayType',
ACTION_MOL_DISPLAY_SKELETAL: 'K-Chem-MolDisplaySkeletal',
ACTION_MOL_DISPLAY_CONDENSED: 'K-Chem-MolDisplayCondensed',
ACTION_MOL_DISPLAY_WIRE: 'K-Chem-MolDisplayWire',
ACTION_MOL_DISPLAY_BALLSTICK: 'K-Chem-MolDisplayBallStick',
ACTION_MOL_DISPLAY_STICKS: 'K-Chem-MolDisplaySticks',
ACTION_MOL_DISPLAY_SPACEFILL: 'K-Chem-MolDisplaySpaceFill',
ACTION_MOL_HIDE_HYDROGENS: 'K-Chem-MolHideHydrogens',
ACTION_MOL_AUTO_COORD_GENERATION: 'K-Chem-MolAutoCoordGeneration',
ACTION_ZOOMIN: 'K-Chem-ZoomIn',
ACTION_ZOOMOUT: 'K-Chem-ZoomOut',
ACTION_RESET: 'K-Chem-Reset',
ACTION_COPY: 'K-Chem-Copy',
ACTION_RESET_ZOOM: 'K-Chem-ResetZoom',
ACTION_LOADFILE: 'K-Chem-LoadFile',
ACTION_LOADDATA: 'K-Chem-LoadData',
ACTION_SAVEFILE: 'K-Chem-SaveFile',
ACTION_CLEAROBJS: 'K-Chem-ClearObjs',
ACTION_CONFIG: 'K-Chem-Config',
DIALOG_CHOOSE_FILE_FORAMT: 'K-Chem-Dialog-Choose-File-Format',
DIALOG_CHOOSE_FILE_FORAMT_FORMATBOX: 'K-Chem-Dialog-Choose-File-Format-FormatBox',
DIALOG_CHOOSE_FILE_FORAMT_PREVIEWER_REGION: 'K-Chem-Dialog-Choose-File-Format-PreviewerRegion',
DIALOG_CHOOSE_FILE_FORAMT_PREVIEWER: 'K-Chem-Dialog-Choose-File-Format-Previewer'
});
/**
* Root config class of {@link Kekule.ChemWidget.ChemObjDisplayer}.
* @class
* @augments Kekule.AbstractConfigs
*
* @property {Kekule.ChemWidget.ChemObjDisplayerIOConfigs} ioConfigs
*/
Kekule.ChemWidget.ChemObjDisplayerConfigs = Class.create(Kekule.AbstractConfigs,
/** @lends Kekule.ChemWidget.ChemObjDisplayerConfigs# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.ChemObjDisplayerConfigs',
/** @private */
initProperties: function()
{
this.addConfigProp('ioConfigs', 'Kekule.ChemWidget.ChemObjDisplayerIOConfigs');
this.addConfigProp('environment2DConfigs', 'Kekule.ChemWidget.ChemObjDisplayerEnvironmentConfigs');
this.addConfigProp('environment3DConfigs', 'Kekule.ChemWidget.ChemObjDisplayerEnvironmentConfigs');
},
/** @private */
initPropValues: function(/*$super*/)
{
this.tryApplySuper('initPropValues') /* $super() */;
this.setPropStoreFieldValue('ioConfigs', new Kekule.ChemWidget.ChemObjDisplayerIOConfigs());
this.setPropStoreFieldValue('environment2DConfigs', new Kekule.ChemWidget.ChemObjDisplayerEnvironmentConfigs());
this.setPropStoreFieldValue('environment3DConfigs', new Kekule.ChemWidget.ChemObjDisplayerEnvironmentConfigs());
}
});
/**
* Config class of I/O options for {@link Kekule.ChemWidget.ChemObjDisplayer}.
* @class
* @augments Kekule.AbstractConfigs
*
* @property {Bool} canonicalizeBeforeSave Whether canonicalize molecules in displayer before saving them.
* @property {Bool} autoGenerateCoordsAfterLoad Whether generate coords when loading molecule without atom coords (e.g. SMILES).
*/
Kekule.ChemWidget.ChemObjDisplayerIOConfigs = Class.create(Kekule.AbstractConfigs,
/** @lends Kekule.ChemWidget.ChemObjDisplayerIOConfigs# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.ChemObjDisplayerIOConfigs',
/** @private */
initProperties: function()
{
this.addBoolConfigProp('canonicalizeBeforeSave', true);
this.addBoolConfigProp('autoGenerateCoordsAfterLoad', true);
}
});
/**
* Config class of 2D rendering environment options for {@link Kekule.ChemWidget.ChemObjDisplayer}.
* @class
* @augments Kekule.AbstractConfigs
*
* @property {Bool} antialias Whether use a more aggressive antialias strategy than the browser's default.
* @property {Float} antialiasBlurRatio The blur level to make the 2D rendering more smooth.
* Effects will only be take when property {@link Kekule.ChemWidget.ChemObjDisplayerEnvironmentConfigs.antialias} is true.
* @property {Float} overSamplingRatio The oversampling ratio to make the 2D rendering more smooth.
* Effects will only be take when property {@link Kekule.ChemWidget.ChemObjDisplayerEnvironmentConfigs.antialias} is true.
*/
Kekule.ChemWidget.ChemObjDisplayerEnvironmentConfigs = Class.create(Kekule.AbstractConfigs,
/** @lends Kekule.ChemWidget.ChemObjDisplayerEnvironmentConfigs# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.ChemObjDisplayerEnvironmentConfigs',
/** @private */
initProperties: function()
{
this.addBoolConfigProp('antialias', false);
this.addFloatConfigProp('antialiasBlurRatio', 0);
this.addFloatConfigProp('overSamplingRatio', 1);
}
});
/**
* An widget related to display chem objects (especially molecules).
* Note, this class is simply a base class for viewer and editor. User should not use this one directly.
* @class
* @augments Kekule.ChemWidget.AbstractWidget
*
* @param {Variant} parentOrElementOrDocument
* @param {Kekule.ChemObject} chemObj
* @param {Int} renderType Display in 2D or 3D. Value from {@link Kekule.Render.RendererType}.
* @param {Kekule.ChemWidget.ChemObjDisplayerConfigs} displayerConfigs Configs of current displayer.
*
* @property {Kekule.ChemWidget.ChemObjDisplayerConfigs} displayerConfigs Configs of current displayer.
* @property {Int} renderType Display in 2D or 3D. Value from {@link Kekule.Render.RendererType}. Read only.
* @property {Kekule.ChemObject} chemObj Object to be drawn. Set this property will repaint the client.
* @property {Bool} chemObjLoaded Whether the chemObj is successful loaded and drawn in viewer.
* @property {Bool} resetAfterLoad Whether reset display (remove rotate, zoom and so on) after set a new chem obj.
* @property {Object} renderConfigs Configuration for rendering.
* @property {Int} moleculeDisplayType Display type of molecule in displayer. Value from {@link Kekule.Render.Molecule2DDisplayType} or {@link Kekule.Render.Molecule3DDisplayType}.
* @property {Hash} drawOptions A series of params to render chem object.
* @property {Float} zoom Zoom ratio to draw chem object, equal to drawOptions.zoom.
* @property {Float} initialZoom Initial zoom when just loading a chem object. Default is 1.
* @property {Bool} autoSize Whether the widget change its size to fit the dimension of chem object.
* @property {Int} padding Padding between chem object and edge of widget, in px. Only works when autoSize is true.
* @property {Hash} baseCoordOffset Usually displayer draw object at center of widget, use this property to make
* the drawing center moved from widget center.
* Note: this property is useless when autoSize == true.
* @property {Hash} transformParams A combination of (@link Kekule.ChemWidget.ChemObjDisplayer.drawOptions} and (@link Kekule.ChemWidget.ChemObjDisplayer.baseCoordOffset}.
* @property {String} backgroundColor Get or set background color of displayer. Default is transparent.
* @property {Bool} enableLoadNewFile Whether open a external file to displayer is allowed.
* @property {Array} allowedInputFormatIds Formats that shown in input file dialog. Default is null, means accept all available formats.
* @property {Array} allowedOutputFormatIds Formats that shown in output file dialog. Default is null, means accept all available formats.
* @property {String} defaultInputFormatId
* @property {String} defaultOutputFormatId
* @property {Hash} standardizationOptions Possible options when do standardization on molecule before saving.
*/
/**
* Invoked when the a chem object (or null) is loaded into the displayer.
* event param of it has one fields: {obj: Object}.
* @name Kekule.ChemWidget.ChemObjDisplayer#load
* @event
*/
/**
* Invoked when the displayer is repainted.
* event param of it has one fields: {obj: Object}.
* @name Kekule.ChemWidget.ChemObjDisplayer#repaint
* @event
*/
Kekule.ChemWidget.ChemObjDisplayer = Class.create(Kekule.ChemWidget.AbstractWidget,
/** @lends Kekule.ChemWidget.ChemObjDisplayer# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.ChemObjDisplayer',
/** @construct */
initialize: function(/*$super, */parentOrElementOrDocument, chemObj, renderType, displayerConfigs)
{
this._paintFlag = 0; // used internally
//this._errorReportElem = null; // use internally
this._bgColorMap = {}; // use internally
this._contextTransformOpsMap = new Kekule.MapEx();
this._reactChemObjDrawBind = this._reactChemObjDraw.bind(this);
this.setPropStoreFieldValue('resetAfterLoad', true);
this.setPropStoreFieldValue('renderType', renderType || Kekule.Render.RendererType.R2D); // must set this value first
this.setPropStoreFieldValue('displayerConfigs', displayerConfigs || this.createDefaultConfigs());
this.tryApplySuper('initialize', [parentOrElementOrDocument]) /* $super(parentOrElementOrDocument) */;
if (chemObj)
{
this.setChemObj(chemObj);
}
//this.setUseCornerDecoration(true);
this.setEnableLoadNewFile(true);
//this.setPropStoreFieldValue('displayerConfigs', displayerConfigs || this.createDefaultConfigs());
},
/** @private */
doFinalize: function(/*$super*/)
{
//this.setChemObj(null);
/*
var r = this.getPropStoreFieldValue('boundInfoRecorder');
if (r)
r.finalize();
this.setPropStoreFieldValue('boundInfoRecorder', null);
*/
this.clearSubViews();
this.setPropStoreFieldValue('chemObj', null);
this.getPainter().finalize();
var b = this.getPropStoreFieldValue('drawBridge');
var ctx = this.getPropStoreFieldValue('drawContext');
if (ctx && b)
b.releaseContext(ctx);
if (b && b.finalize)
b.finalize();
this.setPropStoreFieldValue('drawBridge', null);
this.setPropStoreFieldValue('drawContext', null);
if (this._contextTransformOpsMap)
this._contextTransformOpsMap.finalize();
this.tryApplySuper('doFinalize') /* $super() */;
},
/** @private */
initProperties: function()
{
this.defineProp('displayerConfigs', {'dataType': 'Kekule.ChemWidget.ChemObjDisplayerConfigs', 'serializable': false, 'scope': PS.PUBLIC});
this.defineProp('enableLoadNewFile', {'dataType': DataType.BOOL});
this.defineProp('allowedInputFormatIds', {'dataType': DataType.ARRAY});
this.defineProp('allowedOutputFormatIds', {'dataType': DataType.ARRAY});
this.defineProp('defaultInputFormatId', {'dataType': DataType.STRING, 'serializable': false});
this.defineProp('defaultOutputFormatId', {'dataType': DataType.STRING, 'serializable': false});
this.defineProp('standardizationOptions', {'dataType': DataType.HASH});
this.defineProp('resetAfterLoad', {'dataType': DataType.BOOL});
this.defineProp('chemObj', {'dataType': 'Kekule.ChemObject', 'serializable': false, 'scope': PS.PUBLISHED,
'setter': function(value)
{
var oldObj = this.getPropStoreFieldValue('chemObj');
//if (value !== oldObj) // some times oldObj itself may change and may need to repaint
{
this.setPropStoreFieldValue('chemObj', value);
this.chemObjChanged(value, oldObj);
}
}
});
this.defineProp('chemObjData', {'dataType': DataType.STRING, 'serializable': false, 'getter': null,
'setter': function(value)
{
var jsonObj;
if (typeof(value) === 'object')
jsonObj = value;
else
jsonObj = JSON.parse(value);
//console.log('json', jsonObj, value);
if (jsonObj)
{
var chemObj;
chemObj = (jsonObj.format)? Kekule.IO.loadFormatData(jsonObj.data, jsonObj.format):
(jsonObj.mimeType)? Kekule.IO.loadMimeData(jsonObj.data, jsonObj.mimeType):
null;
if (chemObj)
this._tryAutoGenerateChemObjCoordsAndLoad(chemObj);
}
}
});
this.defineProp('chemObjLoaded', {'dataType': DataType.BOOL, 'serializable': false, 'scope': PS.PUBLIC,
'setter': null,
'getter': function() { return this.getChemObj() && this.getPropStoreFieldValue('chemObjLoaded'); }
});
this.defineProp('renderType', {'dataType': DataType.INT, 'serializable': false, 'enumSource': Kekule.Render.RendererType, 'scope': PS.PUBLISHED,
'setter': function(value)
{
if (!this.getAllowRenderTypeChange())
{
Kekule.error(Kekule.$L('ErrorMsg.RENDER_TYPE_CHANGE_NOT_ALLOWED'));
return;
}
var oldValue = this.getRenderType();
if (value !== oldValue)
{
this.setPropStoreFieldValue('renderType', value);
this.resetRenderType(oldValue, value);
}
}
});
this.defineProp('renderConfigs', {'dataType': DataType.OBJECT, 'serializable': false, 'scope': PS.PUBLIC,
'getter': function()
{
var result = this.getPropStoreFieldValue('renderConfigs');
if (!result)
result = (this.getRenderType() === Kekule.Render.RendererType.R3D)?
Kekule.Render.Render3DConfigs.getInstance():
Kekule.Render.Render2DConfigs.getInstance();
return result;
},
'setter': function(value)
{
this.setPropStoreFieldValue('renderConfigs', value);
var painter = this.getPropStoreFieldValue('painter');
if (painter)
painter.setRenderConfigs(this.getRenderConfigs());
}
});
this.defineProp('backgroundColor', {'dataType': DataType.STRING, 'scope': PS.PUBLISHED,
'setter': function(value)
{
this.setPropStoreFieldValue('backgroundColor', value);
//this.setBackgroundColorOfType(value, this.getRenderType());
this._bgColorMap[this.getRenderType().toString()] = value;
this.backgroundColorChanged();
}
});
this.defineProp('inheritedRenderStyles', {'dataType': DataType.ARRAY, 'scope': PS.PUBLIC,
'getter': function()
{
var result = this.getPropStoreFieldValue('inheritedRenderStyles');
if (!result)
{
result = [];
this.setPropStoreFieldValue('inheritedRenderStyles', result);
}
return result;
},
'setter': function(value)
{
this.setPropStoreFieldValue('inheritedRenderStyles', value || []);
this.requestRepaint();
}
});
this.defineProp('inheritedRenderColor', {'dataType': DataType.BOOL, 'scope': PS.PUBLISHED,
'getter': function()
{
var inheritedStyles = this.getInheritedRenderStyles();
return !!(inheritedStyles && (inheritedStyles.indexOf('color') >= 0));
},
'setter': function(value)
{
if ((!!value) !== this.getInheritedRenderColor())
{
var styles = this.getInheritedRenderStyles();
if (value)
styles.push('color');
else
Kekule.ArrayUtils.remove(styles, 'color');
this.setInheritedRenderStyles(styles);
}
}
});
this.defineProp('inheritedRenderBackgroundColor', {'dataType': DataType.BOOL, 'scope': PS.PUBLISHED,
'getter': function()
{
var inheritedStyles = this.getInheritedRenderStyles();
return !!(inheritedStyles && (inheritedStyles.indexOf('backgroundColor') >= 0));
},
'setter': function(value)
{
if ((!!value) !== this.getInheritedRenderBackgroundColor())
{
var styles = this.getInheritedRenderStyles();
if (value)
styles.push('backgroundColor');
else
Kekule.ArrayUtils.remove(styles, 'backgroundColor');
this.setInheritedRenderStyles(styles);
}
}
});
this.defineProp('enableCustomCssProperties', {'dataType': DataType.BOOL,
'setter': function(value){
if (value != this.getEnableCustomCssProperties())
{
this.setPropStoreFieldValue('enableCustomCssProperties', !!value);
this.requestRepaint();
}
}
});
//this.defineProp('actualOverSamplingRatio', {'dataType': DataType.FLOAT, 'setter': null, 'scope': PS.PRIVATE}); // private
this.defineProp('transformParams', {'dataType': DataType.HASH, 'serializable': false, 'scope': PS.PUBLIC,
'getter': function()
{
var result = Object.extend({}, this.getDrawOptions());
result.screenCoordOffset = this.getBaseCoordOffset();
return result;
},
'setter': function(value)
{
var param = value || {};
this.setDrawOptions(param);
this.setBaseCoordOffset(param.baseCoordOffset);
this.drawOptionChanged();
}
});
this.defineProp('drawOptions', {'dataType': DataType.HASH, 'serializable': false, 'scope': PS.PUBLIC,
'getter': function()
{
var result = this.getPropStoreFieldValue('drawOptions');
if (!result)
{
result = {};
this.setPropStoreFieldValue('drawOptions', result);
}
return result;
},
'setter': function(value)
{
this.setPropStoreFieldValue('drawOptions', value);
this.drawOptionChanged();
}
});
this.defineProp('zoom', {'dataType': DataType.FLOAT, 'serializable': false,
'getter': function()
{
var op = this.getDrawOptions() || {};
return op.zoom;
},
'setter': function(value)
{
this.zoomTo(value);
return this;
}
});
this.defineProp('initialZoom', {'dataType': DataType.FLOAT});
this.defineProp('autofit', {'dataType': DataType.BOOL, 'serializable': false,
'getter': function()
{
var op = this.getDrawOptions() || {};
return op.autofit;
},
'setter': function(value)
{
var op = this.getDrawOptions() || {};
op.autofit = value;
return this.setDrawOptions(op);
}
});
this.defineProp('autoShrink', {'dataType': DataType.BOOL, 'serializable': false,
'getter': function()
{
var op = this.getDrawOptions() || {};
return op.autoShrink;
},
'setter': function(value)
{
var op = this.getDrawOptions() || {};
op.autoShrink = value;
return this.setDrawOptions(op);
}
});
this.defineProp('moleculeDisplayType', {'dataType': DataType.INT, 'serializable': false,
'getter': function()
{
var op = this.getDrawOptions() || {};
return op.moleculeDisplayType;
},
'setter': function(value)
{
this.getDrawOptions().moleculeDisplayType = value;
this.drawOptionChanged();
return this;
}
});
this.defineProp('hideHydrogens', {'dataType': DataType.BOOL, 'serializable': false,
'getter': function()
{
var op = this.getDrawOptions() || {};
return op.hideHydrogens;
},
'setter': function(value)
{
this.getDrawOptions().hideHydrogens = !!value;
this.drawOptionChanged();
return this;
}
});
this.defineProp('allowCoordBorrow', {'dataType': DataType.BOOL, 'serializable': false, 'scope': PS.PUBLIC,
'getter': function()
{
return Kekule.oneOf(this.getDrawOptions().allowCoordBorrow, this.getRenderConfigs().getGeneralConfigs().getAllowCoordBorrow());
}
});
//this.defineProp('zoom', {'dataType': DataType.FLOAT, 'serializable': false});
this.defineProp('autoSize', {'dataType': DataType.BOOL, 'serializable': false,
'setter': function(value)
{
if (value != this.getAutoSize())
{
this.setPropStoreFieldValue('autoSize', value);
if (value && this.allowAutoSize())
this.drawOptionChanged(); // force repaint
}
}
});
this.defineProp('padding', {'dataType': DataType.INT, 'serializable': false,
'setter': function(value)
{
if (value != this.getPadding())
{
this.setPropStoreFieldValue('padding', value);
/*
if (this.allowAutoSize())
this.drawOptionChanged(); // force repaint
*/
this.getDrawOptions().autofitContextPadding = value;
this.drawOptionChanged();
}
}
});
this.defineProp('baseCoordOffset', {'dataType': DataType.Hash, 'serializable': false,
'setter': function(value)
{
this.setPropStoreFieldValue('baseCoordOffset', value);
if (this.getChemObjLoaded())
this.repaint();
}
});
// private properties
this.defineProp('drawBridge', {'dataType': DataType.OBJECT, 'serializable': false, 'scope': PS.PRIVATE,
'setter': null,
'getter': function(slient)
{
var result = this.getPropStoreFieldValue('drawBridge');
if (!result && !this.__$drawBridgeInitialized$__)
{
this.__$drawBridgeInitialized$__ = true; // avoid call this.createDrawBridge() multiple times
result = this.createDrawBridge(/*slient*/);
this.setPropStoreFieldValue('drawBridge', result);
}
return result;
}
});
this.defineProp('drawContext', {'dataType': DataType.OBJECT, 'serializable': false, 'scope': PS.PRIVATE,
'setter': null,
'getter': function()
{
var result = this.getPropStoreFieldValue('drawContext');
if (!result)
{
var bridge = this.getDrawBridge();
if (bridge)
{
var elem = this.getDrawContextParentElem();
if (!elem)
return null;
else
{
//var dim = Kekule.HtmlElementUtils.getElemScrollDimension(elem);
//var dim = Kekule.HtmlElementUtils.getElemClientDimension(elem);
var dim = Kekule.HtmlElementUtils.getElemOffsetDimension(elem);
result = bridge.createContext(elem, dim.width, dim.height, this.getContextCreationParams(this.getRenderType(), bridge));
/*
if (result !== elem)
Kekule.HtmlElementUtils.addClass(result, CNS.DYN_CREATED);
*/
// a little smaller than current element, avoid scroll bars in when setting CSS3's resize: both property
this.setPropStoreFieldValue('drawContext', result);
}
}
}
return result;
}
});
this.defineProp('painter', {'dataType': 'Kekule.Render.ChemObjPainter', 'serializable': false, 'scope': PS.PRIVATE,
'setter': null,
'getter': function()
{
var result = this.getPropStoreFieldValue('painter');
if (!result)
{
result = this.createNewPainter();
}
return result;
}
});
this.defineProp('rootRenderer', {'dataType': 'Kekule.Render.AbstractRenderer', 'serializable': false, 'scope': PS.PRIVATE,
'setter': null,
'getter': function()
{
var p = this.getPainter();
return p? p.getRenderer(): null;
}
});
// private object to record all bound infos
this.defineProp('boundInfoRecorder', {'dataType': 'Kekule.Render.BoundInfoRecorder', 'serializable': false, 'scope': Class.PropertyScope.PRIVATE, 'setter': null,
'getter': function() { var p = this.getRootRenderer(); return p && p.getBoundInfoRecorder(); }
});
// private, sub views for individual objects in displayer
this.defineProp('subViews', {'dataType': DataType.ARRAY, 'serializable': false, 'setter': null,
'getter': function(canCreate) {
var result = this.getPropStoreFieldValue('subViews');
if (!result && canCreate)
{
result = [];
this.setPropStoreFieldValue('subViews', result);
}
return result;
}
});
},
/** @ignore */
initPropValues: function(/*$super*/)
{
this.tryApplySuper('initPropValues') /* $super() */;
this.setStandardizationOptions({'unmarshalSubFragments': false, 'clearHydrogens': false}); // do not auto clear explicit H
},
/** @ignore */
doEndUpdate: function(modifiedPropNames)
{
this.tryApplySuper('doEndUpdate', [modifiedPropNames]);
if (this._requestRepainting) // pending painting job in begin/endUpadte block
{
this.repaint(this._requestRepainting.overrideOptions);
}
},
/** @ignore */
elementBound: function(element)
{
this.setObserveElemResize(true);
},
/** @ignore */
doWidgetShowStateChanged: function(/*$super, */isShown)
{
this.tryApplySuper('doWidgetShowStateChanged', [isShown]) /* $super(isShown) */;
/*
As position/size calculation may be wrong when displayed = false,
during the show phase, whole context should be force repainted.
*/
if (isShown)
this.setChemObj(this.getChemObj());
},
/** @ignore */
doFileDragDrop: function(/*$super, */files)
{
if (!files /* || files.length > 1 */)
return this.tryApplySuper('doFileDragDrop') /* $super() */;
else // if only one file is dropped in, output the file content
{
var self = this;
(function()
{
Kekule.IO.loadFileData(files[0], function(chemObj, success)
{
if (success)
{
self.setChemObj(chemObj);
}
});
}).defer(); // call load later, avoid error in load process that prevent result true is returned
return true;
}
},
/** @ignore */
notifyStyleAttribChanged: function(targetElem)
{
this.tryApplySuper('notifyStyleAttribChanged', [targetElem]);
var inheritedStyles = this.getInheritedRenderStyles() || [];
if (inheritedStyles.length)
{
this.requestRepaint();
}
},
/**
* Whether changing render type is allowed in current type of displayer.
* Default is false, descendants may override this method.
* @returns {Bool}
*/
getAllowRenderTypeChange: function()
{
return false;
},
/**
* Notify the render type has been changed.
* Descendants may override this method.
* @private
*/
resetRenderType: function(oldType, newType)
{
var chemObj = this.getChemObj();
var bridge = this.getPropStoreFieldValue('drawBridge');
var context = this.getPropStoreFieldValue('drawContext');
if (bridge)
{
if (context)
bridge.releaseContext(context);
}
this.setPropStoreFieldValue('drawContext', null);
this.setPropStoreFieldValue('drawBridge', null);
this.__$drawBridgeInitialized$__ = false; // important, marks the draw bridge should be reinitialized
var newBgColor = this.getBackgroundColorOfType(newType);
this.getDrawOptions().moleculeDisplayType = this.getDefaultMoleculeDisplayType(newType); // reset display type
//this.setBackgroundColor(newBgColor);
this.setPropStoreFieldValue('backgroundColor', newBgColor);
this.backgroundColorChanged(true); // notify back color change but not repaint, as painter currently is still old one
//if (chemObj) // repaint
//this.setChemObj(chemObj || null);
// clear old draw options if oldObj is set
this._doLoadOnObj(chemObj, {refreshOnly: true});
this.resetDisplay();
},
/**
* Force to recreate drawing context and repaint.
*/
resetRenderEnvironment: function()
{
this.resetRenderType(null, this.getRenderType());
return this;
},
/**
* Returns environment configs of current render mode.
* @private
*/
getDisplayerEnvironmentConfigs: function()
{
var envConfigs = (this.getCoordMode() === Kekule.CoordMode.COORD3D)?
this.getDisplayerConfigs().getEnvironment3DConfigs():
this.getDisplayerConfigs().getEnvironment2DConfigs();
return envConfigs;
},
/** @private */
getContextCreationParams: function(renderType, drawBridge)
{
var result;
var renderConfig = this.getRenderConfigs();
var displayerConfig = this.getDisplayerConfigs();
if (renderType === Kekule.Render.RendererType.R3D)
result = {
'alpha': true,
'antialias': renderConfig.getEnvironmentConfigs().getAntialias()
};
else // 2D
result = {
'alpha': true,
'antialias': displayerConfig.getEnvironment2DConfigs().getAntialias(),
'antialiasBlurRatio': displayerConfig.getEnvironment2DConfigs().getAntialiasBlurRatio(),
'overSamplingRatio': displayerConfig.getEnvironment2DConfigs().getOverSamplingRatio()
};
return result;
},
/**
* Create a default editor config object.
* Descendants may override this method.
* @returns {Kekule.Editor.BaseEditorConfigs}
* @private
*/
createDefaultConfigs: function()
{
return new Kekule.ChemWidget.ChemObjDisplayerConfigs();
},
/**
* Returns coord mode according to current renderType.
* @returns {Int}
*/
getCoordMode: function()
{
return (this.getRenderType() === Kekule.Render.RendererType.R3D)?
Kekule.CoordMode.COORD3D: Kekule.CoordMode.COORD2D;
},
/**
* Set renderType according to coord mode.
* @param {Int} coordMode
*/
setCoordMode: function(coordMode)
{
var rType = (coordMode === Kekule.CoordMode.COORD3D)?
Kekule.Render.RendererType.R3D: Kekule.Render.RendererType.R2D;
this.setRenderType(rType);
},
/** @private */
loadPredefinedResDataToProp: function(propName, resData, success)
{
if (propName === 'chemObj') // only this property can be set by predefined resource
{
if (success)
{
try
{
//var ext = resData.uri? Kekule.UrlUtils.extractFileExt(resData.uri): null;
var chemObj = Kekule.IO.loadTypedData(resData.data, resData.resType, resData.resUri);
if (!chemObj) // try regard resType as format ID
{
chemObj = Kekule.IO.loadFormatData(resData.data, resData.resType);
}
if (chemObj)
{
this._tryAutoGenerateChemObjCoordsAndLoad(chemObj);
}
else
Kekule.error(Kekule.$L('ErrorMsg.LOAD_CHEMDATA_FAILED'));
//this.setChemObj(chemObj);
}
catch(e)
{
this.reportException(e);
}
}
else // else, failed
{
// NOTE: just report a text msg rather than an exception object.
// In IE and Chrome, it seems that error object can not cross context, so that may cause Illegal invocation error in Kekule.ExceptionHandler
this.reportException(/*Kekule.ErrorMsg.CANNOT_LOAD_RES_OF_URI*/Kekule.$L('ErrorMsg.CANNOT_LOAD_RES_OF_URI') + resData.resUri || '');
//Kekule.throwException(Kekule.ErrorMsg.CANNOT_LOAD_RES_OF_URI + resData.resUri || '');
}
}
},
/** @private */
reportException: function(e, exceptionLevel)
{
/*
var msg = Kekule.getExceptionMsg(e);
if (!this._errorReportElem) // create error element
{
this._errorReportElem = this.getDocument().createElement('span');
this._errorReportElem.className = Kekule.Widget.HtmlClassNames.PART_ERROR_REPORT;
this.getElement().appendChild(this._errorReportElem);
}
this._errorReportElem.innerHTML = msg;
Kekule.StyleUtils.setDisplay(this._errorReportElem, '');
*/
Kekule.raise(e, exceptionLevel || Kekule.ExceptionLevel.ERROR);
},
/* @private */
/*
hideExceptionReport: function()
{
if (this._errorReportElem)
{
Kekule.StyleUtils.setDisplay(this._errorReportElem, 'none');
}
},
*/
/** @private */
createDrawBridge: function(slient)
{
var M = (this.getRenderType() === Kekule.Render.RendererType.R3D)?
Kekule.Render.DrawBridge3DMananger: Kekule.Render.DrawBridge2DMananger;
var result = M.getPreferredBridgeInstance();
if (!result) // can not find suitable draw bridge
{
if (!slient)
{
var errorMsg = M.getUnavailableMessage() || Kekule.error(Kekule.$L('ErrorMsg.DRAW_BRIDGE_NOT_SUPPORTED'));
if (errorMsg)
this.reportException(errorMsg, Kekule.ExceptionLevel.NOT_FATAL_ERROR);
}
// Kekule.error(/*Kekule.ErrorMsg.DRAW_BRIDGE_NOT_SUPPORTED*/Kekule.$L('ErrorMsg.DRAW_BRIDGE_NOT_SUPPORTED'));
}
/* infinite loop, remove this part
if (this.getBackgroundColor() && result.setClearColor)
result.setClearColor(this.getDrawContext(), this.getBackgroundColor());
*/
return result;
},
/**
* Returns the root element to hold the drawing context parents.
* Descendants may override this method
* @returns {HTMLElement}
*/
getClientElement: function()
{
return this.getElement();
},
/**
* Returns parent element to create draw context inside.
* Descendants can override this method.
*/
getDrawContextParentElem: function(disableAutoCreate)
{
//return this.getElement();
var result = this._drawContextParentElem;
if (!result && !disableAutoCreate) // create new
{
result = this.getDocument().createElement('div'); // IMPORTANT: span may cause dimension calc problem of context
this._drawContextParentElem = result;
Kekule.HtmlElementUtils.addClass(result, CNS.DYN_CREATED);
Kekule.HtmlElementUtils.addClass(result, CCNS.DISPLAYER_DRAWCONTEXT_PARENT);
// IMPORTANT: force to fullfill the parent, otherwise draw context dimension calculation may have problem
//result.style.display = 'block';
result.style.width = '100%';
result.style.height = '100%';
// insert as first child
var root = this.getClientElement();
var currFirst = Kekule.DomUtils.getFirstChildElem(root);
if (currFirst)
root.insertBefore(result, currFirst);
else
root.appendChild(result);
}
return result;
},
/**
* Whether current painter can meet the requirement of auto size.
* @returns {Bool}
*/
allowAutoSize: function()
{
// TODO: Currently autosize is not enabled in 3D mode
return this.getRenderType() === Kekule.Render.RendererType.R2D;
},
/**
* Whether context and draw bridge can modify existing graphic content.
* @returns {Bool}
*/
canModifyPartialGraphic: function(context)
{
var b = this.getDrawBridge();
return b.canModifyGraphic? b.canModifyGraphic(context || this.getDrawContext()): false;
},
/** @private */
doInsertedToDom: function()
{
this.doResize();
},
/** @private */
doResize: function()
{
//console.log('refit draw context');
// when the size of chem viewer is changed, context should also be adjusted and object should be redrawn.
this.refitDrawContext(this.isPainting()); // if is paiting, do not call repaint
},
/** @private */
refitDrawContext: function(doNotRepaint)
{
//if (this.getDrawBridge(true) && this.getDrawContext())
if (this.getDrawBridge() && this.getDrawContext())
{
//var dim = Kekule.HtmlElementUtils.getElemScrollDimension(this.getElement());
var dim = Kekule.HtmlElementUtils.getElemClientDimension(this.getDrawContextParentElem());
//console.log(this.getElement().id, dim, Kekule.HtmlElementUtils.getElemScrollDimension(this.getElement()));
//this.getDrawBridge().setContextDimension(this.getDrawContext(), dim.width, dim.height);
var success = this.changeContextDimension(dim);
if (!doNotRepaint && success)
this.repaint();
}
},
/**
* Returns dimension of context.
* @returns {Hash}
*/
getContextDimension: function()
{
if (this.getDrawBridge() && this.getDrawContext())
return this.getDrawBridge().getContextDimension(this.getDrawContext());
else
return null;
},
/** @private */
_getContextOverSamplingRatio: function()
{
var envConfigs = this.getDisplayerEnvironmentConfigs();
var enableAntialias = envConfigs.getAntialias && envConfigs.getAntialias();
var overSamplingRatio = (enableAntialias && envConfigs.getOverSamplingRatio && envConfigs.getOverSamplingRatio()) || 1;
return overSamplingRatio;
},
/**
* Change the dimension of context.
* @param {Hash} newDimension
* @returns {Bool} Return true if dimension change successfully.
*/
changeContextDimension: function(newDimension)
{
/*
if (this.getDrawBridge() && this.getDrawContext())
{
var width = newDimension.width;
var height = newDimension.height;
var elem = this.getDrawBridge().getContextElem(this.getDrawContext());
if (elem) // use transform CSS property to do resampling
{
var overSamplingRatio = this._getContextOverSamplingRatio();
if (overSamplingRatio !== 1 && Kekule.BrowserFeature.cssTranform) // oversampling enabled
{
var scale = 1 / overSamplingRatio;
elem.style.transform = 'scale(' + scale + ',' + scale + ')';
elem.style.transformOrigin = '0 0';
//this.setPropStoreFieldValue('actualOverSamplingRatio', overSamplingRatio); // store the over sampling ratio for rendering
this.getDrawBridge().setContextParam(this.getDrawContext(), 'overSamplingRatio', overSamplingRatio);
}
else
{
elem.style.transform = '';
//this.setPropStoreFieldValue('actualOverSamplingRatio', null); // store the over sampling ratio for rendering
this.getDrawBridge().setContextParam(this.getDrawContext(), 'overSamplingRatio', null);
}
}
//this._resizeContext(this.getDrawContext(), this.getDrawBridge(), width, height);
this.doChangeContextDimension(width, height);
//this.getDrawBridge().setContextDimension(this.getDrawContext(), newDimension.width, newDimension.height);
return true;
}
else
return false;
*/
return this.doChangeContextDimension(this.getDrawContext(), this.getDrawBridge(), newDimension, true);
},
/** @private */
doChangeContextDimension: function(drawContext, drawBridge, newDimension, enableOverSampling)
{
//this._resizeContext(this.getDrawContext(), this.getDrawBridge(), width, height);
if (drawBridge && drawContext)
{
var width = newDimension.width;
var height = newDimension.height;
var elem = drawBridge.getContextElem(drawContext);
if (elem) // use transform CSS property to do resampling
{
var overSamplingRatio = this._getContextOverSamplingRatio();
if (overSamplingRatio !== 1 && Kekule.BrowserFeature.cssTranform && enableOverSampling) // oversampling enabled
{
/*
width *= overSamplingRatio;
height *= overSamplingRatio;
*/
var scale = 1 / overSamplingRatio;
elem.style.transform = 'scale(' + scale + ',' + scale + ')';
elem.style.transformOrigin = '0 0';
//this.setPropStoreFieldValue('actualOverSamplingRatio', overSamplingRatio); // store the over sampling ratio for rendering
drawBridge.setContextParam(drawContext, 'overSamplingRatio', overSamplingRatio);
}
else
{
elem.style.transform = '';
//this.setPropStoreFieldValue('actualOverSamplingRatio', null); // store the over sampling ratio for rendering
drawBridge.setContextParam(drawContext, 'overSamplingRatio', null);
}
}
this._resizeContext(drawContext, drawBridge, width, height);
//this.getDrawBridge().setContextDimension(this.getDrawContext(), newDimension.width, newDimension.height);
return true;
}
else
return false;
},
/** @private */
_resizeContext: function(context, bridge, width, height)
{
if (context && bridge)
bridge.setContextDimension(context, width, height);
},
/** @private */
clearContext: function()
{
var c = this.getPropStoreFieldValue('drawContext');
if (c)
{
var p = this.getPropStoreFieldValue('painter');
if (p)
p.clear(c);
this.getDrawBridge().clearContext(c);
}
},
/** @private */
createNewPainter: function(chemObj)
{
var old = this.getPropStoreFieldValue('painter');
if (old)
{
old.removeEventListener('draw', this._reactChemObjDrawBind);
old.finalize();
}
var drawBridge = this.getDrawBridge();
var result = drawBridge? (new Kekule.Render.ChemObjPainter(this.getRenderType(), chemObj || this.getChemObj(), this.getDrawBridge())): null;
this.setPropStoreFieldValue('painter', result);
if (result)
result.addEventListener('draw', this._reactChemObjDrawBind);
// create new bound info recorder
//this.createNewBoundInfoRecorder(result);
return result;
},
/**
* Returns whether the painter, draw bridge and context are ready, the rendering can be successfully performed.
* @returns {Bool}
*/
isRenderable: function()
{
return !!(/*this.getPainter() &&*/ this.getDrawBridge() && this.getDrawContext());
//return !!(this.getPropStoreFieldValue('painter') && this.getDrawBridge() && this.getDrawContext());
},
/* @private */
/*
createNewBoundInfoRecorder: function(renderer)
{
var old = this.getPropStoreFieldValue('boundInfoRecorder');
if (old)
old.finalize();
var recorder = new Kekule.Render.BoundInfoRecorder(renderer);
//recorder.setTargetContext(this.getObjContext());
this.setPropStoreFieldValue('boundInfoRecorder', recorder);
},
*/
/**
* Called when chemObj property has been changed.
* Usually this will cause the viewer repaint itself.
* @param {Kekule.ChemObject} newObj
* @private
*/
chemObjChanged: function(newObj, oldObj)
{
//if (newObj)
//console.log('change to new Obj', newObj);
this.doLoad(newObj);
if (this.getResetAfterLoad() && oldObj) // clear old draw options if oldObj is set
this.resetDisplay();
},
/**
* Load and display chemObj in viewer
* @param {Kekule.ChemObject} chemObj
*/
load: function(chemObj)
{
this.setChemObj(chemObj);
},
/**
* Do actual job of loading a chemObj.
* @param {Kekule.ChemObject} chemObj
* @ignore
*/
doLoad: function(chemObj, options)
{
this._doLoadOnObj(chemObj);
},
/** @private */
_doLoadOnObj: function(chemObj, options)
{
var refreshOnly = options && options.refreshOnly;
this.clearSubViews(); // clear all old subviews when loading a new chem object
//console.log('doLoad', chemObj);
this.refitDrawContext(true); // ensure the context size is correct, but not force repaint.
//this.hideExceptionReport();
this.setPropStoreFieldValue('chemObjLoaded', false);
//this.clearContext();
try
{
if (chemObj)
{
/*
// debug
chemObj.addOverrideRenderOptionItem({
'atomColor': '#ff0000', 'bondColor': '#00ff00',
//'useAtomSpecifiedColor': false,
'atomRadius': 3
});
*/
var painter = this.createNewPainter(chemObj);
if (painter)
painter.setRenderConfigs(this.getRenderConfigs());
/*
var drawOptions = this.getDrawOptions();
var context = this.getDrawContext();
var baseCoord;
if (this.getAutoSize() && this.allowAutoSize()) // need to resize widget dimension
{
var padding = this.getPadding() || 0;
var renderBox = painter.estimateScreenBox(context, baseCoord, drawOptions);
var width = renderBox.x2 - renderBox.x1 + padding * 2;
var height = renderBox.y2 - renderBox.y1 + padding * 2;
this.getDrawBridge().setContextDimension(context, width, height);
this.setWidth(width + 'px');
this.setHeight(height + 'px');
baseCoord = {'x': width / 2, 'y': height / 2};
}
else
{
baseCoord = drawOptions? drawOptions.baseCoord: null;
if (!baseCoord)
{
var dim = Kekule.HtmlElementUtils.getElemClientDimension(this.getElement());
baseCoord = {'x': dim.width / 2, 'y': dim.height / 2};
}
}
painter.draw(context, baseCoord, drawOptions);
*/
this.repaint();
this.setPropStoreFieldValue('chemObjLoaded', true); // indicate obj loaded successful
if (!refreshOnly)
{
this.invokeEvent('load', {'obj': chemObj});
}
}
else // no object, clear
{
this.clearContext();
if (!refreshOnly)
this.invokeEvent('load', {'obj': chemObj}); // even chemObj is null, this event should also be invoked
}
}
catch(e)
{
this.clearContext();
this.reportException(e);
//console.log(e);
//throw e;
}
finally
{
this.doLoadEnd(this.getChemObj());
}
},
/**
* Called when a chem object is loaded into widget.
* Descendants may override this method.
* @param {Kekule.ChemObject} chemObj If loading process is failed, this param may be null.
* @private
*/
doLoadEnd: function(chemObj)
{
// do nothing here
},
/**
* Load chem object from data of special MIME type or file format.
* @param {Variant} data Usually text content.
* @param {String} mimeType
* @param {String} fromUrlOrFileName From which file or url is this data loaded.
* @param {String} formatId
*/
loadFromData: function(data, mimeType, fromUrlOrFileName, formatId, objAfterLoadCallback)
{
try
{
if (!data)
{
this.setChemObj(null);
return null;
}
else
{
//var ext = fromUrlOrFileName? Kekule.UrlUtils.extractFileExt(fromUrlOrFileName): null;
var chemObj;
if (formatId)
chemObj = Kekule.IO.loadFormatData(data, formatId);
else if (mimeType || fromUrlOrFileName)
chemObj = Kekule.IO.loadTypedData(data, mimeType, fromUrlOrFileName);
if (chemObj)
{
//this.setChemObj(chemObj);
if (objAfterLoadCallback)
this._tryAutoGenerateChemObjCoordsForLoading(chemObj, objAfterLoadCallback);
else
this._tryAutoGenerateChemObjCoordsAndLoad(chemObj);
}
else
Kekule.error(/*Kekule.ErrorMsg.LOAD_CHEMDATA_FAILED*/Kekule.$L('ErrorMsg.LOAD_CHEMDATA_FAILED'));
return chemObj;
}
}
catch(e)
{
this.reportException(e);
}
},
/**
* Load chem object from file object.
* NOTE: browser must support File Reader API to use this method.
* @param {File} file
*/
loadFromFile: function(file)
{
if (!file)
this.setChemObj(null);
else
{
var self = this;
try
{
Kekule.IO.loadFileData(file, function(chemObj, success)
{
if (success)
{
//self.setChemObj(chemObj);
self._tryAutoGenerateChemObjCoordsAndLoad(chemObj);
}
}
);
}
catch (e)
{
this.reportException(e);
}
}
},
/**
* Returns object in displayer that to be saved.
* Usually this should be the chemObj itself.
* Descendants may override this method.
* @returns {Kekule.ChemObject}
* @private
*/
getSavingTargetObj: function()
{
return this.getChemObj();
},
/**
* Save loaded chem object to data.
* @param {String} formatId
* @param {Int} dataType Text or binary. Set null to use default type.
* @param {Kekule.ChemObject} obj Object to save, default is current chemObj loaded in displayer.
* @returns {Variant} Saved data.
*/
saveData: function(formatId, dataType, obj)
{
var obj = obj || this.getSavingTargetObj(); /* this.getChemObj()*/
this.prepareSaveData(obj);
var writer = Kekule.IO.ChemDataWriterManager.getWriterByFormat(formatId, null, obj);
if (writer)
{
var doCanonicalize = this._needToCanonicalizeBeforeSaving() && this.getDisplayerConfigs().getIoConfigs().getCanonicalizeBeforeSave();
if (doCanonicalize && obj.standardize) // canonicalize first
{
//var obj = obj.clone? obj.clone(true): obj; // clone with id
var obj = this._cloneSavingTargetObj(obj);
obj.standardize(this.getStandardizationOptions());
}
if (!dataType)
{
var formatInfo = Kekule.IO.DataFormatsManager.getFormatInfo(formatId);
dataType = formatInfo.dataType;
}
var data = writer.writeData(obj, dataType, formatId);
return data;
}
else
{
Kekule.error(/*Kekule.ErrorMsg.NO_SUITABLE_WRITER_FOR_FORMAT*/Kekule.$L('ErrorMsg.NO_SUITABLE_WRITER_FOR_FORMAT'));
return null;
}
},
/** @private */
_cloneSavingTargetObj: function(obj)
{
var result = obj.clone? obj.clone(true): obj; // clone with id
return result;
},
/**
* Return whether this displayer need to canonicalize molecule before save.
* Descendants may override this method.
* @returns {Bool}
* @private
*/
_needToCanonicalizeBeforeSaving: function()
{
return false;
},
/**
* Called before obj is saved. Descendants can overrride this method.
*/
prepareSaveData: function(obj)
{
// do nothing here
},
/** @private */
_molNeedAutoCoordGeneration: function(mol, coordMode)
{
var result = (mol instanceof Kekule.StructureFragment) && (mol.getNodeCount() > 0) && !mol.nodesHasCoordOfMode(this.getCoordMode(), this.getAllowCoordBorrow(), true);
return result;
},
/** @private */
_isCoordGeneratorAvailable: function(coordMode)
{
if ( Kekule.Calculator && Kekule.Calculator.Services)
{
var serviceName = (coordMode === Kekule.CoordMode.COORD3D) ? Kekule.Calculator.Services.GEN3D : Kekule.Calculator.Services.GEN2D;
return Kekule.Calculator.hasService(serviceName)
}
else
return false;
},
/**
* Returns whether the coord auto generation can be done in current displayer.
* @returns {Bool}
*/
canAutoGenerateCoordForChemObj: function(chemObj, coordMode)
{
if (!chemObj)
chemObj = this.getChemObj();
if (Kekule.ObjUtils.isUnset(coordMode))
coordMode = this.getCoordMode();
var result = (chemObj && chemObj instanceof Kekule.StructureFragment
&& chemObj.hasCtab()
&& Kekule.Calculator && Kekule.Calculator.generateStructure
&& this._isCoordGeneratorAvailable(coordMode));
return result;
},
/** @private */
_tryAutoGenerateChemObjCoords: function(chemObj, coordMode, callback)
{
if (Kekule.ObjUtils.isUnset(coordMode))
coordMode = this.getCoordMode();
/*
if (chemObj instanceof Kekule.Molecule
&& this.getDisplayerConfigs().getIoConfigs().getAutoGenerateCoordsAfterLoad()
&& Kekule.Calculator && Kekule.Calculator.generateStructure) // can auto generate coords
*/
if (this.canAutoGenerateCoordForChemObj(chemObj, coordMode))
{
var is3D = coordMode === Kekule.CoordMode.COORD3D;
//var hasCoords = (chemObj.getNodeCount() <= 0) || chemObj.nodesHasCoordOfMode(this.getCoordMode(), this.getAllowCoordBorrow(), true);
//if (needGeneration) // auto generate
{
var serviceName = is3D? Kekule.Calculator.Services.GEN3D: Kekule.Calculator.Services.GEN2D;
try
{
Kekule.Calculator.generateStructure(chemObj, serviceName, {modifySource: true},
function (generatedMol)
{
callback(generatedMol);
},
function (err)
{
callback(chemObj);
Kekule.error(err);
}
);
}
catch(e)
{
callback(chemObj);
}
}
//else
// callback(chemObj);
}
else // can not generate coords, callback immediately
{
callback(chemObj);
}
},
/** @private */
_tryAutoGenerateChemObjCoordsForLoading: function(chemObj, callback)
{
if (this.getDisplayerConfigs().getIoConfigs().getAutoGenerateCoordsAfterLoad())
{
var needGeneration = this._molNeedAutoCoordGeneration(chemObj, this.getCoordMode());
if (needGeneration)
this._tryAutoGenerateChemObjCoords(chemObj, null, callback);
else
callback(chemObj);
}
else
callback(chemObj);
},
/** @private */
_tryAutoGenerateChemObjCoordsAndLoad: function(chemObj)
{
var self = this;
//console.log('here');
var done = function(chemObj) { self.setChemObj(chemObj); };
this._tryAutoGenerateChemObjCoordsForLoading(chemObj, done);
},
/**
* Automatically create coords for chemObj loaded in displayer.
* @param {Func} callback
*/
autoGenerateChemObjCoords: function(callback, chemObj, coordMode)
{
var replaceCurrent = !chemObj && Kekule.ObjUtils.isUnset(coordMode);
if (!chemObj)
chemObj = this.getChemObj();
if (Kekule.ObjUtils.isUnset(coordMode))
coordMode = this.getCoordMode();
var self = this;
var done = function(generatedObj) {
if (replaceCurrent)
self.load(generatedObj);
if (callback)
callback(generatedObj);
};
this._tryAutoGenerateChemObjCoords(chemObj, coordMode, done);
return this;
},
/** @private */
getDrawClientDimension: function()
{
var dim = Kekule.HtmlElementUtils.getElemClientDimension(this.getCoreElement());
return dim;
},
/**
* Set dimension of drawn client, descendant may override this method.
* @param {Int} width
* @param {Int} height
*/
setDrawDimension: function(width, height)
{
this.setDimension(width, height);
},
/** @private */
calcDrawBaseCoord: function(drawOptions, applyAutoSize)
{
var baseCoord;
var context = this.getDrawContext();
var painter = this.getPainter();
var newDimension;
// note in continuous repainting phase (such as periodical rotation), we disable auto size
if ((!this._isContinuousRepainting) && applyAutoSize && /*this.getAutoSize() && this.allowAutoSize() */ this.getActualAutoSize()) // need to resize widget dimension
{
var padding = this.getPadding() || 0;
var renderBox = painter.estimateScreenBox(context, baseCoord, drawOptions, this.getAllowCoordBorrow());
if (renderBox)
{
var width = renderBox.x2 - renderBox.x1 + padding * 2;
var height = renderBox.y2 - renderBox.y1 + padding * 2;
//this.getDrawBridge().setContextDimension(context, width, height);
this.changeContextDimension({'width': width, 'height': height});
/*
this.setWidth(width + 'px');
this.setHeight(height + 'px');
*/
this.setDrawDimension(width, height);
newDimension = {'width': width, 'height': height};
baseCoord = {'x': width / 2, 'y': height / 2};
}
// debug
/*
console.log('drawOutline', renderBox);
var c1 = {'x': renderBox.x1, 'y': renderBox.y1};
var c2 = {'x': renderBox.x2, 'y': renderBox.y2};
c1 = Kekule.CoordUtils.add(c1, baseCoord);
c2 = Kekule.CoordUtils.add(c2, baseCoord);
this.getDrawBridge().drawRect(context, c1, c2,
{'strokeWidth': 1, 'strokeColor': '#000000', 'fillColor': '#00ff00'}
);
*/
}
else
{
baseCoord = drawOptions? drawOptions.baseCoord: null;
if (!baseCoord)
{
var dim = this.getDrawClientDimension();
baseCoord = {'x': dim.width / 2, 'y': dim.height / 2};
//drawOptions.baseCoord = baseCoord;
//if (this.getRenderType() !== Kekule.Render.RendererType.R3D)
{
var offset = this.getBaseCoordOffset();
if (offset)
{
baseCoord = Kekule.CoordUtils.add(baseCoord, offset);
}
}
}
}
baseCoord = this.getDrawBridge().transformScreenCoordToContext(context, baseCoord);
return {'baseCoord': baseCoord, 'baseCoordPos': Kekule.Render.CoordPos.CENTER, 'newWidgetDimension': newDimension};
},
/**
* Returns draw options actually used by repaint method.
* Returned value may be different to drawOption property to make some custom modifications.
* Descendants may override this method.
* @returns {Hash}
* @private
*/
getActualDrawOptions: function()
{
return this.getDrawOptions();
},
/** @private */
getActualAutoSize: function()
{
// when autofit, the autosize should be be applied
return this.getAutoSize() && this.allowAutoSize() && !this.getAutofit();
},
/** @private */
calcDrawParams: function(overrideOptions, applyAutoSize)
{
var ops = this.getActualDrawOptions();
if (overrideOptions)
ops = Object.extend(ops, overrideOptions);
var baseCoordResult = this.calcDrawBaseCoord(ops, applyAutoSize);
return {
'drawOptions': ops,
'baseCoord': baseCoordResult.baseCoord,
'baseCoordPos': baseCoordResult.baseCoordPos,
'newWidgetDimension': baseCoordResult.newWidgetDimension};
},
/** @private */
_savePainterInitialRenderTransformOptions: function(context, painter)
{
var actualTransformOps = painter.getActualInitialRenderTransformOptions(context);
this._contextTransformOpsMap.set(context, actualTransformOps);
//console.log('store transform ops', actualTransformOps);
},
/**
* Request a repaint process. The actual repainting will be suspended till idle.
* Use this method rather than directly calling repaint() may reduce the concrete repainting times.
* @param {Hash} overrideOptions Transform options to do repainting.
* If this param is set to null, all transform options will be recalculated.
* If overrideOptions.preserveTransformOptions is true, transform options remains same as
* last painting process (rather than recalculated).
*/
requestRepaint: function(overrideOptions)
{
this._requestRepainting = {'overrideOptions': overrideOptions}; // flag indicating need to repaint
var self = this;
var doConcreteRepaint = function()
{
if (self._requestRepainting)
{
self._repaintCore(self._requestRepainting.overrideOptions);
}
self._requestRepainting = null;
};
doConcreteRepaint.delay(0);
},
/**
* Repaint the context with current chem object.
* @param {Hash} overrideOptions Transform options to do repainting.
* If this param is set to null, all transform options will be recalculated.
* If overrideOptions.preserveTransformOptions is true, transform options remains same as
* last painting process (rather than recalculated).
*/
repaint: function(overrideOptions)
{
if (this.isUpdating())
{
// suspend painting in updating
this._requestRepainting = {'overrideOptions': overrideOptions}; // flag indicating need to repaint
}
else
{
//return this.requestRepaint(overrideOptions);
try
{
var result = this._repaintCore(overrideOptions);
}
finally
{
this._requestRepainting = null; // clear request painting flag
}
return result;
}
},
/** @private */
_repaintCore: function(overrideOptions)
{
if (!this.getElement()) // not bound to element, can not draw
return;
if (this.isPainting()) // avoid duplicated repainting
return;
if (!this.isRenderable())
return;
this.beginPaint();
try
{
//var start = (new Date()).getTime();
// update background color
if (this.getInheritedRenderBackgroundColor())
{
var bgColor = this.getComputedStyle('backgroundColor');
this.backgroundColorChanged(true, bgColor || null);
}
else
{
this.backgroundColorChanged(true);
}
if (!overrideOptions || !overrideOptions.doNotClear)
this.clearContext();
if (!this.getChemObj())
return;
var painter = this.getPainter();
/*
var drawOptions = Object.create(this.getActualDrawOptions() || null);
if (overrideOptions)
drawOptions = Object.extend(drawOptions, overrideOptions);
var baseCoord = this.calcDrawBaseCoord(drawOptions);
*/
var context = this.getDrawContext();
var drawParams;
var transformOps;
var transformOpsChanged = true;
if (overrideOptions && overrideOptions.preserveTransformOptions)
{
transformOps = this._contextTransformOpsMap.get(context) || null;
transformOpsChanged = false;
//console.log(transformOps);
drawParams = this.calcDrawParams(transformOps, true);
}
else
drawParams = this.calcDrawParams(overrideOptions, true);
//drawParams.drawOptions.unitLength = 2;
this._lastBaseCoord = drawParams.baseCoord; // save for next time use in drawOptionChanged
//console.log('context center', baseCoord);
// debug
/*
if (this.getRenderType() === Kekule.Render.RendererType.R3D)
baseCoord = null;
*/
//console.log(drawParams, drawParams.baseCoord, drawParams.drawOptions);
if (this.getInheritedRenderColor())
{
var color = this.getComputedStyle('color');
drawParams.drawOptions.color = color;
}
else
{
drawParams.drawOptions.color = null;
}
if (this.getEnableCustomCssProperties())
this._applyCustomCssProps(drawParams.drawOptions);
this._setupRenderingEnvironment(context, this.getDrawBridge());
painter.draw(context, drawParams.baseCoord, drawParams.drawOptions);
if (transformOpsChanged)
{
this._savePainterInitialRenderTransformOptions(context, painter);
}
//var end = (new Date()).getTime();
//var duration = end - start;
//console.log('draw in ' + duration + ' ms');
this.invokeEvent('repaint', {'obj': this.getChemObj()});
}
finally
{
this.endPaint();
}
},
/** @private */
_setupRenderingEnvironment: function(context, drawBridge)
{
var envConfigs = this.getDisplayerEnvironmentConfigs();
if (envConfigs.getAntialias && envConfigs.getAntialias())
{
// blur
var blurRatio = (envConfigs.getAntialiasBlurRatio && envConfigs.getAntialiasBlurRatio()) || 0;
if (blurRatio)
{
var lengthConfigs = this.getRenderConfigs().getLengthConfigs();
var blurRatio = envConfigs.getAntialiasBlurRatio() || 0;
var blurValue = (lengthConfigs.getActualLength('bondLineWidth') * blurRatio) || 0;
//var bridge = this.getDrawBridge();
var bridge = drawBridge || this.getDrawBridge();
if (bridge.setFilter && bridge.clearFilter)
{
if (blurValue)
bridge.setFilter(context, 'blur(' + blurValue + 'px)');
else
bridge.clearFilter(context);
}
}
}
},
/** @private */
beginPaint: function()
{
if (!this._paintFlag)
this._paintFlag = 0;
++this._paintFlag;
},
/** @private */
endPaint: function()
{
if (this._paintFlag > 0)
--this._paintFlag;
if (this._paintFlag < 0)
this._paintFlag = 0;
},
/**
* Returns whether in the painting process.
* @returns {Bool}
*/
isPainting: function()
{
return this._paintFlag > 0;
},
/**
* Event listener of the draw event of painter.
* @param {Hash} e
* @private
*/
_reactChemObjDraw: function(e)
{
var obj = e.obj;
if (obj === this.getChemObj())
{
this.chemObjRendered(obj, e.renderOptions || {});
}
},
/**
* Called after the drawn of chem object in displayer.
* Desendants may override this method to retrieve addtional render params (e.g. renderOptions).
* @param {Kekule.ChemObject} chemObj
* @param {Hash} renderOptions
* @private
*/
chemObjRendered: function(chemObj, renderOptions)
{
// do nothing here
},
/**
* Returns background color used for a special renderType.
* @param {Int} renderType
* @returns {String}
*/
getBackgroundColorOfType: function(renderType)
{
return this._bgColorMap[renderType.toString()];
},
/**
* Set background color used for a special renderType.
* @param {String} color
* @param {Int} renderType
*/
setBackgroundColorOfType: function(color, renderType)
{
this._bgColorMap[renderType.toString()] = color;
if (renderType === this.getRenderType())
this.setBackgroundColor(color);
},
/**
* Called after background color is changed, should repaint the context.
* @private
*/
backgroundColorChanged: function(doNotRepaint, bgColor)
{
var color = bgColor;
if (color === undefined)
color = this.getBackgroundColor();
if (color === 'transparent')
color = null;
//var drawBridge = this.getDrawBridge(true);
var drawBridge = this.getDrawBridge();
if (drawBridge && drawBridge.setClearColor)
{
drawBridge.setClearColor(this.getDrawContext(), color);
if (!doNotRepaint)
this.requestRepaint();
}
},
/** @private */
_getApplicableCustomCssPropNames: function() // returns in JS form
{
var result = {
r2d: [
'unitLength',
'moleculeDisplayType',
'hydrogenDisplayLevel',
'showCharge',
'chargeMarkFontSize',
'chargeMarkMargin',
'chemMarkerFontSize',
'chemMarkerMargin',
'distinguishSingletAndTripletRadical',
'fontSize',
'fontFamily',
'supFontSizeRatio',
'subFontSizeRatio',
'superscriptOverhang',
'subscriptOversink',
'bondLineWidth',
'boldBondLineWidth',
'hashSpacing',
'multipleBondSpacingRatio',
'multipleBondSpacingAbs',
'multipleBondMaxAbsSpacing',
'bondArrowLength',
'bondArrowWidth',
'bondWedgeWidth',
'bondWedgeHashMinWidth',
'bondLengthScaleRatio',
'color',
'atomColor',
'bondColor',
'useAtomSpecifiedColor',
'opacity',
'fillColor',
'strokeColor',
'strokeWidth',
'atomRadius'
],
r3d: [
'displayMultipleBond',
'useVdWRadius',
'nodeRadius',
'connectorRadius',
'nodeRadiusRatio',
'connectorRadiusRatio',
//'connectorLineWidth',
'color',
'atomColor',
'bondColor',
'useAtomSpecifiedColor',
'hideHydrogens',
'bondSpliceMode'
]
};
return result;
},
/** @private */
_getRenderOptionDefinitionsHash: function() // cache returned values
{
if (!this.__$renderOptionDefinitionsHash__)
{
this.__$renderOptionDefinitionsHash__ = {};
var r2dDefs = Kekule.Render.RenderOptionUtils.getOptionDefinitions();
var r3dDefs = Kekule.Render.Render3DOptionUtils.getOptionDefinitions();
var i, l;
for (i = 0, l = r2dDefs.length; i < l; ++i)
{
this.__$renderOptionDefinitionsHash__[r2dDefs[i].name] = r2dDefs[i];
}
for (i = 0, l = r3dDefs.length; i < l; ++i)
{
this.__$renderOptionDefinitionsHash__[r3dDefs[i].name] = r3dDefs[i];
}
}
return this.__$renderOptionDefinitionsHash__;
},
/** @private */
_applyCustomCssProps: function(drawOptions)
{
var defHash = this._getRenderOptionDefinitionsHash();
var applicableProps = this._getApplicableCustomCssPropNames();
var i, l;
for (i = 0, l = applicableProps.r2d.length; i < l; ++i)
{
var name = applicableProps.r2d[i];
this._applyCustomCssPropValue(name, defHash, drawOptions);
}
if (this.getRenderType() === Kekule.Render.RendererType.R3D)
{
for (i = 0, l = applicableProps.r3d.length; i < l; ++i)
{
var name = applicableProps.r3d[i];
this._applyCustomCssPropValue(name, defHash, drawOptions);
}
}
},
/** @private */
_applyCustomCssPropValue: function(propName, defHash, drawOptions)
{
var definition = defHash[propName];
if (definition)
{
var cssValue = this.getComputedCustomStyle(propName);
if (cssValue && cssValue.trim) // ensure is a string value
{
cssValue = cssValue.trim(); // custom value often contains leading blanks
var value = Kekule.StrUtils.convertToType(cssValue, defHash.dataType);
if (Kekule.ObjUtils.notUnset(value))
drawOptions[propName] = value;
}
}
},
/**
* Called after draw options are changed. Should repaint the context.
* Repaint is a time-consuming job. So if only rotate/scale/translate options changes,
* geometryOptionChanged method should be used instead.
* @private
*/
drawOptionChanged: function()
{
this.repaint();
},
/**
* Called after only geometry options (translate, scale, rotations) are changed.
* @private
*/
geometryOptionChanged: function()
{
var painter = this.getPainter();
var drawOptions = this.getDrawOptions();
if (painter && painter.supportGeometryOptionChange())
{
var context = this.getDrawContext();
painter.changeGeometryOptions(context, drawOptions.baseCoord || this._lastBaseCoord, drawOptions);
this._savePainterInitialRenderTransformOptions(context, painter);
this.invokeEvent('repaint', {'obj': this.getChemObj()});
}
else
this.drawOptionChanged();
},
/**
* Notify the displayer that in following process, continuous repainting will start and auto size should be disabled.
* After this phase, endContinuousRepainting() must be called.
*/
beginContinuousRepainting: function()
{
this._isContinuousRepainting = true;
},
/**
* Notify the displayer that continuous phase has ended, turn into normal mode.
*/
endContinuousRepainting: function()
{
if (this._isContinuousRepainting)
{
this._isContinuousRepainting = false;
this.repaint();
}
},
/**
* Reset displayer to initial state (no zoom, rotation and so on).
*/
resetDisplay: function()
{
var op = this.getDrawOptions();
op.zoom = this.getInitialZoom() || 1;
op.rotateX = 0;
op.rotateY = 0;
op.rotateZ = 0;
op.rotateAngle = 0;
op.rotateAxisVector = null;
op.rotateMatrix = null;
//this.drawOptionChanged();
this.setBaseCoordOffset(null);
this.geometryOptionChanged();
return this;
},
/**
* Returns zoom property in drawOptions.
* @returns {Float}
*/
getCurrZoom: function()
{
return this.getDrawOptions().zoom || 1;
},
/**
* Zoom to a specified ratio
* @param {Float} value
* @param {Bool} suspendRendering Set this to true if a immediate repaint is not needed.
* @param {Hash} zoomCenterCoord The center coord for zooming. If not set, center of whole context will be used.
* Note currently in displayer, this coord is not used when zooming.
* Only in the editor descendants, these value will be take into consideration.
*/
zoomTo: function(value, suspendRendering, zoomCenterCoord)
{
this.getDrawOptions().zoom = value;
//this.drawOptionChanged();
if (!suspendRendering)
this.geometryOptionChanged();
return this;
},
/**
* Zoom in.
*/
zoomIn: function(step, zoomCenterCoord)
{
var curr = this.getCurrZoom();
var ratio = ZU.getNextZoomInRatio(curr, step || 1);
return this.zoomTo(ratio, null, zoomCenterCoord);
},
/**
* Zoom out.
*/
zoomOut: function(step, zoomCenterCoord)
{
var curr = this.getCurrZoom();
var ratio = ZU.getNextZoomOutRatio(curr, step || 1);
return this.zoomTo(ratio, null, zoomCenterCoord);
},
/**
* Reset to normal size.
*/
resetZoom: function(zoomCenterCoord)
{
return this.zoomTo(this.getInitialZoom() || 1, null, zoomCenterCoord);
},
/**
* Returns default mol display type in special render mode.
* @param {Int} renderType
* @return {Int}
* @private
*/
getDefaultMoleculeDisplayType: function(renderType)
{
return (renderType === Kekule.Render.RendererType.R3D)?
Kekule.Render.Molecule3DDisplayType.DEFAULT: Kekule.Render.Molecule2DDisplayType.DEFAULT;
},
/**
* Returns current molecule display type.
* @returns {Int}
*/
getCurrMoleculeDisplayType: function()
{
var result = this.getDrawOptions().moleculeDisplayType;
if (!result) // not set, use default
result = this.getDefaultMoleculeDisplayType(this.getRenderType());
return result;
},
/*
* Change molecule display type.
* @param {Int} newType Value from {@link Kekule.Render.Molecule2DDisplayType} or {@link Kekule.Render.Molecule3DDisplayType}.
*/
/*
setMoleculeDisplayType: function(newType)
{
this.getDrawOptions().moleculeDisplayType = newType;
this.drawOptionChanged();
return this;
},
*/
/*
* Hide or show all hydrogen atoms in 3D model.
* @param {Int} newType Value from {@link Kekule.Render.Molecule2DDisplayType} or {@link Kekule.Render.Molecule3DDisplayType}.
*/
/*
setHideHydrogens: function(newValue)
{
this.getDrawOptions().hideHydrogens = newValue;
this.drawOptionChanged();
return this;
},
*/
/**
* Export drawing content in viewer to a data URL for <img> tag to use.
* @param {String} dataType Type of image data, e.g. 'image/png'.
* @param {Hash} options Export options, usually this is a number between 0 and 1
* indicating image quality if the requested type is image/jpeg or image/webp.
* @returns {String}
*/
exportToDataUri: function(dataType, options)
{
//console.log(this.getDrawBridge(), this.getDrawContext());
return this.getDrawBridge().exportToDataUri(this.getDrawContext(), dataType, options);
},
////// about configurator
/** @ignore */
createConfigurator: function(/*$super*/)
{
var result = this.tryApplySuper('createConfigurator') /* $super() */;
result.addEventListener('configChange', function(e){
// render config change need to repaint context
this.repaint();
}, this);
return result;
},
//////////////////////// methods about bound maps ////////////////////////////
/** @private */
clearBoundMap: function()
{
this.getBoundInfoRecorder().clear(this.getDrawContext());
},
/**
* Returns topmost bound item in z-index.
* Descendants may override this method to implement more accurate algorithm.
* @param {Array} boundItems
* @param {Array} excludeObjs Objects in this array will not be returned.
* @returns {Object}
* @private
*/
findTopmostBoundInfo: function(boundItems, excludeObjs)
{
if (boundItems && boundItems.length)
{
var result = null;
var index = boundItems.length - 1;
result = boundItems[index];
while ((index >= 0) && (excludeObjs && (excludeObjs.indexOf(result.obj) >= 0)))
{
--index;
result = boundItems[index];
}
return result;
}
else
return null;
},
/**
* Returns all bound map item at x/y.
* Input coord is based on the screen coord system.
* @returns {Array}
* @private
*/
getBoundInfosAtCoord: function(screenCoord, filterFunc, boundInflation)
{
/*
if (!boundInflation)
throw 'boundInflation not set!';
*/
var boundRecorder = this.getBoundInfoRecorder();
var delta = boundInflation;
//var coord = this.getObjDrawBridge().transformScreenCoordToContext(this.getDrawContext(), screenCoord);
var coord = this.screenCoordToContext(screenCoord);
var refCoord = (this.getRenderType() === Kekule.Render.RendererType.R3D)? {'x': 0, 'y': 0}: null;
var matchedInfos = boundRecorder.getIntersectionInfos(this.getDrawContext(), coord, refCoord, delta, filterFunc);
//console.log(coord, delta, matchedInfos);
return matchedInfos;
},
/**
* returns the topmost bound map item at x/y.
* Input coord is based on the screen coord system.
* @param {Hash} screenCoord
* @param {Array} excludeObjs Objects in this array will not be returned.
* @returns {Object}
*/
getTopmostBoundInfoAtCoord: function(screenCoord, excludeObjs, boundInflation)
{
return this.findTopmostBoundInfo(this.getBoundInfosAtCoord(screenCoord, null, boundInflation), excludeObjs, boundInflation);
},
/**
* Returns all basic drawn object at coord (with inflation) based on screen system.
* @params {Hash} screenCoord
* @param {Number} boundInflation
* @param {Array} excludeObjs
* @param {Array} filterObjClasses If this param is set, only obj match these types will be returned
* @returns {Array}
* @private
*/
getBasicObjectsAtCoord: function(screenCoord, boundInflation, excludeObjs, filterObjClasses)
{
var boundInfos = this.getBoundInfosAtCoord(screenCoord, null, boundInflation);
var result = [];
if (boundInfos)
{
if (excludeObjs)
{
boundInfos = AU.filter(boundInfos, function(boundInfo){
var obj = boundInfos[i].obj;
if (!obj)
return false;
if (obj)
{
if (excludeObjs && excludeObjs.indexOf(obj) >= 0)
return false;
}
return true;
});
}
for (var i = boundInfos.length - 1; i >= 0; --i)
{
var obj = boundInfos[i].obj;
if (obj)
{
if (excludeObjs && excludeObjs.indexOf(obj) >= 0)
continue;
}
result.push(obj);
}
if (result && filterObjClasses) // filter
{
result = AU.filter(result, function(obj)
{
for (var i = 0, l = filterObjClasses.length; i < l; ++i)
{
if (obj instanceof filterObjClasses[i])
return true;
}
return false;
});
}
}
return result;
},
/**
* Returns the topmost basic drawn object at coord based on screen system.
* @params {Hash} screenCoord
* @param {Number} boundInflation
* @param {Array} filterObjClasses If this param is set, only obj match these types will be returned
* @returns {Object}
* @private
*/
getTopmostBasicObjectAtCoord: function(screenCoord, boundInflation, filterObjClasses)
{
var objs = this.getBasicObjectsAtCoord(screenCoord, boundInflation, null, filterObjClasses);
return objs && objs[0];
},
// Coord translate methods
/**
* Translate coord to value of another coord system.
* @param {Hash} coord
* @param {Int} fromSys
* @param {Int} toSys
*/
translateCoord: function(coord, fromSys, toSys)
{
if (!coord)
return null;
var S = Kekule.Render.CoordSystem;
if (fromSys === S.SCREEN)
{
if (toSys === S.SCREEN)
return coord;
else if (toSys === S.CONTEXT)
return this.getDrawBridge()? this.getDrawBridge().transformScreenCoordToContext(this.getDrawContext(), coord): coord;
else // S.OBJ
{
var contextCoord = this.getDrawBridge()? this.getDrawBridge().transformScreenCoordToContext(this.getDrawContext(), coord): coord;
return this.getDrawContext()? this.getRootRenderer().transformCoordToObj(this.getDrawContext(), this.getChemObj(), contextCoord): coord;
}
}
else if (fromSys === S.CONTEXT)
{
if (toSys === S.SCREEN)
return this.getDrawBridge()? this.getDrawBridge().transformContextCoordToScreen(this.getDrawContext(), coord): coord;
else if (toSys === S.CONTEXT)
return coord;
else // S.OBJ
return this.getDrawContext()? this.getRootRenderer().transformCoordToObj(this.getDrawContext(), this.getChemObj(), coord): coord;
}
else // fromSys === S.OBJ
{
if (toSys === S.SCREEN)
{
var contextCoord = this.getRootRenderer().transformCoordToContext(this.getDrawContext(), this.getChemObj(), coord);
return this.getDrawBridge()? this.getDrawBridge().transformContextCoordToScreen(this.getDrawContext(), contextCoord): coord;
}
else if (toSys === S.CONTEXT)
return this.getDrawContext()? this.getRootRenderer().transformCoordToContext(this.getDrawContext(), this.getChemObj(), coord): coord;
else // S.OBJ
return coord;
}
},
/**
* Translate a distance value to a distance in another coord system.
* @param {Hash} coord
* @param {Int} fromSys
* @param {Int} toSys
*/
translateDistance: function(distance, fromSys, toSys)
{
var coord0 = {'x': 0, 'y': 0, 'z': 0};
var coord1 = {'x': distance, 'y': 0, 'z': 0};
var transCoord0 = this.translateCoord(coord0, fromSys, toSys);
var transCoord1 = this.translateCoord(coord1, fromSys, toSys);
return Kekule.CoordUtils.getDistance(transCoord0, transCoord1);
},
/**
* Turn obj coord to context one.
* @param {Hash} objCoord
* @returns {Hash}
*/
objCoordToContext: function(objCoord)
{
var S = Kekule.Render.CoordSystem;
return this.translateCoord(objCoord, S.CHEM, S.CONTEXT);
},
/**
* Turn context coord to obj one.
* @param {Hash} contextCoord
* @returns {Hash}
*/
contextCoordToObj: function(contextCoord)
{
var S = Kekule.Render.CoordSystem;
return this.translateCoord(contextCoord, S.CONTEXT, S.CHEM);
},
/**
* Turn obj coord to screen one.
* @param {Hash} objCoord
* @returns {Hash}
*/
objCoordToScreen: function(objCoord)
{
var S = Kekule.Render.CoordSystem;
return this.translateCoord(objCoord, S.CHEM, S.SCREEN);
},
/**
* Turn screen coord to obj one.
* @param {Hash} contextCoord
* @returns {Hash}
*/
screenCoordToObj: function(screenCoord)
{
var S = Kekule.Render.CoordSystem;
return this.translateCoord(screenCoord, S.SCREEN, S.CHEM);
},
/**
* Turn screen based coord to context one.
* @param {Hash} screenCoord
* @returns {Hash}
*/
screenCoordToContext: function(screenCoord)
{
var S = Kekule.Render.CoordSystem;
return this.translateCoord(screenCoord, S.SCREEN, S.CONTEXT);
},
/**
* Turn context based coord to screen one.
* @param {Hash} screenCoord
* @returns {Hash}
*/
contextCoordToScreen: function(screenCoord)
{
var S = Kekule.Render.CoordSystem;
return this.translateCoord(screenCoord, S.CONTEXT, S.SCREEN);
},
/**
* Turn box coords based on screen system to context one.
* @param {Hash} screenCoord
* @returns {Hash}
*/
screenBoxToContext: function(screenBox)
{
var coord1 = this.screenCoordToContext({'x': screenBox.x1, 'y': screenBox.y1});
var coord2 = this.screenCoordToContext({'x': screenBox.x2, 'y': screenBox.y2});
return {'x1': coord1.x, 'y1': coord1.y, 'x2': coord2.x, 'y2': coord2.y};
},
///////////////////////////////////////////////////////
////// methods about sub views ///////////////////////
/**
* Clear and finalize all sub views in displayer.
*/
clearSubViews: function()
{
var subviews = this.getSubViews();
if (subviews)
{
for (var i = 0, l = subviews.length; i < l; ++i)
{
subviews[i].finalize();
}
}
this.setPropStoreFieldValue('subViews', undefined);
},
/**
* Iterate function on all sub views of displayer.
* @param {Function} func Has params (subView, index).
*/
iterateSubViews: function(func)
{
var subviews = this.getSubViews();
if (subviews)
{
for (var i = 0, l = subviews.length; i < l; ++i)
{
func(subviews[i], i);
}
}
return this;
},
/**
* Get the sub view object for a targetObj.
* @param {Kekule.ChemObject} targetObj
* @param {CLass} subViewClass
* @returns {Kekule.ChemWidget.ChemObjDisplayerSubView}
*/
getSubView: function(targetObj, subViewClass, canCreate)
{
var result;
var subviews = this.getSubViews();
if (subviews)
{
for (var i = 0, l = subviews.length; i < l; ++i)
{
var subview = subviews[i];
if (subview.getTarget() === targetObj && (subview instanceof subViewClass))
{
result = subview;
break;
}
}
}
if (!result && canCreate) // not found, create new
{
var proto = ClassEx.getPrototype(subViewClass);
if (proto.appliableToTarget(targetObj))
{
result = new subViewClass(this, targetObj);
this.getSubViews(true).push(result);
}
}
return result;
},
/**
* Get the sub view object for a targetObj. If such a sub view does not exist, a new one will be created.
* @param {Kekule.ChemObject} targetObj
* @param {CLass} subViewClass
* @returns {Kekule.ChemWidget.ChemObjDisplayerSubView}
*/
fetchSubView: function(targetObj, subViewClass)
{
return this.getSubView(targetObj, subViewClass, true);
}
///////////////////////////////////////////////////////
});
/**
* Base class for sub view classes for displayer.
* User should not use this class directly, the concrete descandants should be used instead.
* Sub view provide a way to extend the ability of its parent widget, focusing on one specified object inside {@link Kekule.ChemWidget.ChemObjDisplayer}.
* @class
* @augments ObjectEx
*
* @param {Kekule.ChemWidget.ChemObjDisplayer} displayer The parent displayer widget.
* @param {Kekule.ChemObject} target The target object inside displayer of this sub view.
*
* @property {Kekule.ChemWidget.ChemObjDisplayer} displayer The parent displayer widget.
* @property {Kekule.ChemObject} target The target object inside displayer of this sub view.
*/
Kekule.ChemWidget.ChemObjDisplayerSubView = Class.create(ObjectEx,
/** @lends Kekule.ChemWidget.ChemObjDisplayerSubView# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.ChemObjDisplayerSubView',
/** @constructs */
initialize: function(displayer, targetObj)
{
this.tryApplySuper('initialize');
this.setPropStoreFieldValue('parent', displayer);
this.setPropStoreFieldValue('target', targetObj);
},
/** @private */
initProperties: function()
{
this.defineProp('parent', {
'dataType': 'Kekule.ChemWidget.ChemObjDisplayer',
'serializable': false,
'setter': null
});
// the target object
this.defineProp('target', {
'dataType': 'Kekule.ChemObject',
'serializable': false,
'getter': function() { return this.getPropStoreFieldValue('target') || this.getParent().getChemObj(); }
});
},
/** @ignore */
initPropValues: function()
{
this.tryApplySuper('initPropValues');
this.setBubbleEvent(true);
},
/** @ignore */
getHigherLevelObj: function()
{
return this.getParent();
},
/**
* Returns whether this type of sub view class can be applied to a object.
* Descendants should override this method.
* @param {Kekule.ChemObject} obj
* @returns {Bool}
* @private
*/
appliableToTarget: function(obj)
{
return false;
}
});
/**
* A special class to give a setting facade for ChemObjDisplayer.
* Do not use this class alone.
* @class
* @augments Kekule.Widget.BaseWidget.Settings
*
* @param {Kekule.ChemWidget.ChemObjDisplayer} displayer
* @ignore
*/
Kekule.ChemWidget.ChemObjDisplayer.Settings = Class.create(Kekule.Widget.BaseWidget.Settings,
/** @lends Kekule.ChemWidget.ChemObjDisplayer.Settings# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.ChemObjDisplayer.Settings',
/** @construct */
initialize: function(/*$super, */displayer)
{
this.tryApplySuper('initialize', [displayer]) /* $super(displayer) */;
},
/** @private */
initProperties: function()
{
//this.defineProp('displayer', {'dataType': 'Kekule.ChemWidget.ChemObjDisplayer', 'serializable': false, 'scope': PS.PUBLIC});
this.defineDelegatedProps(['enableLoadNewFile']);
},
/** @ignore */
getDisplayer: function()
{
return this.getWidget();
}
});
/**
* A special widget class to open a config widget for ChemObjDisplayer.
* Do not use this widget alone.
* @class
* @augments Kekule.Widget.Configurator
*
* @param {Kekule.ChemWidget.ChemObjDisplayer} displayer
*/
Kekule.ChemWidget.ChemObjDisplayer.Configurator = Class.create(Kekule.Widget.Configurator,
/** @lends Kekule.ChemWidget.ChemObjDisplayer.Configurator# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.ChemObjDisplayer.Configurator',
/** @private */
TAB_BTN_DATA_FIELD: '__$data__',
/** @construct */
initialize: function(/*$super, */displayer)
{
this.tryApplySuper('initialize', [displayer]) /* $super(displayer) */;
},
/** @ignore */
initPropValues: function(/*$super*/)
{
this.tryApplySuper('initPropValues') /* $super() */;
this.setLayout(Kekule.Widget.Layout.HORIZONTAL);
},
/** @private */
getCategoryInfos: function(/*$super*/)
{
var result = this.tryApplySuper('getCategoryInfos') /* $super() */;
var displayer = this.getDisplayer();
// renderConfigs and displayerConfigs
var configObjs = [displayer.getDisplayerConfigs(), displayer.getRenderConfigs()];
for (var j = 0, k = configObjs.length; j < k; ++j)
{
var config = configObjs[j];
var props = config.getPropListOfScopes([Class.PropertyScope.PUBLISHED]);
for (var i = 0, l = props.getLength(); i < l; ++i)
{
var propInfo = props.getPropInfoAt(i);
var obj = config.getPropValue(propInfo.name);
if (obj)
{
result.push({
'obj': obj,
'name': propInfo.name,
'title': propInfo.title,
'description': propInfo.description
});
}
}
}
return result;
},
/** @private */
getDisplayer: function()
{
return this.getWidget();
}
});
/**
* Action for loading a file in chem displayer.
* @class
* @augments Kekule.ActionLoadFileData
*/
Kekule.ChemWidget.ActionDisplayerLoadFile = Class.create(/*Kekule.ActionFileOpen*/Kekule.ActionLoadFileData,
/** @lends Kekule.ChemWidget.ActionDisplayerLoadFile# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.ActionDisplayerLoadFile',
/** @private */
HTML_CLASSNAME: CCNS.ACTION_LOADFILE,
/** @constructs */
initialize: function(/*$super, */displayer)
{
this.tryApplySuper('initialize') /* $super() */;
this.setDisplayer(displayer);
this.setText(/*CWT.CAPTION_LOADFILE*/Kekule.$L('ChemWidgetTexts.CAPTION_LOADFILE'));
this.setHint(/*CWT.HINT_LOADFILE*/Kekule.$L('ChemWidgetTexts.HINT_LOADFILE'));
},
/** @private */
initProperties: function()
{
this.defineProp('displayer', {'dataType': 'Kekule.ChemWidget.ChemObjDisplayer', 'serializable': false});
},
/** @private */
doUpdate: function(/*$super*/)
{
this.tryApplySuper('doUpdate') /* $super() */;
var displayer = this.getDisplayer();
this.setEnabled(this.getEnabled() && displayer && displayer.getEnabled() && displayer.getEnableLoadNewFile());
},
/* @private */
/*
doFileOpened: function(files)
{
if (files && files.length)
{
var file = files[0];
this.getDisplayer().loadFromFile(file);
}
}
*/
/** @private */
doDataLoaded: function(data, fileName, loaded)
{
//console.log('do data loaded', fileName);
if (loaded)
this.getDisplayer().loadFromData(data, null, fileName);
}
});
/**
* Base class for actions for chem displayer.
* @class
* @augments Kekule.Action
*
* @param {Kekule.ChemWidget.ChemObjDisplayer} displayer Target displayer widget.
*
* @property {Kekule.ChemWidget.ChemObjDisplayer} displayer Target displayer widget.
*/
Kekule.ChemWidget.ActionOnDisplayer = Class.create(Kekule.Action,
/** @lends Kekule.ChemWidget.ActionOnDisplayer# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.ActionOnDisplayer',
/** @constructs */
initialize: function(/*$super, */displayer, caption, hint)
{
this.tryApplySuper('initialize') /* $super() */;
this.setDisplayer(displayer);
this.setText(caption);
this.setHint(hint);
},
/** @private */
initProperties: function()
{
this.defineProp('displayer', {'dataType': 'Kekule.ChemWidget.ChemObjDisplayer', 'serializable': false});
},
/** @private */
doUpdate: function()
{
var displayer = this.getDisplayer();
this.setEnabled(displayer && displayer.getChemObj() && displayer.getChemObjLoaded() && displayer.getEnabled());
},
/** @ignore */
execute: function(target, htmlEvent)
{
if (!this.getDisplayer() && target instanceof Kekule.ChemWidget.ChemObjDisplayer)
this.setDisplayer(target);
return this.tryApplySuper('execute', [target, htmlEvent]);
}
});
/**
* Action for set chem object to null in chem displayer.
* @class
* @augments Kekule.ActionOnDisplayer
*/
Kekule.ChemWidget.ActionDisplayerClear = Class.create(Kekule.ChemWidget.ActionOnDisplayer,
/** @lends Kekule.ChemWidget.ActionDisplayerClear# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.ActionDisplayerClear',
/** @private */
HTML_CLASSNAME: CCNS.ACTION_CLEAROBJS,
/** @constructs */
initialize: function(/*$super, */displayer)
{
this.tryApplySuper('initialize', [displayer, Kekule.$L('ChemWidgetTexts.CAPTION_CLEAROBJS'), Kekule.$L('ChemWidgetTexts.HINT_CLEAROBJS')]) /* $super(displayer, Kekule.$L('ChemWidgetTexts.CAPTION_CLEAROBJS'), Kekule.$L('ChemWidgetTexts.HINT_CLEAROBJS')) */;
},
/** @private */
doUpdate: function(/*$super*/)
{
this.tryApplySuper('doUpdate') /* $super() */;
var displayer = this.getDisplayer();
this.setEnabled(displayer && displayer.getEnabled() && displayer.getChemObj() /* && displayer.getChemObjLoaded()*/);
},
/** @private */
doExecute: function(target)
{
var displayer = this.getDisplayer();
displayer.setChemObj(null);
}
});
/**
* Action for loading chem object (either from file or from pasted text) to chem displayer.
* @class
* @augments Kekule.ActionOnDisplayer
*/
Kekule.ChemWidget.ActionDisplayerLoadData = Class.create(Kekule.ChemWidget.ActionOnDisplayer,
/** @lends Kekule.ChemWidget.ActionDisplayerLoadData# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.ActionDisplayerLoadData',
/** @private */
HTML_CLASSNAME: CCNS.ACTION_LOADDATA,
/** @constructs */
initialize: function(/*$super, */displayer)
{
this.tryApplySuper('initialize', [displayer, /*CWT.CAPTION_LOADDATA, CWT.HINT_LOADDATA*/Kekule.$L('ChemWidgetTexts.CAPTION_LOADDATA'), Kekule.$L('ChemWidgetTexts.HINT_LOADDATA')]) /* $super(displayer, \*CWT.CAPTION_LOADDATA, CWT.HINT_LOADDATA*\Kekule.$L('ChemWidgetTexts.CAPTION_LOADDATA'), Kekule.$L('ChemWidgetTexts.HINT_LOADDATA')) */;
//this.setDisplayer(displayer);
//this.setText(CWT.CAPTION_LOADDATA);
//this.setHint(CWT.HINT_LOADDATA);
/*
this._openFileAction = new Kekule.ActionFileOpen();
this._openFileAction.update();
this._openFileAction.addEventListener('open', this.reactFileLoad, this);
this._sBtnLoadFromFile = CWT.CAPTION_LOADDATA_FROM_FILE;
*/
},
/** @ignore */
finalize: function(/*$super*/)
{
//this._openFileAction.finalize();
this.tryApplySuper('finalize') /* $super() */;
},
initPropValues: function(/*$super*/)
{
this.tryApplySuper('initPropValues') /* $super() */;
this.setDialogShowType(Kekule.Widget.ShowHideType.DROPDOWN);
},
/** @private */
initProperties: function()
{
// private
this.defineProp('dataDialog', {'dataType': 'Kekule.ChemWidget.LoadDataDialog', 'serializable': false, 'setter': null,
'getter': function()
{
var result = this.getPropStoreFieldValue('dataDialog');
if (!result)
{
result = this.createDataDialog();
this.setPropStoreFieldValue('dataDialog', result);
}
return result;
}
});
this.defineProp('dialogShowType', {'dataType': DataType.INT});
},
/** @private */
createDataDialog: function()
{
var doc = this.getDisplayer().getDocument();
var result = new Kekule.ChemWidget.LoadDataDialog(doc);
return result;
},
/** @private */
doUpdate: function(/*$super*/)
{
this.tryApplySuper('doUpdate') /* $super() */;
var displayer = this.getDisplayer();
this.setEnabled(displayer && displayer.getEnabled() && displayer.getEnableLoadNewFile());
},
/** @private */
doExecute: function(target)
{
var self = this;
var dialog = this.getDataDialog();
dialog.setPropStoreFieldValue('defaultFormatId', this.getDisplayer().getDefaultInputFormatId());
dialog.setAllowedFormatIds(this.getDisplayer().getAllowedInputFormatIds() || Kekule.IO.ChemDataReaderManager.getAllReadableFormatIds());
var formatSelector = dialog._formatSelector;
//var formatItems = this.getFormatSelectorItems(chemObj, writerInfos);
//formatSelector.setItems(formatItems);
// open a dialog to choose format first, then save to file
var showType = this.getDialogShowType();
var openMethod = (showType === Kekule.Widget.ShowHideType.DIALOG)? dialog.openModal: dialog.open;
openMethod.apply(dialog, [
function(result)
{
if (dialog.isPositiveResult(result)) // load
{
/*
var data = dialog._dataEditor.getValue();
var mimeType = dialog._formatSelector.getValue();
//self.setLastFormat(formatId);
var displayer = self.getDisplayer();
displayer.loadFromData(data, mimeType);
*/
/*
var displayer = self.getDisplayer();
displayer.load(dialog.getChemObj());
*/
//self.doLoadToDisplayer(dialog.getChemObj(), dialog);
var dataDetails = dialog.getDataDetails() || {};
var displayer = self.getDisplayer();
//displayer.loadFromData(dataDetails.data, dataDetails.mimeType, dataDetails.fileName, dataDetails.formatId);
self.doLoadInDisplayer(displayer, dataDetails, dialog);
}
}, target, showType]);
},
/* @private */
/*
doLoadToDisplayer: function(chemObj, dialog)
{
var displayer = this.getDisplayer();
displayer._tryAutoGenerateChemObjCoords(chemObj, function(newChemObj){
displayer.load(newChemObj);
});
}
*/
/** @private */
doLoadInDisplayer: function(displayer, dataDetails, dialog)
{
return displayer.loadFromData(dataDetails.data, dataDetails.mimeType, dataDetails.fileName, dataDetails.formatId);
}
});
/**
* Action for saving chem object to file in chem displayer.
* @class
* @augments Kekule.ActionOnDisplayer
*
* @property {Int} dialogShowType Value from {@link Kekule.Widget.ShowHideType}, diplay type of save dialog.
*/
Kekule.ChemWidget.ActionDisplayerSaveFile = Class.create(Kekule.ChemWidget.ActionOnDisplayer,
/** @lends Kekule.ChemWidget.ActionDisplayerSaveFile# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.ActionDisplayerSaveFile',
/** @private */
HTML_CLASSNAME: CCNS.ACTION_SAVEFILE,
/** @constructs */
initialize: function(/*$super, */displayer)
{
this.tryApplySuper('initialize', [displayer, /*CWT.CAPTION_SAVEFILE, CWT.HINT_SAVEFILE*/Kekule.$L('ChemWidgetTexts.CAPTION_SAVEFILE'), Kekule.$L('ChemWidgetTexts.HINT_SAVEFILE')]) /* $super(displayer, \*CWT.CAPTION_SAVEFILE, CWT.HINT_SAVEFILE*\Kekule.$L('ChemWidgetTexts.CAPTION_SAVEFILE'), Kekule.$L('ChemWidgetTexts.HINT_SAVEFILE')) */;
//this.setDisplayer(displayer);
//this.setText(CWT.CAPTION_SAVEFILE);
//this.setHint(CWT.HINT_SAVEFILE);
this._saveAction = new Kekule.ActionFileSave();
},
/** @ignore */
finalize: function(/*$super*/)
{
this._saveAction.finalize();
this.tryApplySuper('finalize') /* $super() */;
},
/** @ignore */
initPropValues: function(/*$super*/)
{
this.tryApplySuper('initPropValues') /* $super() */;
this.setDialogShowType(Kekule.Widget.ShowHideType.DROPDOWN);
},
/** @private */
initProperties: function()
{
// private
this.defineProp('formatDialog', {'dataType': 'Kekule.Widget.Dialog', 'serializable': false, 'setter': null,
'getter': function()
{
var result = this.getPropStoreFieldValue('formatDialog');
if (!result)
{
result = this.createFormatDialog();
this.setPropStoreFieldValue('formatDialog', result);
}
return result;
}
});
this.defineProp('lastFormat', {'dataType': DataType.STRING, 'serializable': false});
this.defineProp('currSaveData', {'dataType': DataType.VARIANT, 'serializable': false});
this.defineProp('dialogShowType', {'dataType': DataType.INT});
},
/** @private */
getChemObjSrcInfo: function(chemObj)
{
var displayer = this.getDisplayer();
return displayer.getChemObjSrcInfo? displayer.getChemObjSrcInfo(chemObj):
chemObj.getSrcInfo? chemObj.getSrcInfo():
null;
},
/** @private */
getChemObjSrcFormat: function(chemObj)
{
var srcInfo = this.getChemObjSrcInfo(chemObj);
return srcInfo? srcInfo.format: null;
},
/** @private */
createFormatDialog: function()
{
var doc = this.getDisplayer().getDocument();
var result = new Kekule.Widget.Dialog(doc, /*CWT.CAPTION_CHOOSEFILEFORMAT*/Kekule.$L('ChemWidgetTexts.CAPTION_SAVEDATA_DIALOG'), [Kekule.Widget.DialogButtons.OK, Kekule.Widget.DialogButtons.CANCEL]);
result.addClassName(CCNS.DIALOG_CHOOSE_FILE_FORAMT);
result.setAutoAdjustSizeOnPopup(true);
// label
var elem = doc.createElement('div');
elem.innerHTML = Kekule.$L('ChemWidgetTexts.CAPTION_SELECT_FORMAT'); //CWT.CAPTION_SELECT_FORMAT;
result.getClientElem().appendChild(elem);
// format selector
elem = doc.createElement('div');
result.getClientElem().appendChild(elem);
var formatSelector = new Kekule.Widget.SelectBox(result);
formatSelector.addClassName(CCNS.DIALOG_CHOOSE_FILE_FORAMT_FORMATBOX);
formatSelector.appendToElem(elem);
formatSelector.addEventListener('valueChange', this.reactFormatSelectorChange, this);
result._formatSelector = formatSelector;
result.setAutoFocusedObject(formatSelector);
// label
var elem = doc.createElement('div');
elem.innerHTML = Kekule.$L('ChemWidgetTexts.CAPTION_PREVIEW_FILE_CONTENT'); //CWT.CAPTION_PREVIEW_FILE_CONTENT;
result.getClientElem().appendChild(elem);
// preview textarea
elem = doc.createElement('div');
elem.className = CCNS.DIALOG_CHOOSE_FILE_FORAMT_PREVIEWER_REGION;
result.getClientElem().appendChild(elem);
var previewTextArea = new Kekule.Widget.TextEditor(result); //new Kekule.Widget.TextArea(result);
previewTextArea.setReadOnly(true);
previewTextArea.setWrap('off').setAutoWrapThreshold(true);
previewTextArea.setToolbarPos(Kekule.Widget.Position.BOTTOM);
previewTextArea.addClassName(CCNS.DIALOG_CHOOSE_FILE_FORAMT_PREVIEWER);
previewTextArea.appendToElem(elem);
result._previewTextArea = previewTextArea;
if (result.setResizable)
result.setResizable(true);
return result;
},
/** @private */
getAvailableWriterInfos: function(chemObj)
{
//var obj = this.getDisplayer().getChemObj();
if (!chemObj)
return [];
else
{
var result = Kekule.IO.ChemDataWriterManager.getAvailableWriterInfos(null, chemObj);
return result;
}
},
/** @private */
getFormatSelectorItems: function(chemObj, writerInfos)
{
//console.log(writerInfos);
var result = [];
var formatIds = [];
var srcInfo = this.getChemObjSrcInfo(chemObj);
var srcFormat = (srcInfo)? srcInfo.format: null;
for (var i = 0, l = writerInfos.length; i < l; ++i)
{
var info = writerInfos[i];
Kekule.ArrayUtils.pushUnique(formatIds, info.formatId);
}
formatIds = Kekule.ArrayUtils.intersect(this.getAllowedFormatIds(), formatIds);
for (var i = 0, l = formatIds.length; i < l; ++i)
{
var idInfo = Kekule.IO.DataFormatsManager.getFormatInfo(formatIds[i]);
if (idInfo)
{
var fileExts = Kekule.ArrayUtils.clone(Kekule.ArrayUtils.toArray(idInfo.fileExts));
for (var j = 0, k = fileExts.length; j < k; ++j)
{
fileExts[j] = '*.' + fileExts[j];
}
var sFileExt = fileExts.join(', ');
var text = idInfo.title;
/*
if (idInfo.mimeType)
text += ' | ' + idInfo.mimeType;
*/
if (sFileExt)
text += ' (' + sFileExt + ')';
var selected = srcFormat? (formatIds[i] === srcFormat):
this.getDisplayer().getDefaultOutputFormatId()? (formatIds[i] === this.getDisplayer().getDefaultOutputFormatId()):
this.getLastFormat()? (this.getLastFormat() === formatIds[i]):
i === 0;
result.push({
'value': idInfo.id,
'text': text,
'title': idInfo.mimeType,
'data': idInfo,
'selected': selected
});
}
}
result.sort(function(a, b)
{
return (a.text < b.text)? -1:
(a.text > b.text)? 1:
0;
}
);
return result;
},
/** @private */
getAllowedFormatIds: function()
{
return this.getDisplayer().getAllowedOutputFormatIds() || Kekule.IO.ChemDataWriterManager.getAllWritableFormatIds();
},
/** @private */
getFileFilters: function(formatItems)
{
var result = [];
for (var i = 0, l = formatItems.length; i < l; ++i)
{
var format = formatItems[i];
var info = format.data;
var title = format.text || format.title || format.value;
var exts = Kekule.ArrayUtils.toArray(info.fileExts);
/*
for (var j = 0, k = exts.length; j < k; ++j)
{
result.push({'title': title, 'filter': '.' + exts[j]});
}
*/
result.push({'title': title, 'filter': '.' + exts.join(',.')});
}
// add all and any filter
result.push(Kekule.NativeServices.FILTER_ALL_SUPPORT);
result.push(Kekule.NativeServices.FILTER_ANY);
return result;
},
/** @private */
updateFormatItems: function(chemObj)
{
this._formatItems = this.getFormatSelectorItems(chemObj, this.getAvailableWriterInfos());
this._formatSelector.setItems(this._formatItems);
var filters = this.getFileFilters(this._formatItems);
this._saveAction.setFilters(filters);
},
/** @private */
reactFormatSelectorChange: function()
{
var formatId = this.getFormatDialog()._formatSelector.getValue();
this.updatePreview(formatId, this.getFormatDialog());
},
/** @private */
updatePreview: function(formatId, dialogWidget)
{
var textArea = dialogWidget._previewTextArea;
textArea.setValue('');
if (formatId)
{
var formatInfo = Kekule.IO.DataFormatsManager.getFormatInfo(formatId);
var obj = this.getTargetObj();
var srcInfo = this.getChemObjSrcInfo(obj);
if (srcInfo && srcInfo.format === formatId && srcInfo.data) // can use src data
{
textArea.setValue(srcInfo.data);
this.setCurrSaveData(srcInfo.data);
}
else
{
// get suitable writer
/*
var writer = Kekule.IO.ChemDataWriterManager.getWriterByFormat(formatId, null, obj);
if (writer)
{
var doCanonicalize = this.getDisplayer().getDisplayerConfigs().getIoConfigs().getCanonicalizeBeforeSave();
if (doCanonicalize && obj.canonicalize) // canonicalize first
{
var obj = obj.clone? obj.clone(true): obj; // clone with id
obj.canonicalize();
}
var data = writer.writeData(obj, formatInfo.dataType, formatId);
textArea.setValue(data);
this.setCurrSaveData(data);
}
*/
var data = this.getDisplayer().saveData(formatId, formatInfo.dataType, obj);
textArea.setValue(data);
this.setCurrSaveData(data);
}
}
},
/** @private */
getTargetObj: function(formatId)
{
return this.getDisplayer().getSavingTargetObj();
},
/** @private */
doUpdate: function(/*$super*/)
{
this.tryApplySuper('doUpdate') /* $super() */;
var displayer = this.getDisplayer();
if (this._saveAction)
this._saveAction.update();
this.setEnabled(displayer && displayer.getEnabled() && displayer.getChemObj()/* && this._saveAction.getEnabled()*/);
},
/** @private */
doExecute: function(target)
{
var self = this;
var dialog = this.getFormatDialog();
var chemObj = this.getTargetObj(); //this.getDisplayer().getChemObj();
var formatSelector = dialog._formatSelector;
var writerInfos = this.getAvailableWriterInfos(chemObj);
var formatItems = this.getFormatSelectorItems(chemObj, writerInfos);
formatSelector.setItems(formatItems);
this.reactFormatSelectorChange();
// update OK button enabled
var saveEnabled = false;
if (this._saveAction)
{
//this._saveAction.update();
saveEnabled = this._saveAction.getEnabled();
}
var btn = dialog.getDialogButton(Kekule.Widget.DialogButtons.OK);
if (btn)
btn.setEnabled(saveEnabled);
// open a dialog to choose format first, then save to file
var showType = this.getDialogShowType();
var openMethod = (showType === Kekule.Widget.ShowHideType.DIALOG)? dialog.openModal: dialog.open;
openMethod.apply(dialog, [
function(result)
{
if (result === Kekule.Widget.DialogButtons.OK) // save
{
//var data = dialog._previewTextArea.getValue();
var data = self.getCurrSaveData();
self._saveAction.setData(data);
var formatId = dialog._formatSelector.getValue();
self.setLastFormat(formatId);
//var fileExts = Kekule.IO.DataFormatsManager.getFileExts(formatId);
var formatInfo = Kekule.IO.DataFormatsManager.getFormatInfo(formatId);
var fileExts = formatInfo.fileExts;
var ext = Kekule.ArrayUtils.toArray(fileExts[0] || fileExts);
var filters = [
{'title': formatInfo.title || formatInfo.mimeType, 'filter': '.' + ext.join(',.')},
Kekule.NativeServices.FILTER_ANY
];
var fileName = /*CWT.S_DEF_SAVE_FILENAME*/Kekule.$L('ChemWidgetTexts.S_DEF_SAVE_FILENAME') + '.' + ext;
self._saveAction.setFileName(fileName);
self._saveAction.setFilters(filters);
//console.log(fileName);
self._saveAction.execute(target);
}
}, target, showType]
);
}
});
/**
* Action for reset viewer.
* @class
* @augments Kekule.ChemWidget.ActionOnDisplayer
*/
Kekule.ChemWidget.ActionDisplayerCopy = Class.create(Kekule.ChemWidget.ActionOnDisplayer,
/** @lends Kekule.ChemWidget.ActionDisplayerCopy# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.ActionDisplayerCopy',
/** @private */
HTML_CLASSNAME: CCNS.ACTION_COPY,
/** @constructs */
initialize: function(/*$super, */displayer)
{
this.tryApplySuper('initialize', [displayer, Kekule.$L('ChemWidgetTexts.CAPTION_COPY_WHOLE'), Kekule.$L('ChemWidgetTexts.HINT_COPY_WHOLE')]);
},
/** @private */
doUpdate: function(/*$super*/)
{
this.tryApplySuper('doUpdate') /* $super() */;
if (this.getEnabled())
{
var srcObj = this.getDisplayer().getChemObj();
this.setEnabled(srcObj && srcObj.clone);
}
},
/** @private */
doExecute: function()
{
var displayer = this.getDisplayer();
if (displayer)
{
var srcObj = displayer.getChemObj();
if (srcObj)
Kekule.Widget.clipboard.setObjects(Kekule.IO.MimeType.JSON, [srcObj]);
}
}
});
/**
* Action for reset viewer.
* @class
* @augments Kekule.ChemWidget.ActionOnDisplayer
*/
Kekule.ChemWidget.ActionDisplayerReset = Class.create(Kekule.ChemWidget.ActionOnDisplayer,
/** @lends Kekule.ChemWidget.ActionDisplayerReset# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.ActionDisplayerReset',
/** @private */
HTML_CLASSNAME: CCNS.ACTION_RESET,
/** @constructs */
initialize: function(/*$super, */displayer)
{
this.tryApplySuper('initialize', [displayer, /*CWT.CAPTION_RESETVIEW, CWT.HINT_RESETVIEW*/Kekule.$L('ChemWidgetTexts.CAPTION_RESETVIEW'), Kekule.$L('ChemWidgetTexts.HINT_RESETVIEW')]) /* $super(displayer, \*CWT.CAPTION_RESETVIEW, CWT.HINT_RESETVIEW*\Kekule.$L('ChemWidgetTexts.CAPTION_RESETVIEW'), Kekule.$L('ChemWidgetTexts.HINT_RESETVIEW')) */;
},
/** @private */
doExecute: function()
{
this.getDisplayer().resetDisplay();
}
});
/**
* Action for zoom in the viewer.
* @class
* @augments Kekule.ChemWidget.ActionViewer
*/
Kekule.ChemWidget.ActionDisplayerZoomIn = Class.create(Kekule.ChemWidget.ActionOnDisplayer,
/** @lends Kekule.ChemWidget.ActionDisplayerZoomIn# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.ActionDisplayerZoomIn',
/** @private */
HTML_CLASSNAME: CCNS.ACTION_ZOOMIN,
/** @constructs */
initialize: function(/*$super, */displayer)
{
this.tryApplySuper('initialize', [displayer, /*CWT.CAPTION_ZOOMIN, CWT.HINT_ZOOMIN*/Kekule.$L('ChemWidgetTexts.CAPTION_ZOOMIN'), Kekule.$L('ChemWidgetTexts.HINT_ZOOMIN')]) /* $super(displayer, \*CWT.CAPTION_ZOOMIN, CWT.HINT_ZOOMIN*\Kekule.$L('ChemWidgetTexts.CAPTION_ZOOMIN'), Kekule.$L('ChemWidgetTexts.HINT_ZOOMIN')) */;
},
/** @private */
doExecute: function()
{
this.getDisplayer().zoomIn();
}
});
/**
* Action for zoom out the viewer.
* @class
* @augments Kekule.ChemWidget.ActionViewer
*/
Kekule.ChemWidget.ActionDisplayerZoomOut = Class.create(Kekule.ChemWidget.ActionOnDisplayer,
/** @lends Kekule.ChemWidget.ActionDisplayerZoomOut# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.ActionDisplayerZoomOut',
/** @private */
HTML_CLASSNAME: CCNS.ACTION_ZOOMOUT,
/** @constructs */
initialize: function(/*$super, */displayer)
{
this.tryApplySuper('initialize', [displayer, /*CWT.CAPTION_ZOOMOUT, CWT.HINT_ZOOMOUT*/Kekule.$L('ChemWidgetTexts.CAPTION_ZOOMOUT'), Kekule.$L('ChemWidgetTexts.HINT_ZOOMOUT')]) /* $super(displayer, \*CWT.CAPTION_ZOOMOUT, CWT.HINT_ZOOMOUT*\Kekule.$L('ChemWidgetTexts.CAPTION_ZOOMOUT'), Kekule.$L('ChemWidgetTexts.HINT_ZOOMOUT')) */;
},
/** @private */
doExecute: function()
{
this.getDisplayer().zoomOut();
}
});
/**
* Action for reset zoom to 1 on viewer.
* @class
* @augments Kekule.ChemWidget.ActionViewer
*/
Kekule.ChemWidget.ActionDisplayerResetZoom = Class.create(Kekule.ChemWidget.ActionOnDisplayer,
/** @lends Kekule.ChemWidget.ActionDisplayerResetZoom# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.ActionDisplayerResetZoom',
/** @private */
HTML_CLASSNAME: CCNS.ACTION_RESET_ZOOM,
/** @constructs */
initialize: function(/*$super, */displayer)
{
this.tryApplySuper('initialize', [displayer, /*CWT.CAPTION_RESETZOOM, CWT.HINT_RESETZOOM*/Kekule.$L('ChemWidgetTexts.CAPTION_RESETZOOM'), Kekule.$L('ChemWidgetTexts.HINT_RESETZOOM')]) /* $super(displayer, \*CWT.CAPTION_RESETZOOM, CWT.HINT_RESETZOOM*\Kekule.$L('ChemWidgetTexts.CAPTION_RESETZOOM'), Kekule.$L('ChemWidgetTexts.HINT_RESETZOOM')) */;
},
/** @private */
doExecute: function()
{
this.getDisplayer().resetZoom();
}
});
/**
* Action for show or hide all hydrogen atoms in 3D mode displayer.
* @class
* @augments Kekule.ChemWidget.ActionOnDisplayer
*/
Kekule.ChemWidget.ActionDisplayerHideHydrogens = Class.create(Kekule.ChemWidget.ActionOnDisplayer,
/** @lends Kekule.ChemWidget.ActionDisplayerHideHydrogens# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.ActionDisplayerHideHydrogens',
/** @private */
HTML_CLASSNAME: CCNS.ACTION_MOL_HIDE_HYDROGENS,
/** @constructs */
initialize: function(/*$super, */displayer)
{
this.tryApplySuper('initialize', [displayer, /*CWT.CAPTION_HIDEHYDROGENS, CWT.HINT_HIDEHYDROGENS*/Kekule.$L('ChemWidgetTexts.CAPTION_HIDEHYDROGENS'), Kekule.$L('ChemWidgetTexts.HINT_HIDEHYDROGENS')]) /* $super(displayer, \*CWT.CAPTION_HIDEHYDROGENS, CWT.HINT_HIDEHYDROGENS*\Kekule.$L('ChemWidgetTexts.CAPTION_HIDEHYDROGENS'), Kekule.$L('ChemWidgetTexts.HINT_HIDEHYDROGENS')) */;
},
/** @private */
doUpdate: function(/*$super*/)
{
this.tryApplySuper('doUpdate') /* $super() */;
var displayer = this.getDisplayer();
var flag = displayer && (displayer.getRenderType() === Kekule.Render.RendererType.R3D);
this.setDisplayed(/*this.getDisplayed() &&*/ flag).setEnabled(this.getEnabled() && flag);
this.setChecked(displayer && displayer.getHideHydrogens());
},
/** @private */
doExecute: function()
{
var checked = !this.getChecked();
this.setChecked(checked);
this.getDisplayer().setHideHydrogens(checked);
}
});
/**
* Base action for change molecule display type of displayer.
* @class
* @augments Kekule.ChemWidget.ActionOnDisplayer
*/
Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBase = Class.create(Kekule.ChemWidget.ActionOnDisplayer,
/** @lends Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBase# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBase',
/** @constructs */
initialize: function(/*$super, */displayer, caption, hint, displayType)
{
this.tryApplySuper('initialize', [displayer, caption, hint]) /* $super(displayer, caption, hint) */;
this._displayType = displayType;
this.setCheckGroup('__molDisplayType__');
},
/** @private */
doUpdate: function(/*$super*/)
{
this.tryApplySuper('doUpdate') /* $super() */;
var displayer = this.getDisplayer();
this.setChecked(displayer && displayer.getCurrMoleculeDisplayType() === this.getMolDisplayType());
},
/** @private */
doExecute: function()
{
this.getDisplayer().setMoleculeDisplayType(this._displayType);
this.setChecked(true);
},
/**
* Returns molecule display type set by this action.
* @returns {Int}
*/
getMolDisplayType: function()
{
return this._displayType;
}
});
/**
* Base action for change 2D molecule display type of displayer.
* @class
* @augments Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBase
*/
Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBase2D = Class.create(Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBase,
/** @lends Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBase2D# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBase2D',
/** @private */
doUpdate: function(/*$super*/)
{
this.tryApplySuper('doUpdate') /* $super() */;
var displayer = this.getDisplayer();
var flag = displayer && (displayer.getRenderType() === Kekule.Render.RendererType.R2D);
this.setDisplayed(/*this.getDisplayed() &&*/ flag).setEnabled(this.getEnabled() && flag);
}
});
/**
* Base action for change 3D molecule display type of displayer.
* @class
* @augments Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBase
*/
Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBase3D = Class.create(Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBase,
/** @lends Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBase3D# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBase3D',
/** @private */
doUpdate: function(/*$super*/)
{
this.tryApplySuper('doUpdate') /* $super() */;
var displayer = this.getDisplayer();
var flag = displayer && (displayer.getRenderType() === Kekule.Render.RendererType.R3D);
this.setDisplayed(/*this.getDisplayed() &&*/ flag).setEnabled(this.getEnabled() && flag);
}
});
/**
* Action change displayer's molecule display type to {@link Kekule.Render.Molecule2DDisplayType.SKELETAL}.
* @class
* @augments Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBase2D
*/
Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeSkeletal = Class.create(Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBase2D,
/** @lends Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeSkeletal# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeSkeletal',
/** @private */
HTML_CLASSNAME: CCNS.ACTION_MOL_DISPLAY_SKELETAL,
/** @constructs */
initialize: function(/*$super, */viewer)
{
this.tryApplySuper('initialize', [viewer, /*CWT.CAPTION_SKELETAL, CWT.HINT_SKELETAL,*/
Kekule.$L('ChemWidgetTexts.CAPTION_SKELETAL'), Kekule.$L('ChemWidgetTexts.HINT_SKELETAL'),
Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeSkeletal.TYPE]) /* $super(viewer, \*CWT.CAPTION_SKELETAL, CWT.HINT_SKELETAL,*\
Kekule.$L('ChemWidgetTexts.CAPTION_SKELETAL'), Kekule.$L('ChemWidgetTexts.HINT_SKELETAL'),
Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeSkeletal.TYPE) */;
}
});
/** @Ignore */
Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeSkeletal.TYPE = Kekule.Render.Molecule2DDisplayType.SKELETAL;
/**
* Action change displayer's molecule display type to {@link Kekule.Render.Molecule2DDisplayType.CONDENSED}.
* @class
* @augments Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBase2D
*/
Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeCondensed = Class.create(Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBase2D,
/** @lends Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeCondensed# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeCondensed',
/** @private */
HTML_CLASSNAME: CCNS.ACTION_MOL_DISPLAY_CONDENSED,
/** @constructs */
initialize: function(/*$super, */viewer)
{
this.tryApplySuper('initialize', [viewer, /*CWT.CAPTION_CONDENSED, CWT.HINT_CONDENSED,*/
Kekule.$L('ChemWidgetTexts.CAPTION_CONDENSED'), Kekule.$L('ChemWidgetTexts.HINT_CONDENSED'),
Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeCondensed.TYPE]) /* $super(viewer, \*CWT.CAPTION_CONDENSED, CWT.HINT_CONDENSED,*\
Kekule.$L('ChemWidgetTexts.CAPTION_CONDENSED'), Kekule.$L('ChemWidgetTexts.HINT_CONDENSED'),
Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeCondensed.TYPE) */;
}
});
/** @Ignore */
Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeCondensed.TYPE = Kekule.Render.Molecule2DDisplayType.CONDENSED;
/**
* Action change displayer's molecule display type to {@link Kekule.Render.Molecule3DDisplayType.WIRE}.
* @class
* @augments Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBase3D
*/
Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeWire = Class.create(Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBase3D,
/** @lends Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeWire# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeWire',
/** @private */
HTML_CLASSNAME: CCNS.ACTION_MOL_DISPLAY_WIRE,
/** @constructs */
initialize: function(/*$super, */viewer)
{
this.tryApplySuper('initialize', [viewer, /*CWT.CAPTION_WIRE, CWT.HINT_WIRE,*/
Kekule.$L('ChemWidgetTexts.CAPTION_WIRE'), Kekule.$L('ChemWidgetTexts.HINT_WIRE'),
Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeWire.TYPE]) /* $super(viewer, \*CWT.CAPTION_WIRE, CWT.HINT_WIRE,*\
Kekule.$L('ChemWidgetTexts.CAPTION_WIRE'), Kekule.$L('ChemWidgetTexts.HINT_WIRE'),
Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeWire.TYPE) */;
}
});
/** @Ignore */
Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeWire.TYPE = Kekule.Render.Molecule3DDisplayType.WIRE;
/**
* Action change displayer's molecule display type to {@link Kekule.Render.Molecule3DDisplayType.STICKS}.
* @class
* @augments Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBase3D
*/
Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeSticks = Class.create(Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBase3D,
/** @lends Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeSticks# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeSticks',
/** @private */
HTML_CLASSNAME: CCNS.ACTION_MOL_DISPLAY_STICKS,
/** @constructs */
initialize: function(/*$super, */viewer)
{
this.tryApplySuper('initialize', [viewer, /*CWT.CAPTION_STICKS, CWT.HINT_STICKS,*/
Kekule.$L('ChemWidgetTexts.CAPTION_STICKS'), Kekule.$L('ChemWidgetTexts.HINT_STICKS'),
Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeSticks.TYPE]) /* $super(viewer, \*CWT.CAPTION_STICKS, CWT.HINT_STICKS,*\
Kekule.$L('ChemWidgetTexts.CAPTION_STICKS'), Kekule.$L('ChemWidgetTexts.HINT_STICKS'),
Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeSticks.TYPE) */;
}
});
/** @Ignore */
Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeSticks.TYPE = Kekule.Render.Molecule3DDisplayType.STICKS;
/**
* Action change displayer's molecule display type to {@link Kekule.Render.Molecule3DDisplayType.BALL_STICK}.
* @class
* @augments Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBase3D
*/
Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBallStick = Class.create(Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBase3D,
/** @lends Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBallStick# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBallStick',
/** @private */
HTML_CLASSNAME: CCNS.ACTION_MOL_DISPLAY_BALLSTICK,
/** @constructs */
initialize: function(/*$super, */viewer)
{
this.tryApplySuper('initialize', [viewer, /*CWT.CAPTION_BALLSTICK, CWT.HINT_BALLSTICK,*/
Kekule.$L('ChemWidgetTexts.CAPTION_BALLSTICK'), Kekule.$L('ChemWidgetTexts.HINT_BALLSTICK'),
Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBallStick.TYPE]) /* $super(viewer, \*CWT.CAPTION_BALLSTICK, CWT.HINT_BALLSTICK,*\
Kekule.$L('ChemWidgetTexts.CAPTION_BALLSTICK'), Kekule.$L('ChemWidgetTexts.HINT_BALLSTICK'),
Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBallStick.TYPE) */;
}
});
/** @Ignore */
Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBallStick.TYPE = Kekule.Render.Molecule3DDisplayType.BALL_STICK;
/**
* Action change displayer's molecule display type to {@link Kekule.Render.Molecule3DDisplayType.SPACE_FILL}.
* @class
* @augments Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBase3D
*/
Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeSpaceFill = Class.create(Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeBase3D,
/** @lends Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeSpaceFill# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeSpaceFill',
/** @private */
HTML_CLASSNAME: CCNS.ACTION_MOL_DISPLAY_SPACEFILL,
/** @constructs */
initialize: function(/*$super, */viewer)
{
this.tryApplySuper('initialize', [viewer, /*CWT.CAPTION_SPACEFILL, CWT.HINT_SPACEFILL,*/
Kekule.$L('ChemWidgetTexts.CAPTION_SPACEFILL'), Kekule.$L('ChemWidgetTexts.HINT_SPACEFILL'),
Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeSpaceFill.TYPE]) /* $super(viewer, \*CWT.CAPTION_SPACEFILL, CWT.HINT_SPACEFILL,*\
Kekule.$L('ChemWidgetTexts.CAPTION_SPACEFILL'), Kekule.$L('ChemWidgetTexts.HINT_SPACEFILL'),
Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeSpaceFill.TYPE) */;
}
});
/** @Ignore */
Kekule.ChemWidget.ActionDisplayerChangeMolDisplayTypeSpaceFill.TYPE = Kekule.Render.Molecule3DDisplayType.SPACE_FILL;
/**
* Action to automatically generate coordinates and layout atoms in 2D or 3D mode displayer.
* @class
* @augments Kekule.ChemWidget.ActionOnDisplayer
*/
Kekule.ChemWidget.ActionDisplayerAutoGenerateCoordsForMolecule = Class.create(Kekule.ChemWidget.ActionOnDisplayer,
/** @lends Kekule.ChemWidget.ActionDisplayerAutoGenerateCoordsForMolecule# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.ActionDisplayerAutoGenerateCoordsForMolecule',
/** @private */
HTML_CLASSNAME: CCNS.ACTION_MOL_AUTO_COORD_GENERATION,
/** @constructs */
initialize: function(displayer)
{
this.tryApplySuper('initialize', [displayer, Kekule.$L('ChemWidgetTexts.CAPTION_AUTO_COORD_GENERATION'), Kekule.$L('ChemWidgetTexts.HINT_AUTO_COORD_GENERATION')]);
},
/** @private */
getCoordMode: function()
{
/*
var displayer = this.getDisplayer();
return displayer && displayer.getCoordMode();
*/
return null;
},
/** @private */
doUpdate: function()
{
this.tryApplySuper('doUpdate');
var displayer = this.getDisplayer();
var flag = this.getEnabled() && !!(displayer.canAutoGenerateCoordForChemObj(displayer.getChemObj(), Kekule.oneOf(this.getCoordMode(), displayer.getCoordMode())));
this.setDisplayed(flag).setEnabled(flag);
},
/** @private */
doExecute: function()
{
this.getDisplayer().autoGenerateChemObjCoords();
}
});
})();