wilzbach/msa

View on GitHub
src/msa.js

Summary

Maintainability
B
6 hrs
Test Coverage
// models
import SeqCollection from "./model/SeqCollection";

// globals
import Colorator from "./g/colorscheme";
import Columns from "./g/columns";
import Config from "./g/config";
import Package from "./g/package";
import SelCol from "./g/selection/SelectionCol";
import User from "./g/user";
import Visibility from "./g/visibility";
import VisOrdering from "./g/visOrdering";
import Zoomer from "./g/zoomer";

import StageScale from "./g/StageScale";

// MV from backbone
const boneView = require("backbone-childs");
const Eventhandler = require("biojs-events");

// MSA views
import Stage from "./views/Stage";

// statistics
const Stats = require("stat.seqs");

// utils
const $ = require("jbone");
import FileHelper from "./utils/file";
import TreeHelper from "./utils/tree";
import ProxyHelper from "./utils/proxy";

// opts is a dictionary consisting of
// @param el [String] id or reference to a DOM element
// @param seqs [SeqArray] Array of sequences for initlization
// @param conf [Dict] user config
// @param vis [Dict] config of visible views
// @param zoomer [Dict] display settings like columnWidth
const MSA = boneView.extend({

  initialize: function(data) {

    if (!(typeof data !== "undefined" && data !== null)) { data = {}; }
    // check for default arrays
    if (!(data.colorscheme != null)) { data.colorscheme = {}; }
    if (!(data.columns != null)) { data.columns = {}; }
    if (!(data.conf != null)) { data.conf = {}; }
    if (!(data.vis != null)) { data.vis = {}; }
    if (!(data.visorder != null)) { data.visorder = {}; }
    if (!(data.zoomer != null)) { data.zoomer = {}; }
    if (!(data.conserv != null)) { data.conserv = {}; }
    if (!(data.scale != null)) { data.scale = {}; }

    // g is our global Mediator
    this.g = Eventhandler.mixin({});

    // load seqs and add subviews
    this.seqs = this.g.seqs = new SeqCollection(data.seqs, this.g);

    // populate it and init the global models
    this.g.config = new Config(data.conf);
    this.g.package = new Package(this.g);
    this.g.selcol = new SelCol([],{g:this.g});
    this.g.user = new User();
    this.g.vis = new Visibility(data.vis, {model: this.seqs});
    this.g.visorder = new VisOrdering(data.visorder);
    this.g.zoomer = new Zoomer(data.zoomer,{g:this.g, model: this.seqs});

    this.g.scale = new StageScale(data.scale, {g: this.g});

    // store config options for plugins
    this.g.conservationConfig = data.conserv;

    // debug mode
    if (window.location.hostname === "localhost") {
      this.g.config.set("debug", true);
    }

    this._loadSeqs(data);

    // utils
    this.u = {};
    this.u.file = new FileHelper(this);
    this.u.proxy = new ProxyHelper({g: this.g});
    this.u.tree = new TreeHelper(this);

    if (this.g.config.get("eventBus") === true) {
      this.startEventBus();
    }

    if (this.g.config.get("dropImport")) {
      var events =
        {"dragover": this.dragOver,
        "drop": this.dropFile
        };
      this.delegateEvents(events);
    }

    if (data.importURL) {
      this.u.file.importURL(data.importURL, () => {
        return this.render();
      });
    }

    if (data.bootstrapMenu) {
      // pass menu configuration to defaultmenu
      if(data.menu){
        this.menuConfig = data.menu;
      }
      this.g.config.set("bootstrapMenu", true);
    }

    this.draw();
    // add models to the msa (convenience)
    return this.m();
  },

  _loadSeqs: function(data) {
    // stats
    var pureSeq = this.seqs.pluck("seq");
    this.g.stats = new Stats(this.seqs, {useGaps: true});
    this.g.stats.alphabetSize = this.g.config.get("alphabetSize");
    this.g.columns = new Columns(data.columns,this.g.stats);  // for action on the columns like hiding

    // depending config
    this.g.colorscheme = new Colorator(data.colorscheme, pureSeq, this.g.stats);

    // more init
    return this.g.zoomer.setEl(this.el, this.seqs);
  },

  // proxy to the utility package
  importURL: function() {
    return this.u.file.importURL.apply(this.u.file, arguments);
  },

  // add models to the msa (convenience)
  m: function() {
    var m = {};
    m.model = require("./model");
    m.selection = require("./g/selection/Selection");
    m.selcol = require("./g/selection/SelectionCol");
    m.view = require("backbone-viewj");
    m.boneView = require("backbone-childs");
    return this.m = m;
  },

  draw: function() {

    this.removeViews();

    this.addView("stage",new Stage({model: this.seqs, g: this.g}));
    this.$el.addClass("biojs_msa_div");

    // bootstraps the menu bar by default -> destroys modularity
    if (this.g.config.get("bootstrapMenu")) {
      var menuDiv = document.createElement('div');
      var wrapperDiv = document.createElement('div');
      if (!this.el.parentNode) {
        wrapperDiv.appendChild(menuDiv);
        wrapperDiv.appendChild(this.el);
      } else {
        this.el.parentNode.replaceChild(wrapperDiv, this.el);
        wrapperDiv.appendChild(menuDiv);
        wrapperDiv.appendChild(this.el);
      }

      var bootstrapOpts = {el: menuDiv,
        msa: this,
      };
      if(this.menuConfig){
        bootstrapOpts.menu = this.menuConfig;
      }
      var defMenu = new msa.menu.defaultmenu(bootstrapOpts);
      defMenu.render();
    }

    return $(window).on("resize", (e) => {
      var f = function() {
        return this.g.zoomer.autoResize();
      };
      return setTimeout(f.bind(this), 5);
    }
    );
  },

  dragOver: function(e) {
    // prevent the normal browser actions
    e.preventDefault();
    e.target.className = 'hover';
    return false;
  },

  dropFile: function(e) {
    e.preventDefault();
    var files = e.target.files || e.dataTransfer.files;
    this.u.file.importFiles(files);
    return false;
  },

  startEventBus() {
    var busObjs = ["config", "columns", "colorscheme", "selcol" ,"vis", "visorder", "zoomer"];
    return (() => {
      var result = [];
      for (var i = 0, key; i < busObjs.length; i++) {
        key = busObjs[i];
        result.push(this._proxyToG(key));
      }
      return result;
    })();
  },

  _proxyToG: function(key) {
    return this.listenTo(this.g[key], "all",function(name,prev,now,opts) {
      // suppress duplicate events
      if (name === "change") { return; }
      // backbone uses the second argument for the next value -> swap
      if ((typeof opts !== "undefined" && opts !== null)) {
        return this.g.trigger(key + ":" + name,now,prev,opts);
      } else {
        return this.g.trigger(key + ":" + name,now,prev);
      }
    });
  },

  render: function() {
    if (this.seqs === undefined || this.seqs.length === 0) {
      console.log("warning. empty seqs.");
    }
    this.renderSubviews();
    this.g.vis.set("loaded", true);
    return this;
  }
});
export default MSA;