kekule
Version:
Open source JavaScript toolkit for chemoinformatics
1,312 lines (1,280 loc) • 82.5 kB
JavaScript
/*
* requires /lan/classes.js
* requires /utils/kekule.utils.js
* requires /utils/kekule.textHelper.js
* requires /core/kekule.common.js
* requires /core/kekule.metrics.js
* requires /spectrum/kekule.spectrum.core.js
* requires /io/kekule.io.js
* requires /io/jcamp/kekule.io.jcamp.js
* requires /localization
*/
(function(){
"use strict";
var AU = Kekule.ArrayUtils;
var Jcamp = Kekule.IO.Jcamp;
var KS = Kekule.Spectroscopy;
var KU = Kekule.Unit;
var KSType = Kekule.Spectroscopy.SpectrumType;
/**
* Enumeration of the data style in JCAMP DX.
* @enum
*/
Kekule.IO.Jcamp.SpectrumDataStorageStyle = {
/** Use the classic XYData/XYPoints/Peak Table labels. */
CLASSIC: 1,
/** Use the NTuple format. */
NTUPLE: 2,
/**
* Use classic labels when there is only one section in spectrum,
* and use NTuple when there are multiple sections.
*/
SMART: 0
};
/*
* Default options to read/write JCAMP-DX format data.
* @object
*/
Kekule.globalOptions.add('IO.jcamp', {
outputDxVersion: '5.00',
dxDataStorageStyle: Kekule.IO.Jcamp.SpectrumDataStorageStyle.SMART,
dxDataAllowedLoadingErrorRatio: 0.001, // allow 0.1% error when loading data from JCAMP-DX format
dxDataAllowedSavingErrorRatio: 0.0001, // allow 0.01% error when saving data to JCAMP-DX format
dxDataPreferredOrdinateScaledRange: {min: -32767, max: 32767},
dxDataAsdfTableOutputForm: Jcamp.AsdfForm.DIF_DUP, // default output form of ASDF data table
useDxMinMaxValueAsDisplayRange: false, // whether regard the MINX/Y-MAXX/Y as the display range of all spectrum in file
disablePeakAssignmentReading: false,
disablePeakAssignmentWriting: false,
autoHiddenSpectrumRefMolecule: true // whether hide the ref molecule of spectrum automatically and display only the spectrum itself
});
/**
* Some constants for JCAMP-DX format data.
* @object
* @ignore
*/
Kekule.IO.Jcamp.DxConsts = {
CANDIDATE_INDEPENDENT_VAR_SYMBOLS: ['X', 'R', 'U', 'V', 'K', 'P'],
CANDIDATE_DEPENDENT_VAR_SYMBOLS: ['Y', 'Z', 'A', 'B', 'C', 'D']
};
/**
* A helper class for JCAMP-DX formats.
* @class
*/
Kekule.IO.Jcamp.DxUtils = {
/* @private */
_spectrumTypeMap: [ // each item is [JcampSpectrumNameKeyPart, KekuleSpectrumType]
// (since the JCAMP DATATYPE value various a lot, here we can only handle the key part of it)
['INFRARED', KSType.IR],
['NMR', KSType.NMR],
['MASS', KSType.MS],
['ION MOBILITY', KSType.IMS],
['IMS', KSType.IMS],
['RAMAN', KSType.RAMAN],
['UV', KSType.UV_VIS],
['VIS', KSType.UV_VIS],
['', KSType.GENERAL]
],
/** @private */
_dxUnitToMetricsObjMap: {
// general
'ARBITARY UNITS': KU.Arbitrary.ARBITRARY,
'ARBITARY UNIT': KU.Arbitrary.ARBITRARY,
'SECONDS': KU.Time.SECOND,
'SECOND': KU.Time.SECOND,
'MILLISECONDS': KU.Time.MILLISECOND,
'MICROSECONDS': KU.Time.MICROSECOND,
'NANOSECONDS': KU.Time.NANOSECOND,
'PPM': KU.Dimensionless.PARTS_PER_MILLION,
// NMR
'HZ': KU.Frequency.HERTZ,
// IR
'1/CM': KU.WaveNumber.RECIPROCAL_CENTIMETER,
'MICROMETERS': KU.Length.MICROMETER,
'NANOMETERS': KU.Length.NANOMETER,
'TRANSMITTANCE': KU.OpticalTransmittance.TRANSMITTANCE_PERCENT,
'REFLECTANCE': KU.OpticalReflectance.REFLECTANCE,
'ABSORBANCE': KU.OpticalAbsorbance.ABSORBANCE,
'KUBELKA-MUNK': KU.OpticalKubelkaMunk.KUBELKA_MUNK,
// MS
'CHANNEL NUMBER': null,
'COUNTS': KU.Misc.MS_COUNT,
'COUNT': KU.Misc.MS_COUNT,
'M/Z': KU.SpectrumMS.MS_MASS_CHARGE_RATIO,
'RELATIVE ABUNDANCE ': KU.SpectrumMS.MS_RELATIVE_ABUNDANCE,
// Other
'MICROAMPERES': KU.ElectricCurrent.MICROAMPERE,
'NANOAMPERES': KU.ElectricCurrent.NANOAMPERE,
'PICOAMPERES': KU.ElectricCurrent.PICOAMPERE
},
/**
* Returns the corresponding spectrum type for a JCAMP-DX datatype value.
* @param {String} jcampType
* @returns {String}
*/
jcampSpectrumTypeToKekule: function(jcampType)
{
var map = Jcamp.DxUtils._spectrumTypeMap;
var jType = (jcampType || '').toUpperCase();
var result;
for (var i = 0, l = map.length; i < l; ++i)
{
if (jType.indexOf(map[i][0]) >= 0)
{
result = map[i][1];
break;
}
}
if (!result) // not found, use the original type name directly
result = jType;
return result;
},
/**
* Returns the core part of JCAMP-DX datatype value for a Kekule spectrum type.
* @param {String} spectrumType
* @returns {String}
*/
kekuleSpectrumTypeToJcampCoreName: function(spectrumType)
{
var map = Jcamp.DxUtils._spectrumTypeMap;
var result;
for (var i = 0, l = map.length; i < l; ++i)
{
if (spectrumType === map[i][1])
{
result = map[i][0];
break;
}
}
if (!result) // not found, use the original type name directly
result = jType;
return result;
},
/**
* Returns a suitable unit string registered in metrics system of Kekule.js.
* @param {String} unitStr Unit label stored in DX file.
* @returns {String}
*/
dxUnitToMetricsUnitSymbol: function(unitStr)
{
if (!unitStr)
return '';
var map = Jcamp.DxUtils._dxUnitToMetricsObjMap;
var metricsObj = map[unitStr];
var result = metricsObj && metricsObj.symbol;
if (!result) // not found in map, return the raw string, but since in JCAMP all are uppercased, we need to convert to lower here
result = unitStr.toLowerCase().upperFirst();
return result;
},
/**
* Returns a unit symbol for JCAMP-DX data.
* @param {String} symbol
* @returns {String}
*/
mertricsUnitSymbolToDxUnit: function(symbol)
{
if (!symbol)
return '';
var map = Jcamp.DxUtils._dxUnitToMetricsObjMap;
var names = Kekule.ObjUtils.getOwnedFieldNames(map);
for (var i = 0, l = names.length; i < l; ++i)
{
var name = names[i];
var unitObj = map[name];
if (unitObj && unitObj.symbol === symbol)
{
return name;
}
}
return symbol.toUpperCase();
},
/** @private */
calcNumFactorForRange: function(minValue, maxValue, allowedErrorRatio, preferredScaleRangeMin, preferredScaleRangeMax)
{
return Jcamp.Utils.calcNumFactorForRange(minValue, maxValue, allowedErrorRatio, preferredScaleRangeMin, preferredScaleRangeMax);
}
};
// create some ldr info for DX format
Kekule.IO.Jcamp.LabelTypeInfos.createInfos([
// IR
//['RESOLUTION', Jcamp.ValueType.STRING], // already defined in jcamp.base.js
// MS
['SPECTROMETER TYPE', Jcamp.ValueType.STRING, null, Jcamp.LabelCategory.PARAMTER, KS.SpectrumType.MS],
['INLET', Jcamp.ValueType.STRING, null, Jcamp.LabelCategory.PARAMTER, KS.SpectrumType.MS],
['IONIZATION MODE', Jcamp.ValueType.STRING, null, Jcamp.LabelCategory.PARAMTER, KS.SpectrumType.MS],
// NMR
['OBSERVE FREQUENCY', Jcamp.ValueType.AFFN, null, Jcamp.LabelCategory.PARAMTER, KS.SpectrumType.NMR], // in MHz
['OBSERVE NUCLEUS', Jcamp.ValueType.STRING, null, Jcamp.LabelCategory.PARAMTER, KS.SpectrumType.NMR],
['SOLVENT REFERENCE', Jcamp.ValueType.AFFN, null, Jcamp.LabelCategory.PARAMTER, KS.SpectrumType.NMR], // Solvent lock signal in ppm
['DELAY', Jcamp.ValueType.SIMPLE_AFFN_GROUP, null, Jcamp.LabelCategory.PARAMTER, KS.SpectrumType.NMR],
['ACQUISITION MODE', Jcamp.ValueType.STRING, null, Jcamp.LabelCategory.PARAMTER, KS.SpectrumType.NMR],
// IMS
['IMS PRESSURE', Jcamp.ValueType.AFFN, null, Jcamp.LabelCategory.PARAMTER, KS.SpectrumType.IMS], // in kilopascal
['CARRIER GAS', Jcamp.ValueType.STRING, null, Jcamp.LabelCategory.PARAMTER, KS.SpectrumType.IMS],
['DRIFT GAS', Jcamp.ValueType.STRING, null, Jcamp.LabelCategory.PARAMTER, KS.SpectrumType.IMS],
['ELECTRIC FIELD', Jcamp.ValueType.SIMPLE_AFFN_GROUP, null, Jcamp.LabelCategory.PARAMTER, KS.SpectrumType.IMS], // actually a (AFFN, AFFN) pair
['ION POLARITY', Jcamp.ValueType.STRING, null, Jcamp.LabelCategory.PARAMTER, KS.SpectrumType.IMS],
['IONIZATION MODE', Jcamp.ValueType.STRING, null, Jcamp.LabelCategory.PARAMTER, KS.SpectrumType.IMS],
['IMS TEMPERATURE', Jcamp.ValueType.SIMPLE_AFFN_GROUP, null, Jcamp.LabelCategory.PARAMTER, KS.SpectrumType.IMS], // actually (AFFN[, AFFN])
['SHUTTER OPENING TIME', Jcamp.ValueType.AFFN, null, Jcamp.LabelCategory.PARAMTER, KS.SpectrumType.IMS],
// chromatography/ms
['MASS ANALYSER', Jcamp.ValueType.STRING, null, Jcamp.LabelCategory.PARAMTER, KS.SpectrumType.CHROMATOGRAPHY],
['TANDEM SCANNING METHOD', Jcamp.ValueType.STRING, null, Jcamp.LabelCategory.PARAMTER, KS.SpectrumType.CHROMATOGRAPHY], // actually (STRING, {AFFN})
['INTERFACE', Jcamp.ValueType.STRING, null, Jcamp.LabelCategory.PARAMTER, KS.SpectrumType.CHROMATOGRAPHY],
['CHROMATOGRAPHY TYPE', Jcamp.ValueType.STRING, null, Jcamp.LabelCategory.PARAMTER, KS.SpectrumType.CHROMATOGRAPHY],
['CHROMATOGRAPHY SOLVENTS', Jcamp.ValueType.STRING, null, Jcamp.LabelCategory.PARAMTER, KS.SpectrumType.CHROMATOGRAPHY], // actually lines of (N,C,U)
['ADDITIVES', Jcamp.ValueType.STRING, null, Jcamp.LabelCategory.PARAMTER, KS.SpectrumType.CHROMATOGRAPHY], // actually lines of (N,C,U)
['DIMENSIONALITY', Jcamp.ValueType.AFFN, null, Jcamp.LabelCategory.PARAMTER, KS.SpectrumType.CHROMATOGRAPHY]
]);
/**
* Reader for reading a DX data block of JCAMP document tree.
* @class
* @augments Kekule.IO.Jcamp.DataBlockReader
*/
Kekule.IO.Jcamp.DxDataBlockReader = Class.create(Kekule.IO.Jcamp.DataBlockReader,
/** @lends Kekule.IO.Jcamp.DxDataBlockReader# */
{
/** @private */
CLASS_NAME: 'Kekule.IO.Jcamp.DxDataBlockReader',
/** @private */
initProperties: function()
{
this.defineProp('currDataClass', {'dataType': DataType.STRING, 'setter': false, 'serializable': false, 'scope': Class.PropertyScope.PRIVATE});
this.defineProp('currVarInfos', {'dataType': DataType.HASH, 'setter': false, 'serializable': false, 'scope': Class.PropertyScope.PRIVATE});
this.defineProp('currNTuplesInfos', {'dataType': DataType.HASH, 'setter': false, 'serializable': false, 'scope': Class.PropertyScope.PRIVATE});
this.defineProp('currNTuplesVarInfos', {'dataType': DataType.HASH, 'setter': false, 'serializable': false, 'scope': Class.PropertyScope.PRIVATE,
'getter': function()
{
var i = this.getCurrNTuplesInfos();
return i && i.varInfos;
}
});
this.defineProp('currNTuplesVarRawInfos', {'dataType': DataType.HASH, 'setter': false, 'serializable': false, 'scope': Class.PropertyScope.PRIVATE,
'getter': function()
{
var i = this.getCurrNTuplesInfos();
return i && i.varRawInfos;
}
});
this.defineProp('dataSectionsWithPeakAssignments', {'dataType': DataType.ARRAY, 'setter': false, 'serializable': false, 'scope': Class.PropertyScope.PRIVATE});
},
/** @ignore */
initPropValues: function()
{
this.tryApplySuper('initPropValues');
this._initCurrVarInfos();
},
/** @private */
_initCurrVarInfos: function()
{
this.setPropStoreFieldValue('currVarInfos', {});
this.setPropStoreFieldValue('currNTuplesVarInfos', {});
},
/** @ignore */
_initLdrHandlers: function()
{
var map = this.tryApplySuper('_initLdrHandlers');
map[Jcamp.Consts.LABEL_BLOCK_BEGIN] = this.doStoreSpectrumTitleLdr.bind(this);
map['DATATYPE'] = this.doStoreSpectrumDataTypeLdr.bind(this, 'DATATYPE');
map['DATACLASS'] = this.doStoreSpectrumDataClassLdr.bind(this);
var doStoreSpectrumDataLdrBind = this.doStoreSpectrumDataLdr.bind(this);
var dataTableLdrNames = this._getDataTableLdrNames();
for (var i = 0, l = dataTableLdrNames.length; i < l; ++i)
map[dataTableLdrNames[i]] = doStoreSpectrumDataLdrBind;
/*
map['PEAK TABLE'] = this.doStoreSpectrumDataLdr.bind(this, 'PEAK TABLE');
map['XYDATA'] = this.doStoreSpectrumDataLdr.bind(this, 'XYDATA');
*/
var varDefLdrNames = this._getDataTableVarDefLdrNames();
var doStoreDataVarInfoLdrBind = this.doStoreDataVarInfoLdr.bind(this);
for (var i = 0, l = varDefLdrNames.length; i < l; ++i)
map[varDefLdrNames[i]] = doStoreDataVarInfoLdrBind;
var ntuplesVarDefLdrNames = this._getNTuplesDefinitionLdrNames();
var doStoreNTuplesAttributeLdrBind = this.doStoreNTuplesAttributeLdr.bind(this)
for (var i = 0, l = ntuplesVarDefLdrNames.length; i < l; ++i)
map[ntuplesVarDefLdrNames[i]] = doStoreNTuplesAttributeLdrBind;
map['DATATABLE'] = this.doStoreNTuplesDataLdr.bind(this); // NTuples data table
var spectrumParamLdrNames = this._getSpectrumParamLdrNames();
var spectrumNames = Kekule.ObjUtils.getOwnedFieldNames(spectrumParamLdrNames);
for (var i = 0, ii = spectrumNames.length; i < ii; ++i)
{
var spectrumName = spectrumNames[i];
var doStoreSpectrumParamLdrBindWithName = this.doStoreSpectrumParamLdr.bind(this, spectrumName);
var ldrNames = spectrumParamLdrNames[spectrumName];
for (var j = 0, jj = ldrNames.length; j < jj; ++j)
{
map[ldrNames[j]] = doStoreSpectrumParamLdrBindWithName;
}
}
return map;
},
/** @prviate */
_getDataTableVarDefLdrNames: function()
{
return ['XUNITS', 'FIRSTX', 'LASTX', 'MAXX', 'MINX', 'XFACTOR', 'YUNITS', 'MAXY', 'MINY', 'FIRSTY', 'LASTY', 'YFACTOR', 'NPOINTS', /*'RESOLUTION',*/ 'DELTAX',
'XLABEL', 'YLABEL',
'RUNITS', 'AUNITS', 'FIRSTR', 'LASTR', 'DELTAR', 'MAXA', 'MINA', 'RFACTOR', 'AFACTOR', 'FIRSTA', 'ALIAS', 'ZPD'];
},
/** @private */
_getNTuplesDefinitionLdrNames: function()
{
return ['NTUPLES', /*'VAR_NAME'*/'VARNAME', 'SYMBOL', /*'VAR_TYPE'*/'VARTYPE', /*'VAR_FORM'*/'VARFORM', /*'VAR_DIM'*/'VARDIM', 'UNITS', 'FIRST', 'LAST', 'MIN', 'MAX', 'FACTOR',
'PAGE', 'POINTS', /*'NPOINTS'?,*/ /* 'CASNAME',*/
/*'END NTUPLES'*/'ENDNTUPLES'];
},
/** @private */
_getDataTableLdrNames: function()
{
return ['XYDATA', 'XYPOINTS', 'PEAKTABLE', 'PEAKASSIGNMENTS', 'ASSIGNMENTS', 'RADATA'];
},
/** @private */
_isDataTableLabelName: function(labelName)
{
return this._getDataTableLdrNames().indexOf(labelName) >= 0;
},
/** @private */
_getSpectrumParamLdrNames: function()
{
return {
'ir': ['RESOLUTION'],
'uv_vis': ['RESOLUTION'],
'nmr': ['.OBSERVEFREQUENCY', '.OBSERVENUCLEUS', '.SOLVENTREFERENCE', '.DELAY', '.ACQUISITIONMODE'],
'ms': ['.SPECTROMETERTYPE', '.INLET', '.IONIZATIONMODE'],
'ims': ['.IMSPRESSURE', '.CARRIERGAS', '.DRIFTGAS', '.ELECTRICFIELD', '.IONPOLARITY', '.IONIZATIONMODE', '.IMSTEMPERATURE', '.SHUTTEROPENINGTIME'],
'chromatography': ['.MASSANALYSER', '.TANDEMSCANNINGMETHOD', '.INTERFACE', '.CHROMATOGRAPHYTYPE', '.CHROMATOGRAPHYSOLVENTS', '.ADDITIVES', '.DIMENSIONALITY']
}
},
/** @ignore */
getLdrNamePrefixForInfoField: function(labelName, labelType, chemObj)
{
var result = this.tryApplySuper('getLdrNamePrefixForInfoField', [labelName, labelType, chemObj]);
if (labelType === Jcamp.LabelType.SPECIFIC) // data specified label
{
// the data type should already be stored in chemObj
var spectrumType = chemObj.getSpectrumType();
if (spectrumType)
result += spectrumType + '.';
}
return result;
},
/** @ignore */
getPendingLdrNames: function()
{
var names = [
'RESOLUTION' // need to detect the unit of XAxis
];
return this.tryApplySuper('getPendingLdrNames').concat(names);
},
/** @private */
doStoreSpectrumTitleLdr: function(ldr, block, chemObj, preferredInfoPropName)
{
chemObj.setTitle(Jcamp.LdrValueParserCoder.parseValue(ldr));
},
/** @private */
doStoreDataVarInfoLdr: function(ldr, block, chemObj, preferredInfoPropName)
{
var info = this.getCurrVarInfos();
info[ldr.labelName] = Jcamp.LdrValueParserCoder.parseValue(ldr);
},
/** @private */
doStoreNTuplesAttributeLdr: function(ldr, block, chemObj, preferredInfoPropName)
{
var info = this.getCurrNTuplesInfos();
var value = Jcamp.LdrValueParserCoder.parseValue(ldr);
if (ldr.labelName === 'ENDNTUPLES') // ntuples end, empty the var infos
{
if (value !== info.name) // begin/end name not same
Kekule.error(Kekule.$L('ErrorMsg.JCAMP_NTUPLES_BEGIN_END_NAME_NOT_MATCH', info.value, value));
this.setPropStoreFieldValue('currNTuplesInfos', null); // NTuples end, empty the info object
}
else if (ldr.labelName === 'NTUPLES') // ntuples head
{
this.setPropStoreFieldValue('currNTuplesInfos', {'name': value, 'varRawInfos': {}}); // create a new NTuples info object
}
else if (ldr.labelName === 'PAGE') // a new page
{
this._doStoreNTuplesPageAttributeLdr(ldr);
}
else
{
info.varRawInfos[ldr.labelName] = value;
}
},
/** @private */
_doStoreNTuplesPageAttributeLdr: function(ldr)
{
var info = this.getCurrNTuplesInfos();
// analysis PageVar=Value pattern
var s = ldr.valueLines.join('');
var parts = s.split('=');
if (parts.length !== 2)
Kekule.error(Kekule.$L('ErrorMsg.JCAMP_NTUPLES_PAGE_DECLARATION_FORMAT_ERROR', s));
else
{
var varSymbol = parts[0].trim();
var sValue = parts[1].trim();
info[ldr.labelName] = {'varSymbol': varSymbol, 'sValue': sValue};
}
},
/** @private */
doStoreSpectrumDataTypeLdr: function(ldrLabelName, ldr, block, chemObj, preferredInfoPropName)
{
var ldrValue = Jcamp.LdrValueParserCoder.parseValue(ldr);
if (ldrValue && ldrValue.toUpperCase) // a normal string value
{
var stype = Jcamp.DxUtils.jcampSpectrumTypeToKekule(ldrValue);
/*
var SType = Kekule.Spectroscopy.SpectrumType;
ldrValue = ldrValue.toUpperCase();
var stype = (ldrValue.indexOf('INFRARED') >= 0)? SType.IR:
(ldrValue.indexOf('NMR') >= 0)? SType.NMR:
(ldrValue.indexOf('MASS') >= 0)? SType.MS:
(ldrValue.indexOf('ION MOBILITY') >= 0 || ldrValue.indexOf('IMS') >= 0)? SType.IMS:
(ldrValue.indexOf('RAMAN') >= 0)? SType.RAMAN:
(ldrValue.indexOf('UV') >= 0)? SType.UV_VIS:
SType.GENERAL;
*/
chemObj.setSpectrumType(stype);
}
// Removed: also save the full LDR value to info property
// this.doStoreLdrToChemObjInfoProp(ldrLabelName, ldr, block, chemObj, preferredInfoPropName);
},
/** @private */
doStoreSpectrumDataClassLdr: function(ldr, block, chemObj, preferredInfoPropName)
{
var ldrValue = Jcamp.LdrValueParserCoder.parseValue(ldr);
this.setPropStoreFieldValue('currDataClass', ldrValue); // record the data class value, for analyis the spectrum data later
},
/** @private */
doStoreSpectrumDataLdr: function(ldr, block, chemObj, preferredInfoPropName)
{
//console.log('doStoreSpectrumDataLdr', ldr);
// check whether the DATA_CLASS matches LDR label name, ensure this is the LDR containing the spectrum data
if (this._isSpectrumDataLdr(ldr, block, chemObj))
{
if (this._isDataTableLabelName(ldr.labelName))
{
var dataValue = Jcamp.LdrValueParserCoder.parseValue(ldr, {doValueCheck: true});
//console.log(ldr, dataValue);
/*
var varFormat = dataValue.format;
var formatDetail = Jcamp.Utils.getDataTableFormatDetails(varFormat);
*/
var formatDetail = dataValue.formatDetail;
var varSymbols = formatDetail.vars;
// retrieve var information, including first/last range and factor
var varInfos = this._retrieveSpectrumDataVarInfos(varSymbols, block, chemObj);
/*
var varSymbolInc = formatDetail.varInc;
var varSymbolLoop = formatDetail.varLoop;
varInfos._bySymbol[varSymbolInc].dependency = Kekule.VarDependency.INDEPENDENT;
*/
// the first var should be independent
varInfos[0].dependency = Kekule.VarDependency.INDEPENDENT;
// rest of them should be dependent
for (var i = 1, l = varInfos.length; i < l; ++i)
{
varInfos[i].dependency = Kekule.VarDependency.DEPENDENT;
}
var varDefinitions = this._createSpectrumVariableDefinitions(varInfos); // ensure X before Y
//console.log(formatDetail);
//console.log(varInfos);
//console.log(dataValue);
//var spectrumData = new Kekule.Spectroscopy.SpectrumData(null, varDefinitions);
var spectrumData = chemObj.getData();
spectrumData.setVariables(varDefinitions);
var isContinuous = (ldr.labelName.indexOf('PEAK') < 0) && (ldr.labelName.indexOf('ASSIGNMENT') < 0);
var isPeakAssignments = (ldr.labelName.indexOf('ASSIGNMENT') >= 0);
spectrumData.setMode(isContinuous? Kekule.Spectroscopy.DataMode.CONTINUOUS: Kekule.Spectroscopy.DataMode.PEAK);
var spectrumDataSection = this._createSpectrumDataSectionByFormat(formatDetail, dataValue.values, varInfos, spectrumData, isPeakAssignments, this.getCurrOptions());
/*
if (spectrumData)
chemObj.setData(spectrumData);
*/
}
}
else // use the default approach
return this.doStoreLdrToChemObjInfoProp(ldr.labelName, ldr, block, chemObj);
},
/** @private */
_retrieveSpectrumDataVarInfos: function(varSymbols, block, chemObj)
{
var result = [];
result._symbols = [];
result._bySymbol = {};
var infoStorage = this.getCurrVarInfos();
//console.log(infoStorage);
var getVarInfoValue = function(name)
{
return infoStorage[name];
}
// since the data table LDR are handled last, information LDR now has already be stored in chemObj
for (var i = 0, l = varSymbols.length; i < l; ++i)
{
var varSymbol = varSymbols[i];
var info = {
'symbol': varSymbol,
'units': getVarInfoValue(varSymbol + 'UNITS'),
'firstValue': getVarInfoValue('FIRST' + varSymbol),
'lastValue': getVarInfoValue('LAST' + varSymbol),
'maxValue': getVarInfoValue('MAX' + varSymbol),
'minValue': getVarInfoValue('MIN' + varSymbol),
'factor': getVarInfoValue(varSymbol + 'FACTOR')
};
if (!info.units && i === 2 && varSymbol === 'W') // W in XYW peak table or XYWA peak assignments, its units follows X
info.units = getVarInfoValue('XUNITS');
result.push(info);
result._symbols.push(varSymbol);
result._bySymbol[varSymbol] = result[result.length - 1];
}
return result;
},
/** @private */
_createSpectrumVariableDefinitions: function(varInfos)
{
var result = [];
var options = this.getCurrOptions();
for (var i = 0, l = varInfos.length; i < l; ++i)
{
var info = varInfos[i];
var vType = info.varType;
if (vType) // VAR_TYPE in NTUPLES
{
if (vType === 'DEPENDENT')
info.dependency = Kekule.VarDependency.DEPENDENT;
else if (vType === 'INDEPENDENT')
info.dependency = Kekule.VarDependency.INDEPENDENT;
else if (vType === 'PAGE') // special page var in NTuples, ignore and do not create instance of VarDefinition
continue;
}
var def = new Kekule.Spectroscopy.SpectrumVarDefinition({
'name': (info.name || '').toLowerCase(), // LDR values are often uppercased
'symbol': info.symbol,
'unit': Jcamp.DxUtils.dxUnitToMetricsUnitSymbol(info.units),
//'minValue': info.minValue,
//'maxValue': info.maxValue,
'dependency': Kekule.ObjUtils.notUnset(info.dependency)? info.dependency: Kekule.VarDependency.DEPENDENT
});
if (Kekule.ObjUtils.notUnset(info.minValue) && Kekule.ObjUtils.notUnset(info.maxValue) && options.useDxMinMaxValueAsDisplayRange)
{
if (info.minValue !== info.maxValue) // some times these value are all 0, means unavailable values?
def.setInfoValue('displayRange', {'min': info.minValue, 'max': info.maxValue});
}
result.push(def);
}
return result;
},
/** @private */
doStoreNTuplesDataLdr: function(ldr, block, chemObj)
{
//var varInfos = this._retrieveNTupleVariableInfos();
//console.log(varInfos);
var ntuplesInfos = this.getCurrNTuplesInfos();
var varInfos = this.getCurrNTuplesVarInfos();
if (!varInfos)
{
varInfos = this._retrieveNTupleVariableInfos();
ntuplesInfos.varInfos = varInfos;
}
var spectrumData = ntuplesInfos.spectrumData;
if (!spectrumData) // create new spectrum data instance when necessary
{
var varDefinitions = this._createSpectrumVariableDefinitions(varInfos);
//spectrumData = new Kekule.Spectroscopy.SpectrumData(null, varDefinitions);
//chemObj.setData(spectrumData);
var spectrumData = chemObj.getData();
spectrumData.setVariables(varDefinitions);
ntuplesInfos.spectrumData = spectrumData;
}
//console.log(varDefinitions);
var dataValue = Jcamp.LdrValueParserCoder.parseValue(ldr, {doValueCheck: true});
//var varFormat = dataValue.format;
//var formatDetail = Jcamp.Utils.getDataTableFormatAndPlotDetails(varFormat);
var formatDetail = dataValue.formatDetail;
var plotDescriptor = formatDetail.plotDescriptor || '';
var localVarSymbols = formatDetail.vars;
var isContinuous = (plotDescriptor.indexOf('PEAK') < 0) && (plotDescriptor.indexOf('ASSIGNMENT') < 0);
var isPeakAssignments = (plotDescriptor.indexOf('ASSIGNMENT') >= 0);
var dataMode = isContinuous? Kekule.Spectroscopy.DataMode.CONTINUOUS: Kekule.Spectroscopy.DataMode.PEAK;
var spectrumDataSection = this._createSpectrumDataSectionByFormat(formatDetail, dataValue.values, varInfos, spectrumData, isPeakAssignments, this.getCurrOptions());
spectrumDataSection.setMode(dataMode);
var pageInfo = ntuplesInfos['PAGE'];
spectrumDataSection.setName(pageInfo.varSymbol + ': ' + pageInfo.sValue);
},
/** @private */
_retrieveNTupleVariableInfos: function()
{
var result = [];
result._bySymbol = {};
result._symbols = [];
var infoStorage = this.getCurrNTuplesVarRawInfos();
//console.log(infoStorage);
var getStorageValue = function(name)
{
return infoStorage[name];
}
var fillVarInfo = function(ldrKey, key, infoObj)
{
var standardizedLdrKey = Jcamp.Utils.standardizeLdrLabelName(ldrKey);
var values = AU.toArray(getStorageValue(standardizedLdrKey));
for (var i = 0, l = values.length; i < l; ++i)
{
if (!infoObj[i])
infoObj[i] = {};
infoObj[i][key] = values[i];
if (ldrKey === 'SYMBOL')
result._symbols[i] = values[i];
}
return infoObj;
}
fillVarInfo('VAR_NAME', 'name', result);
fillVarInfo('SYMBOL', 'symbol', result);
fillVarInfo('VAR_TYPE', 'varType', result);
fillVarInfo('VAR_FORM', 'varForm', result);
fillVarInfo('VAR_DIM', 'varDim', result);
fillVarInfo('UNITS', 'units', result);
fillVarInfo('FIRST', 'firstValue', result);
fillVarInfo('LAST', 'lastValue', result);
fillVarInfo('MIN', 'minValue', result);
fillVarInfo('MAX', 'maxValue', result);
fillVarInfo('FACTOR', 'factor', result);
for (var i = 0, l = result.length; i < l; ++i)
{
if (result[i].symbol)
result._bySymbol[result[i].symbol] = result[i];
}
return result;
},
/* @private */
/*
_ensureCreatingSpectrumNTuplesVariableDefinitions: function(spectrumData)
{
if (spectrumData.getVariableCount() <= 0)
{
var varDefs = this._createSpectrumVariableDefinitions(this._retrieveNTupleVariableInfos());
spectrumData.setVariables(varDefs);
}
return spectrumData.getVariables();
},
*/
/** @private */
doStoreSpectrumParamLdr: function(spectrumName, ldr, block, chemObj, preferredInfoPropName) // save the spectrum specified key params
{
var spectrumType = spectrumName.toUpperCase();
// first ensure the spectrumName matches current spectrum type, if not, bypass this LDR
if (chemObj.getSpectrumType() && chemObj.getSpectrumType() !== spectrumType)
return;
else
{
var labelName = ldr.labelName;
var value = Jcamp.LdrValueParserCoder.parseValue(ldr);
var defaultHandler = function(newValue)
{
/*
var name = labelName;
if (name.startsWith(Jcamp.Consts.SPECIFIC_LABEL_PREFIX))
name = name.substr(Jcamp.Consts.SPECIFIC_LABEL_PREFIX.length);
chemObj.setParameter(name.toLowerCase(), value);
*/
chemObj.setParameter(preferredInfoPropName, newValue);
};
if (spectrumType === KS.SpectrumType.IR || spectrumType === KS.SpectrumType.UV_VIS)
{
if (labelName === 'RESOLUTION') // may be a number or R1,X1,; . . . ;Ri,Xi, same unit to X axis
{
var isSingleNum = value.indexOf(Jcamp.Consts.SIMPLE_VALUE_DELIMITER) < 0;
if (isSingleNum)
{
value = parseFloat(value) || value;
// get the independent var unit
var unit;
for (var i = 0, l = chemObj.getVariableCount(); i < l; ++i)
{
var varDef = chemObj.getVariable(i);
if (varDef.isIndependent())
{
var unitSymbol = varDef.getUnit();
var unitObj = unitSymbol && Kekule.Unit.getUnit(unitSymbol);
if (unitObj.category === Kekule.Unit.Length)
{
unit = unitSymbol;
break;
}
}
}
if (unit)
value = Kekule.Scalar.create(value, unit);
}
//chemObj.setParameter('resolution', value);
defaultHandler(value);
}
}
else if (spectrumType === KS.SpectrumType.NMR)
{
if (labelName === '.OBSERVENUCLEUS')
{
var targetNucleus = (value.indexOf('C') >= 0 && value.indexOf('13') >= 0)? KS.SpectrumNMR.TargetNucleus.C13:
(value.indexOf('H') >= 0)? KS.SpectrumNMR.TargetNucleus.H:
value;
//chemObj.setParameter('nucleus', targetNucleus);
defaultHandler(targetNucleus);
}
else if (labelName === '.OBSERVEFREQUENCY')
{
if (Kekule.NumUtils.isNormalNumber(value))
value = Kekule.Scalar.create(value, Kekule.Unit.Frequency.MEGAHERTZ.symbol); // the value is stored in MHz in Jcamp-DX
defaultHandler(value);
//chemObj.setParameter('observeFrequency', Kekule.Scalar.create(value, Kekule.Unit.Frequency.MEGAHERTZ.symbol)); // the value is stored in MHz in Jcamp-DX
}
else if (labelName === '.SOLVENTREFERENCE')
{
if (Kekule.NumUtils.isNormalNumber(value))
value = Kekule.Scalar.create(value, Kekule.Unit.Dimensionless.PARTS_PER_MILLION.symbol);
defaultHandler(value);
// chemObj.setParameter('solventReference', Kekule.Scalar.create(value, Kekule.Unit.Dimensionless.PARTS_PER_MILLION.symbol));
}
else if (labelName === '.DELAY')
{
if (Kekule.NumUtils.isNormalNumber(value))
value = Kekule.Scalar.create(value, Kekule.Unit.Time.MICROSECOND.symbol);
defaultHandler(value);
// chemObj.setParameter('delays', Kekule.Scalar.create(value, Kekule.Unit.Time.MICROSECOND.symbol));
}
/*
else if (labelName === '.ACQUISITIONMODE')
{
chemObj.setParameter('acquisitionMode', value.toLowerCase());
}
*/
else
defaultHandler(value);
}
else if (spectrumType === KS.SpectrumType.MS)
{
defaultHandler(value);
}
else if (spectrumType === KS.SpectrumType.IMS)
{
defaultHandler(value);
}
else
defaultHandler(value);
// TODO: more spectrum type handlers
}
},
/** @private */
_calcActualVarValue: function(dataValue, varInfo)
{
return dataValue * (varInfo.factor || 1);
},
/** @private */
_createSpectrumDataSectionByFormat: function(formatDetail, data, varinfos, parentSpectrumData, isPeakAssignments, options)
{
var result;
if (formatDetail.format === Jcamp.Consts.DATA_VARLIST_FORMAT_XYDATA)
{
result = this._createXyDataFormatSpectrumDataSection(formatDetail, data, varinfos, parentSpectrumData, options);
}
else if (formatDetail.format === Jcamp.Consts.DATA_VARLIST_FORMAT_XYPOINTS)
{
result = this._createXyPointsFormatSpectrumDataSection(formatDetail, data, varinfos, parentSpectrumData, options);
}
else if (formatDetail.format === Jcamp.Consts.DATA_VARLIST_FORMAT_VAR_GROUPS) // may containing peak assignments
{
if (isPeakAssignments)
result = this._createPeakAssignmentsVarGroupFormatSpectrumDataSection(formatDetail, data, varinfos, parentSpectrumData, options);
else
result = this._createVarGroupFormatSpectrumDataSection(formatDetail, data, varinfos, parentSpectrumData, options);
}
//console.log('data', formatDetail, varinfos, result);
//console.log('section', result.calcDataRange('Y'));
return result;
},
/** @private */
_createXyDataFormatSpectrumDataSection: function(formatDetail, dataLines, varInfos, parentSpectrumData, options)
{
var varSymbolInc = formatDetail.varInc;
var varSymbolLoop = formatDetail.varLoop;
/*
varInfos._bySymbol[varSymbolInc].dependency = Kekule.VarDependency.INDEPENDENT;
var varDefinitions = this._createSpectrumVariableDefinitions(varInfos); // ensure X before Y
var result = new Kekule.Spectroscopy.SpectrumData(null, varDefinitions);
*/
var result = parentSpectrumData.createSection(/*varInfos._symbols*/formatDetail.vars, Kekule.Spectroscopy.DataMode.CONTINUOUS);
result.beginUpdate();
try
{
// calc first/lastX from data lines first
var varIncValueRange = {firstValue: this._calcActualVarValue(dataLines[0][0], varInfos._bySymbol[varSymbolInc]), lastValue: null};
if (dataLines.length > 1) // more than one line, calc deltaX and lastX from the last two lines
{
var delta = (dataLines[dataLines.length - 1][0] - dataLines[dataLines.length - 2][0]) / (dataLines[dataLines.length - 2].length - 1);
varIncValueRange.lastValue = this._calcActualVarValue(dataLines[dataLines.length - 1][0] + delta * (dataLines[dataLines.length - 1].length - 2), varInfos._bySymbol[varSymbolInc]);
}
else
{
varIncValueRange.lastValue = varInfos[varSymbolInc].lastValue;
}
// check first/lastX
// console.log('var first/last compare', varIncValueRange.firstValue, varInfos._bySymbol[varSymbolInc].firstValue, varIncValueRange.lastValue, varInfos._bySymbol[varSymbolInc].lastValue);
var allowedError = Math.abs(varIncValueRange.lastValue - varIncValueRange.firstValue) * options.dxDataAllowedLoadingErrorRatio;
if (Jcamp.Utils.compareFloat(varIncValueRange.firstValue, varInfos._bySymbol[varSymbolInc].firstValue, allowedError) !== 0
|| Jcamp.Utils.compareFloat(varIncValueRange.lastValue, varInfos._bySymbol[varSymbolInc].lastValue, allowedError) !== 0)
{
Kekule.error(Kekule.$L('ErrorMsg.JCAMP_DATA_TABLE_VALUE_FIRST_LAST_NOT_MATCH'));
}
// check pass, build the spectrum data
result.setContinuousVarRange(varSymbolInc, varIncValueRange.firstValue, varIncValueRange.lastValue);
for (var i = 0, ii = dataLines.length; i < ii; ++i)
{
var lineValues = dataLines[i];
for (var j = 1, jj = lineValues.length; j < jj; ++j)
{
result.appendData([undefined, this._calcActualVarValue(lineValues[j], varInfos._bySymbol[varSymbolLoop])]); // omit X
}
}
result.setDataSorted(true);
}
finally
{
result.endUpdate();
}
//console.log(varDefinitions, result);
return result;
},
/** @private */
_createXyPointsFormatSpectrumDataSection: function(formatDetail, data, varInfos, parentSpectrumData, options)
{
/*
var varDefinitions = this._createSpectrumVariableDefinitions(varInfos); // ensure X before Y
var result = new Kekule.Spectroscopy.SpectrumData(null, varDefinitions);
*/
var result = parentSpectrumData.createSection(/*varInfos._symbols*/formatDetail.vars);
result.beginUpdate();
try
{
for (var i = 0, l = data.length; i < l; ++i)
{
// each item is a data group, containing values of all variables
var dataValues = data[i];
var actualValues = [];
for (var j = 0, jj = dataValues.length; j < jj; ++j)
{
var v = this._calcActualVarValue(dataValues[j], varInfos[j]);
actualValues.push(v);
}
result.appendData(actualValues);
}
result.setDataSorted(true);
}
finally
{
result.endUpdate();
}
return result;
},
/** @private */
_createVarGroupFormatSpectrumDataSection: function(formatDetail, data, varInfos, parentSpectrumData, options)
{
/*
var varDefinitions = this._createSpectrumVariableDefinitions(varInfos); // ensure X before Y
var result = new Kekule.Spectroscopy.SpectrumData(null, varDefinitions);
*/
var result = parentSpectrumData.createSection(/*varInfos._symbols*/formatDetail.vars);
result.beginUpdate();
try
{
for (var i = 0, l = data.length; i < l; ++i)
{
// each item is a data group, containing values of all variables
var dataValues = data[i];
var actualValues = [];
for (var j = 0, jj = dataValues.length; j < jj; ++j)
{
var v = this._calcActualVarValue(dataValues[j], varInfos[j]);
actualValues.push(v);
}
result.appendData(actualValues);
}
result.setDataSorted(true);
}
finally
{
result.endUpdate();
}
return result;
},
/** @private */
_createPeakAssignmentsVarGroupFormatSpectrumDataSection: function(formatDetail, data, varInfos, parentSpectrumData, options)
{
//console.log(formatDetail, varInfos);
var hasAssignments = false;
// check if there is really assignment var (A) and peak width (W)
// TODO: in NTuple, how to find out the assignment and width var if they are with different symbol?
var normalVarSymbols = formatDetail.vars.slice(0, 2);
var specialVarTypes = [null, null]; // A: assignment, W: peak width, M: multiplicity
if (formatDetail.vars.length > 2) // XYA or XYWA
{
for (var i = 2, l = formatDetail.vars.length; i < l; ++i)
{
var symbolType = formatDetail.vars[i].toUpperCase()[0];
if (['A', 'W', 'M'].indexOf(symbolType) >= 0)
specialVarTypes.push(symbolType);
else
{
specialVarTypes.push(null);
normalVarSymbols.push(formatDetail.vars[i]);
}
}
var hasPeakAssignments = false;
var result = parentSpectrumData.createSection(normalVarSymbols);
result.beginUpdate();
try
{
var dataRows = [];
for (var i = 0, l = data.length; i < l; ++i)
{
// each item is a data group, containing values of all variables
if (specialVarTypes.length)
{
var originValues = data[i];
var inputValues = [];
var extraDataItem = {};
for (var j = 0, jj = specialVarTypes.length; j < jj; ++j) // handle special var values
{
var vType = specialVarTypes[j];
if (vType === 'M') // muliplicity
{
// TODO: does JCAMP support peak muliplicity? And how to do the conversion?
}
else if (vType === 'W') // width
{
inputValues.push(originValues[j]);
}
else if (vType === 'A') // peak assignment
{
if (Kekule.ObjUtils.notUnset(originValues[j]) && !options.disablePeakAssignmentReading)
extraDataItem.peakAssignmentRaw = [originValues[j]];
}
else
{
var actualValue = this._calcActualVarValue(originValues[j], varInfos[j]);
inputValues.push(actualValue);
}
}
if (Kekule.ObjUtils.getOwnedFieldNames(extraDataItem).length)
{
inputValues._extra = new Kekule.Spectroscopy.SpectrumPeakDetails();
inputValues._extra._peakAssignmentRaw = extraDataItem.peakAssignmentRaw;
hasPeakAssignments = true;
}
//console.log(originValues, inputValues);
dataRows.push(inputValues);
//result.appendData(inputValues);
}
else
dataRows.push(data[i]);
//result.appendData(data[i]);
}
// merge data rows of same value (but different assigment), e.g.:
// (6.71349,0.34, ,<14>)
// (6.71349,0.34, ,<15>)
// which actually means this peak is with two assignment atoms
if (hasPeakAssignments)
dataRows = this._mergePeakAssignmentDataRows(dataRows, specialVarTypes);
for (var i = 0, l = dataRows.length; i < l; ++i)
{
result.appendData(dataRows[i]);
}
result.setDataSorted(true);
if (hasPeakAssignments)
this.getDataSectionsWithPeakAssignments().push(result);
}
finally
{
result.endUpdate();
}
return result;
}
else // with no special assigment
return this._createVarGroupFormatSpectrumDataSection(formatDetail, data, varInfos, parentSpectrumData, options);
},
/** @private */
_mergePeakAssignmentDataRows: function(dataRows, specialVarTypes)
{
var NU = Kekule.NumUtils;
var isSamePeak = function(row1, row2)
{
var result = true;
for (var i = 0, l = specialVarTypes.length; i < l; ++i)
{
if (specialVarTypes[i] !== 'A')
{
var v1 = row1[i];
var v2 = row2[i];
if (v1 != v2 && (NU.isNormalNumber(v1) && NU.isNormalNumber(v2) && !Kekule.NumUtils.isFloatEqual(v1, v2)))
{
result = false;
break;
}
}
}
return result;
}
var findSamePeakRow = function(row, targetRows)
{
for (var i = 0, l = targetRows.length; i < l; ++i)
{
if (isSamePeak(row, targetRows[i]))
return targetRows[i];
}
return null;
}
if (specialVarTypes.indexOf('A') >= 0) // may need to merge peak assignments
{
var result = [];
for (var i = 0, l = dataRows.length; i < l; ++i)
{
var row = dataRows[i];
//console.log(row, row._extra);
var samePeakRow = findSamePeakRow(row, result);
if (samePeakRow && row._extra._peakAssignmentRaw) // find the same peak row, need to merge
{
if (samePeakRow._extra._peakAssignmentRaw)
samePeakRow._extra._peakAssignmentRaw = samePeakRow._extra._peakAssignmentRaw.concat(row._extra._peakAssignmentRaw);
else
samePeakRow._extra._peakAssignmentRaw = row._extra._peakAssignmentRaw;
}
else
result.push(row);
}
return result;
}
else
return dataRows;
},
/** @private */
_isSpectrumDataLdr: function(ldr, block, chemObj)
{
//console.log(chemObj.getClassName());
var result = (!chemObj.getData() || chemObj.getData().getSectionCount() <= 0); // if spectrum data already be built, a data LDR has previously be parsed and this one should not be the data
if (result)
{
//var dataClassValue = chemObj.getInfoValue('DATA CLASS') || null;
var dataClassValue = this.getCurrDataClass() || null;
if (dataClassValue)
{
result = ldr.labelName.replace(/\s/g, '').indexOf(dataClassValue.replace(/\s/g, '')) >= 0; // ignore space in names
}
}
return result;
},
/** @ignore */
doCreateChemObjForBlock: function(block)
{
var result;
var meta = this._getBlockMeta(block);
if (meta.blockType === Jcamp.BlockType.DATA && meta.format === Jcamp.Format.DX)
{
result = new Kekule.Spectroscopy.Spectrum();
//result = [new Kekule.Spectroscopy.Spectrum()]; // TODO: if multiple NTUPLES exists in a file, there may be need to create several Spectrum instances
this.setPropStoreFieldValue('dataSectionsWithPeakAssignments', []);
}
else
result = this.tryApplySuper('doCreateChemObjForBlock', [block]);
return result;
},
/** @ignore */
doBuildCrossRef: function(srcObj, targetObj, refType, refTypeText)
{
this.tryApplySuper('doBuildCrossRef', [srcObj, targetObj, refType, refTypeText]);
if (refType === Jcamp.CrossRefType.STRUCTURE && targetObj instanceof Kekule.StructureFragment && srcObj instanceof Kekule.Spectroscopy.Spectrum)
{
Jcamp.Utils.addMoleculeSpectrumCrossRef(srcObj, targetObj);
var sections = this.getDataSectionsWithPeakAssignments();
if (sections.length)
{
for (var i = 0, l = sections.length; i < l; ++i)
{
this._buildPeakAssignmentRefs(sections[i], targetObj);
}
}
var options = this.getCurrOptions();
if (options.autoHiddenSpectrumRefMolecule && targetObj.setVisible)
targetObj.setVisible(false);
}
},
/** @private */
_buildPeakAssignmentRefs: function(dataSection, targetMol)
{
for (var i = 0, l = dataSection.getDataCount(); i < l; ++i)
{
var extra = dataSection.getExtraInfoAt(i);
if (extra && extra._peakAssignmentRaw)
{
var atoms = [];
for (var j = 0, jj = extra._peakAssignmentRaw.length; j < jj; ++j)
{
var assign = extra._peakAssignmentRaw[j];
var atomIndex = parseInt(assign);
if (Kekule.NumUtils.isNormalNumber(atomIndex) && atomIndex > 0) // the atom index starts with 1
{
var atom = targetMol.getNodeAt(atomIndex - 1);
if (atom)
{
AU.pushUnique(atoms, atom);
}
}
}
extra.setAssignments(atoms);
delete extra._peakAssignmentRaw;
}
}
}
});
/**
* Writer for writing a DX data block to JCAMP document tree.
* The input chem object should be an instance of {@link Kekule.Spectroscopy.Spectrum}.
* @class
* @augments Kekule.IO.Jcamp.BlockWriter
*/
Kekule.IO.Jcamp.DxDataBlockWriter = Class.create(Kekule.IO.Jcamp.BlockWriter,
/** @lends Kekule.IO.Jcamp.DxDataBlockWriter# */
{
/** @private */
CLASS_NAME: 'Kekule.IO.Jcamp.DxDataBlockWriter',
/** @private */
initProperties: function()
{
// private ,storing the map of Jcamp var symbol to Kekule var symbol
this.defineProp('varSymbolMap', {'dataType': DataType.HASH, 'setter': false, 'serializable': false, 'scope': Class.PropertyScope.PRIVATE});
//this.defineProp('sectionPeakAssignmentInfoCache', {'dataType': DataType.OBJECT, 'setter': false, 'serializable': false, 'scope': Class.PropertyScope.PRIVATE});
//this.defineProp('peakSectionInfo', {'dataType': DataType.HASH, 'setter': false, 'serializable': false, 'scope': Class.PropertyScope.PRIVATE});
},
/** @ignore */
_initLdrCreators: function()
{
this.tryApplySuper('_initLdrCreators');
var map = this.getLdrCreatorMap();
map['metaData.date'] = this._dateLdrCreator.bind(this);
map['parameters.NMR.ObserveNucleus'] = this._nmrObserveNucleusLdrCreator.bind(this);;
map['parameters.NMR.ObserveFrequency'] = this._scalarWithSpecifiedUnitLdrCreator.bind(this, Kekule.Unit.Frequency.MEGAHERTZ);
map['parameters.NMR.SolventReference'] = this._scalarWithSpecifiedUnitLdrCreator.bind(this, Kekule.Unit.Dimensionless.PARTS_PER_MILLION);
map['parameters.NMR.Delay'] = this._scalarWithSpecifiedUnitLdrCreator.bind(this, Kekule.Unit.Time.MICROSECOND);
},
/* @ignore */
doWriteBlock: function(chemObj, options)
{
// retrieve and store peak information first
//this.setPropStoreFieldValue('peakSectionInfo', new Kekule.MapEx());
return this.tryApplySuper('doWriteBlock', [chemObj, options]);
},
/** @ignore */
getTitleForBlock: function(chemObj)
{
return chemObj.getTitle() || chemObj.getName() || chemObj.getId();
},
/** @ignore */
doSaveJcampVersionToBlock: function(chemObj, block, options)
{
this.saveToLdrInBlock(block, chemObj, '', options.outputDxVersion || Kekule.globalOptions.IO.jcamp.outputDxVersion, Jcamp.Consts.LABEL_DX_VERSION);
},
/** @ignore */
doGetDataTypeForBlock: function(chemObj)
{
return this.doGetJcampSpectrumDataType(chemObj);
},
/** @ignore */
doSaveChemObjToBlock: function(chemObj, block, options)
{
this.tryApplySuper('doSaveChemObjToBlock', [chemObj, block, options]);
// DATA CLASS placeholder
this.saveToLdrInBlock(block, chemObj, '', '', 'DATA CLASS');
// cross ref
this.doSaveChemObjCrossRefToBlock(chemObj, block, options);
// spectrum data
this.doSaveSpectrumDataToBlock(chemObj, block, options);
},
/** @private */
doSaveChemObjCrossRefToBlock: function(chemObj, block, options)
{
var refMol = chemObj.getRefMolecule();
//console.log(refMol);
if (refMol)
{
var refBlockInfo = this.getBlockInfoFromObj(refMol);
if (refBlockInfo && refBlockInfo.id)
{
var refText = 'STRUCTURE' + Jcamp.Consts.CROSS_REF_TYPE_TERMINATOR +
' BLOCK_ID' + Jcamp.Consts.DATA_LABEL_TERMINATOR + refBlockInfo.id;
this.saveToLdrInBlock(block, chemObj, '', refText, Jcamp.Consts.LABEL_CROSS_REF);
}
}
},
/** @ignore */
doSaveChemObjInfoToBlock: function(chemObj, block, options)
{
// do not inherited parent method here, since we have specified saving functions here
this.doSaveSpectrumKeyMetaToBlock(chemObj, block, options);
this.doSaveSpectrumInfoToBlock(chemObj, block, options);
},
/** @private */
doSaveSpectrumKeyMetaToBlock: function(chemObj, block, options)
{
/*
// save the spectrum type
var jcampType = this.doGetJcampSpectrumDataType(chemObj);
this.saveToLdrInBlock(block, chemObj, '', jcampType, Jcamp.Consts.LABEL_DATA_TYPE, true);
*/
},
/** @private */
doGetJcampSpectrumDataType: function(spectrum)
{
var result;
var spectrumType = spectrum.getSpectrumType();
//var coreName = Jcamp.DxUtils.kekuleSpectrumTypeToJcampCoreName(spectrumType || KSType.GENERAL);
var peakDetails = this._getSpectrumPeakDetails(spectrum);
if (spectrumType === KSType.IR)
{
result = peakDetails.isPeak? 'INFRARED PEAK TABLE': 'INFRARED SPECTRUM';
}
else if (spectrumType === KSType.RAMAN)
{
result = 'RAMAN SPECTRUM';
}
else if (spectrumType === KSType.NMR)
{
// TODO: 'NMR FID" is not handled
result = peakDetails.isPeakAssignment? 'NMR PEAK ASSIGNMENTS':
peakDetails.isPeak? 'NMR PEAK TABLE':
'NMR SPECTRUM';
}
else if (spectrumType === KSType.MS)
{
result = peakDetails.isPeak? 'MASS SPECTRUM': 'CONTINUOUS MASS SPECTRUM';
}
else if (spectrumType === KSType.UV_VIS)
{
result = 'UV/VIS SPECTRUM'; // TODO: is this suffcient?
}
else if (spectrumType === KSType.IMS)
{
result = peakDetails.isPeakAssignment? 'IMS PEAK ASSIGNMENTS':
peakDetails.isPeak? 'IMR PEAK TABLE':
'ION MOBILITY SPECTRUM';
}
else // general
{
result = null;
}
return result;
},
/** @private */
_getSpectrumPeakDetails: function(spectrum) // returns whether all sections are peak table/peak assignments
{
var result = {isPeak: true, isPeakAssignment: true};
for (var i = 0, l = spectrum.getDataSectionCount(); i < l; ++i)
{
var section = spectrum.getDataSectionAt(i);
result.isPeak = result.isPeak && section.isPeakSection();
result.isPeakAssignment = result.isPeakAssignment && section.hasPeakAssignments();
if (!result.isPeak && !result.isPeakAssign