kekule
Version:
Open source JavaScript toolkit for chemoinformatics
3,032 lines • 104 kB
JavaScript
/**
* @fileoverview
* Related types and classes of spectrum sub view for ChemObjDisplayer.
* @author Partridge Jiang
*/
/*
* requires /lan/classes.js
* requires /utils/kekule.utils.js
* requires /xbrowsers/kekule.x.js
* requires /core/kekule.common.js
* requries /spectroscopy/kekule.spectrum.core.js
* requires /widgets/kekule.widget.base.js
* requires /widgets/kekule.widget.menus.js
* requires /widgets/kekule.widget.dialogs.js
* requires /widgets/kekule.widget.helpers.js
* requires /widgets/kekule.widget.keys.js
* requires /widgets/chem/kekule.chemWidget.base.js
* requires /widgets/chem/kekule.chemWidget.chemObjDisplayers.js
* requires /widgets/chem/kekule.chemWidget.viewers.js
* requires /widgets/operation/kekule.actions.js
*
* requires /localization/kekule.localize.widget.js
*/
(function(){
"use strict";
Kekule._registerAfterLoadSysProc(function(){
// the following code will be run after both spectroscopy and widget modules are loaded
if (!Kekule.ChemWidget || !Kekule.Spectroscopy)
return;
var AU = Kekule.ArrayUtils;
var OU = Kekule.ObjUtils;
var ZU = Kekule.ZoomUtils;
Kekule.globalOptions.add('chemWidget.viewer', {
enableSpectrumView: true,
enableLocalSpectrumView: !false
});
/**
* Config class of spectrum sub view ({@link Kekule.ChemWidget.Viewer.SpectrumSubView}).
* @class
* @augments Kekule.AbstractConfigs
*
* @property {Bool} enableSpectrumView Whether turning on the spectrum view mode when a single spectrum object is loaded in viewer.
* @property {Bool} enableLocalSpectrumView Whether applying specified interactions to a spectrum inside chem space (with other types of objects) in viewer.
* @property {Number} spectrumAxisLabelFontSizeMin The minimal font size to draw spectrum axis labels in spectrum mode.
* @property {Number} spectrumAxisLabelFontSizeMax The maximal font size to draw spectrum axis labels in spectrum mode.
* @property {Number} spectrumAxisLabelFontSizeFixed If this value is set, the spectrum axis labels will always be drawn in this size in spectrum mode, regardless of the zoom settings.
* @property {Hash} enableSpectrumDataHotTrackOnMode Whether spectrum data hot track is enabled in certain mode.
* This property is a hash object while the key is the value of data mode.
* A 'default' key/value can be used here to provide the default setting of this property.
* @property {Hash} enableSpectrumDataSelectOnMode Whether spectrum data select is enabled in certain mode.
* This property is a hash object while the key is the value of data mode.
* A 'default' key/value can be used here to provide the default setting of this property.
* @property {Hash} enableSpectrumDataMultiSelectOnMode Whether spectrum data multi-select is enabled in certain mode.
* This property is a hash object while the key is the value of data mode.
* A 'default' key/value can be used here to provide the default setting of this property.
* @property {Hash} spectrumDataHotTrackUiMarkersOnMode Whether spectrum data multi-select is enabled in certain mode.
* This property is a hash object while the key is the value of data mode.
* A 'default' key/value can be used here to provide the default setting of this property.
* @property {Hash} spectrumDataHotTrackUiMarkersOnMode The displayed UI markers when hot tracking spectrum data.
* This property is a hash object while the key is the value of data mode.
* The hash value is an array of {@link Kekule.ChemWidget.Viewer.SpectrumSubView.UiMarker}.
* A 'default' key/value can be used here to provide the default setting of this property.
* @property {Hash} spectrumDataSelectUiMarkersOnMode The displayed UI markers when selecting spectrum data.
* This property is a hash object while the key is the value of data mode.
* The hash value is an array of {@link Kekule.ChemWidget.Viewer.SpectrumSubView.UiMarker}.
* A 'default' key/value can be used here to provide the default setting of this property.
* @property {Hash} spectrumHotTrackDataPointMarkerDrawStyles Render styles of hot track data point marker in spectrum.
* @property {Hash} spectrumSelectDataPointMarkerDrawStyles Render styles of data point selection marker in spectrum.
* @property {Hash} spectrumHotTrackedDataPointDetailMarkerDrawStyles Render styles of hot track data detail marker in spectrum.
* @property {Hash} spectrumSelectedDataPointDetailMarkerDrawStyles Render styles of data detail selection marker in spectrum.
* @property {Int} spectrumDataPointMarkerSize Size of data point UI marker.
* @property {Int} spectrumDataPointDetailMarkerPadding Padding of data point detail marker to data point marker.
* @property {Hash} spectrumPeakHotTrackStyles Render styles of hot tracked spectrum peak.
* @property {Hash} spectrumPeakSelectStyles Render styles of selected spectrum peak.
*
* @property {Number} spectrumDataPointSelectInflation
*
* @property {Array} spectrumZoomPrimaryModifierKeys Modifier keys (Shift/Ctrl/Alt) used when zooming on the primary axis of spectrum with mouse wheel.
* @property {Array} spectrumZoomSecondaryModifierKeys Modifier keys (Shift/Ctrl/Alt) used when zooming on the secondary axis of spectrum with mouse wheel.
* @property {Array} spectrumZoomBothModifierKeys Modifier keys (Shift/Ctrl/Alt) used when zooming on both axises of spectrum with mouse wheel.
*/
Kekule.ChemWidget.ChemObjDisplayerSpectrumViewConfigs = Class.create(Kekule.AbstractConfigs,
/** @lends Kekule.ChemWidget.ChemObjDisplayerSpectrumViewConfigs# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.ChemObjDisplayerSpectrumViewConfigs',
/** @private */
initProperties: function()
{
this.addBoolConfigProp('enableSpectrumView', undefined);
this.addBoolConfigProp('enableLocalSpectrumView', undefined);
this.addNumConfigProp('spectrumAxisLabelFontSizeMin', 15);
this.addNumConfigProp('spectrumAxisLabelFontSizeMax', 35);
this.addNumConfigProp('spectrumAxisLabelFontSizeFixed', 18);
this.addNumConfigProp('spectrumAxisScaleLabelFontSizeMin', 13);
this.addNumConfigProp('spectrumAxisScaleLabelFontSizeMax', 32);
this.addNumConfigProp('spectrumAxisScaleLabelFontSizeFixed', 15);
// configs about spectrum data UI marker
this.addHashConfigProp('enableSpectrumDataHotTrackOnMode', undefined);
this.addHashConfigProp('enableSpectrumDataSelectOnMode', undefined);
this.addHashConfigProp('enableSpectrumDataMultiSelectOnMode', undefined);
this.addHashConfigProp('spectrumDataHotTrackUiMarkersOnMode', undefined);
this.addHashConfigProp('spectrumDataSelectUiMarkersOnMode', undefined);
//this.addBoolConfigProp('enableSpectrumDataPointMarker', true);
//this.addBoolConfigProp('enableSpectrumDataPointDetailMarker', true);
this.addHashConfigProp('spectrumHotTrackDataPointMarkerDrawStyles', undefined);
this.addHashConfigProp('spectrumSelectDataPointMarkerDrawStyles', undefined);
this.addHashConfigProp('spectrumHotTrackedDataPointDetailMarkerDrawStyles', undefined);
this.addHashConfigProp('spectrumSelectedDataPointDetailMarkerDrawStyles', undefined);
this.addIntConfigProp('spectrumDataPointMarkerSize', 15);
this.addIntConfigProp('spectrumDataPointMarkerWidth', 2);
this.addIntConfigProp('spectrumDataPointDetailMarkerValuePrecision', 8);
this.addIntConfigProp('spectrumDataPointDetailMarkerPadding', 5);
//this.addBoolConfigProp('enableSpectrumPeakHotTrack', true);
//this.addBoolConfigProp('enableSpectrumPeakSelect', true);
this.addHashConfigProp('spectrumPeakHotTrackStyles', undefined);
this.addHashConfigProp('spectrumPeakSelectStyles', undefined);
//this.addBoolConfigProp('hideSpectrumDataPointMarkerOnHotTrackedPeak', true);
this.addNumConfigProp('spectrumDataPointSelectInflation', 1.5);
//this.addBoolConfigProp('enableSpectrumDataHint', true);
this.defineProp('spectrumZoomPrimaryModifierKeys', {'dataType': DataType.ARRAY});
this.defineProp('spectrumZoomSecondaryModifierKeys', {'dataType': DataType.ARRAY});
this.defineProp('spectrumZoomBothModifierKeys', {'dataType': DataType.ARRAY});
},
/** @ignore */
initPropDefValues: function()
{
this.tryApplySuper('initPropDefValues');
var DM = Kekule.Spectroscopy.DataMode;
var value = {'default': true};
//value[DM.CONTINUOUS] = true;
//value[DM.PEAK] = true;
this.setEnableSpectrumDataHotTrackOnMode(value);
var value = {'default': false};
//value[DM.CONTINUOUS] = false;
//value[DM.PEAK] = true;
this.setEnableSpectrumDataSelectOnMode(value);
var value = {'default': false};
this.setEnableSpectrumDataMultiSelectOnMode(value);
var value = {};
value[DM.CONTINUOUS] = [SVM.DATA_POINT, SVM.DATA_DETAIL];
value[DM.PEAK] = [SVM.PEAK, SVM.DATA_DETAIL];
this.setSpectrumDataHotTrackUiMarkersOnMode(value);
var value = {};
value[DM.CONTINUOUS] = [SVM.DATA_POINT/*, SVM.DATA_DETAIL*/];
value[DM.PEAK] = [SVM.PEAK/*, SVM.DATA_DETAIL*/];
this.setSpectrumDataSelectUiMarkersOnMode(value);
this.setSpectrumHotTrackDataPointMarkerDrawStyles({
'color': '#c21717',
'strokeWidth': 1,
'opacity': 1
});
this.setSpectrumSelectDataPointMarkerDrawStyles({
'color': '#0077d6',
'strokeWidth': 1,
'opacity': 1
});
this.setSpectrumHotTrackedDataPointDetailMarkerDrawStyles({
'fontFamily': 'Arial, Helvetica, sans-serif',
'fontSize': 15,
'color': '#c21717',
'strokeWidth': 2,
'opacity': 1
});
this.setSpectrumSelectedDataPointDetailMarkerDrawStyles({
'fontFamily': 'Arial, Helvetica, sans-serif',
'fontSize': 15,
'color': '#0077d6',
'strokeWidth': 2,
'opacity': 1
});
this.setSpectrumPeakHotTrackStyles({
'color': '#c21717'
});
this.setSpectrumPeakSelectStyles({
//'color': '#f50f0f'
'color': '#0077d6',
'spectrum_dataStrokeWidthRatio': 0.05
});
this.setSpectrumZoomPrimaryModifierKeys([]);
this.setSpectrumZoomSecondaryModifierKeys(['shift']);
this.setSpectrumZoomBothModifierKeys(['alt']);
},
/** @ignore */
initPropValues: function()
{
this.tryApplySuper('initPropValues');
//this.setEnableSpectrumView(Kekule.globalOptions.chemWidget.viewer.enableSpectrumView);
//this.setEnableLocalSpectrumView(Kekule.globalOptions.chemWidget.viewer.isLocalSpectrumViewEnabled);
},
getActualEnableSpectrumView: function()
{
return Kekule.oneOf(this.getEnableSpectrumView(), Kekule.globalOptions.chemWidget.viewer.enableSpectrumView);
},
getActualEnableLocalSpectrumView: function()
{
return Kekule.oneOf(this.getEnableLocalSpectrumView(), Kekule.globalOptions.chemWidget.viewer.enableLocalSpectrumView);
},
getDataModeSpecifiedConfigValue: function(propName, dataMode)
{
var configValue = this.getPropValue(propName);
if (DataType.isObjectValue(configValue))
{
var result = configValue[dataMode];
if (result === undefined)
result = configValue.default;
return result;
}
else
return configValue;
},
setDataModeSpecifiedConfigValue: function(propName, value, dataMode)
{
var configValue = this.getPropValue(propName);
if (!configValue)
{
configValue = {};
this.setPropValue(propName, configValue);
}
if (Kekule.ObjUtils.notUnset(dataMode))
configValue[dataMode] = value;
else
configValue.default = value;
},
/** @private */
getDataModeSpecifiedBoolConfigValueOfModeList: function(propName, dataModes, opAnd)
{
var modes = AU.toArray(dataModes);
var result = this.getDataModeSpecifiedConfigValue(propName, modes[0]);
if (!result && opAnd)
return result;
for (var i = 0, l = modes.length; i < l; ++i)
{
if (opAnd)
{
result = result && this.getDataModeSpecifiedConfigValue(propName, modes[i]);
if (!result)
return result;
}
else
result = result || this.getDataModeSpecifiedConfigValue(propName, modes[i]);
}
return result;
}
});
// Extend ChemObjDisplayerConfigs, add ChemObjDisplayerSpectrumViewConfigs into it
ClassEx.extendMethods(Kekule.ChemWidget.ChemObjDisplayerConfigs, {
'initProperties': function($origin)
{
$origin();
this.addConfigProp('spectrumViewConfigs', 'Kekule.ChemWidget.ChemObjDisplayerSpectrumViewConfigs');
},
'initPropDefValues': function($origin)
{
$origin();
this.setPropStoreFieldValue('spectrumViewConfigs', new Kekule.ChemWidget.ChemObjDisplayerSpectrumViewConfigs());
}
});
/**
* A facade sub view class to wrap all methods about spectrum for {@link Kekule.ChemWidget.Viewer}.
* @class
* @augments Kekule.ChemWidget.ViewerSubView
*
* @property {Kekule.ChemWidget.Viewer} viewer Parent viewer object. Readonly.
* @property {Kekule.Spectroscopy.Spectrum} spectrum Target spectrum of sub view.
* @property {Hash} viewportRanges The viewport range of spectrum in this sub view.
* @property {Array} hotTrackedDataItemsEx Array of hot track data items. Each item is a hash {dataSection, dataValue}.
* @property {Array} selectedDataItemEx Array of selected data items. Each item is a hash {dataSection, dataValue}.
*/
/**
* Invoked when the pointer is hot tracking on a spectrum data item.
* event param of it has fields: {spectrum: {@link Kekule.Spectroscopy.Spectrum}, dataItems: array of {dataSection, dataValue}}.
* @name Kekule.ChemWidget.Viewer.SpectrumSubView#hotTrackOnSpectrumData
* @event
*/
/**
* Invoked when the spectrum data item(s) is selected in view.
* event param of it has fields: {spectrum: {@link Kekule.Spectroscopy.Spectrum}, dataItems: array of {dataSection, dataValue}}.
* @name Kekule.ChemWidget.Viewer.SpectrumSubView#spectrumDataSelectionChange
* @event
*/
Kekule.ChemWidget.Viewer.SpectrumSubView = Class.create(Kekule.ChemWidget.ViewerSubView,
/** @lends Kekule.ChemWidget.Viewer.SpectrumSubView# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.Viewer.SpectrumSubView',
/** @constructs */
initialize: function(parent, targetObj)
{
this.tryApplySuper('initialize', [parent, targetObj]);
},
/** @private */
initProperties: function()
{
// the target spectrum object
this.defineProp('spectrum', {
'dataType': 'Kekule.Spectroscopy.Spectrum',
'serializable': false,
'getter': function() { return this.getTarget(); },
'setter': function(value) { this.setTarget(value); }
});
// the viewport range of spectrum view
this.defineProp('viewportRanges', {
'dataType': DataType.HASH,
'serializable': false
/*
'getter': function()
{
var cache = this._getSpectrumRenderCache();
return cache && cache.viewportRanges;
},
'setter': function(value)
{
var cache = this._getSpectrumRenderCache();
if (cache)
cache.viewportRanges = value;
}
*/
//'getter': function() { return this.getViewer().getSpectrumViewportRanges(); },
//'setter': function(value) { this.getViewer().setSpectrumViewportRanges(value); }
});
// private, the hot tracked and selected spectrum peak
this.defineProp('hotTrackedDataItem', {
'dataType': DataType.OBJECT,
'serializable': false,
'getter': function()
{
var itemEx = this.getHotTrackedDataItemEx();
return itemEx && itemEx.dataItem;
},
'setter': function(value)
{
var section = value && this._findSectionOfDataItem(value, true);
var itemEx = section? {'section': section, 'dataValue': value}: null;
this.changeHotTrackedDataItem(itemEx, true);
}
});
this.defineProp('hotTrackedDataItemEx', {
'dataType': DataType.OBJECT,
'serializable': false,
'getter': function()
{
var itemsEx = this.getHotTrackedDataItemsEx();
return itemsEx && itemsEx[0];
},
'setter': function(value)
{
var itemsEx = this._fetchDataItemsEx(value);
this.changeHotTrackedDataItems(itemsEx, true);
}
});
this.defineProp('hotTrackedDataItemsEx', {
'dataType': DataType.ARRAY,
'serializable': false,
'getter': function()
{
return this.getPropStoreFieldValue('hotTrackedDataItemsEx') || [];
},
'setter': function(value)
{
this.changeHotTrackedDataItems(value, true);
}
});
this.defineProp('selectedDataItem', {
'dataType': DataType.OBJECT,
'serializable': false,
'getter': function(value)
{
var itemEx = this.getSelectedDataItemEx();
return itemEx && itemEx.dataItem;
},
'setter': function(value)
{
var section = value && this._findSectionOfDataItem(value, true);
var itemEx = section? {'section': section, 'dataValue': value}: null;
this.changeSelectedDataItems(itemEx && [itemEx], true);
}
});
this.defineProp('selectedDataItems', {
'dataType': DataType.ARRAY,
'serializable': false,
'getter': function(value)
{
var itemsEx = this.getSelectedDataItemsEx();
if (itemsEx)
{
var result = [];
for (var i = 0, l = itemsEx.length; i < l; ++i)
{
result.push(itemsEx[i].dataItem);
}
return result;
}
else
return [];
},
'setter': function(value)
{
var itemsEx = this._fetchDataItemsEx(value);
this.changeSelectedDataItems(itemsEx, true);
}
});
this.defineProp('selectedDataItemEx', {
'dataType': DataType.OBJECT,
'serializable': false,
'getter': function()
{
var itemsEx = this.getSelectedDataItemsEx();
return itemsEx && itemsEx[0];
},
'setter': function(value)
{
this.changeSelectedDataItems(value && [value], true);
}
});
this.defineProp('selectedDataItemsEx', {
'dataType': DataType.ARRAY,
'serializable': false,
'getter': function()
{
return this.getPropStoreFieldValue('selectedDataItemsEx') || [];
},
'setter': function(value)
{
this.changeSelectedDataItems(value, true);
}
});
// whether the spectrum is displayed with reversed axises position in spectrum view
this.defineProp('isSpectrumWithReversedAxises', {
'dataType': DataType.BOOL,
'serializable': false,
'setter': null,
'getter': function()
{
var renderCache = this._getSpectrumRenderCache();
return !!(renderCache && renderCache.spectrum_reversedAxises);
}
});
},
/** @ignore */
appliableToTarget: function(obj)
{
return obj instanceof Kekule.Spectroscopy.Spectrum;
},
/** @private */
_getSpectrumRenderer: function()
{
var spectrum = this.getSpectrum();
if (spectrum)
{
var context = this.getViewer().getDrawContext();
var rootRenderer = this.getViewer().getRootRenderer();
var renderer = rootRenderer && rootRenderer.getDirectRendererForChildObj && rootRenderer.getDirectRendererForChildObj(context, spectrum);
return renderer;
}
else
return null;
},
/** @private */
_getSpectrumRenderCache: function()
{
var renderer = this._getSpectrumRenderer();
var context = this.getViewer().getDrawContext();
return renderer && renderer.getRenderCache(context);
},
/**
* Returns all the data modes of displayed data section of spectrum.
* @returns {Array}
*/
getDisplayedDataModes: function()
{
var spectrum = this.getSpectrum();
var sections = (spectrum && spectrum.getDisplayedDataSections() || []);
var result = [];
for (var i = 0, l = sections.length; i < l; ++i)
{
result.push(sections[i].getMode());
}
return result;
},
/**
* Returns the client screen box of spectrum data curve (without the axis and labels).
* @returns {Hash}
*/
getClientScreenBox: function()
{
var viewer = this.getViewer();
//if (viewer.isInSpectrumMode())
{
var spectrum = this.getSpectrum(); //viewer.getChemObj();
if (spectrum)
{
var boundInfoRecorder = viewer.getBoundInfoRecorder();
var bound = boundInfoRecorder && boundInfoRecorder.getBound(viewer.getDrawContext(), spectrum.getPseudoRenderSubObject('data'));
if (bound.shapeType === Kekule.Render.MetaShapeType.RECT)
{
return Kekule.BoxUtils.createBox(bound.coords[0], bound.coords[1]);
}
else
return null;
}
}
return null;
},
/** @private */
fetchViewportRanges: function()
{
var result = {};
var VD = Kekule.VarDependency;
result[VD.INDEPENDENT] = this.getViewportRange(VD.INDEPENDENT);
result[VD.DEPENDENT] = this.getViewportRange(VD.DEPENDENT);
return result;
},
/**
* Returns the actual spectrum view port range of a var axis.
* @param {Int} varDependency
* @returns {Hash}
*/
getViewportRange: function(varDependency)
{
var result = (this.getViewportRanges() || {})[varDependency] || {};
return Object.extend({'rangeFrom': 0, 'rangeTo': 1}, result);
},
/**
* Set the spectrum viewport range of a var axis.
* @param {Int} varDependency
* @param {Number} rangeFrom
* @param {Number} rangeTo
*/
setViewportRange: function(varDependency, rangeFrom, rangeTo)
{
var ranges = this.getViewportRanges();
if (!ranges)
{
ranges = {};
this.setViewportRanges(ranges);
}
var range = ranges[varDependency];
if (!range)
{
range = {};
ranges[varDependency] = range;
}
range.rangeFrom = Kekule.oneOf(rangeFrom, 0);
range.rangeTo = Kekule.oneOf(rangeTo, 1);
this.applyToRenderOptions();
return this;
},
/**
* Reset the spectrum viewport range.
* @param {Int} varDependency If not set, both dep/indep vars will be reset.
*/
resetViewportRange: function(varDependency)
{
if (Kekule.ObjUtils.isUnset(varDependency))
this.setViewportRanges(undefined);
else
{
var old = this.getViewportRange(varDependency);
if (old && old[varDependency])
delete old[varDependency];
}
this.applyToRenderOptions();
},
/** @private */
getViewportZoomInfo: function(varDependency)
{
if (!varDependency)
varDependency = Kekule.VarDependency.INDEPENDENT;
var range = this.getViewportRange(varDependency);
return {
'zoom': 1 / Math.abs(range.rangeTo - range.rangeFrom),
'center': (range.rangeTo + range.rangeFrom) / 2
};
},
/** @private */
setViewportZoom: function(varDependency, zoom, zoomCenter)
{
if (!varDependency)
varDependency = Kekule.VarDependency.INDEPENDENT;
if (Kekule.ObjUtils.isUnset(zoomCenter))
{
var oldZoomInfo = this.getViewportZoomInfo(varDependency);
zoomCenter = oldZoomInfo.center;
}
var delta = 1 / zoom / 2;
var range = {
'rangeFrom': zoomCenter - delta,
'rangeTo': zoomCenter + delta
};
this.setViewportRange(varDependency, range.rangeFrom, range.rangeTo);
},
/** @private */
_zoomViewportTo: function(value, zoomCenterCoord, direction, varDependency)
{
var clientBox = this.getClientScreenBox();
var currViewportRange = this.getViewportRange(varDependency);
var oldRangeValue = currViewportRange.rangeTo - currViewportRange.rangeFrom;
var newRangeValue = 1 / value;
var rangeDelta = newRangeValue - oldRangeValue;
var fixedPointRatio;
if (!zoomCenterCoord)
fixedPointRatio = 0.5;
else
{
fixedPointRatio = (direction === 'x')?
((zoomCenterCoord.x - clientBox.x1) / (clientBox.x2 - clientBox.x1)):
((clientBox.y2 - zoomCenterCoord.y) / (clientBox.y2 - clientBox.y1));
}
var newRangeFrom = currViewportRange.rangeFrom - rangeDelta * fixedPointRatio;
var newRangeTo = currViewportRange.rangeTo + rangeDelta * (1 - fixedPointRatio);
this.setViewportRange(varDependency, newRangeFrom, newRangeTo);
return this;
},
/**
* Zoom the spectrum viewport.
* @param {Number} delta
* @param {Hash} zoomCenterCoord
* @param {Array} directions e.g. ['x', 'y'].
* @private
*/
zoomViewportByDelta: function(delta, zoomCenterCoord, directions)
{
var viewer = this.getViewer();
//if (viewer.isInSpectrumMode())
//if (viewer.enableSpectrumInteraction())
{
viewer.beginUpdate();
try
{
for (var i = 0, l = directions.length; i < l; ++i)
{
var direction = directions[i];
var targetOnIndep = direction === 'x';
var reversedAxises = this.getIsSpectrumWithReversedAxises();
if (reversedAxises)
targetOnIndep = !targetOnIndep;
var varDependency = targetOnIndep ? Kekule.VarDependency.INDEPENDENT : Kekule.VarDependency.DEPENDENT;
var zoomRatio;
var curr = this.getViewportZoomInfo(varDependency).zoom;
if (delta > 0)
{
zoomRatio = ZU.getNextZoomInRatio(curr, delta);
}
else if (delta < 0)
{
zoomRatio = ZU.getNextZoomOutRatio(curr, -delta);
}
this._zoomViewportTo(zoomRatio, zoomCenterCoord, direction, varDependency);
}
}
finally
{
viewer.endUpdate(); // repaint here
}
//viewer.geometryOptionChanged();
}
},
/** @private */
_applyViewportRanges: function(/*affectedOptions*/)
{
var viewer = this.getViewer();
//if (viewer.isInSpectrumMode())
{
var varDependencies = [Kekule.VarDependency.INDEPENDENT, Kekule.VarDependency.DEPENDENT];
var varTypes = ['independent', 'dependent'];
var targetSpectrum = this.getTarget();
for (var i = 0, ii = varTypes.length; i < ii; ++i)
{
var viewPortRange = this.getViewportRange(varDependencies[i]);
var optionNameFrom = 'spectrum_viewport' + varTypes[i].upperFirst() + 'DataRangeFrom';
var optionNameTo = 'spectrum_viewport' + varTypes[i].upperFirst() + 'DataRangeTo';
/*
affectedOptions[optionNameFrom] = viewPortRange.rangeFrom;
affectedOptions[optionNameTo] = viewPortRange.rangeTo;
*/
var overrideOptions = {};
overrideOptions[optionNameFrom] = viewPortRange.rangeFrom;
overrideOptions[optionNameTo] = viewPortRange.rangeTo;
targetSpectrum.addOverrideRenderOptionItem(overrideOptions);
}
}
},
/**
* Apply spectrum view settings to render options of viewer.
* @param {Object} renderOptions
* @private
*/
applyToRenderOptions: function(/*renderOptions*/)
{
this._applyViewportRanges(/*renderOptions*/);
this.doRepaintSpectrum();
},
/** @private */
doRepaintSpectrum: function()
{
var viewer = this.getViewer();
var renderer = viewer.getRootRenderer();
if (renderer)
{
//console.log('request update');
renderer.update(viewer.getDrawContext(), [{'obj': this.getTarget()}]);
this.updateSpectrumDataItemUiMarkers(); // update the UI marker position since the data rendering has been changed
//this.getViewer().repaintUiMarker();
}
},
/** @private */
_getHotTrackedDataItemRenderStyles: function()
{
var viewer = this.getViewer();
return viewer && viewer.getDisplayerConfigs().getSpectrumViewConfigs().getSpectrumPeakHotTrackStyles();
},
/** @private */
_getSelectedDataItemRenderStyles: function()
{
var viewer = this.getViewer();
return viewer && viewer.getDisplayerConfigs().getSpectrumViewConfigs().getSpectrumPeakSelectStyles();
},
/** @private */
_findSectionOfDataItem: function(dataItem, displayedSectionOnly)
{
var spectrum = this.getSpectrum();
var sections = displayedSectionOnly? spectrum.getDisplayedDataSections(): spectrum.getDataSections();
for (var i = 0, l = sections.length; i < l; ++i)
{
var section = sections[i];
if (section.indexOfDataItem(dataItem) >= 0)
return section;
}
return null;
},
/** @private */
_fetchDataItemsEx: function(value)
{
var items = value? AU.toArray(value): [];
var itemsEx = [];
for (var i = 0, l = items.length; i < l; ++i)
{
var item = items[i];
var itemEx;
if (DataType.isObjectValue(item) && item.section && item.dataValue) // is already Ex hash
itemEx = item;
else
{
var section = item && this._findSectionOfDataItem(item, true);
itemEx = section ? {'section': section, 'dataValue': item} : null;
}
if (itemEx)
itemsEx.push(itemEx);
}
return itemsEx;
},
/** @private */
_indexOfDataItemEx: function(itemEx, itemExList)
{
var result = itemExList.indexOf(itemEx);
if (result <= 0)
{
for (var i = 0, l = itemExList.length; i < l; ++i)
{
var curr = itemExList[i];
if (itemEx.section && curr.section === itemEx.section)
{
if (itemEx.section.isPeakSection())
{
var currDataSrc = curr.section._getDataValueSrc(curr.dataValue);
if (currDataSrc && itemEx.section._getDataValueSrc(itemEx.dataValue) === currDataSrc)
{
result = i;
break;
}
}
else
{
if (OU.equal(itemEx.dataValue, curr.dataValue))
{
result = i;
break;
}
}
}
}
}
return result;
},
/**
* Add data item(s) to selection.
* @param {Variant} items
*/
addToSelection: function(items)
{
var selection = AU.clone(this.getSelectedDataItemsEx());
var itemsEx = this._fetchDataItemsEx(items);
for (var i = 0, l = itemsEx.length; i < l; ++i)
{
var itemEx = itemsEx[i];
if (this._indexOfDataItemEx(itemEx, selection) >= 0) // already inside, bypass
continue;
else
selection.push(itemEx);
}
this.changeSelectedDataItems(selection, true);
},
/**
* Remove data item(s) from selection.
* @param {Variant} items
*/
removeFromSelection: function(items)
{
var selection = AU.clone(this.getSelectedDataItemsEx());
var itemsEx = this._fetchDataItemsEx(items);
for (var i = 0, l = itemsEx.length; i < l; ++i)
{
var itemEx = itemsEx[i];
var index = this._indexOfDataItemEx(itemEx, selection);
if (index >= 0) // inside, remove it
{
selection.splice(index, 1);
}
}
this.changeSelectedDataItems(selection, true);
},
/**
* Toggle selection state of data items.
* @param {Variant} items
*/
toggleSelectingState: function(items)
{
var selection = AU.clone(this.getSelectedDataItemsEx());
var itemsEx = this._fetchDataItemsEx(items);
for (var i = 0, l = itemsEx.length; i < l; ++i)
{
var itemEx = itemsEx[i];
var index = this._indexOfDataItemEx(itemEx, selection);
if (index >= 0) // inside, remove it
{
selection.splice(index, 1);
}
else // not inside, add it
{
selection.push(itemEx);
}
}
this.changeSelectedDataItems(selection, true);
},
/**
* Select data item(s).
* @param {Variant} items
*/
select: function(items)
{
var itemsEx = this._fetchDataItemsEx(items);
this.changeSelectedDataItems(itemsEx, true);
},
/** @private */
doClearHotTrackedItems: function()
{
this.changeHotTrackedDataItems(null, false);
},
/** @private */
doClearSelectedItems: function()
{
this.changeSelectedDataItems(null, false);
},
/**
* Called when the hot tracked peak item is changed.
* @private
*/
_hotTrackedDataItemsChanged: function(oldItemsEx, newItemsEx)
{
this._hotTrackedOrSelectDataItemsChanged(oldItemsEx, newItemsEx, false);
this.invokeEvent('hotTrackOnSpectrumData', {
'spectrum': this.getSpectrum(),
'dataItems': newItemsEx,
'prevDataItems': oldItemsEx
});
},
/**
* Called when the selected peak item is changed.
* @private
*/
_selectedDataItemsChanged: function(oldItemsEx, newItemsEx)
{
this._hotTrackedOrSelectDataItemsChanged(oldItemsEx, newItemsEx, true);
this.invokeEvent('spectrumDataSelectionChange', {
'spectrum': this.getSpectrum(),
'dataItems': newItemsEx,
'prevDataItems': oldItemsEx
});
/*
var spectrum = this.getSpectrum();
if (oldItemEx)
{
if (this._selectedDataItemRenderStyles)
spectrum.removeDataItemOverrideRenderOptionItem(oldItemEx.dataValue, this._selectedDataItemRenderStyles);
}
if (newItemEx)
{
var renderStyles = this._getSelectedDataItemRenderStyles();
if (renderStyles)
{
spectrum.addDataItemOverrideRenderOptionItem(newItemEx.dataValue, renderStyles);
this._selectedDataItemRenderStyles = renderStyles;
}
}
*/
},
/** @private */
_hotTrackedOrSelectDataItemsChanged: function(oldItemsEx, newItemsEx, isSelect)
{
var viewer = this.getViewer();
var spectrum = this.getSpectrum();
var needRepaintSpectrum = false;
//var configs = this.getViewer().getDisplayerConfigs().getSpectrumViewConfigs();
var dataItemRenderStylesField = isSelect? '_selectDataItemRenderStyles': '_hotTrackedDataItemRenderStyles';
if (newItemsEx && newItemsEx.length) // empty hot tracked or select item in other sub views
{
/*
this._removeHotTrackedOrSelectedDataItemInSiblingSpectrumViews(isSelect);
if (isSelect)
viewer.setVisibleOfUiMarkerGroup(Kekule.ChemWidget.ViewerUiMarkerGroup.SELECT, false, false);
else
viewer.setVisibleOfUiMarkerGroup(Kekule.ChemWidget.ViewerUiMarkerGroup.HOTTRACK, false, false);
*/
if (isSelect)
viewer.clearSelectedItems([this], false);
else
viewer.clearHotTrackedItems([this], false);
}
if (oldItemsEx)
{
for (var i = 0, l = oldItemsEx.length; i < l; ++i)
{
this._updateHotTrackOrSelectUiMarkerToDataItem(null, isSelect, i); // hide old markers
if (this[dataItemRenderStylesField])
{
spectrum.removeDataItemOverrideRenderOptionItem(oldItemsEx[i].dataValue, this[dataItemRenderStylesField]);
needRepaintSpectrum = true;
}
}
}
if (newItemsEx)
{
for (var i = 0, l = newItemsEx.length; i < l; ++i)
{
var updateDataItemResult = this._updateHotTrackOrSelectUiMarkerToDataItem(newItemsEx[i], isSelect, i);
if (updateDataItemResult.needRepaintSpectrum)
needRepaintSpectrum = true;
}
}
else
this._updateHotTrackOrSelectUiMarkerToDataItem(null, isSelect);
//viewer.updateUiMarkers(true);
if (needRepaintSpectrum)
viewer.requestRepaint();
},
/* @private */
/*
_removeHotTrackedOrSelectedDataItemInSiblingSpectrumViews(isSelect)
{
var viewer = this.getViewer();
var self = this;
viewer.iterateSubViews(function(subView){
if (subView !== self && subView instanceof Kekule.ChemWidget.Viewer.SpectrumSubView)
{
if (isSelect)
subView.changeSelectedDataItems(null, false);
else
subView.changeHotTrackedDataItems(null, false);
}
});
},
*/
/** @private */
_updateHotTrackOrSelectUiMarkerToDataItem: function(dataItemEx, isSelect, markerIndex)
{
var viewer = this.getViewer();
var configs = this.getViewer().getDisplayerConfigs().getSpectrumViewConfigs();
var dataItemRenderStylesField = isSelect? '_selectDataItemRenderStyles': '_hotTrackedDataItemRenderStyles';
var uiMarkerElements = (dataItemEx && dataItemEx.dataValue)?
(isSelect? this._getSpectrumDataSelectElements(configs, dataItemEx.section): this._getSpectrumDataHotTrackElements(configs, dataItemEx.section)):
[];
var dataPointMarkerVisible = uiMarkerElements.indexOf(SVM.DATA_POINT) >= 0;
var dataDetailMarkerVisible = uiMarkerElements.indexOf(SVM.DATA_DETAIL) >= 0;
var detailMarkerName = isSelect? 'selectDataDetail': 'hotTrackDataDetail';
//if (isSelect && OU.notUnset(markerIndex))
detailMarkerName += (markerIndex || 0);
var pointMarkerName = isSelect? 'selectDataPoint': 'hotTrackDataPoint';
//if (isSelect && OU.notUnset(markerIndex))
pointMarkerName += (markerIndex || 0);
var detailMarker = viewer.getSpectrumUiMarker(detailMarkerName, !!dataDetailMarkerVisible);
var pointMarker = viewer.getSpectrumUiMarker(pointMarkerName, !!dataPointMarkerVisible);
//console.log('hot track', newItemEx, hotTrackElements);
var needRepaintSpectrum = false;
if (dataItemEx && uiMarkerElements.length) // do need to display some hot track elements
{
var spectrum = this.getSpectrum();
var dataValue = dataItemEx.dataValue;
var dataSection = dataItemEx.section;
//if (hotTrackElements.length) // do need to display some hot track elements
{
if (dataSection.isPeakSection() && (uiMarkerElements.indexOf(SVM.PEAK) >= 0)) // hot track peak
{
var renderStyles = isSelect? this._getSelectedDataItemRenderStyles(): this._getHotTrackedDataItemRenderStyles();
if (renderStyles)
{
spectrum.addDataItemOverrideRenderOptionItem(dataItemEx.dataValue, renderStyles);
this[dataItemRenderStylesField] = renderStyles;
needRepaintSpectrum = true;
}
}
if (dataPointMarkerVisible || dataDetailMarkerVisible)
{
var clientBox = this.getClientScreenBox();
var baseContextCoord = this.calcCoordAtSpectrumData(dataValue, Kekule.Render.CoordSystem.CONTEXT);
var detailText = null;
if (dataDetailMarkerVisible) // data detail
detailText = this._formatSpectrumDataValueString(dataValue, configs.getSpectrumDataPointDetailMarkerValuePrecision());
//console.log('update', dataValue, detailText);
//this._updateSpectrumCurrDataPointMarker(baseContextCoord, detailText, dataPointMarkerVisible, dataDetailMarkerVisible, clientBox);
}
var detailStyles = isSelect? configs.getSpectrumSelectedDataPointDetailMarkerDrawStyles(): configs.getSpectrumHotTrackedDataPointDetailMarkerDrawStyles();
var pointStyles = isSelect? configs.getSpectrumSelectDataPointMarkerDrawStyles(): configs.getSpectrumHotTrackDataPointMarkerDrawStyles();
this._updateDataDetailMarker(detailMarker, baseContextCoord, {'text': detailText}, dataDetailMarkerVisible, detailStyles, clientBox, false);
this._updateDataPointMarker(pointMarker, baseContextCoord, null, dataPointMarkerVisible, pointStyles, clientBox, false);
}
}
else
{
if (detailMarker)
viewer.hideUiMarker(detailMarker, false);
if (pointMarker)
viewer.hideUiMarker(pointMarker, false);
}
//viewer.repaintUiMarker();
return {'needRepaintSpectrum': needRepaintSpectrum};
},
/**
* Recalculate the position and content of hot track / select UI markers according to the properties of this sub view.
* Note this method will not force the repaint.
* @private
*/
updateSpectrumDataItemUiMarkers: function(updateVisibleOnly)
{
//this.getViewer()._hideAllSpectrumUiMarkers(false); // hide all first, then update
// hot tracked
var hotTrackedDataItemsEx = this.getHotTrackedDataItemsEx();
if (hotTrackedDataItemsEx)
{
for (var i = 0, l = hotTrackedDataItemsEx.length; i < l; ++i)
{
var hotTrackedDataItemEx = hotTrackedDataItemsEx[i];
this._updateHotTrackOrSelectUiMarkerToDataItem(hotTrackedDataItemEx, false, i);
}
}
else if (!updateVisibleOnly)
{
this._updateHotTrackOrSelectUiMarkerToDataItem(null, false, 0);
}
// selected
var selectedDataItemsEx = this.getSelectedDataItemsEx();
if (selectedDataItemsEx)
{
for (var i = 0, l = selectedDataItemsEx.length; i < l; ++i)
{
var selectedDataItemEx = selectedDataItemsEx[i];
this._updateHotTrackOrSelectUiMarkerToDataItem(selectedDataItemEx, true, i);
}
}
else if (!updateVisibleOnly)
{
this._updateHotTrackOrSelectUiMarkerToDataItem(null, true, 0);
}
},
/** @private */
_isSameHotTrackedOrSelectedItems: function(objs1, objs2)
{
var o1 = objs1, o2 = objs2;
if (o1 && !o1.length)
o1 = null;
if (o2 && !o2.length)
o2 = null;
if (o1 == o2)
return true;
else if (o1 && o2)
{
return AU.compare(o1, o2, function(a, b) { return (a === b)? 0: -1}) === 0;
}
else // !objs1 || !objs2
return false;
},
/** @private */
changeHotTrackedDataItems: function(newItemsEx, doRepaint)
{
var old = this.getHotTrackedDataItemsEx();
if (!this._isSameHotTrackedOrSelectedItems(old, newItemsEx))
{
var viewer = this.getViewer();
if (doRepaint)
viewer.beginUpdateUiMarkers();
try
{
this.setPropStoreFieldValue('hotTrackedDataItemsEx', newItemsEx);
this._hotTrackedDataItemsChanged(old, newItemsEx);
if (doRepaint)
viewer.updateUiMarkers(true);
}
finally
{
if (doRepaint)
viewer.endUpdateUiMarkers();
}
}
},
/** @private */
changeSelectedDataItems: function(newItemsEx, doRepaint)
{
var old = this.getSelectedDataItemsEx();
if (!this._isSameHotTrackedOrSelectedItems(old, newItemsEx))
{
var viewer = this.getViewer();
if (doRepaint)
viewer.beginUpdateUiMarkers();
try
{
this.setPropStoreFieldValue('selectedDataItemsEx', newItemsEx);
this._selectedDataItemsChanged(old, newItemsEx);
if (doRepaint)
viewer.updateUiMarkers(true);
}
finally
{
if (doRepaint)
viewer.endUpdateUiMarkers();
}
}
},
// methods about UI markers /////////
/** @private */
_getSpectrumDataHotTrackElements: function(spectrumViewConfigs, dataSection)
{
var configs = spectrumViewConfigs;
var dataMode = dataSection.getMode();
var needHotTrack = configs.getDataModeSpecifiedConfigValue('enableSpectrumDataHotTrackOnMode', dataMode);
var activeMarkers = !needHotTrack? []: configs.getDataModeSpecifiedConfigValue('spectrumDataHotTrackUiMarkersOnMode', dataMode);
return activeMarkers;
},
/** @private */
_getSpectrumDataSelectElements: function(spectrumViewConfigs, dataSection)
{
var configs = spectrumViewConfigs;
var dataMode = dataSection.getMode();
var needSelect = configs.getDataModeSpecifiedConfigValue('enableSpectrumDataSelectOnMode', dataMode);
var activeMarkers = !needSelect? []: configs.getDataModeSpecifiedConfigValue('spectrumDataSelectUiMarkersOnMode', dataMode);
return activeMarkers;
},
/** @private */
_formatSpectrumDataValueString: function(spectrumDataValue, precision)
{
var dataVarSymbols = Kekule.ObjUtils.getOwnedFieldNames(spectrumDataValue);
var detailTextParts = [];
for (var i = 0, l = dataVarSymbols.length; i < l; ++i)
{
if (Kekule.NumUtils.isNormalNumber(spectrumDataValue[dataVarSymbols[i]]))
detailTextParts.push(dataVarSymbols[i] + ': ' + spectrumDataValue[dataVarSymbols[i]].toPrecision(precision));
}
return detailTextParts.join(', ');
},
/** @private */
_updateDataPointMarker: function(marker, contextCoord, params, visible, styles, spectrumClientBox, doRepaint)
{
if (!marker)
return;
var viewer = this.getViewer();
var pVisible = visible && contextCoord;
if (pVisible)
{
var isInideBox = Kekule.CoordUtils.isInsideBox(contextCoord, spectrumClientBox);
pVisible = !!isInideBox;
}
if (!pVisible)
{
viewer.hideUiMarker(marker, doRepaint);
}
else // if (pVisible)
{
//if (pVisible)
{
var configs = viewer.getDisplayerConfigs().getSpectrumViewConfigs();
var unitLength = viewer.getRenderConfigs().getLengthConfigs().getUnitLength() || 1;
// TODO: currently the shape is fixed to a cross
var shapeInfo;
var lineWidth = (configs.getSpectrumDataPointMarkerWidth() || 1) * unitLength;
var c = contextCoord;
if (configs.getSpectrumDataPointMarkerSize())
{
var halfSize = (configs.getSpectrumDataPointMarkerSize() / 2) * unitLength;
shapeInfo = [
Kekule.Render.MetaShapeUtils.createShapeInfo(
Kekule.Render.MetaShapeType.LINE,
[{'x': c.x - halfSize, 'y': c.y}, {'x': c.x + halfSize, 'y': c.y}], {'width': lineWidth}
),
Kekule.Render.MetaShapeUtils.createShapeInfo(
Kekule.Render.MetaShapeType.LINE,
[{'x': c.x, 'y': c.y - halfSize}, {'x': c.x, 'y': c.y + halfSize}], {'width': lineWidth}
)
];
}
else if (spectrumClientBox) // a cross all over spectrum client area
{
shapeInfo = [
Kekule.Render.MetaShapeUtils.createShapeInfo(
Kekule.Render.MetaShapeType.LINE,
[{'x': spectrumClientBox.x1, 'y': c.y}, {'x': spectrumClientBox.x2, 'y': c.y}], {'width': lineWidth}
),
Kekule.Render.MetaShapeUtils.createShapeInfo(
Kekule.Render.MetaShapeType.LINE,
[{'x': c.x, 'y': spectrumClientBox.y1}, {'x': c.x, 'y': spectrumClientBox.y2}], {'width': lineWidth}
)
];
}
var pointDrawStyles = Object.create(/*configs.getSpectrumDataPointMarkerDrawStyles()*/styles);
pointDrawStyles.strokeWidth *= unitLength;
viewer.modifyShapeBasedMarker(marker, shapeInfo, pointDrawStyles, false);
viewer.showUiMarker(marker, doRepaint);
}
}
},
/** @private */
_updateDataDetailMarker: function(marker, contextCoord, params, visible, styles, spectrumClientBox, doRepaint)
{
if (!marker)
return;
var viewer = this.getViewer();
var text = params.text;
var pVisible = visible && contextCoord && text;
if (pVisible)
{
var isInideBox = Kekule.CoordUtils.isInsideBox(contextCoord, spectrumClientBox);
pVisible = !!isInideBox;
}
if (!pVisible)
{
viewer.hideUiMarker(marker, doRepaint);
}
else
{
var configs = viewer.getDisplayerConfigs().getSpectrumViewConfigs();
var unitLength = viewer.getRenderConfigs().getLengthConfigs().getUnitLength() || 1;
var detailDrawStyles = Object.create(/*configs.getSpectrumDataPointDetailMarkerDrawStyles()*/styles);
detailDrawStyles.fontSize *= unitLength;
if (text)
{
// determinate the position of detail text
var drawBridge = viewer.getDrawBridge();
var canMeasureText = drawBridge.canMeasureText && drawBridge.canMeasureText();
var canMeasureDrawnText = drawBridge.canMeasureDrawnText && drawBridge.canMeasureDrawnText();
var canModifyText = drawBridge.canMeasureText && drawBridge.canModifyText();
var textBox;
if (canMeasureText)
textBox = drawBridge.measureText(viewer.getDrawContext(), text, detailDrawStyles);
else if (canMeasureDrawnText && canModifyText)
{
// draw the text out first
//this.modifyTextBasedMarker(marker, null, text, detailDrawStyles, true);
var context = this.getViewer().getUiContext();
var textElem = drawBridge.drawText(context, {'x': 0, 'y': 0}, text, detailDrawStyles);
// then do the measure
textBox = drawBridge.measureDrawnText(context, textElem);
// remove textElem at last
drawBridge.removeDrawnElem(context, textElem);
}
var textPadding = (configs.getSpectrumDataPointDetailMarkerPadding() || 0) * unitLength;
var spaceings = {
'x1': contextCoord.x - spectrumClientBox.x1,
'x2': spectrumClientBox.x2 - contextCoord.x,
'y1': contextCoord.y - spectrumClientBox.y1,
'y2': spectrumClientBox.y2 - contextCoord.y
}
var textBoxXAlignment, textBoxYAlignment;
var textCoord = {};
if (spaceings.x2 - textPadding >= textBox.width)
{
textCoord.x = contextCoord.x + textPadding;
textBoxXAlignment = Kekule.Render.BoxXAlignment.LEFT;
}
else if (spaceings.x1 - textPadding >= textBox.width)
{
textCoord.x = contextCoord.x - textPadding;
textBoxXAlignment = Kekule.Render.BoxXAlignment.RIGHT;
}
else if (spaceings.x2 >= spaceings.x1)
{
textCoord.x = spectrumClientBox.x2 - textBox.width;
textBoxXAlignment = Kekule.Render.BoxXAlignment.LEFT;
}
else
{
textCoord.x = spectrumClientBox.x1 + textBox.width;
textBoxXAlignment = Kekule.Render.BoxXAlignment.RIGHT;
}
if (spaceings.y1 - textPadding >= textBox.height)
{
textCoord.y = contextCoord.y - textPadding;
textBoxYAlignment = Kekule.Render.BoxYAlignment.BOTTOM;
}
else if (spaceings.y2 - textPadding >= textBox.height)
{
textCoord.y = contextCoord.y + textPadding;
textBoxYAlignment = Kekule.Render.BoxYAlignment.TOP;
}
else if (spaceings.y1 >= spaceings.y2)
{
textCoord.y = spectrumClientBox.y1 + textBox.height;
textBoxYAlignment = Kekule.Render.BoxYAlignment.BOTTOM;
}
else
{
textCoord.y = spectrumClientBox.y2 - textBox.height;
textBoxYAlignment = Kekule.Render.BoxYAlignment.TOP;
}
detailDrawStyles.textBoxXAlignment = textBoxXAlignment;
detailDrawStyles.textBoxYAlignment = textBoxYAlignment;
}
//console.log(textCoord, textBoxXAlignment, textBoxYAlignment);
viewer.modifyTextBasedMarker(marker, textCoord, text, detailDrawStyles, false);
viewer.showUiMarker(marker, doRepaint);
}
},
/////////////////////////////////////
/**
* Reset the spectrum view settings.
*/
resetView: function()
{
this.changeHotTrackedDataItems(null, false);
this.changeSelectedDataItems(null, false);
this.resetViewportRange();
},
/** @private */
getSpectrumDataValueInfoFromIndependent: function(independentValues, candicateSections, extraOptions)
{
var spectrum = this.getSpectrum();
var sections = candicateSections || spectrum.getDisplayedDataSections() || [];
for (var i = 0, l = sections.length; i < l; ++i)
{
var sec = sections[i];
var v = sec.getDataValueFromIndependent(independentValues, extraOptions);
if (Kekule.ObjUtils.notUnset(v))
return {'value': v, 'section': sec};
}
return null;
},
/**
* Calculate values of dependant variable values from independent variable values.
* @param {Variant} independentValues A hash value with symbol: number map, or an array of values, or a single number value.
* @param {Hash} extraOptions
* @returns {Hash}
*/
calcValueFromIndependent: function(independentValues, extraOptions)
{
var indepHashValues = {};
if (typeof(independentValues) === 'number') // a direct number value,
indepHashValues = this._convArrayToSymboledHash([independentValues], this.getLocalVarSymbolsOfDependency(Kekule.VarDependency.INDEPENDENT));
else if (DataType.isArrayValue(independentValues))
indepHashValues = this._convArrayToSymboledHash(independentValues, this.getLocalVarSymbolsOfDependency(Kekule.VarDependency.INDEPENDENT));
else
indepHashValues = independentValues;
return this.doCalcValueFromIndependent(indepHashValues, extraOptions);
},
///////// spectrum coord transform methods ////////////
/**
* Translate a coord in fromCoordSys to coord values of spectrum internal data.
* @param {Object} coord
* @param {Int} fromCoordSys
* @returns {Object} coord in spectrum data scope, {x, y}.
*/
translateCoordToSpectrum: function(coord, fromCoordSys)
{
var contextCoord = this.getViewer().translateCoord(coord, fromCoordSys, Kekule.Render.CoordSystem.CONTEXT);
var transMatrix = this._getSpectrumRenderCache().spectrumInvDataTransformMatrix;
var transformFunc = this.getViewer().getRenderType() === Kekule.Render.RendererType.R3D?
Kekule.CoordUtils.transform3DByMatrix: Kekule.CoordUtils.transform2DByMatrix;
return transMatrix? transformFunc(contextCoord, transMatrix): contextCoord;
},
/**
* Translate a coord from spectrum internal data scope to toCoordSys.
* @param {Object} coord {x, y}.
* @param {Int} toCoordSys
* @returns {Object} coord in toCoordSys.
*/
translateCoordFromSpectrum: function(coord, toCoordSys)
{
var transMatrix = this._getSpectrumRenderCache().spectrumDataTransformMatrix;
var transformFunc = this.getViewer().getRenderType() === Kekule.Render.RendererType.R3D?
Kekule.CoordUtils.transform3DByMatrix: Kekule.CoordUtils.transform2DByMatrix;
var contextCoord = transMatrix? transformFunc(coord, transMatrix): coord;
return this.getViewer().translateCoord(contextCoord, Kekule.Render.CoordSystem.CONTEXT, toCoordSys);
},
/**
* Returns the data item info at current coord {x, y}.
* @param {Object} coord
* @param {Int} fromCoordSys
* @param {Hash} options
* @returns {Object} Including fields {dataValue, dataSection}
*/
getSpectrumDataValueItemInfoAtCoord: function(coord, fromCoordSys, options)
{
var specCoord = this.translateCoordToSpectrum(coord, fromCoordSys);
var indepValue = this.getIsSpectrumWithReversedAxises()? specCoord.y: specCoord.x;
var spectrum = this.getSpectrum();
var sections = spectrum.getDisplayedDataSections() || [];
for (var i = 0, l = sections.length; i < l; ++i)
{
var section = sections[i];
var dataValue = section.getDataValueFromIndependent(indepValue, options);
if (dataValue)
return {'dataValue': dataValue, 'dataSection': section};
}
return null;
},
/**
* Calculate out the actual spectrum data value from the independent variable values at current coord {x, y}.
* @param {Object} coord
* @param {Int} fromCoordSys
* @param {Hash} options
* @returns {Object}
*/
calcSpectrumDataAtCoord: function(coord, fromCoordSys, options)
{
var specCoord = this.translateCoordToSpectrum(coord, fromCoordSys);
var indepValue = this.getIsSpectrumWithReversedAxises()? specCoord.y: specCoord.x;
var spectrum = this.getSpectrum();
var sections = spectrum.getDisplayedDataSections() || [];
for (var i = 0, l = sections.length; i < l; ++i)
{
var section = sections[i];
var dataValue = section.calcValueFromIndependent(indepValue, options);
if (dataValue)
return dataValue;
}
return null;
},
/**
* Calculate out the actual spectrum data range from the independent variable value range between coords.
* @param {Array} coords
* @param {Int} fromCoordSys
* @param {Hash} options
* @returns {Object}
*/
calcSpectrumDataRangeInCoordRange: function(coords, fromCoordSys, options)
{
var isReversedAxises = this.getIsSpectrumWithReversedAxises();
var fromCoordIndepField = isReversedAxises? 'y': 'x';
var fromCoordRange = Kekule.Spectroscopy.Utils.expandDataRange({}, coords);
var specCoords = [
this.translateCoordToSpectrum({'x': fromCoordRange.x.min, 'y': fromCoordRange.y.min}, fromCoordSys),
this.translateCoordToSpectrum({'x': fromCoordRange.x.max, 'y': fromCoordRange.y.max}, fromCoordSys)
];
var indepValues = [specCoords[0][fromCoordIndepField], specCoords[1][fromCoordIndepField]];
var indepValueRange = {'min': Math.min(indepValues[0], indepValues[1]), 'max': Math.max(indepValues[0], indepValues[1])};
var spectrum = this.getSpectrum();
var sections = spectrum.getDisplayedDataSections() || [];
for (var i = 0, l = sections.length; i < l; ++i)
{
var section = sections[i];
var dataValueRangeEx = section.calcValueRangeFromIndependentRangeEx([indepValueRange], options);
if (dataValueRangeEx && dataValueRangeEx.range)
{
dataValueRangeEx.dataSection = section; // add additional section info
return dataValueRangeEx;
}
}
return null;
},
/**
* Calculate out the primary data value from the independent variable value range between coords.
* @param {Array} coords
* @param {Int} fromCoordSys
* @param {Hash} options
* @returns {Hash} {dataSection, dataValue}
*/
calcSpectrumPrimaryDataValueInCoordRangeEx: function(coords, fromCoordSys, options)
{
var op = Object.create(options || null);
op.findNearest = true;
op.findInReversedOrder = true; // ensure find data from top to button in z-index when rendering
var rangeEx = this.calcSpectrumDataRangeInCoordRange(coords, fromCoordSys, op);
if (!rangeEx)
return null;
var dataSection = rangeEx.dataSection;
var dataValue;
if (dataSection.isPeakSection() && rangeEx.nearest)
dataValue = rangeEx.nearest.dataValue;
else
{
var depVarSymbol = dataSection.getLocalVarSymbolsOfDependency(Kekule.VarDependency.DEPENDENT)[0];
if (depVarSymbol)
{
var spectrum = this.getSpectrum();
var peakPosition = Kekule.Spectroscopy.Utils.getSpectrumPeakPosition(spectrum);
var dataValues = rangeEx.dataValues;
var matchedValue = dataValues[0];
//for (var i = 1, l = dataValues.length; i < l; ++i)
for (var i = dataValues.length - 1; i >= 0; --i) // from rendering z-index top to bottom
{
var v = dataValues[i];
if ((peakPosition === Kekule.Spectroscopy.DataPeakPosition.MIN && v[depVarSymbol] < matchedValue[depVarSymbol])
|| (v[depVarSymbol] > matchedValue[depVarSymbol]))
matchedValue = v;
}
dataValue = matchedValue;
}
else
dataValue = dataValues[Math.round((dataValues.length - 1) / 2) + 1];
}
return {'dataValue': dataValue, 'dataSection': dataSection};
},
/**
* Calculate out the primary data value from the independent variable value range between coords.
* @param {Array} coords
* @param {Int} fromCoordSys
* @param {Hash} options
* @returns {Object} The data value.
*/
calcSpectrumPrimaryDataValueInCoordRange: function(coords, fromCoordSys, options)
{
return this.calcSpectrumPrimaryDataValueInCoordRangeEx(coords, fromCoordSys, options).dataValue;
},
/**
* Calculate out the coord {x, y} of an actual spectrum data value.
* @param {Hash} dataValue
* @param {Int} toCoordSys
* @param {Hash} options
* @returns {Object}
*/
calcCoordAtSpectrumData: function(dataValue, toCoordSys, options)
{
var isReversedAxis = this.getIsSpectrumWithReversedAxises();
var spectrum = this.getSpectrum();
var section = (spectrum.getDisplayedDataSections() || [])[0]; // all displayed sections should has the same var settings
if (section)
{
var specCoord = this._spectrumSectionDataValueToSpectrumCoord(dataValue, section, isReversedAxis);
return specCoord && this.translateCoordFromSpectrum(specCoord, toCoordSys);
}
else
return null;
},
/**
* Calculate out the coord {x, y} of the actual spectrum data at indepValues.
* @param {Hash} indepValues
* @param {Int} toCoordSys
* @param {Hash} options
* @returns {Object}
*/
calcCoordAtSpectrumDataFromIndependent: function(indepValues, toCoordSys, options)
{
var spectrum = this.getSpectrum();
var sections = spectrum.getDisplayedDataSections() || [];
var isReversedAxis = this.getIsSpectrumWithReversedAxises();
for (var i = 0, l = sections.length; i < l; ++i)
{
var section = sections[i];
var dataValue = section.calcValueFromIndependent(indepValue, options);
if (dataValue)
{
var specCoord = this._spectrumSectionDataValueToSpectrumCoord(dataValue, section, isReversedAxis);
return specCoord && this.translateCoordFromSpectrum(specCoord, toCoordSys);
}
}
return null;
},
/** @private */
_spectrumSectionDataValueToSpectrumCoord: function(dataValue, dataSection, isReversedAxies)
{
var indepVarSymbols = dataSection.getLocalVarSymbolsOfDependency(Kekule.VarDependency.INDEPENDENT);
var depVarSymbols = dataSection.getLocalVarSymbolsOfDependency(Kekule.VarDependency.DEPENDENT);
var retrieveFirstValueOfSymbols = function(dataValue, symbols)
{
for (var i = 0, l = symbols.length; i < l; ++i)
{
var v = dataValue[symbols[i]]
if (Kekule.ObjUtils.notUnset(v))
return v;
}
return null;
};
var indepValue = retrieveFirstValueOfSymbols(dataValue, indepVarSymbols);
var depValue = retrieveFirstValueOfSymbols(dataValue, depVarSymbols);
var result = {
'x': isReversedAxies? depValue: indepValue,
'y': isReversedAxies? indepValue: depValue
};
return result;
}
});
/*
ClassEx.defineProp(Kekule.ChemWidget.Viewer, 'spectrumUiMarkers', {
'dataType': DataType.HASH,
'serializable': false,
'setter': null,
'getter': function()
{
var result = this.getPropStoreFieldValue('spectrumUiMarkers');
if (!result)
{
result = {};
this.setPropStoreFieldValue('spectrumUiMarkers', result);
}
return result;
}
});
*/
/** @ignore */
Kekule.ChemWidget.ViewerUiMarkerGroup.SPECTRUM_UI_MARKER_GROUP = 'spectrum';
ClassEx.extend(Kekule.ChemWidget.Viewer, {
/** @private */
createShapeBasedSpectrumUiMarker: function(markerName, group)
{
var result = this.createShapeBasedMarker(null, null, {'name': markerName, 'groups': [group, Kekule.ChemWidget.ViewerUiMarkerGroup.SPECTRUM_UI_MARKER_GROUP]}, false);
//this.getSpectrumUiMarkers()[markerName] = result;
return result;
},
/** @private */
createTextBasedSpectrumUiMarker: function(markerName, group)
{
var result = this.createTextBasedMarker(null, null, null, {'name': markerName, 'groups': [group, Kekule.ChemWidget.ViewerUiMarkerGroup.SPECTRUM_UI_MARKER_GROUP]}, false);
//this.getSpectrumUiMarkers()[markerName] = result;
return result;
},
/** @private */
getSpectrumUiMarker: function(markerName, autoCreate)
{
var result = this.getUiMarkers().getMarkerOfName(markerName); //this.getSpectrumUiMarkers()[markerName];
if (!result && autoCreate)
{
// create new one automatically
var textBased = markerName.indexOf('DataDetail') >= 0;
var group = (markerName.indexOf('select') >= 0)? Kekule.ChemWidget.ViewerUiMarkerGroup.SELECT:
(markerName.indexOf('hotTrack') >= 0)? Kekule.ChemWidget.ViewerUiMarkerGroup.HOTTRACK:
null;
result = textBased?
this.createTextBasedSpectrumUiMarker(markerName, group):
this.createShapeBasedSpectrumUiMarker(markerName, group);
//this.getSpectrumUiMarkers()[markerName] = result;
}
return result;
},
/** @private */
getSpectrumUiMarkers: function()
{
return this.getUiMarkers().getMarkersOfGroup(Kekule.ChemWidget.ViewerUiMarkerGroup.SPECTRUM_UI_MARKER_GROUP);
},
/** @private */
_hideAllSpectrumUiMarkers: function(updateRenderer)
{
this.setVisibleOfUiMarkerGroup(Kekule.ChemWidget.ViewerUiMarkerGroup.SPECTRUM_UI_MARKER_GROUP, false, updateRenderer);
/*
var markers = this.getSpectrumUiMarkers();
var markerNames = OU.getOwnedFieldNames(markers);
for (var i = 0, l = markerNames.length; i < l; ++i)
this.hideUiMarker(markers[markerNames[i]], false);
if (updateRenderer)
this.repaintUiMarker();
*/
},
/** @private */
updateSpectrumViewDataItemUiMarkers: function()
{
//this.beginUpdateUiMarkers();
try
{
//console.log('update ui markers');
this._hideAllSpectrumUiMarkers(false);
this.iterateSubViews(function (subView)
{
if (subView instanceof Kekule.ChemWidget.Viewer.SpectrumSubView)
{
subView.updateSpectrumDataItemUiMarkers(true);
}
});
}
finally
{
//this.endUpdateUiMarkers();
}
}
});
// extend the Viewer widget
ClassEx.extendMethods(Kekule.ChemWidget.Viewer, {
/** @private */
_createSpectrumView: function($origin)
{
return this.fetchSubView(this.getChemObj(), Kekule.ChemWidget.Viewer.SpectrumSubView);
//return new Kekule.ChemWidget.Viewer.SpectrumView(this);
},
/**
* Check if the viewer loaded only a spectrum.
* @returns {Bool}
* @private
*/
_isDisplayingSingleSpectrum: function($origin)
{
var obj = this.getChemObj();
return !!(obj && obj instanceof Kekule.Spectroscopy.Spectrum);
},
/**
* Returns a sub view for target spectrum.
* @param {Kekule.Spectroscopy.Spectrum} target
* @returns {Kekule.ChemWidget.Viewer.SpectrumSubView}
*/
getSpectrumView: function($origin, target)
{
return this.fetchSubView(target || this.getChemObj(), Kekule.ChemWidget.Viewer.SpectrumSubView);
},
/**
* Check if the loaded object should be displayed in spectrum mode.
* @returns {Bool}
*/
isInSpectrumMode: function($origin)
{
if (!Kekule.Spectroscopy || !Kekule.Spectroscopy.Spectrum)
return false;
else
return (this.getRenderType() === Kekule.Render.RendererType.R2D)
&& this.getDisplayerConfigs().getSpectrumViewConfigs().getActualEnableSpectrumView()
&& this._isDisplayingSingleSpectrum();
},
/**
* Check if local spectrum mode is enabled in this viewer.
* @returns {Bool}
*/
isLocalSpectrumViewEnabled: function($origin)
{
if (!Kekule.Spectroscopy || !Kekule.Spectroscopy.Spectrum)
return false;
else
return (this.getRenderType() === Kekule.Render.RendererType.R2D)
&& this.getDisplayerConfigs().getSpectrumViewConfigs().getActualEnableLocalSpectrumView();
},
/**
* Check if the viewer is in spectrum or local spectrum mode.
* @returns {Bool}
*/
enableSpectrumInteraction: function($origin)
{
return this.isInSpectrumMode() || this.isLocalSpectrumViewEnabled();
},
/** @ignore */
getEnableUiContext: function($origin)
{
return true;
},
/** @ignore */
allowAutoSize: function($origin)
{
if (this.isInSpectrumMode()) // autoSize should be disabled in spectrum view
return false;
else
return $origin();
},
/** @ignore */
getActualDrawOptions: function($origin)
{
var ops = $origin();
if (this.isInSpectrumMode() && this.getSpectrumView())
{
var result = Object.create(ops);
if (Kekule.ObjUtils.isUnset(ops.retainAspect))
result.retainAspect = false;
// force spectrum to fit the client area of viewer
//result.autoSize = false;
result.autofit = true;
return result;
}
else
return ops;
},
/** @ignore */
doLoad: function($origin, chemObj)
{
if (this.isInSpectrumMode())
{
var sview = this.getSpectrumView();
if (sview)
sview.resetView();
/*
var spectrumIaController = this.getIaController('spectrum');
if (!spectrumIaController)
this.addIaController('spectrum', new Kekule.ChemWidget.SpectrumViewerBasicInteractionController(this), true);
else
this.setDefIaControllerId('spectrum');
*/
}
/*
else // exit spectrum view, use the default IA controller
//this.setDefIaControllerId('default');
this.setDefIaControllerId('spectrum');
*/
return $origin(chemObj);
},
/* @ignore */
/*
chemObjRendered: function($origin, chemObj, renderOptions)
{
if (this.isInSpectrumMode())
{
var sview = this.getSpectrumView();
sview.setPropStoreFieldValue('isSpectrumViewWithReversedAxises', !!renderOptions.spectrum_reversedAxises);
}
return $origin(chemObj, renderOptions);
},
*/
/** @ignore */
_repaintCore: function($origin, overrideOptions)
{
var isInSpectrumMode = this.isInSpectrumMode();
var enableLocalSpectrumView = this.isLocalSpectrumViewEnabled();
if (isInSpectrumMode || enableLocalSpectrumView)
{
var ops = Object.create(overrideOptions || {});
ops._spectrum_render_enable_sub_bounds = true;
//var sview = this.getSpectrumView();
//if (sview)
if (isInSpectrumMode)
{
var configs = this.getDisplayerConfigs().getSpectrumViewConfigs();
ops.spectrum_axisLabelFontSizeMin = configs.getSpectrumAxisLabelFontSizeMin(); // 15;
ops.spectrum_axisLabelFontSizeMax = configs.getSpectrumAxisLabelFontSizeMax(); // 35;
ops.spectrum_axisLabelFontSizeFixed = configs.getSpectrumAxisLabelFontSizeFixed(); //15;
ops.spectrum_axisScaleLabelFontSizeMin = configs.getSpectrumAxisScaleLabelFontSizeMin(); // 15;
ops.spectrum_axisScaleLabelFontSizeMax = configs.getSpectrumAxisScaleLabelFontSizeMax(); // 35;
ops.spectrum_axisScaleLabelFontSizeFixed = configs.getSpectrumAxisScaleLabelFontSizeFixed(); //15;
//sview.applyToRenderOptions(ops);
}
return $origin(ops);
}
else
return $origin(overrideOptions);
},
/** @ignore */
doUpdateUiMarkers: function($origin)
{
//console.log('do update ui markers');
this.updateSpectrumViewDataItemUiMarkers();
return $origin();
},
/** @ignore */
repaintUiMarker: function($origin)
{
/*
if (updateMarkers)
this.updateSpectrumViewDataItemUiMarkers();
*/
return $origin();
},
/** @ignore */
getCurrZoom: function($origin)
{
if (this.isInSpectrumMode() && this.getSpectrumView())
{
return this.getSpectrumView().getViewportZoomInfo().zoom;
}
else
return $origin();
},
/** @ignore */
zoomTo: function($origin, value, suspendRendering, zoomCenterCoord)
{
if (this.isInSpectrumMode())
{
var sview = this.getSpectrumView();
if (sview)
{
var clientBox = sview.getClientScreenBox();
var currViewportRange = sview.getViewportRange(Kekule.VarDependency.INDEPENDENT);
var oldRangeValue = currViewportRange.rangeTo - currViewportRange.rangeFrom;
var newRangeValue = 1 / value;
var rangeDelta = newRangeValue - oldRangeValue;
var fixedPointRatio = zoomCenterCoord ? ((zoomCenterCoord.x - clientBox.x1) / (clientBox.x2 - clientBox.x1)) : 0.5;
var newRangeFrom = currViewportRange.rangeFrom - rangeDelta * fixedPointRatio;
var newRangeTo = currViewportRange.rangeTo + rangeDelta * (1 - fixedPointRatio);
sview.setViewportRange(Kekule.VarDependency.INDEPENDENT, newRangeFrom, newRangeTo);
if (!suspendRendering)
this.geometryOptionChanged();
}
else
$origin(value, suspendRendering, zoomCenterCoord);
}
else
$origin(value, suspendRendering, zoomCenterCoord);
return this;
},
/** @ignore */
resetZoom: function($origin, zoomCenterCoord)
{
if (this.isInSpectrumMode())
{
var sview = this.getSpectrumView();
if (sview)
{
sview.resetViewportRange();
//this.geometryOptionChanged();
return this;
}
else
return $origin(zoomCenterCoord);
}
else
return $origin(zoomCenterCoord);
},
/** @ignore */
resetDisplay: function($origin)
{
if (this.isInSpectrumMode())
{
var sview = this.getSpectrumView();
if (sview)
sview.resetView();
}
return $origin(); // this.geometryOptionChanged() will be called here
}
});
/** @private */
Kekule.ChemWidget.Viewer.SpectrumSubView.UiMarker = {
DATA_POINT: 1,
DATA_DETAIL: 2,
PEAK: 4
};
var SVM = Kekule.ChemWidget.Viewer.SpectrumSubView.UiMarker;
/*
ClassEx.defineProp(Kekule.ChemWidget.ViewerBasicInteractionController, 'spectrumCurrDataPointMarker', {
'dataType': 'Kekule.ChemWidget.AbstractUIMarker',
'serializable': false,
'getter': function()
{
var result = this.getPropStoreFieldValue('spectrumCurrDataPointMarker');
if (!result)
{
result = this.createSpectrumCurrDataPointMarker();
this.setPropStoreFieldValue('spectrumCurrDataPointMarker', result);
}
return result;
}
});
ClassEx.defineProp(Kekule.ChemWidget.ViewerBasicInteractionController, 'spectrumCurrDataPointDetailMarker', {
'dataType': 'Kekule.ChemWidget.AbstractUIMarker',
'serializable': false,
'getter': function()
{
var result = this.getPropStoreFieldValue('spectrumCurrDataPointDetailMarker');
if (!result)
{
result = this.createSpectrumCurrDataPointDetailMarker();
this.setPropStoreFieldValue('spectrumCurrDataPointDetailMarker', result);
}
return result;
}
});
*/
ClassEx.extend(Kekule.ChemWidget.ViewerBasicInteractionController, {
/** @private */
getSpectrumUiMarker: function(markerName, autoCreate)
{
return this.getViewer().getSpectrumUiMarker(markerName, autoCreate);
},
/** @private */
_setSpectrumUiMarkersVisible: function(visible, updateRenderer)
{
var viewer = this.getViewer();
var pointMarker = this.getSpectrumUiMarker('hotTrackDataPoint', visible); //this.getSpectrumCurrDataPointMarker();
var detailMarker = this.getSpectrumUiMarker('hotTrackDataDetail', visible); //this.getSpectrumCurrDataPointDetailMarker();
if (visible)
{
if (pointMarker)
viewer.showUiMarker(pointMarker, false);
if (detailMarker)
viewer.showUiMarker(detailMarker, false);
}
else
{
if (pointMarker)
viewer.hideUiMarker(pointMarker, false);
if (detailMarker)
viewer.hideUiMarker(detailMarker, false);
}
if (updateRenderer)
viewer.repaintUiMarker();
},
/** @private */
hideSpectrumUiMarkers: function(updateRenderer)
{
this._setSpectrumUiMarkersVisible(false, updateRenderer);
},
/** @private */
showSpectrumUiMarkers: function(updateRenderer)
{
this._setSpectrumUiMarkersVisible(true, updateRenderer);
},
/** @private */
_getAncestorSpectrumObj: function(obj)
{
if (obj instanceof Kekule.Spectroscopy.Spectrum)
return obj;
else if (obj)
{
var parent = obj.getParent? obj.getParent(): obj.parent;
if (parent)
return this._getAncestorSpectrumObj(parent);
else
return null;
}
else
return null;
},
/** @private */
_getSpectrumObjAtScreenCoord: function(coord, boundInflation)
{
//var coord = this.
var viewer = this.getViewer();
var boundObj = viewer.getTopmostBasicObjectAtCoord(coord, boundInflation || 0);
//return (boundObj instanceof Kekule.Spectroscopy.Spectrum)? boundObj: null;
return this._getAncestorSpectrumObj(boundObj); // boundObj may be a child PseudoRenderSubObject in rendering
},
/** @private */
getActiveSpectrumViewAtScreenCoord: function(screenCoord)
{
var result;
var viewer = this.getViewer();
if (viewer.enableSpectrumInteraction())
{
var boundInflation = viewer.getViewerConfigs().getInteractionConfigs().getObjBoundTrackInflation() || 0;
var currSpectrum = this._getSpectrumObjAtScreenCoord(screenCoord, boundInflation);
if (currSpectrum)
{
result = viewer.getSpectrumView(currSpectrum);
}
}
return result;
},
/** @private */
isTransformingSpectrum: function()
{
return !!this._transformInfo.currSpectrum;
}
});
// Extend the ViewerBasicInteractionController to enable the move/zoom interactions of spectrum in viewer
ClassEx.extendMethods(Kekule.ChemWidget.ViewerBasicInteractionController, {
/** @ignore */
_beginInteractTransformAtCoord: function($origin, screenX, screenY, clientX, clientY, htmlEvent, pointerId)
{
//this.tryApplySuper('_beginInteractTransformAtCoord', [screenX, screenY, clientX, clientY, htmlEvent]);
$origin(screenX, screenY, clientX, clientY, htmlEvent, pointerId);
/*
//console.log('begin interaction');
var viewer = this.getViewer();
//if (viewer.isInSpectrumMode())
if (viewer.enableSpectrumInteraction())
{
var currSpectrum = this._getSpectrumObjAtScreenCoord(this._getEventMouseCoord(htmlEvent));
//console.log('begin inter', currSpectrum);
if (currSpectrum)
{
var sview = viewer.getSpectrumView(currSpectrum);
if (sview)
{
this._transformInfo.currSpectrum = currSpectrum;
this._transformInfo.initSpectrumViewportRanges = sview.fetchViewportRanges();
//this._transformInfo.initSpectrumViewportRanges = viewer.getSpectrumView().fetchViewportRanges();
this._updateSpectrumCurrDataPointMarker(null, null, false);
}
else
{
this._transformInfo.currSpectrum = undefined;
this._transformInfo.initSpectrumViewportRanges = undefined;
}
}
}
*/
var sview = this.getActiveSpectrumViewAtScreenCoord(this._getEventMouseCoord(htmlEvent));
if (sview)
{
var currSpectrum = sview.getSpectrum();
this._transformInfo.currSpectrum = currSpectrum;
this._transformInfo.spectrumActuallyTransformed = false; // a flag, whether the pointer is actually moved and a concrete transform has been done
this._transformInfo.initSpectrumViewportRanges = sview.fetchViewportRanges();
//this._transformInfo.initSpectrumViewportRanges = viewer.getSpectrumView().fetchViewportRanges();
//this.hideSpectrumUiMarkers(true);
}
else
{
this._transformInfo.currSpectrum = undefined;
this._transformInfo.initSpectrumViewportRanges = undefined;
}
},
/** @ignore */
_endInteractTransform: function($origin)
{
this._transformInfo.currSpectrum = undefined;
this._transformInfo.initSpectrumViewportRanges = undefined;
//this.tryApplySuper('_endInteractTransform');
$origin();
},
/** @ignore */
moveByXYDistance: function($origin, currX, currY)
{
var viewer = this.getViewer();
//if (viewer.isInSpectrumMode())
if (viewer.enableSpectrumInteraction())
{
var info = this._transformInfo;
if (info && info.isTransforming && (!info.calculating))
{
/*
if (viewer.getRenderType() === Kekule.Render.RendererType.R3D)
return;
*/
var sview = info.currSpectrum && viewer.getSpectrumView(info.currSpectrum);
if (sview)
{
info.calculating = true;
try
{
var currCoord = {'x': currX, 'y': currY};
if (Kekule.CoordUtils.isEqual(currCoord, info.lastCoord)) // coord has no change bypass
{
// do nothing
}
else
{
var delta = Kekule.CoordUtils.substract(currCoord, info.transformInitCoord);
//console.log('move', delta);
var clientBox = sview.getClientScreenBox();
var moveRatios = [
delta.x / (clientBox.x2 - clientBox.x1), // x
-delta.y / (clientBox.y2 - clientBox.y1) // y
];
var reversedAxises = sview.getIsSpectrumWithReversedAxises();
var varDependencies = reversedAxises ?
[Kekule.VarDependency.DEPENDENT, Kekule.VarDependency.INDEPENDENT] :
[Kekule.VarDependency.INDEPENDENT, Kekule.VarDependency.DEPENDENT];
viewer.beginUpdate();
try
{
for (var i = 0, l = moveRatios.length; i < l; ++i)
{
var moveRatio = moveRatios[i];
var viewportRange = info.initSpectrumViewportRanges[varDependencies[i]] || {};
var viewportRangeFrom = viewportRange.rangeFrom || 0;
var viewportRangeTo = viewportRange.rangeTo || 1;
var rangeDelta = (viewportRangeTo - viewportRangeFrom) * moveRatio;
sview.setViewportRange(varDependencies[i], viewportRangeFrom - rangeDelta, viewportRangeTo - rangeDelta);
}
} finally
{
viewer.endUpdate();
}
//viewer._repaintCore();
info.lastCoord = currCoord;
info.spectrumActuallyTransformed = true;
}
} finally
{
info.calculating = false;
}
}
else
//return this.tryApplySuper('moveByXYDistance', [currX, currY]);
return $origin(currX, currY);
}
}
else
//return this.tryApplySuper('moveByXYDistance', [currX, currY]);
return $origin(currX, currY);
},
/** @private */
doZoomSpectrumViewer: function($origin, spectrum, delta, zoomCenterCoord, directions)
{
var v = this.getViewer();
//if (v.isInSpectrumMode())
if (v.enableSpectrumInteraction())
{
var sview = v.getSpectrumView(spectrum);
if (sview)
sview.zoomViewportByDelta(delta, zoomCenterCoord, directions);
}
},
/** @private */
_mayNeedSpectrumDataHotTrack: function($origin, spectrumView, spectrumViewConfigs)
{
//var spectrum = spectrumView.getSpectrum();
var displayedDataModes = spectrumView.getDisplayedDataModes();
var configs = spectrumViewConfigs;
return configs && configs.getDataModeSpecifiedBoolConfigValueOfModeList('enableSpectrumDataHotTrackOnMode', displayedDataModes);
/*
return configs && (configs.getEnableSpectrumDataPointMarker() || configs.getEnableSpectrumDataPointDetailMarker()
|| configs.getEnableSpectrumPeakHotTrack());
*/
},
/** @private */
_getSpectrumDataSelectCoordInflationRange: function($origin, screenCoord, spectrumViewConfigs)
{
var selectInflation = spectrumViewConfigs.getSpectrumDataPointSelectInflation();
var coordDelta = {x: selectInflation, y: selectInflation};
var screenCoords = [
Kekule.CoordUtils.substract(screenCoord, coordDelta),
Kekule.CoordUtils.add(screenCoord, coordDelta),
];
return screenCoords;
},
/** @private */
doTrySelectSpectrumData: function($origin, screenCoord, isToggle, viewer, subView, subViewConfigs)
{
//var viewer = this.getViewer();
{
//var sview = this.getActiveSpectrumViewAtScreenCoord(screenCoord);
var sview = subView;
if (sview)
{
var selectDataItemEx = null;
//var configs = viewer.getDisplayerConfigs().getSpectrumViewConfigs();
var configs = subViewConfigs;
//if (configs.getDataModeSpecifiedBoolConfigValueOfModeList('enableSpectrumDataSelectOnMode', sview.getDisplayedDataModes()))
{
//var peakDataValue = null;
var clientBox = sview.getClientScreenBox();
if (Kekule.CoordUtils.isInsideBox(screenCoord, clientBox))
{
var screenCoords = this._getSpectrumDataSelectCoordInflationRange(screenCoord, configs);
var spectrumDataValueResult = sview.calcSpectrumPrimaryDataValueInCoordRangeEx(screenCoords, Kekule.Render.CoordSystem.SCREEN);
if (spectrumDataValueResult) // do has the primary value need to be selected
{
var dataSection = spectrumDataValueResult.dataSection;
var dataValue = spectrumDataValueResult.dataValue;
/*
if (dataValue && dataSection.isPeakSection() && configs.getDataModeSpecifiedConfigValue('enableSpectrumDataSelectOnMode', dataSection.getMode())) // select peak
peakDataValue = dataValue;
*/
selectDataItemEx = {'section': dataSection, 'dataValue': dataValue};
}
}
}
if (isToggle && configs.getDataModeSpecifiedBoolConfigValueOfModeList('enableSpectrumDataMultiSelectOnMode', sview.getDisplayedDataModes()))
sview.toggleSelectingState(selectDataItemEx);
else
sview.select(selectDataItemEx);
//sview.setSelectedDataItemEx(selectDataItemEx);
viewer.requestRepaint();
}
}
},
/** @private */
doTryHotTrackSpectrumData: function($origin, screenCoord, viewer, subView, subViewConfigs)
{
var sview = subView;
var configs = subViewConfigs;
//var viewer = this.getViewer();
//var configs = viewer.getDisplayerConfigs().getSpectrumViewConfigs();
//var sview = this.getActiveSpectrumViewAtScreenCoord(screenCoord);
//if (sview)
{
//var spectrum = sview.getSpectrum();
//var hotTrackElements = 0;
var hotTrackDataItemEx = null;
//if (this._mayNeedSpectrumDataHotTrack(sview, configs))
{
// check if pointer moves over spectrum client box and need to displaying the data point marker
var clientBox = sview.getClientScreenBox();
if (Kekule.CoordUtils.isInsideBox(screenCoord, clientBox))
{
/*
var selectInflation = configs.getSpectrumDataPointSelectInflation();
var coordDelta = {x: selectInflation, y: selectInflation};
var screenCoords = [
Kekule.CoordUtils.substract(screenCoord, coordDelta),
Kekule.CoordUtils.add(screenCoord, coordDelta),
];
*/
var screenCoords = this._getSpectrumDataSelectCoordInflationRange(screenCoord, configs);
var spectrumDataValueResult = sview.calcSpectrumPrimaryDataValueInCoordRangeEx(screenCoords, Kekule.Render.CoordSystem.SCREEN);
if (spectrumDataValueResult) // do has the primary value need to be hot tracked first
{
var dataSection = spectrumDataValueResult.dataSection;
var dataValue = spectrumDataValueResult.dataValue;
hotTrackDataItemEx = {'section': dataSection, 'dataValue': dataValue};
// debug
/*
var hotTrackDataItemEx2 = {'section': dataSection, 'dataValue': Object.extend({}, dataValue)};
for (var key in hotTrackDataItemEx2.dataValue)
{
hotTrackDataItemEx2.dataValue[key] *= 1.2;
}
*/
}
}
}
/*
if (hotTrackDataItemEx2)
sview.setHotTrackedDataItemsEx([hotTrackDataItemEx, hotTrackDataItemEx2]);
else
*/
sview.setHotTrackedDataItemsEx(hotTrackDataItemEx && [hotTrackDataItemEx]);
//console.log('spectrum set hot items', hotTrackDataItemEx);
}
//else // move out of spectrum
// viewer.setVisibleOfUiMarkerGroup([Kekule.ChemWidget.ViewerUiMarkerGroup.HOTTRACK, Kekule.ChemWidget.ViewerUiMarkerGroup.SPECTRUM_UI_MARKER_GROUP], false, true);
},
/** @ignore */
doTryHotTrackBasicObject: function($origin, screenCoord, boundInflation)
{
var viewer = this.getViewer();
var configs = viewer.getDisplayerConfigs().getSpectrumViewConfigs();
var sview = this.getActiveSpectrumViewAtScreenCoord(screenCoord);
if (sview && viewer.getRenderType() === Kekule.Render.RendererType.R2D && this._mayNeedSpectrumDataHotTrack(sview, configs))
{
return this.doTryHotTrackSpectrumData(screenCoord, viewer, sview, configs);
}
else
return $origin(screenCoord, boundInflation);
},
/** @ignore */
doTrySelectBasicObject: function($origin, screenCoord, isToggle, boundInflation)
{
var viewer = this.getViewer();
var configs = viewer.getDisplayerConfigs().getSpectrumViewConfigs();
var sview = this.getActiveSpectrumViewAtScreenCoord(screenCoord);
if (sview && viewer.getRenderType() === Kekule.Render.RendererType.R2D
&& configs.getDataModeSpecifiedBoolConfigValueOfModeList('enableSpectrumDataSelectOnMode', sview.getDisplayedDataModes()))
{
return this.doTrySelectSpectrumData(screenCoord, isToggle, viewer, sview, configs);
}
else
return $origin(screenCoord, isToggle, boundInflation);
},
/** @private */
react_dblclick: function($origin, e)
{
if (this.needReactEvent(e))
{
//var spectrumReset = false;
var sview = this.getActiveSpectrumViewAtScreenCoord(this._getEventMouseCoord(e));
if (sview)
{
var viewer = this.getViewer();
viewer.beginUpdate();
try
{
if (sview)
{
sview.resetView();
//spectrumReset = true;
}
}
finally
{
viewer.endUpdate();
}
}
else
$origin(e);
}
},
/* @private */
/*
react_pointerup: function($origin, e)
{
if (this.needReactEvent(e) && e.getButton() === Kekule.X.Event.MouseButton.LEFT)
{
//this._transformInfo.isTransforming = false;
if (this._transformInfo.isTransforming && !this._transformInfo.spectrumActuallyTransformed) // pointer not moved in transforming, actually a simple click
{
var screenCoord = this._getEventMouseCoord(e);
var isToggle = e.getShiftKey() || e.getCtrlKey();
this.doTrySelectSpectrumData(screenCoord, isToggle);
}
}
return $origin(e);
},
*/
/* @private */
/*
react_pointermove: function($origin, e)
{
var result;
this.getViewer().beginUpdateUiMarkers();
try
{
//console.log('inside', this.getViewer().isUpdatingUiMarkers(), this.getViewer()._uiMarkerUpdateFlag);
result = $origin(e);
if (this.needReactEvent(e) && !this.isTransformingSpectrum())
{
var screenCoord = this._getEventMouseCoord(e);
this.doTryHotTrackSpectrumData(screenCoord);
}
}
finally
{
this.getViewer().endUpdateUiMarkers();
}
return result;
},
*/
/** @private */
react_pointerleave: function($origin, e)
{
this.hideSpectrumUiMarkers(true);
return $origin(e);
},
/** @private */
react_mousewheel: function($origin, e)
{
if (this.needReactEvent(e))
{
var sview = this.getActiveSpectrumViewAtScreenCoord(this._getEventMouseCoord(e));
if (sview)
{
var viewer = this.getViewer();
var spectrum = sview.getSpectrum();
//this._updateSpectrumCurrDataPointMarker(null, null, false);
this.hideSpectrumUiMarkers(true);
var configs = viewer.getDisplayerConfigs().getSpectrumViewConfigs();
var delta = e.wheelDeltaY || e.wheelDelta;
if (delta)
delta /= 120;
//var centerCoord = e.getOffsetCoord();
var centerCoord = this._getEventMouseCoord(e);
var directions = Kekule.ChemWidget.SpectrumViewUtils.getZoomDirectionsFromEvent(e, configs);
this.doZoomSpectrumViewer(spectrum, delta, centerCoord, directions);
e.preventDefault();
return true;
}
}
return $origin(e);
}
});
ClassEx.extendMethods(Kekule.ChemWidget.ActionDisplayerZoomIn, {
doExecute: function($origin, target, htmlEvent)
{
var viewer = this.getDisplayer();
if (viewer.isInSpectrumMode && viewer.isInSpectrumMode())
{
var directions = Kekule.ChemWidget.SpectrumViewUtils.getZoomDirectionsFromEvent(htmlEvent, viewer.getDisplayerConfigs().getSpectrumViewConfigs());
var sview = viewer.getSpectrumView();
if (sview)
sview.zoomViewportByDelta(1, null, directions);
else
$origin();
}
else
$origin();
}
});
ClassEx.extendMethods(Kekule.ChemWidget.ActionDisplayerZoomOut, {
doExecute: function($origin, target, htmlEvent)
{
var viewer = this.getDisplayer();
if (viewer.isInSpectrumMode && viewer.isInSpectrumMode())
{
var directions = Kekule.ChemWidget.SpectrumViewUtils.getZoomDirectionsFromEvent(htmlEvent, viewer.getDisplayerConfigs().getSpectrumViewConfigs());
var sview = viewer.getSpectrumView();
if (sview)
sview.zoomViewportByDelta(-1, null, directions);
else
$origin();
}
else
$origin();
}
});
/**
* A special object to connect two {@link Kekule.ChemWidget.Viewer} widgets, one displaying the spectrum and another displaying the molecule.
* When hot track or select peak in spectrum, the correlated atoms will be marked and vice versa.
* @class
* @augments ObjectEx
*
* @property {Kekule.ChemWidget.Viewer} spectrumViewer The viewer displaying spectrum.
* @property {Kekule.ChemWidget.Viewer} moleculeViewer The viewer displaying molecule.
* @property {Bool} autoLoadCorrelatedMolecule Whether load correlated molecule in molecule viewer automatically when a new spectrum is loaded in spectrum viewer.
* @property {Bool} fromSpectrumToMolecule Whether show hot track or selection in molecule viewer when user hot track or select data in spectrum viewer.
* @property {Bool} fromMoleculeToSpectrum Whether show hot track or selection in spectrum viewer when user hot track or select object in molecule viewer.
* @property {Bool} enabled
*/
/**
* Invoked when spectrum data items / molecule assignment objects are selected in either view.
* event param of it has field: {assignmentDetails: array}.
* In the array, each item is a hash of {spectrum, dataSection, dataValue, assignments}.
* @name Kekule.ChemWidget.SpectrumCorrelationConnector#assignmentSelected
* @event
*/
/**
* Invoked when spectrum data items / molecule assignment objects are hot tracked in either view.
* event param of it has field: {assignmentDetails: array}.
* In the array, each item is a hash of {spectrum, dataSection, dataValue, assignments}.
* @name Kekule.ChemWidget.SpectrumCorrelationConnector#assignmentHotTracked
* @event
*/
Kekule.ChemWidget.SpectrumCorrelationConnector = Class.create(ObjectEx,
/** @lends Kekule.ChemWidget.SpectrumCorrelationConnector# */
{
/** @private */
CLASS_NAME: 'Kekule.ChemWidget.SpectrumCorrelationConnector',
/** @constructs */
initialize: function(spectrumViewer, moleculeViewer)
{
this.reactLoadEventOnSpectrumViewerBind = this.reactLoadEventOnSpectrumViewer.bind(this);
this.reactEventOnSpectrumViewerBind = this.reactEventOnSpectrumViewer.bind(this);
this.reactEventOnMoleculeViewerBind = this.reactEventOnMoleculeViewer.bind(this);
this.tryApplySuper('initialize');
if (spectrumViewer)
this.setSpectrumViewer(spectrumViewer);
if (moleculeViewer)
this.setMoleculeViewer(moleculeViewer);
},
/** @private */
initProperties: function()
{
this.defineProp('spectrumViewer', {
'dataType': 'Kekule.ChemWidget.Viewer',
'serializable': false,
'setter': function(value)
{
var old = this.getSpectrumViewer();
if (old !== value)
{
this.doSpectrumViewerChange(value, old);
this.setPropStoreFieldValue('spectrumViewer', value);
}
}
});
this.defineProp('moleculeViewer', {
'dataType': 'Kekule.ChemWidget.Viewer',
'serializable': false,
'setter': function(value)
{
var old = this.getMoleculeViewer();
if (old !== value)
{
this.doMoleculeViewerChange(value, old);
this.setPropStoreFieldValue('moleculeViewer', value);
}
}
});
this.defineProp('autoLoadCorrelatedMolecule', {'dataType': DataType.BOOL});
this.defineProp('fromSpectrumToMolecule', {'dataType': DataType.BOOL});
this.defineProp('fromMoleculeToSpectrum', {'dataType': DataType.BOOL});
this.defineProp('enabled', {'dataType': DataType.BOOL});
},
/** @ignore */
initPropValues: function()
{
this.tryApplySuper('initPropValues');
this.setAutoLoadCorrelatedMolecule(true);
this.setFromSpectrumToMolecule(true);
this.setFromMoleculeToSpectrum(true);
this.setEnabled(true);
},
/** @ignore */
doFinalize: function()
{
this.setMoleculeViewer(null);
this.setSpectrumViewer(null);
this.tryApplySuper('doFinalize');
},
/** @private */
_getLoadedSpectrums: function(root)
{
if (!root)
{
var viewer = this.getSpectrumViewer();
root = viewer && viewer.getChemObj();
}
if (root)
{
if (root instanceof Kekule.Spectroscopy.Spectrum)
return [root];
else
{
return root.filterChildren(function(obj){
return (obj && obj instanceof Kekule.Spectroscopy.Spectrum);
}, true);
}
}
else
return [];
},
/** @private */
_getRefMoleculesOfSpectrum: function(spectrum)
{
return spectrum.getRefMolecules() || [];
},
/** @private */
_getDisplayedSpectrumDataSections: function()
{
var result = [];
var spectrums = this._getLoadedSpectrums();
for (var i = 0, l = spectrums.length; i < l; ++i)
{
if (spectrums[i].getVisible())
{
var sections = spectrums[i].getDisplayedDataSections();
result = result.concat(sections);
}
}
return result;
},
/** @private */
_getSpectrumDataItemAssignments: function(dataSection, dataValueOrIndex)
{
var assignments;
var dataDetail;
if (typeof(dataValueOrIndex) === 'number') // data index
dataDetail = dataSection.getExtraInfoAt(dataValueOrIndex);
else
dataDetail = dataSection.getExtraInfoOf(dataValueOrIndex);
if (dataDetail && (dataDetail.getAssignments || dataDetail.assignments))
{
assignments = dataDetail.getAssignments() || dataDetail.assignments;
}
return assignments;
},
/** @private */
_getDataItemsExOfCorrelatedMolObject: function(molObject, dataSections)
{
var spectrums = [];
var result = [];
for (var i = 0, l = dataSections.length; i < l; ++i)
{
var section = dataSections[i];
var spectrum = section.getParentSpectrum();
var spectrumIndex = spectrums.indexOf(spectrum);
if (spectrumIndex < 0)
{
spectrumIndex = spectrums.length;
spectrums.push(spectrum);
result[spectrumIndex] = {'spectrum': spectrum, dataItemsEx: []};
}
var dataItemsEx = result[spectrumIndex].dataItemsEx;
if (section.isPeakSection()) // now only handled peak
{
for (var i = 0, l = section.getDataCount(); i < l; ++i)
{
var assignments = this._getSpectrumDataItemAssignments(section, i);
if (assignments && assignments.length)
{
var match = assignments.indexOf(molObject) >= 0;
if (match)
dataItemsEx.push({'section': section, 'dataValue': section.getValueAt(i)});
}
}
}
}
return result;
},
/** @private */
doSpectrumViewerChange: function(newViewer, oldViewer)
{
if (oldViewer)
{
oldViewer.removeEventListener('load', this.reactLoadEventOnSpectrumViewerBind);
oldViewer.removeEventListener('hotTrackOnSpectrumData', this.reactEventOnSpectrumViewerBind);
oldViewer.removeEventListener('spectrumDataSelectionChange', this.reactEventOnSpectrumViewerBind);
}
if (newViewer)
{
newViewer.addEventListener('load', this.reactLoadEventOnSpectrumViewerBind);
newViewer.addEventListener('hotTrackOnSpectrumData', this.reactEventOnSpectrumViewerBind);
newViewer.addEventListener('spectrumDataSelectionChange', this.reactEventOnSpectrumViewerBind);
this.doTryLoadSpectrumRefMoleculeInViewer(newViewer.getChemObj());
}
},
/** @private */
doMoleculeViewerChange: function(newViewer, oldViewer)
{
if (oldViewer)
{
oldViewer.removeEventListener('hotTrackOnObjects', this.reactEventOnMoleculeViewerBind);
oldViewer.removeEventListener('selectionChange', this.reactEventOnMoleculeViewerBind);
}
if (newViewer)
{
newViewer.addEventListener('hotTrackOnObjects', this.reactEventOnMoleculeViewerBind);
newViewer.addEventListener('selectionChange', this.reactEventOnMoleculeViewerBind);
}
},
/** @private */
doSelectOrHotTrackSpectrumAssignments: function(assignments, isSelect, autoLoadMolInViewer)
{
var molecule;
var molChildren = [];
for (var i = 0, l = assignments.length; i < l; ++i)
{
var assignment = assignments[i];
if (assignment instanceof Kekule.ChemStructureObject)
{
var currMol = assignment.getRootFragment();
if (currMol)
{
if (!molecule)
molecule = currMol;
else if (molecule !== currMol) // assignment on different molecule, a wrong situation?
continue;
AU.pushUnique(molChildren, assignment);
}
}
}
var molViewer = this.getMoleculeViewer();
if (molecule)
{
if (molViewer.getChemObj() !== molecule && autoLoadMolInViewer)
{
this.doLoadMoleculeInViewer(molecule);
}
}
//console.log(molChildren, isSelect);
if (isSelect)
molViewer.setSelectedObjects(molChildren);
else
molViewer.setHotTrackedObjects(molChildren);
},
/** @private */
doSelectOrHotTrackSpectrumPeaks: function(molObjects, isSelect)
{
var displayedDataSections = this._getDisplayedSpectrumDataSections();
var dataItemsExMap = new Kekule.MapEx();
try
{
var spectrums = [];
var assignmentPairs = [];
// group up the dataItemEx to spectrum-section-dataValue seq
for (var i = 0, l = molObjects.length; i < l; ++i)
{
var dataItemGroup = this._getDataItemsExOfCorrelatedMolObject(molObjects[i], displayedDataSections);
if (dataItemGroup && dataItemGroup.length)
{
for (var j = 0, k = dataItemGroup.length; j < k; ++j)
{
var spectrum = dataItemGroup[j].spectrum;
var dataItemsEx = dataItemsExMap.get(spectrum);
if (!dataItemsEx)
{
spectrums.push(spectrum);
dataItemsEx = [];
dataItemsExMap.set(spectrum, dataItemsEx);
}
AU.pushUnique(dataItemsEx, dataItemGroup[j].dataItemsEx);
assignmentPairs.push({'assignments': [molObjects[i]], 'spectrum': spectrum, 'dataSection': dataItemsEx.section, 'dataValue': dataItemsEx.dataValue});
}
}
}
// hot track or select
var dataItemSet = false;
var spectrumViewer = this.getSpectrumViewer();
for (var i = 0, l = spectrums.length; i < l; ++i)
{
var dataItemsEx = dataItemsExMap.get(spectrums[i]);
var sview = spectrumViewer.getSubView(spectrums[i], Kekule.ChemWidget.Viewer.SpectrumSubView, true);
if (sview)
{
if (isSelect)
sview.setSelectedDataItemsEx(dataItemsEx);
else
sview.setHotTrackedDataItemsEx(dataItemsEx);
dataItemSet = true;
// since now only one hot tracked / selected sub view can be applied, we will exit the loop after setting the first sub view
break;
}
}
if (!dataItemSet) // no actual selected / hot tracked item, clear the spectrum viewer
{
if (isSelect)
spectrumViewer.clearSelectedItems(null, true);
else
spectrumViewer.clearHotTrackedItems(null, true);
}
this._invokeHotTrackOrSelectAssignmentPairEvent(assignmentPairs, isSelect);
}
finally
{
dataItemsExMap.finalize();
}
},
/** @private */
_invokeHotTrackOrSelectAssignmentPairEvent: function(assignmentPairs, isSelect)
{
if (isSelect)
this.invokeEvent('assignmentSelected', {'assignmentDetails': assignmentPairs});
else
this.invokeEvent('assignmentHotTracked', {'assignmentDetails': assignmentPairs});
},
/** @private */
doLoadMoleculeInViewer: function(molecule)
{
if (molecule)
molecule.setVisible(true); // some times the correlated molecule is automatically hidden when loading spectrum data
this.getMoleculeViewer().setChemObj(molecule);
},
/** @private */
doTryLoadSpectrumRefMoleculeInViewer: function(spectrumViewerRootObj)
{
if (!this.getSpectrumViewer() || !this.getMoleculeViewer())
return;
if (/*spectrumViewerRootObj &&*/ this.getAutoLoadCorrelatedMolecule())
{
var spectrums = this._getLoadedSpectrums(spectrumViewerRootObj) || [];
var molecule = null;
for (var i = 0, l = spectrums.length; i < l; ++i)
{
var mols = this._getRefMoleculesOfSpectrum(spectrums[i]);
if (mols.length)
{
molecule = mols[0];
break;
}
}
// if (molecule) // enable auto clear molecule viewer
{
this.doLoadMoleculeInViewer(molecule);
}
}
},
/** @private */
reactLoadEventOnSpectrumViewer: function(e)
{
if (this.getEnabled())
this.doTryLoadSpectrumRefMoleculeInViewer(e.obj);
},
/** @private */
reactEventOnSpectrumViewer: function(e)
{
if (!this.getEnabled())
return;
if (!this.getFromSpectrumToMolecule())
return;
if (this._isReactingSpectrumViewerEvent) // avoid loop events
return;
this._isReactingSpectrumViewerEvent = true;
try
{
var eventName = e.name;
var dataItems = e.dataItems || [];
var assignmentItems = [];
var assignmentPairs = [];
for (var i = 0, l = dataItems.length; i < l; ++i)
{
var section = dataItems[i].section;
if (section.isPeakSection()) // now only handles peak assignments
{
var dataValue = dataItems[i].dataValue;
/*
var dataDetail = section.getExtraInfoOf(dataValue);
if (dataDetail && (dataDetail.getAssignments || dataDetail.assignments))
{
var assignments = dataDetail.getAssignments() || dataDetail.assignments;
if (assignments)
{
AU.pushUnique(assignmentItems, assignments);
}
}
*/
var assignments = this._getSpectrumDataItemAssignments(section, dataValue);
if (assignments)
{
AU.pushUnique(assignmentItems, assignments);
assignmentPairs.push({'spectrum': section.getParentSpectrum(), 'dataSection': section, 'dataValue': dataValue, 'assignments': assignments});
}
}
}
var isSelect = eventName === 'spectrumDataSelectionChange';
this.doSelectOrHotTrackSpectrumAssignments(assignmentItems, isSelect, this.getAutoLoadCorrelatedMolecule());
this._invokeHotTrackOrSelectAssignmentPairEvent(assignmentPairs, isSelect);
}
finally
{
this._isReactingSpectrumViewerEvent = false;
}
},
/** @private */
reactEventOnMoleculeViewer: function(e)
{
if (!this.getEnabled())
return;
if (!this.getFromMoleculeToSpectrum())
return;
if (this._isReactingMoleculeViewerEvent)
return;
this._isReactingMoleculeViewerEvent = true;
try
{
var eventName = e.name;
var objects = e.objects || [];
this.doSelectOrHotTrackSpectrumPeaks(objects, eventName === 'selectionChange');
}
finally
{
this._isReactingMoleculeViewerEvent = false;
}
}
});
/** @private */
Kekule.ChemWidget.SpectrumViewUtils = {
getZoomDirectionsFromEvent: function(htmlEvent, spectrumViewConfigs)
{
var KU = Kekule.Widget.KeyboardUtils;
var modifierKeyParams = KU.getKeyParamsFromEvent(htmlEvent, true);
var directions;
if (KU.matchKeyParams(modifierKeyParams, KU.createModifierKeyParamsFromArray(spectrumViewConfigs.getSpectrumZoomSecondaryModifierKeys()), true))
directions = ['y'];
else if (KU.matchKeyParams(modifierKeyParams, KU.createModifierKeyParamsFromArray(spectrumViewConfigs.getSpectrumZoomBothModifierKeys()), true))
directions = ['x', 'y'];
else
directions = ['x'];
return directions;
}
};
});
})();