/**
   * @param {Property.<RealMolecule>} moleculeProperty
   * @param {RealMoleculesViewProperties} viewProperties
   * @param {Object} [options]
   * @constructor
   */
  function JSmolViewerNode( moleculeProperty, viewProperties, options ) {

    options = _.extend( {
      viewerFill: 'white',
      viewerStroke: 'black', // {string} color of the viewer's background
      viewerSize: new Dimension2( 200, 200 )
    }, options );

    this.moleculeProperty = moleculeProperty; // @private
    this.viewProperties = viewProperties; // @private
    this.options = options; // @private

    // @private Put the Jmol object in a div, sized to match the Jmol object
    this.div = document.createElement( 'div' );
    this.div.style.width = options.viewerSize.width + 'px';
    this.div.style.height = options.viewerSize.height + 'px';
    this.div.style.border = '1px solid ' + options.viewerStroke;

    // @private JSmol must be initialized after the sim is running
    this.applet = null;

    // @public {Property.Array.<Element>} elements in the molecule displayed by the viewer
    this.elementsProperty = new Property( null );

    options.preventTransform = true;
    DOM.call( this, this.div, options );
  }
  //REVIEW: Note that this may change significantly if we go with a three.js/webgl solution
  function Molecule3DNode( completeMolecule, initialBounds, useHighRes ) {
    var self = this;

    this.draggingProperty = new Property( false );

    // prepare the canvas
    this.canvas = document.createElement( 'canvas' );
    this.context = this.canvas.getContext( '2d' );
    this.backingScale = useHighRes ? Util.backingScale( this.context ) : 1;
    this.canvas.className = 'canvas-3d';
    this.canvas.style.position = 'absolute';
    this.canvas.style.left = '0';
    this.canvas.style.top = '0';
    this.setMoleculeCanvasBounds( initialBounds );

    // construct ourself with the canvas (now properly initially sized)
    DOM.call( this, this.canvas, {
      preventTransform: true
    } );

    // map the atoms into our enhanced format
    this.currentAtoms = completeMolecule.atoms.map( to3d );

    // center the bounds of the atoms
    var bounds3 = Bounds3.NOTHING.copy();
    this.currentAtoms.forEach( function( atom ) {
      bounds3.includeBounds( new Bounds3( atom.x - atom.covalentRadius, atom.y - atom.covalentRadius,
        atom.z - atom.covalentRadius, atom.x + atom.covalentRadius, atom.y + atom.covalentRadius,
        atom.z + atom.covalentRadius ) );
    } );
    var center3 = bounds3.center;
    if ( center3.magnitude ) {
      this.currentAtoms.forEach( function( atom ) {
        atom.subtract( center3 );
      } );
    }

    // compute our outer bounds so we can properly scale our transform to fit
    var maxTotalRadius = 0;
    this.currentAtoms.forEach( function( atom ) {
      maxTotalRadius = Math.max( maxTotalRadius, atom.magnitude + atom.covalentRadius );
    } );
    this.maxTotalRadius = maxTotalRadius;

    var gradientMap = {}; // element symbol => gradient
    this.currentAtoms.forEach( function( atom ) {
      if ( !gradientMap[ atom.element.symbol ] ) {
        gradientMap[ atom.element.symbol ] = self.createGradient( atom.element );
      }
    } );
    this.gradientMap = gradientMap;

    this.dragging = false;

    this.lastPosition = Vector2.ZERO;
    this.currentPosition = Vector2.ZERO;

    if ( grabInitialTransforms ) {
      this.masterMatrix = Matrix3.identity();
    }
  }