wilzbach/msa

View on GitHub
src/g/zoomer.js

Summary

Maintainability
A
0 mins
Test Coverage
var Zoomer;
var Model = require("backbone-thin").Model;
// pixel properties for some components
module.exports = Zoomer = Model.extend({

  constructor: function(attributes,options) {
    this.calcDefaults(options.model);
    Model.apply(this, arguments);
    this.g = options.g;

    // events
    this.listenTo( this, "change:labelIdLength change:labelNameLength change:labelPartLength change:labelCheckLength", (function() {
      return this.trigger("change:labelWidth", this.getLabelWidth());
    }), this
    );
    this.listenTo( this, "change:metaLinksWidth change:metaIdentWidth change:metaGapWidth", (function() {
      return this.trigger("change:metaWidth", this.getMetaWidth());
    }), this
    );

    return this;
  },

  defaults: {

    // general
    alignmentWidth: "auto",
    alignmentHeight: 225,
    columnWidth: 15,
    rowHeight: 15,
    autoResize: true, // only for the width

    // labels
    labelIdLength: 20,
    labelNameLength: 100,
    labelPartLength: 15,
    labelCheckLength: 15,
    labelFontsize: 13,
    labelLineHeight: "13px",

    // marker
    markerFontsize: "10px",
    stepSize: 1,
    markerStepSize: 2,
    markerHeight: 20,

    // canvas
    residueFont: "13", // in px
    canvasEventScale: 1,
    minLetterDrawSize: 11,

    // overview box
    boxRectHeight: 2,
    boxRectWidth: 2,
    overviewboxPaddingTop: 10,
    overviewboxWidth: "fixed",   // "auto" (fitting div) or "fixed"
    overviewboxHeight: "fixed",  // "fixed" or in px

    // meta cell
    metaGapWidth: 35,
    metaIdentWidth: 40,
    // metaLinksWidth: 25

    // internal props
    _alignmentScrollLeft: 0,
    _alignmentScrollTop: 0
    },

  // sets some defaults, depending on the model
  calcDefaults: function(model) {
    var maxLen = model.getMaxLength();
    if (maxLen < 200 && model.length < 30) {
      this.defaults.boxRectWidth = this.defaults.boxRectHeight = 5;
    }
    return this;
  },

  // @param n [int] maxLength of all seqs
  getAlignmentWidth: function(n) {
    if (this.get("autoResize") && n !== undefined) {
      return this.get("columnWidth") * n;
    }
    if (this.get("alignmentWidth") === undefined || this.get("alignmentWidth") === "auto" || this.get("alignmentWidth") === 0) {
      return this._adjustWidth();
    } else {
      return this.get("alignmentWidth");
    }
  },

  // @param n [int] number of residues to scroll to the right
  setLeftOffset: function(n) {
    var val = (n);
    val = Math.max(0, val);
    val -= this.g.columns.calcHiddenColumns(val);
    return this.set("_alignmentScrollLeft", val * this.get('columnWidth'));
  },

  // @param n [int] row that should be on top
  setTopOffset: function(n) {
    var val = Math.max(0, (n - 1));
    var height = 0;
    for (var i = 0; 0 < val ? i <= val : i >= val; 0 < val ? i++ : i--) {
      var seq = this.model.at(i);
      height += seq.attributes.height || 1;
    }
    return this.set("_alignmentScrollTop",height * this.get("rowHeight"));
  },

  // length of all elements left to the main sequence body: labels, metacell, ..
  getLeftBlockWidth: function() {
     var paddingLeft = 0;
     if (this.g.vis.get("labels")) { paddingLeft += this.getLabelWidth(); }
     if (this.g.vis.get("metacell")) { paddingLeft += this.getMetaWidth(); }
     //paddingLeft += 15 # scroll bar
     return paddingLeft;
  },

  getMetaWidth: function() {
     var val = 0;
     if (this.g.vis.get("metaGaps")) { val += this.get("metaGapWidth"); }
     if (this.g.vis.get("metaIdentity")) { val += this.get("metaIdentWidth"); }
     if (this.g.vis.get("metaLinks")) { val += this.get("metaLinksWidth"); }
     return val;
  },

  getLabelWidth: function() {
     var val = 0;
     if (this.g.vis.get("labelName")) { val += this.get("labelNameLength"); }
     if (this.g.vis.get("labelId")) { val += this.get("labelIdLength"); }
     if (this.g.vis.get("labelPartition")) { val += this.get("labelPartLength"); }
     if (this.g.vis.get("labelCheckbox")) { val += this.get("labelCheckLength"); }
     return val;
  },

  _adjustWidth: function() {
    if (!(this.el !== undefined && this.model !== undefined)) { return; }
    if ((this.el.parentNode != null) && this.el.parentNode.offsetWidth !== 0) {
      var parentWidth = this.el.parentNode.offsetWidth;
    } else {
      parentWidth = document.body.clientWidth - 35;
    }

    // TODO: dirty hack
    var maxWidth = parentWidth - this.getLeftBlockWidth();
    var calcWidth = this.getAlignmentWidth( this.model.getMaxLength() - this.g.columns.get('hidden').length);
    var val = Math.min(maxWidth,calcWidth);
    // round to a valid AA box
    val = Math.floor( val / this.get("columnWidth")) * this.get("columnWidth");

    //@set "alignmentWidth", val
    return this.attributes.alignmentWidth = val;
  },

  autoResize: function() {
    if (this.get("autoResize")) {
      return this._adjustWidth(this.el, this.model);
    }
  },

  // max is the maximal allowed height
  autoHeight: function(max) {
    // TODO!
    // make seqlogo height configurable
    var val = this.getMaxAlignmentHeight();
    if (max !== undefined && max > 0) {
      val = Math.min(val, max);
    }

    return this.set("alignmentHeight", val);
  },

  setEl: function(el, model) {
    this.el = el;
    return this.model = model;
  },

  // updates both scroll properties (if needed)
  _checkScrolling: function(scrollObj, opts) {
    var xScroll = scrollObj[0];
    var yScroll = scrollObj[1];

    this.set("_alignmentScrollLeft", xScroll, opts);
    return this.set("_alignmentScrollTop", yScroll, opts);
  },

  getMaxAlignmentHeight: function() {
    var height = 0;
    this.model.each(function(seq) {
      return height += seq.attributes.height || 1;
    });

    return (height * this.get("rowHeight"));
  },

  getMaxAlignmentWidth: function() {
    return this.model.getMaxLength() * this.get("columnWidth");
  }
});