UNPKG

kekule

Version:

Open source JavaScript toolkit for chemoinformatics

1,463 lines (1,416 loc) 72.9 kB
/** * @fileoverview * File for supporting CMLSpect (The CML dialect for spectrum data). * @author Partridge Jiang */ /* * requires /lan/classes.js * requires /lan/xmlJsons.js * requires /core/kekule.common.js * requires /utils/kekule.domHelper.js * requires /spectroscopy/kekule.spectrum.core.js * requires /io/kekule.io.js * requires /io/jcamp/kekule.io.jcamp.base.js * requires /io/cml/kekule.io.cml.js * requires /localization */ (function(){ "use strict"; /* * Add default options to read/write CML format data. * @object */ Kekule.globalOptions.add('IO.cml', { // options for reader enableExtractSampleInsideSpectrum: true, // if true, when reading molecule out from the child <sample> element of <spectrum>, a parent element will be created to hold both molecule and spectrum autoHideSampleInsideSpectrum: true, // if true, the sample molecule inside <spectrum> will be hidden from displaying // options for writer autoInsertHiddenRefMoleculeToSample: true, // if true, a hidden referred molecule will be automatically insert to the <sample> child element of <spectrum> //spectrumDataValueOutputDigitCountAfterDecimalPoint: 8, spectrumDataValueOutputPrecisionCount: 8, autoConvertNmrDataFreqToUnit: 'Hz' // in JSpectView convention, the frequency of NMR continuous spectrum should be in unit Hz }); var AU = Kekule.ArrayUtils; var OU = Kekule.ObjUtils; var DU = Kekule.DomUtils; var CmlUtils = Kekule.IO.CmlUtils; var CmlDomUtils = Kekule.IO.CmlDomUtils; var ST = Kekule.Spectroscopy.SpectrumType; var SPS = Kekule.Spectroscopy.PeakShape; var SPM = Kekule.Spectroscopy.PeakMultiplicity // add additional unit conversion map data Kekule.IO.CmlUtils._cmlUnitConvMap.push(['moverz', 'm/z', 'm/z', false]); /** @private */ Kekule.IO.CML.SPECTRUM_OBJREF_FIELDNAME = '__$objRef$__'; Kekule.IO.CML.SPECTRUM_DATA_OBJREF_FLAG_FIELDNAME = '__$hasObjRef$__' /** @ignore */ Kekule.IO.CML.Spect = {}; /** * Some consts for CMLSpect. * @object */ Kekule.IO.CML.Spect.Consts = { SI_UNITS_NAMESPACE_DEF_PREFIX: 'siUnits', SI_UNITS_NAMESPACE_URI: 'http://www.xml-cml.org/units/siUnits', UNITS_NAMESPACE_DEF_PREFIX: 'units', UNITS_NAMESPACE_URI: 'http://www.xml-cml.org/units/units', JSPECVIEW_NAMESPACE_DEF_PREFIX: 'jspecview', JSPECVIEW_NAMESPACE_URI: 'http://jspecview.sf.net/convention.html', JCAMP_NAMESPACE_DEF_PREFIX: 'jcamp', JCAMP_NAMESPACE_URI: 'http://www.iupac.org/jcamp/dict', JCAMP_UNITS_NAMESPACE_DEF_PREFIX: 'jcampUnits', JCAMP_UNITS_NAMESPACE_URI: 'http://www.iupac.org/jcamp/dict/units', CML_DICT_NAMESPACE_DEF_PREFIX: 'cml', CML_DICT_NAMESPACE_URI: 'http://www.xml-cml.org/dict/cmlDict', XML_SCHEMA_INSTANCE_NAMESPACE_DEF_PREFIX: 'xsi', XML_SCHEMA_INSTANCE_NAMESPACE_URI: 'http://www.w3.org/2001/XMLSchema-instance', CMLSPECT_SCHEMA_LOCATIONS_ATTRIBNAME: 'schemaLocation', CMLSPECT_SCHEMA_LOCATIONS: [ 'http://www.xml-cml.org/dict/jcampDict dict/jcampDict.xml', 'http://www.xml-cml.org/schema schema.xsd', 'http://www.xml-cml.org/dict/cml dict/cmlDict.xml', 'http://www.xml-cml.org/dict/cmlDict dict/simpleCmlDict.xml', 'http://www.xml-cml.org/units/units dict/unitsDict.xml', 'http://www.xml-cml.org/units/siUnits dict/siUnitsDict.xml' ], ANNOTATION_METATYPE: 'annotation', INFO_METATYPE: 'info' }; var CMLSpectConsts = Kekule.IO.CML.Spect.Consts; /** * Util functions for CMLSpect data. * @class */ Kekule.IO.CmlSpectUtils = { /* @private */ //NAMESPACE_JCAMP: 'jcamp', // in CMLSpect file, many attributes (especially the name of meta/condition/parameter element) has this namespace (e.g. 'jcamp:NMR_OBSERVERFREQUENCY') /** @private */ _spectrumTypeMap: [ ['NMR', ST.NMR], ['infrared', ST.IR], ['ir', ST.IR], ['massSpectrum', ST.MS], ['mass', ST.MS], ['UV/VIS', ST.UV_VIS], ['uv', ST.UV_VIS], ['vis', ST.UV_VIS] ], /** @private */ _peakShapeMap: [ ['sharp', SPS.SHARP], ['broad', SPS.BROAD] ], /** @private */ _peakMultiplicityMap: [ ['singlet', SPM.SINGLET], ['doublet', SPM.DOUBLET], ['triplet', SPM.TRIPLET], ['quartet', SPM.QUARTET], ['quintet', SPM.QUINTET], ['sextuplet', SPM.SEXTUPLET], ['multiplet', SPM.MULTIPLET], ['s', SPM.SINGLET], ['d', SPM.DOUBLET], ['dd', SPM.DOUBLE_DOUBLET], ['ddd', SPM.TRIPLE_DOUBLET], ['t', SPM.TRIPLET], ['m', SPM.MULTIPLET] ], /** @private */ _spectrumInfoKeyMap: [ ['jcamp:NMR_OBSERVEFREQUENCY', 'NMR.ObserveFrequency'], ['cml:field', 'NMR.ObserveFrequency'], //??? ['jcamp:NMR_OBSERVENUCLEUS', 'NMR.ObserveNucleus'], ['nmr:OBSERVENUCLEUS', 'NMR.ObserveNucleus'], ['jcamp:resolution', 'Resolution'] ], /** * Get the corresponding {@link Kekule.Spectroscopy.SpectrumType} value for a CML spectrum type string. * @param {String} cmlSpectrumType * @returns {String} */ cmlSpectrumTypeToKekule: function(cmlSpectrumType) { var valueTypeMap = Kekule.IO.CmlSpectUtils._spectrumTypeMap; var sType = ST.GENERAL; // default var tValue = cmlSpectrumType.toLowerCase(); for (var i = 0, l = valueTypeMap.length; i < l; ++i) { var item = valueTypeMap[i]; if (tValue.indexOf(item[0].toLowerCase()) >= 0) { sType = item[1]; break; } } return sType; }, /** * Get the corresponding CML type value from {@link Kekule.Spectroscopy.SpectrumType}. * @param {String} kekuleSpectrumType * @returns {String} */ kekuleSpectrumTypeToCml: function(kekuleSpectrumType) { var result = null; var valueTypeMap = Kekule.IO.CmlSpectUtils._spectrumTypeMap; for (var i = 0, l = valueTypeMap.length; i < l; ++i) { var item = valueTypeMap[i]; if (kekuleSpectrumType === item[1]) { result = item[0]; break; } } return result; }, /** * Returns a key name for Kekule spectrum corresponding to the CML parameter key. * @param {String} cmlKey * @param {String} spectrumType * @param {String} jcampNsPrefix * @returns {String} */ cmlSpectrumInfoDataKeyToKekule: function(cmlKey, spectrumType, jcampNsPrefix) { if (!jcampNsPrefix) jcampNsPrefix = CMLSpectConsts.JCAMP_NAMESPACE_DEF_PREFIX; //CmlSpectUtils.NAMESPACE_JCAMP; var map = CmlSpectUtils._spectrumInfoKeyMap; for (var i = 0, l = map.length; i < l; ++i) { if (cmlKey === map[i][0]) return map[i][1]; } // not found, extract the core part of namespace styled key name var nameDetails = CmlUtils.getCmlNsValueDetails(cmlKey); if (nameDetails.namespace && nameDetails.namespace === jcampNsPrefix) // we may need to check the jcamp dictionary { var localName = nameDetails.localName; if (spectrumType) { var spectrumPrefix = spectrumType && (spectrumType + '_'); var p = localName.indexOf(spectrumPrefix); if (p >= 0) // remove the prefix like 'NMR_', and add the '.' localName = Kekule.IO.Jcamp.Consts.SPECIFIC_LABEL_PREFIX + localName.substr(p + spectrumPrefix.length); } var jcampName = localName.toUpperCase(); return Kekule.IO.Jcamp.Utils.jcampLabelNameToKekule(jcampName, spectrumType); } return CmlUtils.cmlNsTokenToKekule(cmlKey); // default }, /** * Returns a key name for Kekule spectrum corresponding to the CML parameter key. * @param {String} cmlKey * @param {String} spectrumType * @param {String} jcampNsPrefix * @returns {String} */ kekuleSpectrumInfoDataKeyToCml: function(kekuleKey, spectrumType, jcampNsPrefix) { if (!jcampNsPrefix) jcampNsPrefix = CMLSpectConsts.JCAMP_NAMESPACE_DEF_PREFIX; //CmlSpectUtils.NAMESPACE_JCAMP var map = CmlSpectUtils._spectrumInfoKeyMap; for (var i = 0, l = map.length; i < l; ++i) { if (kekuleKey === map[i][1]) return map[i][0]; } // not found in map, check if kekuleKey is based on JCAMP? if so, add the corresponding JCAMP namespace var nameDetails = Kekule.Spectroscopy.MetaPropNamespace.getPropertyNameDetail(kekuleKey); if (nameDetails.namespace === jcampNsPrefix) // explicit jcamp label name return nameDetails.namespace + ':' + nameDetails.coreName; else { var jcampLabelName = Kekule.IO.Jcamp.Utils.kekuleLabelNameToJcamp(kekuleKey, spectrumType, true); //console.log('is jcamp?', kekuleKey, jcampLabelName); if (jcampLabelName) { return CmlSpectUtils._convPossibleJcampLabelNameToCml(jcampLabelName, spectrumType, jcampNsPrefix); } else // not found in JCAMP label name map, custom name? return nameDetails.namespace + ':' + nameDetails.coreName; } /* else if (nameDetails.namespace === spectrumType && spectrumType) // with a spectrum prefix, check in JCAMP map { } */ //return Kekule.IO.CmlUtils.kekuleNsTokenToCml(kekuleKey); // default }, /** @private */ _convPossibleJcampLabelNameToCml: function(jcampLabelName, spectrumType, jcampNsPrefix) { var nameDetails = Kekule.IO.Jcamp.Utils.analysisLdrLabelName(jcampLabelName, false); var isSpecific = nameDetails.labelType === Kekule.IO.Jcamp.LabelType.SPECIFIC; var isPrivate = nameDetails.labelType === Kekule.IO.Jcamp.LabelType.PRIVATE; var coreName = nameDetails.coreName; var localName = (isSpecific && spectrumType)? spectrumType + '_' + coreName: coreName; return isPrivate? localName: /*CmlSpectUtils.NAMESPACE_JCAMP*/jcampNsPrefix + ':' + localName; }, /** * Convert a CML peak shape string to value of {@link Kekule.Spectroscopy.PeakShape}. * @param {String} cmlMultiplicity * @returns {String} */ cmlPeakShapeToKekule: function(cmlPeakShape) { var map = CmlSpectUtils._peakShapeMap; var s = cmlPeakShape.toLowerCase(); for (var i = 0, l = map.length; i < l; ++i) { if (s === map[i][0]) return map[i][1]; } return cmlPeakShape; }, /** * Convert a Kekule peak shape string to CML value. * @param {String} kPeakShape * @returns {String} */ kekulePeakShapeToCml: function(kPeakShape) { var map = CmlSpectUtils._peakShapeMap; for (var i = 0, l = map.length; i < l; ++i) { if (kPeakShape === map[i][1]) return map[i][0]; } return kPeakShape; }, /** * Convert a CML peak multiplicity string to value of {@link Kekule.Spectroscopy.PeakMultiplicity}. * @param {String} cmlMultiplicity * @returns {Variant} */ cmlPeakMultiplicityToKekule: function(cmlMultiplicity) { var map = CmlSpectUtils._peakMultiplicityMap; var s = cmlMultiplicity.toLowerCase(); for (var i = 0, l = map.length; i < l; ++i) { if (s === map[i][0]) return map[i][1]; } return cmlMultiplicity; }, /** * Convert a Kekule peak multiplicity const int or description string value to CML. * @param {Variant} cmlMultiplicity * @returns {String} */ kekulePeakMultiplicityToCml: function(kMultiplicity) { var map = CmlSpectUtils._peakMultiplicityMap; for (var i = 0, l = map.length; i < l; ++i) { if (kMultiplicity === map[i][1]) return map[i][0]; } return kMultiplicity; }, /** * Output a float to CML string. * @param {Number} value * @param {Int} maxDigitCount * @returns {string} */ floatToCmlString: function(value, maxDigitCount) { if (maxDigitCount) return Kekule.NumUtils.toPrecision(value, maxDigitCount, true, true); //return value.toPrecision(maxDigitCount); else return value.toString(); }, /** * Check if a variable definition already existing in spectrum. If true, returns it. * @param {Kekule.Spectroscopy.Spectrum} spectrum * @param {String} varSymbol * @param {String} varUnit * @param {String} name * @param {Int} dependency * @returns {Kekule.Spectroscopy.SpectrumVarDefinition} */ getSpectrumVarDef: function(spectrum, varSymbol, varUnit, name, dependency) { if (!dependency) dependency = Kekule.VarDependency.INDEPENDENT; var varDefs = spectrum.getVariables(); for (var i = 0, l = varDefs.length; i < l; ++i) { var varDef = varDefs[i]; if ((!varSymbol || varSymbol === varDef.getSymbol()) && (!varUnit || varUnit === varDef.getUnit()) && (!name || name === varDef.getName()) && (dependency === varDef.getDependency())) return varDef; } return null; } }; var CmlSpectUtils = Kekule.IO.CmlSpectUtils; /** * CML <sample> element reader. * @class * @augments Kekule.IO.CmlBaseListReader */ Kekule.IO.CmlSampleReader = Class.create(Kekule.IO.CmlBaseListReader, /** @lends Kekule.IO.CmlSampleReader# */ { /** @private */ CLASS_NAME: 'Kekule.IO.CmlSampleReader' }); /** * Base writer to write spectrum data section object to CML. * @class * @augments Kekule.IO.CmlSpectrumDataSectionBaseWriter */ Kekule.IO.CmlSpectrumDataSectionBaseWriter = Class.create(Kekule.IO.CmlElementWriter, /** @lends Kekule.IO.CmlSpectrumDataSectionBaseWriter# */ { /** @private */ CLASS_NAME: 'Kekule.IO.CmlSpectrumDataWriter', /** @private */ getSpectrumDataSectionVarInfos: function(targetDataSection) { var result = { 'dependent': {}, 'independent': {} }; // get the independant and denpendant vars var varInfos = targetDataSection.getActualLocalVarInfos(); var varUnitSymbols = {}; var varSymbols = {}; for (var i = 0, l = varInfos.length; i < l; ++i) { var varDef = targetDataSection.getLocalVarDef(i); var currKey; if (varDef.getDependency() === Kekule.VarDependency.DEPENDENT) currKey = 'dependent'; else currKey = 'independent'; var currInfoItem = result[currKey]; currInfoItem.symbol = varDef.getSymbol(); currInfoItem.unitSymbol = varDef.getUnit(); currInfoItem.name = varDef.getName(); currInfoItem.description = varDef.getDescription(); var continuosRange = targetDataSection.getContinuousVarRange(i); if (continuosRange) { currInfoItem.range = { 'fromValue': continuosRange.fromValue, 'toValue': continuosRange.toValue, 'count': targetDataSection.getDataCount() } } if (result.dependent.symbol && result.independent.symbol) break; } result.varCount = (!result.dependent.symbol? 0: 1) + (!result.independent.symbol? 0: 1); return result; } }); /** * Reader to read a <peaklist> element inside <spectrum>. * @class * @augments Kekule.IO.CmlElementReader */ Kekule.IO.CmlSpectrumPeakListReader = Class.create(Kekule.IO.CmlElementReader, /** @lends Kekule.IO.CmlSpectrumPeakListReader# */ { /** @private */ CLASS_NAME: 'Kekule.IO.CmlSpectrumPeakListReader', /** @constructs */ initialize: function() { this.tryApplySuper('initialize'); this._peakDetailsWithAssignments = []; // a private field to storing all involved child readers }, /** @ignore */ doFinalize: function() { this._peakDetailsWithAssignments = null; this.tryApplySuper('doFinalize'); }, /** @ignore */ doReadElement: function(elem, parentObj, parentReader, options) { var spectrum = (parentObj instanceof Kekule.Spectroscopy.Spectrum)? parentObj: null; if (spectrum) // we need to set the variables of parent spectrum { var peaks = []; // TODO: ignoring <peakGroup> in list, since lacking the document of <peakGroup>. We simply iterate all child <peak> elements. var domHelper = this.getDomHelper(); var peakElems = domHelper.getElementsByTagNameNS(this.getCoreNamespaceURI(), 'peak', elem); //var peakReader = Kekule.IO.CmlElementReaderFactory.getReader('peak'); var peakReader = this.doGetChildElementReader('peak'); if (peakReader) { //this.copySettingsToChildHandler(peakReader); try { for (var i = 0, l = peakElems.length; i < l; ++i) { var peakResult = peakReader.readElement(peakElems[i], null, this); if (peakResult) peaks.push(peakResult); } } finally { //peakReader.finalize(); // child readers will be released at the finalized method of parent reader } } if (peaks.length) { var result; var spectrumData = spectrum.getData(); var varInfos = this._analysisPeakVarInfo(peaks, spectrum); if (varInfos) { var varSymbols = this._addPeakVariablesToSpectrumData(varInfos, spectrumData, spectrum); if (varSymbols && varSymbols.length) { result = spectrumData.createSection(varSymbols, Kekule.Spectroscopy.DataMode.PEAK); //new Kekule.Spectroscopy.SpectrumDataSection(); //result.setLocalVarSymbols(varSymbols); //result.setMode(Kekule.Spectroscopy.DataMode.PEAK); this._setVarLocalInfos(varInfos, result); this._addPeakDataToSpectrumData(peaks, varSymbols, result, spectrumData, spectrum); } } return result; } else return null; } else return null; }, /** @ignore */ doDoneReadingDocument: function() { // after the document is build, try set the assignments of peaks var details = this._peakDetailsWithAssignments; for (var i = 0, l = details.length; i < l; ++i) { var d = details[i]; if (d instanceof Kekule.Spectroscopy.SpectrumPeakDetails) { var owner = d.getOwner(); //if (owner && owner.getObjById) { var ids = d.getInfoValue(Kekule.IO.CML.SPECTRUM_OBJREF_FIELDNAME); var objs = []; for (var j = 0, k = ids.length; j < k; ++j) { var id = ids[j]; var obj = this.getLoadedObjById(id) || (owner && owner.getObjById && owner.getObjById(id)); if (obj) objs.push(obj); } if (objs.length) d.setAssignments(objs); //console.log('set assignment', d, ids, objs); delete d.getInfo()[Kekule.IO.CML.SPECTRUM_OBJREF_FIELDNAME]; } } } this._peakDetailsWithAssignments = []; }, /** @private */ _analysisPeakVarInfo: function(peaks, spectrumObj) // iterate peaks data and find out the variable informations { var variableInfos = {}; var varCount = 0; //var for (var i = 0, l = peaks.length; i < l; ++i) { var peak = peaks[i]; var value = peak.value; var symbols = (value && Kekule.ObjUtils.getOwnedFieldNames(value, false)) || []; for (var j = 0, k = symbols.length; j < k; ++j) { if (!variableInfos[symbols[j]]) { variableInfos[symbols[j]] = {}; ++varCount; } } //TODO: fill units info of var, here we simply assume all peaks uses the same units system var units = peak.units; if (units) { var symbols = Kekule.ObjUtils.getOwnedFieldNames(units, false) || []; for (var j = 0, k = symbols.length; j < k; ++j) { if (variableInfos[symbols[i]] && !variableInfos[symbols[i]].units) { var unitSymbol = Kekule.IO.CmlUtils.cmlUnitStrToMetricsUnitSymbol(units[symbols[i]]); variableInfos[symbols[i]].units = unitSymbol; if (!unitSymbol) variableInfos[symbols[i]].title = Kekule.IO.CmlUtils.getCmlNsValueLocalPart(units[symbols[i]]); } } } } if (varCount <= 0) // no variables, just returns null return null; else if (varCount === 1) // only one explicit symbol, may be ommitted the y values in NMR? { if (spectrumObj && spectrumObj.getSpectrumType() === Kekule.Spectroscopy.SpectrumType.NMR) // we manually add the implicit y { variableInfos.y = { 'units': Kekule.Unit.Arbitrary.ARBITRARY.symbol, 'displayLabel': Kekule.Unit.Arbitrary.ARBITRARY.name, 'defaultValue': 1 }; } } return variableInfos; }, /** @private */ _addPeakVariablesToSpectrumData: function(varInfos, spectrumDataObj, spectrumObj) { if (!varInfos) // no var info, returns directly return null; var symbols = Kekule.ObjUtils.getOwnedFieldNames(varInfos, false); // get the independent var, should be x/X, or the first varibale var indepIndex = symbols.indexOf('x'); if (indepIndex < 0) indepIndex = symbols.indexOf('X'); if (indepIndex < 0) indepIndex = 0; // ensure the independent var at first var indepInfo = symbols.splice(indepIndex, 1); symbols.unshift(indepInfo[0]); // add var definitions var varSymbols = []; for (var i = 0, l = symbols.length; i < l; ++i) { var initParams = { 'symbol': symbols[i], 'dependency': (i === 0)? Kekule.VarDependency.INDEPENDENT: Kekule.VarDependency.DEPENDENT }; if (varInfos[symbols[i]].title) initParams.displayLabel = varInfos[symbols[i]].title; if (varInfos[symbols[i]].units) initParams.unit = varInfos[symbols[i]].units; // check if there is already a varDef of this condition var varDef = Kekule.IO.CmlSpectUtils.getSpectrumVarDef(spectrumObj, initParams.symbol, initParams.unit, null, initParams.dependency); if (varDef && !varDef.getDisplayLabel() && initParams.displayLabel) varDef.setDisplayLabel(initParams.displayLabel); if (!varDef) { varDef = new Kekule.Spectroscopy.SpectrumVarDefinition(initParams); spectrumDataObj.appendVariable(varDef); } /* if (varInfos[symbols[i]].defaultValue) // set default value at data section, avoid affect existed varDef in spectrumData spectrumDataSection.setDefaultVarValue(varDef, varInfos[symbols[i]].defaultValue); //spectrumDataObj.setDefaultVarValue(varDef, varInfos[symbols[i]].defaultValue); */ varSymbols.push(initParams.symbol); } return varSymbols; // A flag means variable adding successful }, /** @private */ _setVarLocalInfos: function(varInfos, spectrumDataSection) { var symbols = Kekule.ObjUtils.getOwnedFieldNames(varInfos, false); for (var i = 0, l = symbols.length; i < l; ++i) { var info = varInfos[symbols[i]]; if (Kekule.ObjUtils.notUnset(info.defaultValue)) { spectrumDataSection.setDefaultVarValue(symbols[i], info.defaultValue); } } }, /** @private */ _addPeakDataToSpectrumData: function(peaks, varSymbols, spectrumDataSection, spectrumDataObj, spectrumObj) { for (var i = 0, ii = peaks.length; i < ii; ++i) { var peak = peaks[i]; var values = []; for (var j = 0, jj = varSymbols.length; j < jj; ++j) { var symbol = varSymbols[j]; var value = peak.value[symbol]; values.push(value); } this._setPeakDetails(peak, values, spectrumDataSection); spectrumDataSection.appendData(values); } spectrumDataSection.setDataSorted(true); }, /** @private */ _setPeakDetails: function(peak, dataValue, spectrumDataSection) { if (peak.subStructure || peak.multiplicity || peak.shape) { var detail = new Kekule.Spectroscopy.SpectrumPeakDetails({ 'multiplicity': peak.multiplicity, 'shape': peak.shape }); if (peak.subStructure) // has <peakStructure> child element detail.setInfoValue('structure', peak.subStructure); var objRefIds = this._getPeakObjRefIds(peak); if (objRefIds) { detail.setInfoValue(Kekule.IO.CML.SPECTRUM_OBJREF_FIELDNAME, objRefIds); // save the ref ids, handling it when the spectrum is added to chem space //spectrumDataSection[Kekule.IO.CML.SPECTRUM_DATA_OBJREF_FLAG_FIELDNAME] = true; // flag, indicating the cross ref should be handled later this._peakDetailsWithAssignments.push(detail); } spectrumDataSection.setExtraInfoOf(dataValue, detail); return detail; } return null; }, /** @private */ _getPeakObjRefIds: function(peak) { var ids = []; for (var i = 0 , l = Kekule.IO.CML.ATOMS_REF_ATTRIBS.length; i < l; ++i) { var key = Kekule.IO.CML.ATOMS_REF_ATTRIBS[i]; if (peak[key]) ids = ids.concat(peak[key].split(/\s/)); } for (var i = 0 , l = Kekule.IO.CML.BONDS_REF_ATTRIBS.length; i < l; ++i) { var key = Kekule.IO.CML.BONDS_REF_ATTRIBS[i]; if (peak[key]) ids = ids.concat(peak[key].split(/\s/)); } var result = []; for (var i = 0, l = ids.length; i < l; ++i) { var id = ids[i].trim(); if (id) result.push(id); } return result.length? result: null; } }); /** * Writer to write a <peaklist> element inside <spectrum>. * @class * @augments Kekule.IO.CmlSpectrumDataSectionBaseWriter */ Kekule.IO.CmlSpectrumPeakListWriter = Class.create(Kekule.IO.CmlSpectrumDataSectionBaseWriter, /** @lends Kekule.IO.CmlSpectrumPeakListWriter# */ { /** @private */ CLASS_NAME: 'Kekule.IO.CmlSpectrumPeakListWriter', /** @ignore */ doCreateElem: function(obj, parentElem) { return this.createChildElem('peakList', parentElem); }, /** @ignore */ doWriteObject: function(obj, targetElem, options) { return this.writePeaks(obj, targetElem, options); }, /** @private */ writePeaks: function(dataSection, targetElem, options) { var dataVarInfo = this.getSpectrumDataSectionVarInfos(dataSection); if (dataVarInfo.varCount < 2) // less than two vars, illegal { // TODO: error message here? } else // output data peaks { var peakWriter = this.getChildPeakWriter(); if (peakWriter) { var indepSymbol = dataVarInfo.independent.symbol; var indepUnit = dataVarInfo.independent.unitSymbol; var depSymbol = dataVarInfo.dependent.symbol; var depUnit = dataVarInfo.dependent.unitSymbol; var self = this; dataSection.forEach(function (value, index) { // form a peak hash and use it to write to CML var peakObj = { xValue: value[indepSymbol], xUnits: indepUnit, yValue: value[depSymbol], yUnits: depUnit }; var extra = dataSection.getExtraInfoOf(value); var extraHash; if (extra instanceof Kekule.Spectroscopy.SpectrumPeakDetails) extraHash = self._peakDetailsToHash(extra); else if (DataType.isObjectValue(extra)) extraHash = extra; if (extraHash) peakObj = Object.extend(peakObj, extraHash); peakWriter.writeObject(peakObj, targetElem, options); }); } } }, /** @private */ getChildPeakWriter: function() { var result = new Kekule.IO.CmlSpectrumPeakWriter(); if (result) { this.copySettingsToChildHandler(result); this._appendChildHandler(result); } return result; }, /** @private */ _peakDetailsToHash: function(peakDetails) { var result = { 'shape': peakDetails.getShape(), 'multiplicity': peakDetails.getMultiplicity(), 'assignments': peakDetails.getAssignments() }; var subStructure = peakDetails.getInfoValue('structure'); if (subStructure) result.structure = subStructure; return result; } }); /** * Reader to read a <peak> element inside <spectrum>. * @class * @augments Kekule.IO.CmlElementReader */ Kekule.IO.CmlSpectrumPeakReader = Class.create(Kekule.IO.CmlElementReader, /** @lends Kekule.IO.CmlSpectrumPeakReader# */ { /** @private */ CLASS_NAME: 'Kekule.IO.CmlSpectrumPeakReader', /** @ignore */ doReadElement: function(elem, parentObj, parentReader, options) { var result = {}; this.readPeakAttribs(result, elem, this.getDomHelper()); this.iterateChildElements(elem, result, this, function(childElem, childResult) { var tagName = DU.getLocalName(childElem); var tagNameLower = tagName.toLowerCase(); if (tagNameLower === 'peakstructure') { if (!result.subStructure) result.subStructure = []; result.subStructure.push(childResult); } }); return result; }, /** @private */ readPeakAttribs: function(peakObj, elem, domHelper) { // important attribs are id, type, title and convention var attribs = Kekule.IO.CmlDomUtils.fetchCmlElemAttributeValuesToJson(elem, null, true, domHelper); var attribKeys = Kekule.ObjUtils.getOwnedFieldNames(attribs); for (var i = 0, l = attribKeys.length; i < l; ++i) { var key = attribKeys[i]; var keyLower = key.toLowerCase(); if (keyLower === 'peakheight') // regard the peakheight as the same of yValue { key = 'yValue'; keyLower = 'yvalue'; } var value = attribs[key]; if (keyLower.indexOf('units') >= 0) // xUnits/yUnits attrib { var symbol = this._getVarSymbolInName(key, 'units'); if (!peakObj.units) peakObj.units = {}; peakObj.units[symbol] = Kekule.IO.CmlUtils.cmlUnitStrToMetricsUnitSymbol(value); } else if (keyLower.indexOf('value') >= 0) // xValue/yValue attrib { var symbol = this._getVarSymbolInName(key, 'value'); if (!peakObj.value) peakObj.value = {}; peakObj.value[symbol] = Kekule.IO.CmlUtils.tryParseFloat(value); } else if (keyLower === 'peakmultiplicity') { peakObj.multiplicity = CmlSpectUtils.cmlPeakMultiplicityToKekule(value); } else if (keyLower === 'peakshape') { peakObj.shape = CmlSpectUtils.cmlPeakShapeToKekule(value); } else peakObj[key] = value; } }, /** @private */ _getVarSymbolInName: function(name, baseName) { var p = name.toLowerCase().indexOf(baseName.toLowerCase()); return (p > 0)? name.substring(0, p): null; } }); /** * Writer to write a <peak> element inside <spectrum>. * @class * @augments Kekule.IO.CmlElementWriter */ Kekule.IO.CmlSpectrumPeakWriter = Class.create(Kekule.IO.CmlElementWriter, /** @lends Kekule.IO.CmlSpectrumPeakWriter# */ { /** @private */ CLASS_NAME: 'Kekule.IO.CmlSpectrumPeakWriter', /** @ignore */ doCreateElem: function(obj, parentElem) { return this.createChildElem('peak', parentElem); }, /** @ignore */ doWriteObject: function(obj, targetElem, options) { this.writePeakAttribs(obj, targetElem, options); }, /** @private */ writePeakAttribs: function(peakObj, targetElem, options) { var domHelper = this.getDomHelper(); var cmlHash= this._peakHashToCmlHash(peakObj); var keys = Kekule.ObjUtils.getOwnedFieldNames(cmlHash); var decimalCount = options.spectrumDataValueOutputPrecisionCount || 0; for (var i = 0, l = keys.length; i < l; ++i) { var key = keys[i]; var value = cmlHash[key]; if (['xvalue', 'yvalue'].indexOf(key.toLowerCase()) >= 0) CmlDomUtils.setCmlElemAttribute(targetElem, key, CmlSpectUtils.floatToCmlString(value, decimalCount), domHelper); if (key.toLowerCase() === 'structure' && DataType.isObjectValue(value)) // sub structure this.writePeakSubStructure(value, targetElem, options); else CmlDomUtils.setCmlElemAttribute(targetElem, key, value, domHelper); } }, /** @private */ getChildPeakStructureWriter: function() { var result = new Kekule.IO.CmlSpectrumPeakStructureWriter(); if (result) { this.copySettingsToChildHandler(result); this._appendChildHandler(result); } return result; }, /** @private */ writePeakSubStructure: function(subStructure, peakElem, options) { var writer = this.getChildPeakStructureWriter(); if (writer) writer.writeObj(subStructure, peakElem, options); }, /** @private */ _peakHashToCmlHash: function(peakHash) { var result = {}; var basicFields = ['xValue', 'yValue']; for (var i = 0, l = basicFields.length; i < l; ++i) { var key = basicFields[i]; var value = peakHash[key]; if (Kekule.ObjUtils.notUnset(value)) { result[key] = value; } } if (peakHash.xUnits) result.xUnits = CmlUtils.metricsUnitSymbolToCmlUnitStr(peakHash.xUnits); if (peakHash.yUnits) result.yUnits = CmlUtils.metricsUnitSymbolToCmlUnitStr(peakHash.yUnits); if (peakHash.shape) result.peakShape = CmlSpectUtils.kekulePeakShapeToCml(peakHash.shape); if (peakHash.multiplicity) result.peakMultiplicity = CmlSpectUtils.kekulePeakMultiplicityToCml(peakHash.multiplicity); if (peakHash.assignments && peakHash.assignments.length) { var atomIds = [], bondIds = []; for (var i = 0, l = peakHash.assignments.length; i < l; ++i) { var obj = peakHash.assignments[i]; var currIds = (obj instanceof Kekule.AbstractAtom)? atomIds: (obj instanceof Kekule.ChemStructureConnector)? bondIds: null; if (currIds) { var id = (obj.getId && obj.getId()) || this.autoIdentifyForObj(obj); if (id) currIds.push(id); } } if (atomIds.length) result.atomRefs = atomIds.join(' '); if (bondIds.length) result.bondRefs = bondIds.join(' '); } return result; } }); /** * Reader to read a <peakStructure> element inside <spectrum>. * @class * @augments Kekule.IO.CmlElementReader */ Kekule.IO.CmlSpectrumPeakStructureReader = Class.create(Kekule.IO.CmlElementReader, /** @lends Kekule.IO.CmlSpectrumPeakStructureReader# */ { /** @private */ CLASS_NAME: 'Kekule.IO.CmlSpectrumPeakStructureReader', /** @ignore */ doReadElement: function(elem, parentObj, parentReader, options) { var result = {}; var attribs = Kekule.IO.CmlDomUtils.fetchCmlElemAttributeValuesToJson(elem, null, true, this.getDomHelper()); var attribKeys = Kekule.ObjUtils.getOwnedFieldNames(attribs); for (var i = 0, l = attribKeys.length; i < l; ++i) { var key = attribKeys[i]; var keyLower = key.toLowerCase(); var value = attribs[key]; // TODO: currently do not handle object ref in sub structures if (Kekule.IO.CML.ATOMS_REF_ATTRIBS.indexOf(key) >= 0 || Kekule.IO.CML.BONDS_REF_ATTRIBS.indexOf(key) >= 0) continue; if (keyLower === 'units') { result.unit = Kekule.IO.CmlUtils.cmlUnitStrToMetricsUnitSymbol(value) || Kekule.IO.CmlUtils.getCmlNsValueLocalPart(value); } else result[key] = value; } return result; } }); /** * Writer to write a <peakStructure> element inside <spectrum>. * @class * @augments Kekule.IO.CmlElementWriter */ Kekule.IO.CmlSpectrumPeakStructureWriter = Class.create(Kekule.IO.CmlElementWriter, /** @lends Kekule.IO.CmlSpectrumPeakStructureWriter# */ { /** @private */ CLASS_NAME: 'Kekule.IO.CmlSpectrumPeakStructureWriter', /** @ignore */ doCreateElem: function(obj, parentElem) { return this.createChildElem('peakStructure', parentElem); }, /** @ignore */ doWriteObject: function(obj, targetElem, options) { var domHelper = this.getDomHelper(); var keys = Kekule.ObjUtils.getOwnedFieldNames(obj); for (var i = 0, l = keys.length; i < l; ++i) { var key = keys[i]; var value = obj[key]; if (key === 'unit') value = CmlUtils.metricsUnitSymbolToCmlUnitStr(value); CmlDomUtils.setCmlElemAttribute(targetElem, key, value, domHelper); } return targetElem; }, }); /** * Reader to read a <xaxis>/<yaxis> element inside <spectrumData>. * @class * @augments Kekule.IO.CmlElementReader */ Kekule.IO.CmlSpectrumDataAxisReader = Class.create(Kekule.IO.CmlElementReader, /** @lends Kekule.IO.CmlSpectrumDataAxisReader# */ { /** @private */ CLASS_NAME: 'Kekule.IO.CmlSpectrumDataAxisReader', /** @private */ initProperties: function() { // a private property, stores the symbol of axis this.defineProp('axisSymbol', {'dataType': DataType.STRING, 'serializable': false}); // a private property, stores the array object read from child <array> element this.defineProp('childArrayObj', {'dataType': DataType.OBJECT, 'serializable': false}); }, /** @private */ doReadElement: function(elem, parentObj, parentReader, options) { var tagName = DU.getLocalName(elem).toLowerCase(); var p = tagName.indexOf('axis'); var symbol = tagName.substring(0, p); var dataMultiplier = DU.getSameNSAttributeValue(elem, 'multiplierToData', this.getDomHelper()); if (dataMultiplier) dataMultiplier = parseFloat(dataMultiplier); this.setAxisSymbol(symbol); this.tryApplySuper('doReadElement', [elem, parentObj, parentReader]); this.iterateChildElements(elem, parentObj, parentReader); var result = { 'symbol': this.getAxisSymbol(), 'array': this.getChildArrayObj() }; if (dataMultiplier) result.dataMultiplier = dataMultiplier; //console.log('read axis', tagName, result); return result; }, /** @ignore */ doReadChildElement: function(elem, parentObj, parentReader) { var tagName = DU.getLocalName(elem).toLowerCase(); if (tagName === 'array') // TODO: now we only handles the <array> element inside <axis> { //var reader = Kekule.IO.CmlElementReaderFactory.getReader(elem); var reader = this.doGetChildElementReader(elem); if (reader) { //this.copySettingsToChildHandler(reader); if (reader.setExpandSteppedArray) // do not expand an array with start/end/size reader.setExpandSteppedArray(false); if (reader.setDefaultItemDataType) // automatically convert the values in array to number reader.setDefaultItemDataType('xsd:float'); var arrayObj = reader.readElement(elem, parentObj, parentReader); if (arrayObj) { this.setChildArrayObj(arrayObj); } } } else return this.tryApplySuper('doReadChildElement', [elem, parentObj, parentReader]); } }); /** * Reader to read a <spectrumData> element inside <spectrum>. * The <spectrumData> element is used to hold the continous data in CMLSpect. * Usually, it should containing child element <xaxis> and <yaxis>. * @class * @augments Kekule.IO.CmlElementReader */ Kekule.IO.CmlSpectrumDataReader = Class.create(Kekule.IO.CmlElementReader, /** @lends Kekule.IO.CmlSpectrumDataReader# */ { /** @private */ CLASS_NAME: 'Kekule.IO.CmlSpectrumDataReader', /** @private */ doReadElement: function(elem, parentObj, parentReader, options) { if (parentObj instanceof Kekule.Spectroscopy.Spectrum) // we need to set the variables to parent spectrum { //var result = new Kekule.Spectroscopy.SpectrumData(); var result = new Kekule.Spectroscopy.SpectrumDataSection(); parentObj.appendDataSection(result); //console.log(parentObj.getData(), parentObj.getData().getActiveSection() === result); //var result = this.tryApplySuper('doReadElement', [elem]); // do not pass parentObj to it, since we will handle with the return values later var childResults = this.iterateChildElements(elem, parentObj, parentReader); this._handleChildResults(childResults, result); // append spectrumData to spectrum /* if (parentObj && parentObj instanceof Kekule.Spectroscopy.Spectrum) parentObj.setData(result); */ return result; } //else // parent is not spectrum, use the default handling process inherited // return this.tryApplySuper('doReadElement', [elem, parentObj, parentReader]); }, /** @private */ _handleChildResults: function(childResults, spectrumDataSectionObj) { var axisDataObjs = []; for (var i = 0, l = childResults.length; i < l; ++i) { var childResult = childResults[i]; var childElem = childResult.element; var childElemTagName = DU.getLocalName(childElem).toLowerCase(); if (childElemTagName.indexOf('axis') >= 0) // is <xaxis>/<yaxis> child element, need to store the array values of it { if (childResult.result.symbol.toLowerCase() === 'x') // ensure xaxis as the first item of axisDataObjs axisDataObjs.unshift(childResult.result); else axisDataObjs.push(childResult.result); } } if (axisDataObjs.length >= 2) // we should have at least two axis to form a spectrum this._fillSpectrumData(axisDataObjs, spectrumDataSectionObj); }, /** @private */ _fillSpectrumData: function(axisDataObjs, spectrumDataSectionObj) { var spectrumObj = spectrumDataSectionObj.getParentSpectrum(); var spectrumDataObj = spectrumObj.getData(); var dataSize = 0; var axisVarDefParamList = []; var localVarSymbols = []; // get the var definition informations and calc the data size for (var i = 0, l = axisDataObjs.length; i < l; ++i) { var isIndependent = (i === 0); // TODO: here we simply regard the first axis (x) as the independent var //var varDef = this._addSpectrumDataVariables(axisDataObjs[i], spectrumDataSectionObj, spectrumDataObj, spectrumObj, isIndependent); var axisVarDefParams = this._getAxisVarDefParams(axisDataObjs[i], isIndependent); axisVarDefParamList.push(axisVarDefParams) //spectrumDataObj.appendVariable(varDef); //localVarSymbols.push(varDef.getSymbol()); //localVarSymbols.push(axisVarDefParams.symbol); //console.log('varDef', axisDataObjs[i], varDef, spectrumDataObj.getVariables()); var size = axisDataObjs[i].array.size || (axisDataObjs[i].array.values && axisDataObjs[i].array.values.length); if (size > dataSize) dataSize = size; } // create var definitions and local settings var localVarSymbols = this._addSpectrumDataVarDefs(axisVarDefParamList, spectrumDataObj, spectrumObj); // add varDef to spectrum first spectrumDataSectionObj.setLocalVarSymbols(localVarSymbols); // then set the local var infos this._setDataSectionLocalVarInfos(axisVarDefParamList, spectrumDataSectionObj); // then the data for (var i = 0; i < dataSize; ++i) { var dataItem = []; for (var j = 0, k = axisDataObjs.length; j < k; ++j) { var dataMultiplier = axisDataObjs[j].dataMultiplier || 1; var arrayValues = axisDataObjs[j].array.values; if (!arrayValues) // a continuous var dataItem.push(undefined); else { dataItem.push(arrayValues[i] * dataMultiplier); } } spectrumDataSectionObj.appendData(dataItem); } spectrumDataSectionObj.setDataSorted(true); }, /** @private */ _getAxisVarDefParams: function(axisDataObj, isIndependent) { var symbol = axisDataObj.symbol; var initParams = {'symbol': symbol, 'dependency': isIndependent? Kekule.VarDependency.INDEPENDENT: Kekule.VarDependency.DEPENDENT}; var extraInfo = {}; var arrayObj = axisDataObj.array; var fieldNames = OU.getOwnedFieldNames(arrayObj); var hasExtraInfo = false; for (var i = 0, l = fieldNames.length; i < l; ++i) { var fname = fieldNames[i].toLowerCase(); var value = arrayObj[fieldNames[i]]; if (fname === 'unit' || fname === 'units') { initParams.unit = Kekule.IO.CmlUtils.cmlUnitStrToMetricsUnitSymbol(value); // if label not set, use unit text as the label // this rule is for unit like arbitrary, which do not has a metrics symbol (initParam.unit === ''), // setting a display label may help to recognize the graph if (!initParams.unit && !initParams.displayLabel) initParams.displayLabel = Kekule.IO.CmlUtils.getCmlNsValueLocalPart(value); } else if (fname === 'title') { initParams.name = value; } else if (fname === 'start' || fname === 'end' || fname === 'size' || fname === 'stepsize') { // do nothing here, we will handle this continous var info later } else if (fname === 'value' || fname === 'values' || fname === 'datatype') // value of axis, no need to be handled in var definition part { } else { extraInfo[fieldNames[i]] = value; hasExtraInfo = true; } } var isContinuousVar = !arrayObj.values && Kekule.ObjUtils.notUnset(arrayObj.start) && Kekule.ObjUtils.notUnset(arrayObj.end); var continuousInfo; if (isContinuousVar) { continuousInfo = {'start': arrayObj.start, 'end': arrayObj.end}; } return { 'symbol': symbol, 'initParams': initParams, 'continuousInfo': continuousInfo, 'hasExtraInfo': hasExtraInfo, 'extraInfo': extraInfo }; }, /** @private */ _addSpectrumDataVarDefs: function(axisVarDefParamList, spectrumDataObj, spectrumObj) { var varSymbols = []; for (var i = 0, l = axisVarDefParamList.length; i < l; ++i) { var varDefParams = axisVarDefParamList[i]; var initParams = varDefParams.initParams; var varDef = Kekule.IO.CmlSpectUtils.getSpectrumVarDef(spectrumObj, initParams.symbol, initParams.unit, initParams.name, initParams.dependency); if (varDef && !varDef.getDisplayLabel() && initParams.displayLabel) varDef.setDisplayLabel(initParams.displayLabel); if (!varDef) { varDef = new Kekule.Spectroscopy.SpectrumVarDefinition(initParams); spectrumDataObj.appendVariable(varDef); } varSymbols.push(varDef.getSymbol()); } return varSymbols; }, /** @private */ _setDataSectionLocalVarInfos: function(axisVarDefParamList, spectrumDataSection) { for (var i = 0, l = axisVarDefParamList.length; i < l; ++i) { var varDefParams = axisVarDefParamList[i]; var varSymbol = varDefParams.symbol; var continuousInfo = varDefParams.continuousInfo; if (continuousInfo) { spectrumDataSection.setContinuousVarRange(varSymbol, continuousInfo.start, continuousInfo.end); } if (varDefParams.hasExtraInfo) { var extraInfo = varDefParams.extraInfo; var fnames = Kekule.ObjUtils.getOwnedFieldNames(extraInfo); for (var i = 0, l = fnames.length; i < l; ++i) { spectrumDataSection.setLocalVarInfoValue(varSymbol, fnames[i], extraInfo[fnames[i]]); } } } } /* @private */ /* _addSpectrumDataVariables(axisDataObj, spectrumDataSection, spectrumDataObj, spectrumObj, isIndependent) { // fill the variables of spectrumData var axisVarDefInfos = this._getAxisVarDefParams(axisDataObj, isIndependent); var initParams = axisVarDefInfos.initParams; var extraInfo = axisVarDefInfos.extraInfo; var hasExtraInfo = axisVarDefInfos.hasExtraInfo; var varDef = Kekule.IO.CmlSpectUtils.getSpectrumVarDef(spectrumObj, initParams.symbol, initParams.unit, null, initParams.dependency); if (varDef && !varDef.getDisplayLabel() && initParams.displayLabel) varDef.setDisplayLabel(initParams.displayLabel); if (!varDef) { varDef = new Kekule.Spectroscopy.SpectrumVarDefinition(initParams); spectrumDataObj.appendVariable(varDef); } // set extra info at section level, avoid affecting the global settings in spectrumData if (hasExtraInfo) { var fnames = Kekule.ObjUtils.getOwnedFieldNames(extraInfo); for (var i = 0, l = fnames.length; i < l; ++i) { spectrumDataSection.setLocalVarInfoValue(varDef.getSymbol(), fnames[i], extraInfo[fnames[i]]); } } var isContinuousVar = !arrayObj.values && arrayObj.start && arrayObj.end; if (isContinuousVar) { //spectrumDataObj.setContinuousVarRange(varDef, arrayObj.start, arrayObj.end); spectrumDataSection.setContinuousVarRange(varDef, arrayObj.start, arrayObj.end); } return varDef; } */ }); /** * Writer to write a <spectrumData> element inside <spectrum>. * @class * @augments Kekule.IO.CmlSpectrumDataSectionBaseWriter */ Kekule.IO.CmlSpectrumDataWriter = Class.create(Kekule.IO.CmlSpectrumDataSectionBaseWriter, /** @lends Kekule.IO.CmlSpectrumDataWriter# */ { /** @private */ CLASS_NAME: 'Kekule.IO.CmlSpectrumDataWriter', /** @ignore */ doCreateElem: function(obj, parentElem) { return this.createChildElem('spectrumData', parentElem); }, /** @ignore */ doWriteObject: function(obj, targetElem, options) { return this.writeSpectrumData(obj, targetElem, options); }, /** @private */ writeSpectrumData: function(spectrumDataSection, targetElem, options) { //var varInfos = spectrumDataSection.getActualLocalVarInfos(); var dataVarInfo = this.getSpectrumDataSectionVarInfos(spectrumDataSection); if (dataVarInfo.varCount < 2) // less than two vars, illegal { // TODO: error message here? } else // output data axis { var keys = ['independent', 'dependent']; var arrays = []; for (var i = 0, l = keys.length; i < l; ++i) { var key = keys[i]; var isIndep = (key === 'independent'); var arrayObj = { '_tagName': isIndep? 'xaxis': 'yaxis', 'varSymbol': dataVarInfo[key].symbol, 'unit': dataVarInfo[key].unitSymbol, 'title': dataVarInfo[key].name, 'dataType': DataType.FLOAT, 'size': DataType.count || spectrumDataSection.getDataCount() }; var range = dataVarInfo[key].range; if (range) { arrayObj.start = range.fromValue; arrayObj.end = range.toValue; arrayObj.isContinuous = true; } else arrayObj.values = []; arrayObj = this._prepareUnitConversionInfoForArrayObj(arrayObj, isIndep, spectrumDataSection, options); arrays.push(arrayObj); } // collect array values var decimalCount = (options && options.spectrumDataValueOutputPrecisionCount) || 0; spectrumDataSection.forEach(function(value, index){ for (var i = 0, l = arrays.length; i < l; ++i) { var needUnitConversion = arrays[i]._originUnitObj && arrays[i]._unitObj; if (!arrays[i].isContinuous) // bypass continuous range vars { var symbol = arrays[i].varSymbol; // TODO: here we simple convert the unavailable value (NaN or undefined) to 0, is it safe? var dvalue = value[symbol] || 0; if (needUnitConversion) // need to do unit conversion { dvalue = arrays[i]._originUnitObj.convertValueTo(dvalue, arrays[i]._unitObj); } //var allowedError = allowedErrorRate? dvalue * allowedErrorRate arrays[i].values.push(CmlSpectUtils.floatToCmlString(dvalue, decimalCount)); } } }); // write values to element var arrayWriter = this.doGetChildObjectWriter(DataType.ARRAY, spectrumDataSection, this); if (arrayWriter) { for (var i = 0, l = arrays.length; i < l; ++i) { var arrayObj = arrays[i]; var axisElem = this.createChildElem(arrayObj._tagName, targetElem); arrayWriter.writeObject(arrayObj, axisElem, options); } } } }, /** @private */ _prepareUnitConversionInfoForArrayObj: function(arrayObj, isIndep, spectrumDataSection, options) { var spectrum = spectrumDataSection.getParentSpectrum(); if (isIndep && spectrum && spectrum.getSpectrumType() === Kekule.Spectroscopy.SpectrumType.NMR && options.autoConvertNmrDataFreqToUnit) { var unitObj = arrayObj.unit && Kekule.Unit.ge