javascript for manipulating molecule properties in MRV

User 870ab5b546

19-01-2017 15:28:17

We use molecule properties extensively. MarvinSketch had available Javascript methods that would allow us to manipulate molecule properties of the structur ein the applet. MarvinJS, unfortunately, does not yet recognize the concept of molecule properties, let alone provide methods to manipulate them. We have been using an AJAX call to a JSP page that uses JChem methods to manipulate molecule properties, but it is awkward, and we've recently found a case where it does not suffice for our needs. So, I wrote some Javascript that imports the MRV into an XMLDoc and allows the user to add, change, or remove a molecule property in a document containing a single molecule (not useful for reactions). I'm not sure how robust the method is for different cases, but it should give you a good starting point.


/* Puts a molecule property into the MRV. Assumes tree in order
* <molecule><propertyList><property><scalar>value
* or
* <molecule><propertyList><property><scalar><![CDATA[value]]>
* There are several cases:
* * If the value is not empty (null or empty string) and there is no existing
* property with the appropriate name, and there is no existing property at all,
* add a property list, then add the property with its value.
* * If the value is not empty (null or empty string) and there is no existing
* property with the appropriate name, but there are other existing properties,
* add the property with its value.
* * If the value is not empty and there is an existing property with the same
* name, modify the value of the existing property.
* * If the value is empty and there is no existing property with the same name,
* do nothing.
* * If the value is empty and there is an existing property with the same name,
* remove the property. If it is the only property in the property list, remove
* the property list as well.
* Uses CDATA field iff value contains any <>&;/ characters. Not sure why the ;
* character triggers CDATA, but that's what JChem does.
*/
function modifyMrvProperty(mrv, propertyName, propertyValue) {
"use strict";
var nodeNum,
numNodes,
moleculeNodeChild,
propertyListNode = null,
aPropertyNode = null,
targetPropertyNode = null,
scalarNode = null,
scalarChildNode = null,
serializer = new XMLSerializer();
var parser = new DOMParser();
var xmlDoc = parser.parseFromString(mrv.replace(/\\n/g, ''), 'text/xml');
var moleculeNodes = xmlDoc.getElementsByTagName('molecule');
var moleculeNode = moleculeNodes[0];
// find the propertyList node under the molecule node
numNodes = moleculeNode.childNodes.length;
for (nodeNum = 0; nodeNum < numNodes; nodeNum++) {
moleculeNodeChild = moleculeNode.childNodes[nodeNum];
if (moleculeNodeChild.nodeName === 'propertyList') {
propertyListNode = moleculeNodeChild;
break;
} // if nodeName
} // for each molecule node child
// if not already one, make propertyList node, add to moleculeNode
if (propertyListNode == null) {
if (isEmpty(propertyValue)) return mrv; // no change in original MRV
else { // did not find existing propertyList; making one
propertyListNode = xmlDoc.createElement('propertyList');
moleculeNodeChild = moleculeNode.childNodes[0];
moleculeNode.insertBefore(propertyListNode, moleculeNodeChild);
} // if propertyValue is empty
} // if need to add a property list node
// find the target property node
numNodes = propertyListNode.childNodes.length;
for (nodeNum = 0; nodeNum < numNodes; nodeNum++) {
aPropertyNode = propertyListNode.childNodes[nodeNum];
if (aPropertyNode.title === propertyName) {
targetPropertyNode = aPropertyNode;
if (isEmpty(propertyValue)) {
propertyListNode.removeChild(targetPropertyNode);
// see if there are any other properties
numNodes = propertyListNode.childNodes.length;
for (nodeNum = 0; nodeNum < numNodes; nodeNum++) {
aPropertyNode = propertyListNode.childNodes[nodeNum];
if (aPropertyNode.nodeName === 'property') {
return serializer.serializeToString(xmlDoc);
} // if nodeName
} // for each propertyList node child
// no other properties; remove propertyListNode, return MRV
moleculeNode.removeChild(propertyListNode);
return serializer.serializeToString(xmlDoc);
} // if propertyValue is empty
break;
} // if title
} // for each propertyList node child
// if not already one, make target property node, add
// scalar node as child, add to propertyListNode
if (targetPropertyNode == null) {
// did not find existing reactionIds property; making one
targetPropertyNode = xmlDoc.createElement('property');
targetPropertyNode.setAttribute('dictRef', propertyName);
targetPropertyNode.setAttribute('title', propertyName);
scalarNode = xmlDoc.createElement('scalar');
targetPropertyNode.appendChild(scalarNode);
propertyListNode.appendChild(targetPropertyNode);
} // if need to add the target property node
// get scalar node
numNodes = targetPropertyNode.childNodes.length;
for (nodeNum = 0; nodeNum < numNodes; nodeNum++) {
scalarNode = targetPropertyNode.childNodes[nodeNum];
if (scalarNode.nodeName === 'scalar') break;
} // for each targetPropertyNode child
// remove existing child of scalar node that has old value
numNodes = scalarNode.childNodes.length;
for (nodeNum = 0; nodeNum < numNodes; nodeNum++) {
scalarChildNode = scalarNode.childNodes[nodeNum];
if (scalarChildNode.nodeName === '#text'
|| scalarChildNode.nodeName === '#cdata-section') {
scalarNode.removeChild(scalarChildNode);
break;
} // if found a child node with old value
} // for each scalar node child
// make node with new value, add back into scalar node
scalarNode.appendChild(needsToBeInCDATA(propertyValue)
? xmlDoc.createCDATASection(propertyValue)
: xmlDoc.createTextNode(propertyValue));
return serializer.serializeToString(xmlDoc);
} // modifyMrvProperty()

// returns true if string should be in a <![CDATA[ ]]> field in an XML document.
function needsToBeInCDATA(s) {
"use strict";
if (isEmpty(s)) {
return true;
}
var i, c, cdataChars = '&<>;/';
for (i = 0; i < cdataChars.length; i += 1) {
c = cdataChars.charAt(i);
if (s.indexOf(c) != -1) {
return true;
}
}
return false;
} // needsToBeInCDATA()