draw(reaction, target, themeName = 'light', weights = null, textAbove = '{reagents}', textBelow = '', infoOnly = false) {
    this.themeManager = new ThemeManager(this.molOpts.themes, themeName); // Normalize the weights over the reaction molecules

    if (this.opts.weights.normalize) {
      let max = -Number.MAX_SAFE_INTEGER;
      let min = Number.MAX_SAFE_INTEGER;

      if (weights.hasOwnProperty('reactants')) {
        for (let i = 0; i < weights.reactants.length; i++) {
          for (let j = 0; j < weights.reactants[i].length; j++) {
            if (weights.reactants[i][j] < min) {
              min = weights.reactants[i][j];
            }

            if (weights.reactants[i][j] > max) {
              max = weights.reactants[i][j];
            }
          }
        }
      }

      if (weights.hasOwnProperty('reagents')) {
        for (let i = 0; i < weights.reagents.length; i++) {
          for (let j = 0; j < weights.reagents[i].length; j++) {
            if (weights.reagents[i][j] < min) {
              min = weights.reagents[i][j];
            }

            if (weights.reagents[i][j] > max) {
              max = weights.reagents[i][j];
            }
          }
        }
      }

      if (weights.hasOwnProperty('products')) {
        for (let i = 0; i < weights.products.length; i++) {
          for (let j = 0; j < weights.products[i].length; j++) {
            if (weights.products[i][j] < min) {
              min = weights.products[i][j];
            }

            if (weights.products[i][j] > max) {
              max = weights.products[i][j];
            }
          }
        }
      }

      let abs_max = Math.max(Math.abs(min), Math.abs(max));

      if (abs_max === 0.0) {
        abs_max = 1;
      }

      if (weights.hasOwnProperty('reactants')) {
        for (let i = 0; i < weights.reactants.length; i++) {
          for (let j = 0; j < weights.reactants[i].length; j++) {
            weights.reactants[i][j] /= abs_max;
          }
        }
      }

      if (weights.hasOwnProperty('reagents')) {
        for (let i = 0; i < weights.reagents.length; i++) {
          for (let j = 0; j < weights.reagents[i].length; j++) {
            weights.reagents[i][j] /= abs_max;
          }
        }
      }

      if (weights.hasOwnProperty('products')) {
        for (let i = 0; i < weights.products.length; i++) {
          for (let j = 0; j < weights.products[i].length; j++) {
            weights.products[i][j] /= abs_max;
          }
        }
      }
    }

    let svg = null;

    if (target === null || target === 'svg') {
      svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
      svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
      svg.setAttributeNS(null, 'width', 500 + '');
      svg.setAttributeNS(null, 'height', 500 + '');
    } else if (typeof target === 'string' || target instanceof String) {
      svg = document.getElementById(target);
    } else {
      svg = target;
    }

    while (svg.firstChild) {
      svg.removeChild(svg.firstChild);
    }

    let elements = [];
    let maxHeight = 0.0; // Reactants

    for (var i = 0; i < reaction.reactants.length; i++) {
      if (i > 0) {
        elements.push({
          width: this.opts.plus.size * this.opts.scale,
          height: this.opts.plus.size * this.opts.scale,
          svg: this.getPlus()
        });
      }

      let reactantWeights = null;

      if (weights && weights.hasOwnProperty('reactants') && weights.reactants.length > i) {
        reactantWeights = weights.reactants[i];
      }

      let reactantSvg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
      this.drawer.draw(reaction.reactants[i], reactantSvg, themeName, reactantWeights, infoOnly, [], this.opts.weights.normalize);
      let element = {
        width: reactantSvg.viewBox.baseVal.width * this.opts.scale,
        height: reactantSvg.viewBox.baseVal.height * this.opts.scale,
        svg: reactantSvg
      };
      elements.push(element);

      if (element.height > maxHeight) {
        maxHeight = element.height;
      }
    } // Arrow


    elements.push({
      width: this.opts.arrow.length * this.opts.scale,
      height: this.opts.arrow.headSize * 2.0 * this.opts.scale,
      svg: this.getArrow()
    }); // Text above the arrow / reagents

    let reagentsText = "";

    for (var i = 0; i < reaction.reagents.length; i++) {
      if (i > 0) {
        reagentsText += ", ";
      }

      let text = this.drawer.getMolecularFormula(reaction.reagents[i]);

      if (text in formulaToCommonName) {
        text = formulaToCommonName[text];
      }

      reagentsText += SvgWrapper.replaceNumbersWithSubscript(text);
    }

    textAbove = textAbove.replace('{reagents}', reagentsText);
    const topText = SvgWrapper.writeText(textAbove, this.themeManager, this.opts.fontSize * this.opts.scale, this.opts.fontFamily, this.opts.arrow.length * this.opts.scale);
    let centerOffsetX = (this.opts.arrow.length * this.opts.scale - topText.width) / 2.0;
    elements.push({
      svg: topText.svg,
      height: topText.height,
      width: this.opts.arrow.length * this.opts.scale,
      offsetX: -(this.opts.arrow.length * this.opts.scale + this.opts.spacing) + centerOffsetX,
      offsetY: -(topText.height / 2.0) - this.opts.arrow.margin,
      position: 'relative'
    }); // Text below arrow

    const bottomText = SvgWrapper.writeText(textBelow, this.themeManager, this.opts.fontSize * this.opts.scale, this.opts.fontFamily, this.opts.arrow.length * this.opts.scale);
    centerOffsetX = (this.opts.arrow.length * this.opts.scale - bottomText.width) / 2.0;
    elements.push({
      svg: bottomText.svg,
      height: bottomText.height,
      width: this.opts.arrow.length * this.opts.scale,
      offsetX: -(this.opts.arrow.length * this.opts.scale + this.opts.spacing) + centerOffsetX,
      offsetY: bottomText.height / 2.0 + this.opts.arrow.margin,
      position: 'relative'
    }); // Products

    for (var i = 0; i < reaction.products.length; i++) {
      if (i > 0) {
        elements.push({
          width: this.opts.plus.size * this.opts.scale,
          height: this.opts.plus.size * this.opts.scale,
          svg: this.getPlus()
        });
      }

      let productWeights = null;

      if (weights && weights.hasOwnProperty('products') && weights.products.length > i) {
        productWeights = weights.products[i];
      }

      let productSvg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
      this.drawer.draw(reaction.products[i], productSvg, themeName, productWeights, infoOnly, [], this.opts.weights.normalize);
      let element = {
        width: productSvg.viewBox.baseVal.width * this.opts.scale,
        height: productSvg.viewBox.baseVal.height * this.opts.scale,
        svg: productSvg
      };
      elements.push(element);

      if (element.height > maxHeight) {
        maxHeight = element.height;
      }
    }

    let totalWidth = 0.0;
    elements.forEach(element => {
      let offsetX = element.offsetX ?? 0.0;
      let offsetY = element.offsetY ?? 0.0;
      element.svg.setAttributeNS(null, 'x', Math.round(totalWidth + offsetX));
      element.svg.setAttributeNS(null, 'y', Math.round((maxHeight - element.height) / 2.0 + offsetY));
      element.svg.setAttributeNS(null, 'width', Math.round(element.width));
      element.svg.setAttributeNS(null, 'height', Math.round(element.height));
      svg.appendChild(element.svg);

      if (element.position !== 'relative') {
        totalWidth += Math.round(element.width + this.opts.spacing + offsetX);
      }
    });
    svg.setAttributeNS(null, 'viewBox', `0 0 ${totalWidth} ${maxHeight}`);
    svg.style.width = totalWidth + 'px';
    svg.style.height = maxHeight + 'px';
    return svg;
  }
Draws the parsed reaction smiles data to a canvas element.

@param {Object} reaction The reaction object returned by the reaction smiles parser.
@param {(String|SVGElement)} target The id of the HTML canvas element the structure is drawn to - or the element itself.
@param {String} themeName='dark' The name of the theme to use. Built-in themes are 'light' and 'dark'.
@param {?Object} weights=null The weights for reactants, agents, and products.
@param {String} textAbove='{reagents}' The text above the arrow.
@param {String} textBelow='' The text below the arrow.
@param {?Object} weights=null The weights for reactants, agents, and products.
@param {Boolean} infoOnly=false Only output info on the molecule without drawing anything to the canvas.

@returns {SVGElement} The svg element