Tutorial > Developing Plugins

The ChemDoodle Web Components library is an incredibly powerful toolkit for building scientific applications. Even so, there are many cases where 3rd party or very specific functionality needs to be integrated, such as querying a private database or adding in your own file format. However, modifying the library's core source code can be tedious and is not practical if you or the users of your functionality want to make use of updates to the ChemDoodle Web Components.

This is where plugins are useful. It is very easy to develop custom plugins that will add to or modify the functionality of the ChemDoodle Web Components while leaving the core library source code undisturbed. This page covers the techniques needed to write them efficiently.

The JavaScript Module Pattern

The JavaScript module pattern allows for many techniques in JavaScript development, including private variables, simple plugin development and efficient minification.

It looks like this:

(function(input_vars) {
  // this is a private scope
})(input_vars);

This outer function will be executed, sending the input_vars to the inner scope, where the plugin will be defined. The inner scope is private so any variables and functions will not pollute the global JavaScript namespace. Make sure to always use the var syntax!

A lot of neat things can be accomplished with the module pattern, which we will not go into depth here. The following are a few resources that provide more insight into the module pattern:

Adding Functionality

Functionality can be added to the ChemDoodle Web Components library in several ways. A function can be added to the global ChemDoodle variable, a function can be added to a class or package in the library, or a new class or package can be added.

Adding a function to the global ChemDoodle variable

Adding a function to the global ChemDoodle variable is quick method to add a small amount of functionality to the library in an easily accessible way. The ChemDoodle global variable is sent into the module, and a function is added to it. The following simple example shows how to add a function to the global ChemDoodle variable that counts the number of nitrogen atoms in a molecule.

// the module
(function(ChemDoodle) {
  
  // private variables go here, they cannot be accessed beyond this local scope
  let labelToMatch = 'N';

  // append the function to the ChemDoodle variable
  ChemDoodle.countNitrogens = function(molecule){
    let count = 0;
    for(let i = 0, ii = molecule.atoms.length; i<ii; i++){
      if(molecule.atoms[i].label==labelToMatch){
        count++;
      }
    }
    return count;
  };

})(ChemDoodle);

Now we can call ChemDoodle.countNitrogens() at any time.

Adding a function to a package in the ChemDoodle Web Components library is an identical procedure, except that the package you are adding the function to will be sent into the module instead of the global ChemDoodle variable, like so:

(function(p) {
  p.myFunction = function(){};
})(ChemDoodle.packageToAddTo);

Notice that we used the name p for the package variable inside the module. This is much more convenient than using the full package name or path. Regardless, local variables within the scope of a module will automatically be shortened by minification tools.

Adding a function to a class in the ChemDoodle Web Components library

Instead of adding a static function to the library, we may want to extend a class to add functionality specific to created instances. In the next example we add a function to the parent Canvas class to programmatically tell if it contains exactly 1 molecule.

(function(c) {

  c.prototype.has1Molecule = function(){
    return this.molecules.length === 1;
  };

})(ChemDoodle._Canvas);

Now you can call the has1Molecule() function for any instantiated Canvas. A couple things to note, first we sent in ChemDoodle._Canvas to the module, this is because the parent Canvas class is abstract, and all abstract classes in the ChemDoodle Web Components start with an underscore. Secondly, we used the prototype keyword so that all instances of the class will also have this function. If we did not use the prototype keyword, then only the abstract class would have the function defined.

In addition to adding new functionality, we can modify existing functionality by overriding class functions. For instance, lets say hypothetically that we are creating an application that passes a molecule between Canvases, and we would like to keep track of how many times the molecule has been passed. We can override the getMolecule() function to do this for us as shown in the following code:

(function(c) {

  // save the old function so we can call it in ours and not lose the ability to render
  // make sure we set it to the prototype so that any references to the 'this' keyword will still work
  c.prototype.oldGetMoleculeFunction = c.prototype.getMolecule;

  // replace the getMolecule() function
  c.prototype.getMolecule = function(){
    // call the old one to retain functionality
    let mol = this.oldGetMoleculeFunction();
    // if passCount exists, increment it, otherwise set it to 1
    if(mol.passCount){
      mol.passCount++;
    }else{
      mol.passCount=1;
    }
    return mol;
  };

})(ChemDoodle._Canvas);

Now each molecule will keep track of the number of times it was passed in the passCount variable.

These examples are quite trivial, but there are some very complex problems that can be solved by using these plugin techniques, preventing you from modifying the core library source. It should be noted that overriding or changing the behavior of the core library in this way may confuse users of your plugin. Make sure to clearly state what your plugin does and what it changes. It may be more beneficial to instead create a new child of the ChemDoodle Web Components' classes, so that behavior is more predictable.

The classes and functions within the ChemDoodle Web Components library are documented in the API.

Adding Classes to the ChemDoodle Web Components library

Classes can be added to the library in the same way that functions are. Creating children of the library classes has already been discussed in the tutorial. The following code shows how to package the HideAndSeekCanvas in a plugin module.

(function(c) {

  c.HideAndSeekCanvas = function(id, width, height, color){
    this.color = color;
    this.fillOverlay = true;
    this.mouseover = function(){
      this.fillOverlay = false;
      this.repaint();
    }
    this.mouseout = function(){
      this.fillOverlay = true;
      this.repaint();
    }
    this.drawChildExtras = function(ctx){
      if(this.fillOverlay){
        ctx.fillStyle=this.color;
        ctx.fillRect(0, 0, this.width, this.height);
      }
    }
    this.create(id, width, height);
  }
  c.HideAndSeekCanvas.prototype = new c._Canvas();

})(ChemDoodle);

That's it, we just set it to the ChemDoodle object and it can now be instantiated through the global ChemDoodle variable just like every other Canvas.

Packaging the Plugin

Packaging the Plugin for use and distribution is quite easy. Just save your module(s) in a JavaScript file (with the extension .js). Users of your plugin will just need to include its URI after the ChemDoodle Web Components URIs have been included.

You may be interested in also providing a packed or minified version of your plugin. Several free tools are available to do so. We use the Google Closure Compiler to minify the ChemDoodle Web Components library.

Get your work done with our popular desktop software.