UNPKG

kekule

Version:

Open source JavaScript toolkit for chemoinformatics

1,505 lines (1,451 loc) 47.5 kB
/** * @fileoverview * Reader and Writer classes for MDL V2000 and V3000 formats. * @author Partridge Jiang */ /* * requires /lan/classes.js * requires /core/kekule.common.js * requires /core/kekule.elements.js * requires /core/kekule.electrons.js * requires /core/kekule.structures.js * requires /core/kekule.reactions.js * requires /utils/kekule.textHelper.js * requires /io/kekule.io.js * requires /io/mdl/kekule.io.mdlBase.js * requires /io/mdl/kekule.io.mdl2000.js * requires /io/mdl/kekule.io.mdl3000.js * requires /localization */ /* * Default options to read/write CML format data. * @object */ Kekule.globalOptions.add('IO.mdl', { mdlVersion: Kekule.IO.MdlVersion.V2000, coordMode: Kekule.CoordMode.UNKNOWN }); /** * Class to read and anaylsis MDL 2000 / 3000 Connection Table block. * @class * @augments Kekule.IO.MdlBlockReader * @private */ Kekule.IO.MdlStructureFragmentReader = Class.create(Kekule.IO.MdlBlockReader, /** @lends Kekule.IO.MdlStructureFragmentReader# */ { /** @private */ CLASS_NAME: 'Kekule.IO.MdlStructureFragmentReader', /** @constructs */ initialize: function(/*$super, */coordMode) { this.tryApplySuper('initialize') /* $super() */; this.setCoordMode(coordMode || Kekule.CoordMode.UNKNOWN); }, /** @private */ initProperties: function() { this.defineProp('coordMode', {'dataType': DataType.INT, 'defaultValue': Kekule.CoordMode.UNKNOWN}); }, /** @private */ createFragment: function(parentObj) { // descendants should override this. }, /** @private */ createCtabReader: function(mdlVersion) { if (mdlVersion == Kekule.IO.MdlVersion.V3000) return new Kekule.IO.Mdl3kCTabReader(); else return new Kekule.IO.Mdl2kCTabReader(); }, /** @private */ getCtabVersion: function(textBuffer) { var pos = textBuffer.getCurrLineNo(); var line = textBuffer.readLine(); textBuffer.setCurrLineNo(pos); // check version tag at the end of line var s = line.substr(33, 6).trim(); if (s == 'V3000') return Kekule.IO.MdlVersion.V3000; else if (s == 'V2000') return Kekule.IO.MdlVersion.V2000; else // wrong version flag, it may be a CTAB 2000 { //Kekule.error(Kekule.ErrorMsg.NOT_MDL_FORMAT_DATA); //return null; return Kekule.IO.MdlVersion.V2000; } }, /** * Create CTab of fragment by ctabInfo JSON data. * @param {Kekule.StructureFragment} fragment * @param {Hash} ctabInfo * @returns {Kekule.StructureConnectionTable} * @private */ createStructureFromJson: function(ctabInfo, parentObj) { var fragment = this.createFragment(); if (fragment) Kekule.IO.MdlStructureUtils.fillFragment(fragment, ctabInfo, this.getCoordMode()); return fragment; }, /** @private */ doReadBlock: function(/*$super, */textBuffer, parentObj) { // check ctab format, V2000 or V3000 var version = this.getCtabVersion(textBuffer); if (!version) return null; /* var buffer = (version == Kekule.IO.MdlVersion.V2000)? textBuffer: new Kekule.IO.Mdl3kTextBuffer(textBuffer.getLines()); // V3000 need different type of text buffer */ var ctabReader = this.createCtabReader(version); /* if (version == Kekule.IO.MdlVersion.V3000) var ctabInfo = ctabReader.readBlock(textBuffer.getText(), parentObj); else var ctabInfo = ctabReader.doReadBlock(buffer, parentObj); */ //var ctabInfo = ctabReader.readBlock(textBuffer.getUnreadLines(), parentObj); var ctabInfo = ctabReader.doReadBlock(textBuffer, parentObj); if (ctabInfo.atomInfos.coordMode) this._forceCoordMode = ctabInfo.atomInfos.coordMode; var result = this.createStructureFromJson(ctabInfo, parentObj); return result; } }); /** * Class to write a {@link Kekule.StructureFragment} to MDL 2000 / 3000 Connection Table block. * @class * @augments Kekule.IO.MdlBlockWriter * @private */ Kekule.IO.MdlStructureFragmentWriter = Class.create(Kekule.IO.MdlBlockWriter, /** @lends Kekule.IO.MdlStructureFragmentWriter# */ { /** @private */ CLASS_NAME: 'Kekule.IO.MdlStructureFragmentWriter', /** @constructs */ initialize: function(/*$super, */version, coordMode) { this.tryApplySuper('initialize') /* $super() */; this.setMdlVersion(version || Kekule.IO.MdlVersion.V2000); this.setCoordMode(coordMode || Kekule.CoordMode.UNKNOWN); }, /** @private */ initProperties: function() { this.defineProp('mdlVersion', {'dataType': DataType.INT, 'defaultValue': Kekule.IO.MdlVersion.V2000}); this.defineProp('coordMode', {'dataType': DataType.INT, 'defaultValue': Kekule.CoordMode.UNKNOWN}); }, /** @private */ createCtabWriter: function(mdlVersion) { if (mdlVersion == Kekule.IO.MdlVersion.V3000) return new Kekule.IO.Mdl3kCTabWriter(this.getCoordMode()); else return new Kekule.IO.Mdl2kCTabWriter(this.getCoordMode()); }, /** @private */ doWriteBlock: function(/*$super, */obj, textBuffer) { var ctabWriter = this.createCtabWriter(this.getMdlVersion()); var text = ctabWriter.writeBlock(obj); textBuffer.writeText(text); } }); /** * Directly read CTab block of MDL 3000 MOL file. * First 4 lines of MDL 3000 MOL file are for backward compatibility and has little use. * So this reader skips them and read CTAB block directly. * @class * @augments Kekule.IO.MdlStructureFragmentReader * @private */ Kekule.IO.Mdl3kMoleculeCTabReader = Class.create(Kekule.IO.MdlStructureFragmentReader, /** @lends Kekule.IO.Mdl3kMoleculeCTabReader# */ { /** @private */ CLASS_NAME: 'Kekule.IO.Mdl3kMoleculeCTabReader', /** @private */ createFragment: function(parentObj) { return new Kekule.Molecule(); }, /** @private */ doReadBlock: function(/*$super, */textBuffer, parentObj) { var ctabReader = this.createCtabReader(Kekule.IO.MdlVersion.V3000); //var ctabInfo = ctabReader.readBlock(textBuffer.getUnreadLines(), parentObj); var ctabInfo = ctabReader.doReadBlock(textBuffer, parentObj); return this.createStructureFromJson(ctabInfo, parentObj); } }); /** * Directly write CTab block of MDL 3000 MOL file. * First 4 lines of MDL 3000 MOL file are for backward compatibility and has little use. * So this writer skips them and write CTAB block directly. * @class * @augments Kekule.IO.MdlStructureFragmentWriter * @private */ Kekule.IO.Mdl3kMoleculeCTabWriter = Class.create(Kekule.IO.MdlStructureFragmentWriter, /** @lends Kekule.IO.Mdl3kMoleculeCTabWriter# */ { /** @private */ CLASS_NAME: 'Kekule.IO.Mdl3kMoleculeCTabWriter', /** @constructs */ initialize: function(/*$super, */coordMode) { this.tryApplySuper('initialize', [Kekule.IO.MdlVersion.V3000, coordMode]) /* $super(Kekule.IO.MdlVersion.V3000, coordMode) */; }, /** @private */ doWriteBlock: function(/*$super, */obj, textBuffer) { var ctabWriter = new Kekule.IO.Mdl3kCTabWriter(this.getCoordMode());; var text = ctabWriter.writeBlock(obj); textBuffer.writeText(text); } }); /** * Class to read and anaylsis MDL MOL 2000/3000 data and create Molecule. * @class * @augments Kekule.IO.MdlStructureFragmentReader */ Kekule.IO.MdlMoleculeReader = Class.create(Kekule.IO.MdlStructureFragmentReader, /** @lends Kekule.IO.MdlMoleculeReader# */ { /** @private */ CLASS_NAME: 'Kekule.IO.MdlMoleculeReader', /** @private */ createFragment: function(parentObj) { var result = new Kekule.Molecule(); return result; }, /** * Read MOL 2000/3000 file header lines. * @param {Kekule.TextLinesBuffer} textBuffer * @param {Object} parentObj * @return {Hash} * @private */ readHeaderInfo: function(textBuffer, parentObj) { var result = {}; // line 1: molecule name var name = textBuffer.readLine(); if (name) result.name = name; // line 2: MOL file information var infoLine = textBuffer.readLine(); var info = this.readInfoLine(infoLine); if (info) result = Object.extend(result, info); // line 3: comment var comment = textBuffer.readLine(); if (comment) result.comment = comment; return result; }, /** * Read information line (line 2) of a MOL 2000 / 3000 file. * @param {String} line * @returns {Hash} * @private */ readInfoLine: function(line) { var result = {}; if (line.trim()) // info line can be blank { // IIPPPPPPPPMMDDYYHHmmddSSssssssssssEEEEEEEEEEEERRRRRR // II: User's first and last initials var s = line.substr(0, 2).trim(); if (s) result.userAbbr = s; // PPPPPPPP: program name s = line.substr(2, 8).trim(); if (s) result.programName = s; // MMDDYYHHmm: Date and time //var date; s = line.substr(10, 10); if (s.trim()) { /* date = new Date(); var month = (parseInt(line.substr(10, 2).trim()) || 1) - 1; var day = parseInt(line.substr(12, 2).trim()) || 0; var year = parseInt(line.substr(14, 2), 10) || 0; if (year >= 70) // assume 1970-1999 year += 1900; else // 2000-2069 year += 2000 date.setFullYear(year, month, day); var hour = parseInt(line.substr(16, 2), 10) || 0; var minute = parseInt(line.substr(18, 2), 10) || 0; date.setHours(hour, minute); result.date = date; */ result.date = Kekule.IO.MdlUtils.analysisMdlDateTimeStr(s, false); } // dd: dimensional codes, 2D or 3D s = line.substr(20, 2); result.coordMode = (s == '3D')? Kekule.CoordMode.COORD3D: ((s == '2D')? Kekule.CoordMode.COORD2D: Kekule.CoordMode.UNKNOWN ); // SSssssssssss: scaling factors // TODO: not parsed here, may be this property is important. // EEEEEEEEEEEE: energy, no need to parse // RRRRRR: registry info, no need to parse } return result; }, /** @private */ doReadBlock: function(/*$super, */textBuffer, parentObj) { var headerInfo = this.readHeaderInfo(textBuffer, parentObj); if (headerInfo && (typeof(headerInfo.coordMode) != 'undefined')) this.setCoordMode(headerInfo.coordMode); var mol = this.tryApplySuper('doReadBlock', [textBuffer, parentObj]) /* $super(textBuffer, parentObj) */; // add header information to mol if (mol && headerInfo) { headerInfo.name? mol.setName(headerInfo.name): null; headerInfo.userAbbr? mol.setInfoValue('author', headerInfo.userAbbr): null; headerInfo.programName? mol.setInfoValue('generator', headerInfo.programName): null; headerInfo.date? mol.setInfoValue('date', headerInfo.date): null; headerInfo.comment? mol.setInfoValue('comment', headerInfo.comment): null; } // check if next line is M END // in 3k, this line will not be read by ctab reader, so just read and discard it if (!textBuffer.eof()) { var line = textBuffer.getCurrLine(); if (line.trim().toUpperCase() === 'M END') textBuffer.readLine(); } return mol; } }); /** * Class to write {@link Kekule.Molecule} to MDL MOL 2000/3000 data. * @class * @augments Kekule.IO.MdlStructureFragmentWriter */ Kekule.IO.MdlMoleculeWriter = Class.create(Kekule.IO.MdlStructureFragmentWriter, /** @lends Kekule.IO.MdlMoleculeWriter# */ { /** @private */ CLASS_NAME: 'Kekule.IO.MdlMoleculeWriter', /** * Write MOL 2000/3000 file header lines. * @param {Kekule.Molecule} mol * @param {Kekule.TextLinesBuffer} textBuffer * @private */ writeHeaderInfo: function(mol, textBuffer) { // line 1: molecule name textBuffer.writeLine(mol.getName() || /*Kekule.Texts.UNNAMED*/Kekule.$L('Texts.UNNAMED') || ''); // line 2: MOL file information var infoLine = this.generateInfoLine(mol); textBuffer.writeLine(infoLine); // line 3: comment var comment = mol.getInfoValue('comment'); textBuffer.writeLine(comment || ''); }, /** * Get information line (line 2) for writing to MOL 2000 / 3000 file. * @param {Kekule.Molecule} mol * @returns {String} * @private */ generateInfoLine: function(mol) { var s = ''; // IIPPPPPPPPMMDDYYHHmmddSSssssssssssEEEEEEEEEEEERRRRRR // II: User's first and last initials var author = mol.getInfoValue('author'); s += author? author.toString().substr(0, 2).rpad(2): ' '; // PPPPPPPP: program name var progName = mol.getInfoValue('generator'); s += progName? progName.toString().substr(0, 8).rpad(8): Kekule.LIBNAME_CORE.rpad(8); // MMDDYYHHmm: Date and time var date = mol.getInfoValue('date') || new Date(); // default is now s += Kekule.IO.MdlUtils.generateMdlDateTimeStr(date, false); // short date time format // dd: dimensional codes, 2D or 3D s += (this.getCoordMode() == Kekule.CoordMode.COORD3D)? '3D': '2D'; // SSssssssssss: scaling factors // TODO: not handled here, may be this property is important. // EEEEEEEEEEEE: energy, no need to handle // RRRRRR: registry info, no need to handle return s; }, /** * Get molecule data from an object. * @param {Variant} obj */ getMolecule: function(obj) { return Kekule.ChemStructureUtils.getTotalStructFragment(obj); }, /** @private */ doWriteBlock: function(/*$super, */obj, textBuffer) { var mol = this.getMolecule(obj); if (mol) { var molInfo = Kekule.IO.MdlStructureUtils.getMoleculeCtabStructureInfo(mol); this.setCoordMode(molInfo.coordMode); // coord mode can be get from mol info // header this.writeHeaderInfo(mol, textBuffer); if (this.getMdlVersion() == Kekule.IO.MdlVersion.V3000) // add a compatible count line textBuffer.writeLine(Kekule.IO.MdlStructureUtils.generateClassicStyleCountLine(molInfo, this.getMdlVersion())); // Ctab this.tryApplySuper('doWriteBlock', [mol, textBuffer]) /* $super(mol, textBuffer) */; if (this.getMdlVersion() == Kekule.IO.MdlVersion.V3000) // 3k compatible end tag textBuffer.writeLine('M END'); } else // not found { // do nothing } } }); /** * Class to read and anaylsis both MDL SD file data then create Molecule or MoleculeList. * @class * @augments Kekule.IO.MdlBlockReader * @private */ Kekule.IO.MdlStructDataReader = Class.create(Kekule.IO.MdlBlockReader, /** @lends Kekule.IO.MdlStructDataReader# */ { /** @private */ CLASS_NAME: 'Kekule.IO.MdlStructDataReader', /** @private */ DATA_HEAD_PREFIX: Kekule.IO.MDL.SD_DATA_HEAD_PREFIX, //'>', /** @private */ DATA_DBFIELD_PATTERN: /\bDT(\d+)\b/, /** @private */ DATA_KEY_NAME_PATTERN: /\<(.+)\>/, /** @private */ MOL_DELIMITER: Kekule.IO.MDL.MOL_DELIMITER, //'$$$$', /** @private */ doReadBlock: function(textBuffer, parentObj) { var molReader = new Kekule.IO.MdlMoleculeReader(); try { var currKey, currValue; var mol; var mols = []; // repeatly, read mol block than data block while (!textBuffer.eof()) { try { mol = molReader.doReadBlock(textBuffer, null); } catch(e) // tailing blank lines after $$$$ may cause error in reading mol, just ignore it and terminate reading { mol = null; } if (mol) // read data block { mols.push(mol); currKey = null; currValue = null; while (!textBuffer.eof()) { var lineInfo = this.analysisDataLine(textBuffer.readLine()); if (lineInfo) { var lineType = lineInfo.lineType; if (lineType === 'dataheader') currKey = lineInfo.key; else if (lineType === 'datavalue') { if (currKey) { currValue = lineInfo.value; mol.setInfoValue(currKey, currValue); } } else if (lineType === 'moldelimiter') break; // exit data read loop to read the next molecule } } } else break; } if (mols.length > 1) // more than one molecule, returns a list { var result = new Kekule.ChemObjList(); for (var i = 0, l = mols.length; i < l; ++i) result.append(mols[i]); return result; } else return mols[0]; } finally { molReader.finalize(); } }, /** @private */ analysisDataLine: function(line) { var lineType, key, value; var s = line.trim(); if (s.startsWith(this.DATA_HEAD_PREFIX)) // data header, read data key name { lineType = 'dataheader'; key = this.getDataHeaderKey(line); if (key) return {'lineType': lineType, 'key': key}; else return null; } else if (s.startsWith(this.MOL_DELIMITER)) // molecule delimiter { return {'lineType': 'moldelimiter'}; } else // data line or blank line { if (!s) // blank return null; else return {'lineType': 'datavalue', 'value': line}; } }, /** @private */ getDataHeaderKey: function(line) { var key = null; // try get key name var result = this.DATA_KEY_NAME_PATTERN.exec(line); if (result && (result.length > 1)) key = result[1]; else // try get database field name { result = this.DATA_DBFIELD_PATTERN.exec(line); if (result && (result.length > 1)) key = result[1]; else // do not know the exactly key, return whole line key = line; } return key; }, doReadDataBlock: function(textBuffer, mol) { var line = textBuffer.readLine(); // if start with '>', is a data header } }); /** * Class to write {@link Kekule.Molecule} or other molecule list to MDL SD format data. * @class * @augments Kekule.IO.MdlStructureFragmentWriter */ Kekule.IO.MdlStructDataWriter = Class.create(Kekule.IO.MdlBlockWriter, /** @lends Kekule.IO.MdlStructDataWriter# */ { /** @private */ CLASS_NAME: 'Kekule.IO.MdlStructDataWriter', /** @private */ IGNORED_INFO_FIELDS: ['generator', 'author', 'date', 'comment'], /** @constructs */ initialize: function(/*$super, */version, coordMode) { this.tryApplySuper('initialize') /* $super() */; this.setMdlVersion(version || Kekule.IO.MdlVersion.V2000); this.setCoordMode(coordMode || Kekule.CoordMode.UNKNOWN); }, /** @private */ initProperties: function() { this.defineProp('mdlVersion', {'dataType': DataType.INT, 'defaultValue': Kekule.IO.MdlVersion.V2000}); this.defineProp('coordMode', {'dataType': DataType.INT, 'defaultValue': Kekule.CoordMode.UNKNOWN}); }, /** @private */ doWriteBlock: function(chemObj, textBuffer) { var mols = this.getChildMols(chemObj); var molWriter = new Kekule.IO.MdlMoleculeWriter(); molWriter.setMdlVersion(this.getMdlVersion()); molWriter.setCoordMode(this.getCoordMode()); for (var i = 0, l = mols.length; i < l; ++i) { var mol = mols[i]; // write molecule try { var text = molWriter.writeBlock(mol); textBuffer.writeText(text); // write data information var data = this.getMolData(mol); this.doWriteDataBlock(data, textBuffer); // then molecule delimiter textBuffer.writeLine(Kekule.IO.MDL.MOL_DELIMITER); } catch(e) { // emit the error when meet a formula molecule } } }, /** @private */ doWriteDataBlock: function(data, textBuffer) { var keys = Kekule.ObjUtils.getOwnedFieldNames(data); for (var i = 0, l = keys.length; i < l; ++i) { var key = keys[i]; if (this.IGNORED_INFO_FIELDS.indexOf(key) < 0) { var header = Kekule.IO.MDL.SD_DATA_HEAD_PREFIX + ' <' + key + '>'; var value = data[key]; // write data/value and a blank line if (header && value) { textBuffer.writeLine(header); textBuffer.writeLine(value); textBuffer.writeLine(''); } } } }, /** * Returns child molecule in parent chemObj. * Each child molecule need a block in MDL SD file. * @param {Kekule.ChemObject} chemObj * @returns {Array} */ getChildMols: function(chemObj) { var children = Kekule.ChemStructureUtils.getChildStructureObjs(chemObj, true); /* if (chemObj instanceof Kekule.ChemObjList) children = chemObj.getItems(); else if (chemObj instanceof Kekule.CompositeMolecule) children = chemObj.getSubMolecules().getAllObjs(); else if (chemObj instanceof Kekule.ChemStructureObjectGroup) children = chemObj.getAllObjs(); else // single object children = [chemObj]; */ // filter out molecule var result = []; for (var i = 0, l = children.length; i < l; ++i) { var child = children[i]; if (child instanceof Kekule.StructureFragment) result.push(child); } return result; }, /** * Returns a hash with all data need to write to SD content. * @param {Kekule.ChemObject} mol * @returns {Hash} */ getMolData: function(mol) { var info = mol.getInfo? mol.getInfo() || {}: null; return info; } }); /** * Base class to read and anaylsis both MDL RXN 2000 and 3000 data then create Reaction. * @class * @augments Kekule.IO.MdlBlockReader * @private */ Kekule.IO.MdlBaseReactionReader = Class.create(Kekule.IO.MdlBlockReader, /** @lends Kekule.IO.MdlBaseReactionReader# */ { /** @private */ CLASS_NAME: 'Kekule.IO.MdlBaseReactionReader', /** * Read header block (first 4 lines) of a RXN file. * @param {Kekule.TextLinesBuffer} textBuffer * @param {Object} parentObj * @returns {Hash} * @private */ readHeaderBlock: function(textBuffer, parentObj) { var result = {}; var s; // line 1: identifier var line = textBuffer.readLine().trim(); s = line.substr(0, 4); if (s != '$RXN') // identifier wrong { Kekule.error(/*Kekule.ErrorMsg.NOT_MDL_RXN_DATA*/Kekule.$L('ErrorMsg.NOT_MDL_RXN_DATA')); return null; } else // get file version { s = line.substr(4).trim(); if (s == 'V3000') result.version = Kekule.IO.MdlVersion.V3000; else result.version = Kekule.IO.MdlVersion.V2000; } // line 2: reaction name line = textBuffer.readLine().trim(); if (line) result.name = line; // line 3: reaction info // IIIIIIPPPPPPPPPMMDDYYYYHHmmRRRRRRR { line = textBuffer.readLine(); // IIIIII: User's initials s = line.substr(0, 6).trim(); if (s) result.userAbbr = s; // PPPPPPPPP: program name s = line.substr(6, 9).trim(); if (s) result.programName = s; // MMDDYYYYHHmm: date time. // NOTE: early softwares may generate datetime string in short format, doesnot handle this now. s = line.substr(15, 10); if (s.trim()) result.date = Kekule.IO.MdlUtils.analysisMdlDateTimeStr(s, true); // RRRRRRR: reg no, ignored } // line 4: comment line line = textBuffer.readLine().trim(); if (line) result.comment = line; return result; //return Kekule.IO.MdlUtils.parseReactionHeader(textBuffer); }, /** * Get reactant and product count from count line. * @param {Object} line * @private */ readSubstanceCountLine: function(line) { // do nothing here }, /** * Read MOL blocks in RXN file. * @param {Kekule.TextLinesBuffer} textBuffer * @returns {Kekule.Molecule} * @private */ readMolBlock: function(textBuffer) { // do nothing here }, /** @private */ doReadBlock: function(/*$super, */textBuffer, parentObj) { var headerInfo = this.readHeaderBlock(textBuffer, null); // read header info success, this is a legal RXN file, do other jobs var substanceInfo = this.readSubstanceCountLine(textBuffer.readLine()); var result = new Kekule.Reaction(); // then MOL blocks var line = ''; /* while (line != '$MOL') line = textBuffer.readLine(); */ for (var i = 0; i < substanceInfo.reactantCount; ++i) { var mol = this.readMolBlock(textBuffer); if (mol) result.appendReactant(mol); } for (var i = 0; i < substanceInfo.productCount; ++i) { var mol = this.readMolBlock(textBuffer); if (mol) result.appendProduct(mol); } // append meta info if (result && headerInfo) { headerInfo.name? result.setName(headerInfo.name): null; headerInfo.userAbbr? result.setInfoValue('author', headerInfo.userAbbr): null; headerInfo.programName? result.setInfoValue('generator', headerInfo.programName): null; headerInfo.date? result.setInfoValue('date', headerInfo.date): null; headerInfo.comment? result.setInfoValue('comment', headerInfo.comment): null; } return result; } }); /** * Class to read and anaylsis MDL RXN 2000 data and create Reaction. * @class * @augments Kekule.IO.MdlBaseReactionReader * @private */ Kekule.IO.Mdl2kReactionReader = Class.create(Kekule.IO.MdlBaseReactionReader, /** @lends Kekule.IO.Mdl2kReactionReader# */ { /** @private */ CLASS_NAME: 'Kekule.IO.Mdl2kReactionReader', /** @private */ readHeaderBlock: function(/*$super, */textBuffer, parentObj) { var result = this.tryApplySuper('readHeaderBlock', [textBuffer, parentObj]) /* $super(textBuffer, parentObj) */; if (result.version != Kekule.IO.MdlVersion.V2000) { Kekule.error(/*Kekule.ErrorMsg.NOT_MDL2000_RXN_DATA*/Kekule.$L('ErrorMsg.NOT_MDL2000_RXN_DATA')); return null; } else return result; }, /** * Get reactant and product count from count line. * @param {Object} line * @private */ readSubstanceCountLine: function(line) { var result = {}; // rrrppp result.reactantCount = parseInt(line.substr(0, 3), 10) || 0; result.productCount = parseInt(line.substr(3, 3), 10) || 0; return result; }, /** @private */ readMolBlock: function(textBuffer) { var line = textBuffer.readLine(); //textBuffer.getLineAt(textBuffer.getCurrLineNo()); while ((line !== '$MOL') && (!textBuffer.eof())) line = textBuffer.readLine(); // skip heading $MOL mark if (textBuffer.eof()) // can not find mol block return null; // fetch lines until $MOL end tag if found var lines = []; //var line = textBuffer.readLine(); var line = textBuffer.getCurrLine(); while ((line !== '$MOL') && (!textBuffer.eof())) { lines.push(line); textBuffer.incCurrLineNo(); //line = textBuffer.readLine(); line = textBuffer.getCurrLine(); } return (new Kekule.IO.MdlMoleculeReader()).readBlock(lines, null); } }); /** * Class to read and anaylsis MDL RXN 3000 data and create Reaction. * @class * @augments Kekule.IO.MdlBaseReactionReader * @private */ Kekule.IO.Mdl3kReactionReader = Class.create(Kekule.IO.MdlBaseReactionReader, /** @lends Kekule.IO.Mdl3kReactionReader# */ { /** @private */ CLASS_NAME: 'Kekule.IO.Mdl3kReactionReader', /** @private */ createTextBuffer: function() { return (new Kekule.IO.Mdl3kTextBuffer()); }, /** @private */ readHeaderBlock: function(/*$super, */textBuffer, parentObj) { var result = this.tryApplySuper('readHeaderBlock', [textBuffer, parentObj]) /* $super(textBuffer, parentObj) */; if (result.version != Kekule.IO.MdlVersion.V3000) { Kekule.error(/*Kekule.ErrorMsg.NOT_MDL3000_RXN_DATA*/Kekule.$L('ErrorMsg.NOT_MDL3000_RXN_DATA')); return null; } else return result; }, /** @private */ readSubstanceCountLine: function(line) { var result = {}; // M V30 COUNTS rcount pcount var values = Kekule.IO.Mdl3kValueUtils.splitValues(line); if (values.shift().value.trim() != 'COUNTS') // not a count line { Kekule.error(/*Kekule.ErrorMsg.NOT_MDL3000_RXN_COUNTLINE*/Kekule.$L('ErrorMsg.NOT_MDL3000_RXN_COUNTLINE')); return null; } result.reactantCount = parseInt(values.shift().value, 10) || 0; result.productCount = parseInt(values.shift().value, 10) || 0; return result; }, /** @private */ readMolBlock: function(textBuffer) { if (textBuffer.getBlockBuffer) { var buffer = textBuffer.getBlockBuffer('CTAB'); return (new Kekule.IO.Mdl3kMoleculeCTabReader()).doReadBlock(buffer); } else return null; } }); /** * Class to read and anaylsis both RXN 2000 and 3000 data then create Reaction. * @class * @augments Kekule.IO.MdlBlockReader */ Kekule.IO.MdlReactionReader = Class.create(Kekule.IO.MdlBlockReader, /** @lends Kekule.IO.MdlReactionReader# */ { /** @private */ CLASS_NAME: 'Kekule.IO.MdlReactionReader', /** @private */ doReadBlock: function(textBuffer, parentObj) { var reader; var version = Kekule.IO.MdlUtils.getRxnMarkVersion(textBuffer.getCurrLine()); if (version == Kekule.IO.MdlVersion.V3000) reader = new Kekule.IO.Mdl3kReactionReader(); else reader = new Kekule.IO.Mdl2kReactionReader(); return reader.readBlock(textBuffer.getUnreadLines(), parentObj); } }); /** * Class to write both MDL RXN 2000 and 3000 data. * @class * @augments Kekule.IO.MdlBlockWriter * @private */ Kekule.IO.MdlReactionWriter = Class.create(Kekule.IO.MdlBlockWriter, /** @lends Kekule.IO.MdlReactionWriter# */ { /** @private */ CLASS_NAME: 'Kekule.IO.MdlReactionWriter', /** @constructs */ initialize: function(/*$super, */version, coordMode) { this.tryApplySuper('initialize') /* $super() */; this.setMdlVersion(version || Kekule.IO.MdlVersion.V2000); this.setCoordMode(coordMode || Kekule.CoordMode.UNKNOWN); }, /** @private */ initProperties: function() { this.defineProp('mdlVersion', {'dataType': DataType.INT, 'defaultValue': Kekule.IO.MdlVersion.V2000}); this.defineProp('coordMode', {'dataType': DataType.INT, 'defaultValue': Kekule.CoordMode.UNKNOWN}); }, /** * Write header block (first 4 lines) of a RXN file. * @param {Kekule.Reaction} reaction * @param {Kekule.TextLinesBuffer} textBuffer * @private */ writeHeaderBlock: function(reaction, textBuffer) { var s; // line 1: identifier s = '$RXN' + ((this.getMdlVersion() == Kekule.IO.MdlVersion.V3000)? ' ' + Kekule.IO.MDL.VER3000: ''); textBuffer.writeLine(s); // line 2: reaction name textBuffer.writeLine(reaction.getName() || ''); // line 3: reaction info // IIIIIIPPPPPPPPPMMDDYYYYHHmmRRRRRRR { // IIIIII: User's initials var author = reaction.getInfoValue('author') || ''; s = author.substr(0, 6).rpad(6); // PPPPPPPPP: program name var generator = reaction.getInfoValue('generator') || ''; s += generator.substr(0, 9).rpad(9); // MMDDYYYYHHmm: date time. var date = reaction.getInfoValue('date') || new Date(); // default is now // NOTE: early softwares may generate datetime string in short format, doesnot handle this now. s += Kekule.IO.MdlUtils.generateMdlDateTimeStr(date, true); // RRRRRRR: reg no, ignored } textBuffer.writeLine(s); // line 4: comment line var comment = reaction.getInfoValue('comment') || ''; textBuffer.writeLine(comment); }, /** * Get reactant and product count line. * @param {Kekule.Reaction} reaction * @private */ generateSubstanceCountLine: function(reaction) { // 3k: M V30 COUNTS rcount pcount if (this.getMdlVersion() == Kekule.IO.MdlVersion.V3000) return 'M V30 COUNTS ' + Kekule.IO.Mdl3kValueUtils.mergeValues([reaction.getReactantCount(), reaction.getProductCount()]); // 2k: rrrppp return reaction.getReactantCount().toString().lpad(3) + reaction.getProductCount().toString().lpad(3); }, /** * Write delimiter between molecules. * 2k will write '$MOL', 3k should do nothing * @param {Object} textBuffer */ writeMolDelimiterLine: function(textBuffer) { if (this.getMdlVersion() == Kekule.IO.MdlVersion.V2000) textBuffer.writeLine('$MOL'); }, /** * Write MOL blocks in RXN file. * @param {Kekule.Molecule} mol * @param {Kekule.TextLinesBuffer} textBuffer * @private */ writeMolBlock: function(mol, textBuffer) { var writer = this.getMdlVersion() == Kekule.IO.MdlVersion.V3000? new Kekule.IO.Mdl3kMoleculeCTabWriter(this.getCoordMode()): // no need to write compatible lines in 3k new Kekule.IO.MdlMoleculeWriter(this.getMdlVersion(), this.getCoordMode()); var text = writer.writeBlock(mol); textBuffer.writeText(text); }, /** @private */ doWriteBlock: function(/*$super, */reaction, textBuffer) { var is3kMode = this.getMdlVersion() == Kekule.IO.MdlVersion.V3000; // if in 3k mode, reactant and products should be surrounded by block begin/end tag // header this.writeHeaderBlock(reaction, textBuffer); var substanceInfoLine = this.generateSubstanceCountLine(reaction); textBuffer.writeLine(substanceInfoLine); // then MOL blocks if (is3kMode) textBuffer.writeLine('M V30 ' + Kekule.IO.Mdl3kUtils.get3kBlockStartTag('REACTANT')); for (var i = 0; i < reaction.getReactantCount(); ++i) { this.writeMolDelimiterLine(textBuffer); this.writeMolBlock(reaction.getReactantAt(i), textBuffer); } if (is3kMode) textBuffer.writeLine('M V30 ' + Kekule.IO.Mdl3kUtils.get3kBlockEndTag('REACTANT')); if (is3kMode) textBuffer.writeLine('M V30 ' + Kekule.IO.Mdl3kUtils.get3kBlockStartTag('PRODUCT')); for (var i = 0; i < reaction.getProductCount(); ++i) { this.writeMolDelimiterLine(textBuffer); this.writeMolBlock(reaction.getProductAt(i), textBuffer); } if (is3kMode) { textBuffer.writeLine('M V30 ' + Kekule.IO.Mdl3kUtils.get3kBlockEndTag('PRODUCT')); // total end tag textBuffer.writeLine('M END'); } } }); /** * Base abstract reader for MDL 2000 / 3000 format data. * @class * @augments Kekule.IO.ChemDataReader */ Kekule.IO.BaseMdlReader = Class.create(Kekule.IO.ChemDataReader, /** @lends Kekule.IO.BaseMdlReader# */ { /** @private */ CLASS_NAME: 'Kekule.IO.BaseMdlReader', /** @private */ doReadData: function(data, dataType, format) { if (dataType != Kekule.IO.ChemDataType.TEXT) // can not understand data other than text { Kekule.error(/*Kekule.ErrorMsg.MDL_INPUT_DATATYPE_NOT_TEXT*/Kekule.$L('ErrorMsg.MDL_INPUT_DATATYPE_NOT_TEXT')); return null; } else { return this.doReadMdlData(data); } }, /** * Do actual work of reading MDL data. * Descendants should override this method. * @param {String} data * @returns {Kekule.ChemObject} * @private */ doReadMdlData: function(data) { // do nothing here return null; } }); /** * Base abstract writer for MDL 2000 / 3000 format text data. * @class * @augments Kekule.IO.ChemDataWriter * * @property {Int} mdlVersion Output format. V2000 and V3000 has totally different data structures. * @property {Int} coordMode Output 2D or 3D coordinate for molecule. */ Kekule.IO.BaseMdlWriter = Class.create(Kekule.IO.ChemDataWriter, /** @lends Kekule.IO.BaseMdlWriter# */ { /** @private */ CLASS_NAME: 'Kekule.IO.BaseMdlWriter', /** @private */ initialize: function(/*$super, */options) { this.tryApplySuper('initialize', [options]) /* $super(options) */; if (!options) options = {}; this.setMdlVersion(options.mdlVersion || Kekule.globalOptions.IO.mdl.mdlVersion); this.setCoordMode(options.coordMode || Kekule.globalOptions.IO.mdl.coordMode); }, /** @private */ initProperties: function() { this.defineProp('mdlVersion', {'dataType': DataType.INT, 'defaultValue': Kekule.IO.MdlVersion.V2000}); this.defineProp('coordMode', {'dataType': DataType.INT, 'defaultValue': Kekule.CoordMode.UNKNOWN}); }, /** @private */ doWriteData: function(/*$super, */obj, dataType, format) { var dtype = dataType || Kekule.IO.ChemDataType.TEXT; if (dtype != Kekule.IO.ChemDataType.TEXT) // can not understand data other than text { Kekule.error(/*Kekule.ErrorMsg.MDL_OUTPUT_DATATYPE_NOT_TEXT*/Kekule.$L('ErrorMsg.MDL_OUTPUT_DATATYPE_NOT_TEXT')); return null; } else return this.tryApplySuper('doWriteData', [obj, dtype, format]) /* $super(obj, dtype, format) */; } }); /** * Reader for MOL 2000 / 3000 format text data. * Use MolReader.readData() can retrieve a suitable Kekule.Molecule. * Data fetch in should be a string. * @class * @augments Kekule.IO.BaseMdlReader */ Kekule.IO.MdlMolReader = Class.create(Kekule.IO.BaseMdlReader, /** @lends Kekule.IO.MdlMolReader# */ { /** @private */ CLASS_NAME: 'Kekule.IO.MdlMolReader', /** @private */ doReadMdlData: function(data) { var reader = new Kekule.IO.MdlMoleculeReader(); return reader.readBlock(data, null); } }); /** * Writer for MOL 2000 / 3000 format text data. * Use molWriter.writeData(obj) to save Kekule object to MDL text content. * @class * @augments Kekule.IO.BaseMdlWriter */ Kekule.IO.MdlMolWriter = Class.create(Kekule.IO.BaseMdlWriter, /** @lends Kekule.IO.MdlMolWriter# */ { /** @private */ CLASS_NAME: 'Kekule.IO.MdlMolWriter', /** @private */ doWriteData: function(obj, dataType, format) { /* if (dataType != Kekule.IO.ChemDataType.TEXT) // can not understand data other than text { Kekule.error(Kekule.ErrorMsg.MDL_OUTPUT_DATATYPE_NOT_TEXT); return null; } else */ { var writer = new Kekule.IO.MdlMoleculeWriter(this.getMdlVersion(), this.getCoordMode()); return writer.writeBlock(obj); } } }); /** * Reader for MOL 2000 / 3000 SD (structure-data) format text data. * Data fetch in should be a string. * @class * @augments Kekule.IO.BaseMdlReader */ Kekule.IO.MdlSdReader = Class.create(Kekule.IO.BaseMdlReader, /** @lends Kekule.IO.MdlSdReader# */ { /** @private */ CLASS_NAME: 'Kekule.IO.MdlSdReader', /** @private */ doReadMdlData: function(data) { { var reader = new Kekule.IO.MdlStructDataReader(); return reader.readBlock(data, null); } } }); /** * Writer for MOL 2000 / 3000 SD (structure-data) format text data. * @class * @augments Kekule.IO.BaseMdlWriter */ Kekule.IO.MdlSdWriter = Class.create(Kekule.IO.BaseMdlWriter, /** @lends Kekule.IO.MdlSdWriter# */ { /** @private */ CLASS_NAME: 'Kekule.IO.MdlSdWriter', /** @private */ doWriteData: function(obj, dataType, format) { var writer = new Kekule.IO.MdlStructDataWriter(this.getMdlVersion(), this.getCoordMode()); return writer.writeBlock(obj); } }); /** * Reader for RXN 2000/3000 format text data. * Use RxnReader.readData() can retrieve a suitable Kekule.Reaction. * Data fetch in should be a string. * @class * @augments Kekule.IO.BaseMdlReader */ Kekule.IO.MdlRxnReader = Class.create(Kekule.IO.BaseMdlReader, /** @lends Kekule.IO.MdlRxnReader# */ { /** @private */ CLASS_NAME: 'Kekule.IO.MdlRxnReader', /** @private */ doReadMdlData: function(data) { var reader = new Kekule.IO.MdlReactionReader(); return reader.readBlock(data, null); } }); /** * Writer for RXN 2000 / 3000 format text data. * Use RxnReader.writeData(obj) to save a Kekule.Reaction to MDL V2000 or 3000 text. * @class * @augments Kekule.IO.BaseMdlWriter */ Kekule.IO.MdlRxnWriter = Class.create(Kekule.IO.BaseMdlWriter, /** @lends Kekule.IO.MdlRxnWriter# */ { /** @private */ CLASS_NAME: 'Kekule.IO.MdlRxnWriter', /** @private */ doWriteData: function(obj, dataType, format) { /* if (dataType != Kekule.IO.ChemDataType.TEXT) // can not understand data other than text { Kekule.error(Kekule.ErrorMsg.MDL_OUTPUT_DATATYPE_NOT_TEXT); return null; } else */ { var writer = new Kekule.IO.MdlReactionWriter(this.getMdlVersion(), this.getCoordMode()); return writer.writeBlock(obj); } } }); /** * Reader for MOL or RXN 2000/3000 format text data. * Use MdlReader.readData() can retrieve a suitable Kekule object. * Data fetch in should be a string. * @class * @augments Kekule.IO.ChemDataReader */ Kekule.IO.MdlReader = Class.create(Kekule.IO.ChemDataReader, /** @lends Kekule.IO.MdlReader# */ { /** @private */ CLASS_NAME: 'Kekule.IO.MdlReader', /** @private */ doReadData: function(data, dataType, format) { if (dataType != Kekule.IO.ChemDataType.TEXT) // can not understand data other than text { Kekule.error(/*Kekule.ErrorMsg.MDL_INPUT_DATATYPE_NOT_TEXT*/Kekule.$L('ErrorMsg.MDL_INPUT_DATATYPE_NOT_TEXT')); return null; } else { var reader; // check first 4 chars of data, $RXN means reaction, otherwise, MOL var tag = data.substr(0, 4); if (tag == '$RXN') reader = new Kekule.IO.MdlReactionReader(); else reader = new Kekule.IO.MdlMoleculeReader(); return reader.readBlock(data, null); } } }); /** * A general class to write MOL or RXN 2000/3000 format text data. * Use MdlWriter.writeData() to save a reaction or molecule. * @class * @augments Kekule.IO.ChemDataWriter */ Kekule.IO.MdlWriter = Class.create(Kekule.IO.ChemDataWriter, /** @lends Kekule.IO.MdlWriter# */ { /** @private */ CLASS_NAME: 'Kekule.IO.MdlWriter', /** @private */ initialize: function(/*$super, */options) { this.tryApplySuper('initialize', [options]) /* $super(options) */; var op = options || {}; this.setMdlVersion(op.mdlVersion || Kekule.globalOptions.IO.mdl.mdlVersion); this.setCoordMode(op.coordMode || Kekule.globalOptions.IO.mdl.coordMode); }, /** @private */ initProperties: function() { this.defineProp('mdlVersion', {'dataType': DataType.INT, 'defaultValue': Kekule.IO.MdlVersion.V2000}); this.defineProp('coordMode', {'dataType': DataType.INT, 'defaultValue': Kekule.CoordMode.UNKNOWN}); }, /** @private */ doWriteData: function(obj, dataType, format) { var writer; if (obj instanceof Kekule.Reaction) writer = new Kekule.IO.MdlRxnWriter(this.getMdlVersion(), this.getCoordMode()); else if (obj instanceof Kekule.StructureFragment) writer = new Kekule.IO.MdlMolWriter(this.getMdlVersion(), this.getCoordMode()); else { var className = (obj && obj.getClassName)? obj.getClassName(): typeof(obj); Kekule.error(/*Kekule.ErrorMsg.UNABLE_TO_OUTPUT_AS_MDL*/Kekule.$L('ErrorMsg.UNABLE_TO_OUTPUT_AS_MDL').format(className)); return null; } if (writer) return writer.writeData(obj); } }); (function () { // register chem data formats var molFmtId = 'mol'; var rxnFmtId = 'rxn'; var sdFmtId = 'sd'; var mol3kFmtId = 'mol3k'; var rxn3kFmtId = 'rxn3k'; Kekule.IO.DataFormat.MOL = molFmtId; Kekule.IO.DataFormat.RXN = rxnFmtId; Kekule.IO.DataFormat.SD = sdFmtId; Kekule.IO.DataFormat.MOL3K = mol3kFmtId; Kekule.IO.DataFormat.RXN3K = rxn3kFmtId; // extents mime type consts Kekule.IO.MimeType.MDL_MOL = 'chemical/x-mdl-molfile'; Kekule.IO.MimeType.MDL_RXN = 'chemical/x-mdl-rxnfile'; Kekule.IO.MimeType.MDL_SD = 'chemical/x-mdl-sdfile'; /* Kekule.IO.DataFormatsManager.register('mol', 'chemical/x-mdl-molfile', 'mol', Kekule.IO.ChemDataType.TEXT, 'MDL Mol 2000/3000 format'); Kekule.IO.DataFormatsManager.register('rxn', 'chemical/x-mdl-rxnfile', 'rxn', Kekule.IO.ChemDataType.TEXT, 'MDL Reaction 2000/3000 format'); Kekule.IO.DataFormatsManager.register('sd', 'chemical/x-mdl-sdfile', ['sd', 'sdf'], Kekule.IO.ChemDataType.TEXT, 'MDL Structure-Data format'); var molFmtId = Kekule.IO.DataFormatsManager.findFormatId('chemical/x-mdl-molfile'); var rxnFmtId = Kekule.IO.DataFormatsManager.findFormatId('chemical/x-mdl-rxnfile'); var sdFmtId = Kekule.IO.DataFormatsManager.findFormatId('chemical/x-mdl-sdfile'); */ Kekule.IO.DataFormatsManager.register(molFmtId, Kekule.IO.MimeType.MDL_MOL, 'mol', Kekule.IO.ChemDataType.TEXT, 'MDL Mol 2000 format'); Kekule.IO.DataFormatsManager.register(rxnFmtId, Kekule.IO.MimeType.MDL_RXN, 'rxn', Kekule.IO.ChemDataType.TEXT, 'MDL Reaction 2000 format'); Kekule.IO.DataFormatsManager.register(sdFmtId, Kekule.IO.MimeType.MDL_SD, ['sd', 'sdf'], Kekule.IO.ChemDataType.TEXT, 'MDL Structure-Data format'); Kekule.IO.DataFormatsManager.register(mol3kFmtId, Kekule.IO.MimeType.MDL_MOL, 'mol', Kekule.IO.ChemDataType.TEXT, 'MDL Mol 3000 format'); Kekule.IO.DataFormatsManager.register(rxn3kFmtId, Kekule.IO.MimeType.MDL_RXN, 'rxn', Kekule.IO.ChemDataType.TEXT, 'MDL Reaction 3000 format'); // register ChemData reader and writer Kekule.IO.ChemDataReaderManager.register('MDL-mol', Kekule.IO.MdlMolReader, [molFmtId, mol3kFmtId]); Kekule.IO.ChemDataReaderManager.register('MDL-rxn', Kekule.IO.MdlRxnReader, [rxnFmtId, rxn3kFmtId]); Kekule.IO.ChemDataReaderManager.register('MDL-general', Kekule.IO.MdlReader, [molFmtId, mol3kFmtId, rxnFmtId, rxn3kFmtId]); Kekule.IO.ChemDataReaderManager.register('MDL-sd', Kekule.IO.MdlSdReader, sdFmtId); var suitableClasses = [Kekule.StructureFragment, Kekule.ChemObjList, Kekule.ChemStructureObjectGroup, Kekule.ChemSpaceElement, Kekule.ChemSpace]; Kekule.IO.ChemDataWriterManager.register('MDL-mol', Kekule.IO.MdlMolWriter, /*[Kekule.StructureFragment]*/suitableClasses, molFmtId); Kekule.IO.ChemDataWriterManager.register('MDL-mol3k', Kekule.IO.MdlMolWriter, /*[Kekule.StructureFragment]*/suitableClasses, mol3kFmtId, {'createOptions': {'mdlVersion': Kekule.IO.MdlVersion.V3000}}); Kekule.IO.ChemDataWriterManager.register('MDL-rxn', Kekule.IO.MdlRxnWriter, [Kekule.Reaction], rxnFmtId); Kekule.IO.ChemDataWriterManager.register('MDL-rxn3k', Kekule.IO.MdlRxnWriter, [Kekule.Reaction], rxn3kFmtId); //Kekule.IO.ChemDataWriterManager.register('MDL-general', Kekule.IO.MdlWriter, [Kekule.StructureFragment, Kekule.Reaction], [molFmtId, mol3kFmtId, rxnFmtId, rxn3kFmtId]); Kekule.IO.ChemDataWriterManager.register('MDL-sd', Kekule.IO.MdlSdWriter, suitableClasses, sdFmtId); /* TODO: MDL 2000 and 3000 have same MIME type, so they are registered by same format ID TODO: How to save object in different format separately? */ /* // register ChemData reader and writer Kekule.IO.ChemDataReaderManager.register('mdl-general', Kekule.IO.MdlReader, { 'title': 'MDL format general', 'mimeType': '', 'fileExt': ['mol', 'rxn'] }); Kekule.IO.ChemDataReaderManager.register('mol', Kekule.IO.MdlMolReader, { 'title': 'MDL Mol format', 'mimeType': 'chemical/x-mdl-molfile', 'fileExt': 'mol' }); Kekule.IO.ChemDataReaderManager.register('rxn', Kekule.IO.MdlRxnReader, { 'title': 'MDL Reaction format', 'mimeType': 'chemical/x-mdl-rxnfile', 'fileExt': 'rxn' }); Kekule.IO.ChemDataWriterManager.register('mdl-general', Kekule.IO.MdlWriter, [Kekule.ChemStructureFragment, Kekule.Reaction], { 'title': 'MDL format general', 'mimeType': '', 'fileExt': ['mol', 'rxn'] }); Kekule.IO.ChemDataWriterManager.register('mol2000', Kekule.IO.MdlMolWriter, [Kekule.ChemStructureFragment], { 'createOptions': {'mdlVersion': Kekule.IO.MdlVersion.V2000}, 'title': 'MDL Mol 2000 format', 'mimeType': 'chemical/x-mdl-molfile', 'fileExt': 'mol' }); Kekule.IO.ChemDataWriterManager.register('mol3000', Kekule.IO.MdlMolWriter, [Kekule.ChemStructureFragment], { 'createOptions': {'mdlVersion': Kekule.IO.MdlVersion.V3000}, 'title': 'MDL Mol 3000 format', 'mimeType': 'chemical/x-mdl-molfile', 'fileExt': 'mol' }); Kekule.IO.ChemDataWriterManager.register('rxn2000', Kekule.IO.MdlRxnWriter, [Kekule.Reaction], { 'createOptions': {'mdlVersion': Kekule.IO.MdlVersion.V2000}, 'title': 'MDL Reaction 2000 format', 'mimeType': 'chemical/x-mdl-rxnfile', 'fileExt': 'rxn' }); Kekule.IO.ChemDataWriterManager.register('rxn3000', Kekule.IO.MdlRxnWriter, [Kekule.Reaction], { 'createOptions': {'mdlVersion': Kekule.IO.MdlVersion.V3000}, 'title': 'MDL Reaction 3000 format', 'mimeType': 'chemical/x-mdl-rxnfile', 'fileExt': 'rxn' }); */ })();