block by timelyportfolio 5225228

gist of clickme for bl.ocks.org viewing

Full Screen

#Unbelievable clickme d3 straight from R

I deserve no credit for this. Go to clickme for the guy that deserves all the credit. I just simply put it in Gist form for it to show up on bl.ocks.org

index.html

<!DOCTYPE html>
<html>

  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>PCP Heatmap</title>
    <script src="//html5shim.googlecode.com/svn/trunk/html5.js"></script>
<script src="//code.jquery.com/jquery-1.7.1.min.js"></script>
<script src="//documentcloud.github.com/underscore/underscore.js"></script>
<script src="//documentcloud.github.com/backbone/backbone.js"></script>
<script src="//twitter.github.com/bootstrap/assets/js/bootstrap-tooltip.js"></script>
<script src="//twitter.github.com/bootstrap/assets/js/bootstrap-popover.js"></script>
<script src="//mbostock.github.com/d3/d3.js"></script>
<script src="helper.js"></script>
<script src="longitudinal_model.js"></script>
<script src="dashboard_view.js"></script>
<script src="heatmap_view.js"></script>
<script src="pcp_view.js"></script>
    <link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet">
<link href="leanback_style.css" rel="stylesheet">
<link href="longitudinal.css" rel="stylesheet">
  </head>

  <body>

    <div id='dashboard'>
      <div class='clusters'></div>
      <h1></h1>
    </div>
    <main>
      <svg id='heatmap'></svg>
      <svg id='pcp'></svg>
    </main>

    <script type="text/javascript">

      var urlVars = getUrlVars();

      $(document).ready(function() {
        d3.text("data.csv", function(text) {
          var parsedGenes = d3.csv.parse(text);

          var userOptions = { urlVars: urlVars, rowNameColumn: "gene_symbol", rowType: "genes", yAxisName: "log-ratio" };
          var model = new window.LongitudinalModel(parsedGenes, userOptions);

          var dashboardView = new window.DashboardView({ el: "#dashboard", model: model});
          var heatmapView = new window.HeatmapView({ el: "#heatmap", model: model});
          var pcpView = new window.PcpView({ el: "#pcp", model: model});

          d3.select("body").style("padding-top", d3.select("#dashboard").node().offsetHeight);

          resetOnClick(model);
        });
      });
    </script>

  </body>
</html>

dashboard_view.js

// Generated by CoffeeScript 1.4.0
(function() {
  var __hasProp = {}.hasOwnProperty,
    __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };

  window.DashboardView = (function(_super) {

    __extends(DashboardView, _super);

    function DashboardView() {
      return DashboardView.__super__.constructor.apply(this, arguments);
    }

    DashboardView.prototype.initialize = function() {
      var _this = this;
      this.render();
      this.model.on("change:currentCluster", function() {
        return _this.showTitle();
      });
      return this.model.on("change:currentTag", function() {
        return _this.showTitle();
      });
    };

    DashboardView.prototype.events = function() {
      return {
        "click #dashboard .clusters span": "changeCurrentCluster"
      };
    };

    DashboardView.prototype.changeCurrentCluster = function(e) {
      var currentCluster;
      e.stopPropagation();
      this.model.attributes['currentTag'] = null;
      d3.selectAll(".tag_name").classed("current", 0);
      currentCluster = d3.select(e.target);
      d3.selectAll(".clusters span").classed("current", 0);
      if (currentCluster && this.model.get("currentCluster") === currentCluster.attr("name")) {
        return this.model.set({
          currentCluster: null
        });
      } else {
        currentCluster.classed("current", 1);
        return this.model.set({
          currentCluster: currentCluster.attr("name")
        });
      }
    };

    DashboardView.prototype.showTitle = function() {
      var currentCluster, currentTag, title;
      title = "" + (this.getCurrentRows().length) + " " + this.model.options.rowType;
      if (this.model.options.showGroups) {
        title += " from " + (joinSentence(this.model.columnGroups));
      }
      if (this.model.options.showClusters && (currentCluster = this.model.get("currentCluster"))) {
        title += " (Cluster " + currentCluster + ")";
      }
      if (this.model.options.showTags && (currentTag = this.model.get("currentTag"))) {
        title += " (Tagged " + currentTag + ")";
      }
      d3.select("title").text(title);
      return d3.select("#dashboard h1").text(title);
    };

    DashboardView.prototype.getCurrentRows = function() {
      var currentCluster, currentTag;
      if (this.model.options.showClusters && (currentCluster = this.model.get("currentCluster"))) {
        return d3.selectAll(".row[cluster='" + currentCluster + "']")[0];
      } else if (this.model.options.showTags && (currentTag = this.model.get("currentTag"))) {
        return d3.selectAll(".tag[name='" + currentTag + "']")[0];
      } else {
        return this.model.parsedData;
      }
    };

    DashboardView.prototype.render = function() {
      var _this = this;
      this.showTitle();
      if (this.model.options.showClusters) {
        return d3.select("#dashboard .clusters").selectAll("span").data(this.model.clusterNames).enter().append("span").attr("name", function(d) {
          return d;
        }).text(function(d) {
          return "Cluster " + d;
        }).style("background", function(d) {
          return window.clusterColor(d);
        });
      }
    };

    return DashboardView;

  })(Backbone.View);

}).call(this);

heatmap_view.js

// Generated by CoffeeScript 1.4.0
(function() {
  var __hasProp = {}.hasOwnProperty,
    __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };

  window.HeatmapView = (function(_super) {

    __extends(HeatmapView, _super);

    function HeatmapView() {
      return HeatmapView.__super__.constructor.apply(this, arguments);
    }

    HeatmapView.prototype.initialize = function() {
      var _this = this;
      if (!this.model.options.hideHeatmap) {
        this.render();
        this.model.on("change:currentRowId", function() {
          return _this.showCurrentRow();
        });
        this.model.on("change:clickedRowId", function() {
          return _this.showClickedRow();
        });
        this.model.on("change:currentCluster", function() {
          return _this.showCurrentCluster();
        });
        return this.model.on("change:currentTag", function() {
          return _this.showCurrentTag();
        });
      }
    };

    HeatmapView.prototype.events = function() {
      return {
        "mouseover .cell": "changeCurrentRowId",
        "mouseout .row": "changeCurrentRowId",
        "click .row": "changeClickedRowId",
        "click .tag": "changeCurrentTag",
        "click .tag_name": "changeCurrentTag"
      };
    };

    HeatmapView.prototype.render = function() {
      var columnName, rowName,
        _this = this;
      this.heatmapColor = d3.scale.linear().domain([-1.5, 0, 1.5]).range(["#278DD6", "#fff", "#d62728"]);
      this.rowTextScaleFactor = 15;
      this.columnTextScaleFactor = 10;
      this.columnNamesMargin = d3.max((function() {
        var _i, _len, _ref, _results;
        _ref = this.model.columnNames;
        _results = [];
        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
          columnName = _ref[_i];
          _results.push(columnName.length);
        }
        return _results;
      }).call(this));
      this.rowNamesMargin = d3.max((function() {
        var _i, _len, _ref, _results;
        _ref = this.model.rowNames;
        _results = [];
        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
          rowName = _ref[_i];
          _results.push(rowName.length);
        }
        return _results;
      }).call(this));
      this.margin = {
        top: 250,
        right: this.calculateRightMargin(),
        bottom: 50,
        left: 50
      };
      this.cellSize = 25;
      this.width = this.cellSize * this.model.columnNames.length;
      this.height = this.cellSize * this.model.rowNames.length;
      this.heatmap = d3.select(this.el).attr("width", this.width + this.margin.right + this.margin.left).attr("height", this.height + this.margin.top + this.margin.bottom).attr("class", "heatmap").append("g").attr("transform", "translate(" + this.margin.left + "," + this.margin.top + ")");
      this.x = d3.scale.ordinal().domain(d3.range(this.model.longitudinalData[0].length)).rangeBands([0, this.width]);
      this.y = d3.scale.ordinal().domain(d3.range(this.model.rowNames.length)).rangeBands([0, this.height]);
      this.columns = this.heatmap.selectAll(".column").data(this.model.columnNames).enter().append("g").attr("class", "column");
      this.columns.append("text").attr("x", 6).attr("y", this.x.rangeBand() / 2).attr("dy", "-.5em").attr("dx", ".5em").attr("text-anchor", "start").attr("transform", function(d, i) {
        return "translate(" + (_this.x(i)) + ") rotate(-45)";
      }).text(function(d, i) {
        return _this.model.columnNames[i];
      });
      this.addRows();
      if (this.model.options.tagFile) {
        this.addTags();
      }
      this.$("rect.cell").tooltip({
        title: function() {
          return this.__data__.condition.split("_").join(" ") + "<br>" + d3.round(this.__data__.value, 2);
        },
        placement: "top"
      });
      return this.$("rect.tag").tooltip({
        title: function() {
          return this.__data__;
        },
        placement: "top"
      });
    };

    HeatmapView.prototype.calculateRightMargin = function() {
      var maxrowNameLength;
      maxrowNameLength = d3.max(this.model.rowNames.map(function(x) {
        return x.length;
      }));
      return maxrowNameLength * 18;
    };

    HeatmapView.prototype.addRows = function() {
      var getRow, rows, that,
        _this = this;
      that = this;
      getRow = function(row) {
        var cell,
          _this = this;
        return cell = d3.select(this).selectAll(".cell").data(row).enter().append("rect").attr("class", "cell").attr("x", function(d, i) {
          return that.x(i);
        }).attr("width", that.x.rangeBand()).attr("height", that.x.rangeBand()).text(function(d) {
          return d;
        }).style("fill", function(d) {
          return that.heatmapColor(d.value);
        });
      };
      rows = this.heatmap.selectAll(".row").data(this.model.longitudinalData).enter().append("g").attr("class", "row").attr("row-id", function(d, i) {
        return _this.model.rowIds[i];
      }).attr("cluster", function(d, i) {
        return _this.model.clusters[i];
      }).attr("transform", function(d, i) {
        return "translate(0," + (_this.y(i)) + ")";
      }).each(getRow);
      if (!this.model.options.hideRowNames) {
        return rows.append("text").attr("x", this.width + this.calculateRightMargin()).attr("y", this.x.rangeBand() / 2).attr("dy", ".32em").attr("text-anchor", "end").text(function(d, i) {
          return _this.model.rowNames[i];
        });
      }
    };

    HeatmapView.prototype.addTags = function() {
      var heatmapWidth, maxTagNameLength, tagMargin, tagNames, tagScale, tagSize, tags,
        _this = this;
      tags = "";
      $.ajax({
        url: this.model.options.tagFile,
        context: document.body,
        async: false,
        success: function(data) {
          return tags = window.parseTags(data);
        }
      });
      tagNames = window.getTagNames(tags);
      maxTagNameLength = d3.max(tagNames.map(function(x) {
        return x.length;
      })) * 12;
      tagSize = this.cellSize / 2;
      tagMargin = 12;
      heatmapWidth = d3.select("#heatmap").node().offsetWidth;
      tagScale = d3.scale.ordinal().domain(d3.range(tagNames.length)).rangeBands([0, (tagNames.length - 1) * (tagSize + tagMargin)]);
      this.heatmap.selectAll(".row").selectAll(".tag").data(function(d, i) {
        return tags[_this.model.rowNames[i]];
      }).enter().append("rect").attr("class", "tag").attr("x", heatmapWidth).attr("y", this.x.rangeBand() / 4).attr("transform", function(d, i) {
        return "translate(" + tagScale(tagNames.indexOf(d)) + ",0)";
      }).attr("width", this.x.rangeBand() / 2).attr("height", this.x.rangeBand() / 2).attr("row-id", function(d) {
        return d;
      }).text(function(d) {
        return d;
      }).style("fill", function(d, i) {
        return window.tagColor(d);
      }).style("stroke", "none");
      this.heatmap.selectAll(".tag_name").data(tagNames).enter().append("text").attr("class", "tag_name").attr("dx", heatmapWidth + 10).attr("dy", 0).attr("transform", function(d, i) {
        return "translate(" + tagScale(tagNames.indexOf(d)) + (",0) rotate(-45 " + heatmapWidth + " 0)");
      }).attr("text-anchor", "start").attr("name", function(d) {
        return d;
      }).text(function(d) {
        return d;
      }).attr("fill", "black");
      return d3.select("#heatmap").attr("width", parseInt(d3.select("#heatmap").attr("width")) + tagNames.length * 25 + 100);
    };

    HeatmapView.prototype.changeCurrentRowId = function(e) {
      e.stopPropagation();
      return this.model.set({
        currentRowId: e.type === "mouseover" ? d3.select(e.target.parentNode).attr("row-id") : null
      });
    };

    HeatmapView.prototype.changeClickedRowId = function(e) {
      e.stopPropagation();
      return this.model.set({
        clickedRowId: d3.select(e.target.parentNode).attr("row-id")
      });
    };

    HeatmapView.prototype.changeCurrentTag = function(e) {
      var currentTag;
      e.stopPropagation();
      this.model.attributes['currentCluster'] = null;
      d3.selectAll(".clusters span").classed("current", 0);
      d3.selectAll(".tag_name").classed("current", 0);
      currentTag = d3.select(e.target);
      if (currentTag && this.model.get("currentTag") === currentTag.attr("name")) {
        return this.model.set({
          currentTag: null
        });
      } else {
        d3.selectAll(".tag_name[row-id=" + (currentTag.text()) + "]").classed("current", 1);
        return this.model.set({
          currentTag: currentTag.text()
        });
      }
    };

    HeatmapView.prototype.showCurrentRow = function() {
      var currentRowId, currentRowIdId;
      d3.selectAll(".row").filter(":not(.clicked)").classed("current", 0);
      currentRowIdId = this.model.get("currentRowIdId");
      if (currentRowIdId != null) {
        currentRowId = this.heatmap.select("[row-id=" + currentRowIdId + "]");
        return currentRowId.classed("current", 1);
      }
    };

    HeatmapView.prototype.showClickedRow = function() {
      var clickedRow, clickedRowId;
      clickedRowId = this.model.get("clickedRowId");
      if (clickedRowId != null) {
        clickedRow = this.heatmap.select("[row-id=" + clickedRowId + "]");
        d3.select("#pcp").style("top", Math.max(150, clickedRow.filter(".row").node().offsetParent.scrollTop) + 50);
        this.removeClickedRow();
        return clickedRow.classed("current clicked", 1);
      }
    };

    HeatmapView.prototype.removeClickedRow = function() {
      return d3.selectAll(".row").classed("current clicked", 0);
    };

    HeatmapView.prototype.showCurrentCluster = function() {
      var currentCluster, rowsToUpdate,
        _this = this;
      currentCluster = this.model.get("currentCluster");
      $(".row").hide();
      if (currentCluster) {
        rowsToUpdate = this.heatmap.selectAll(".row[cluster='" + currentCluster + "']");
      } else {
        rowsToUpdate = this.heatmap.selectAll(".row");
      }
      $(rowsToUpdate[0]).show();
      return rowsToUpdate.attr("transform", function(d, i) {
        return "translate(0," + (_this.y(i)) + ")";
      });
    };

    HeatmapView.prototype.showCurrentTag = function() {
      var currentTag, rowsToUpdate,
        _this = this;
      currentTag = this.model.get("currentTag");
      $(".row").hide();
      if (currentTag) {
        rowsToUpdate = d3.selectAll($(".tag[row-id='" + currentTag + "']").parent());
      } else {
        rowsToUpdate = this.heatmap.selectAll(".row");
      }
      $(rowsToUpdate[0]).show();
      return rowsToUpdate.attr("transform", function(d, i) {
        return "translate(0," + (_this.y(i)) + ")";
      });
    };

    return HeatmapView;

  })(Backbone.View);

}).call(this);

helper.js

// Generated by CoffeeScript 1.4.0
(function() {

  window.clusterColor = d3.scale.category10();

  window.tagColor = d3.scale.category20();

  window.no_tag = "-";

  window.parseTags = function(content) {
    var tag_lines, tags;
    tag_lines = d3.csv.parseRows(content);
    tags = {};
    tag_lines.map(function(tag_line) {
      var values;
      values = tag_line.slice(1);
      return tags[tag_line[0]] = values.indexOf(window.no_tag) === -1 ? values : [];
    });
    return tags;
  };

  window.getFrequencies = function(array) {
    var frequencies;
    frequencies = {};
    _.chain(array).groupBy(function(p) {
      return p;
    }).each(function(e, i) {
      return frequencies[i] = _.size(e);
    });
    return frequencies;
  };

  window.getTagNames = function(tags) {
    var tagFrequencies, tagValues;
    tagValues = _.chain(tags).values().flatten();
    tagFrequencies = window.getFrequencies(tagValues.value());
    return tagValues.unique().sortBy(function(x) {
      return tagFrequencies[x];
    }).value().reverse();
  };

  window.replace = function(array, replaceItem, replaceWith) {
    var item, _i, _len, _results;
    _results = [];
    for (_i = 0, _len = array.length; _i < _len; _i++) {
      item = array[_i];
      if (item === replaceItem) {
        _results.push(replaceWith);
      } else {
        _results.push(item);
      }
    }
    return _results;
  };

  window.isNumber = function(n) {
    return !isNaN(parseFloat(n)) && isFinite(n);
  };

  window.getUrlVars = function() {
    var vars;
    vars = {};
    window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/g, function(m, key, value) {
      return vars[key] = value;
    });
    return vars;
  };

  window.resetOnClick = function(model) {
    return d3.select("body").on("click", function() {
      model.set({
        clickedRowId: null
      });
      model.set({
        currentTag: null
      });
      d3.selectAll(".tag_name").classed("current", 0);
      model.set({
        currentCluster: null
      });
      return d3.selectAll(".clusters span").classed("current", 0);
    });
  };

  window.joinSentence = function(arr) {
    var arr2, last, sentence;
    arr2 = arr.slice(0);
    last = arr2.pop();
    if (arr2.length > 0) {
      sentence = arr2.join(", ");
      return sentence + " and " + last;
    } else {
      return last;
    }
  };

}).call(this);

leanback_style.css

/* clearfix */
.clearfix:before,
.clearfix:after {
  content: "";
  display: table;
}
.clearfix:after {
  clear: both;
}
.clearfix {
  zoom: 1;
}

body, .gist-meta {
  font: 14px Helvetica Neue;
  /*background: #f4f4f4 url(../images/background.png);*/
}

a {
  color: steelblue !important;
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

body, .gist {
  text-rendering: optimizeLegibility;
  margin-top: 1em;
}

.body {
  width: 960px;
  margin: auto;
}

.logo {
  color: steelblue;
}

.readme {
  margin-bottom: 3em;
}

.time {
  color: #666;
  font-size: smaller;
  font-style: oblique;
}

h1 {
  font-size: 36px;
  font-weight: 300;
}

h2 {
  font-weight: normal;
  color: #666;
}

h2 .description {
  color: #000;
  font-weight: bold;
}

iframe {
  width: 960px;
  height: 500px;
  border: 1px solid #DEDEDE;
}

.about {
  display: block;
  font-weight: 300;
  line-height: 60px;
}

.about .domain {
  color: black;
}

.right {
  float: right;
}

longitudinal.css

body {
  margin: 0 auto;
  padding: 50px 0 0 0; }

main {
  display: block; }

text {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 16px; }

#landing {
  font-size: 24px;
  margin: 0 auto;
  width: 960px; }
  #landing a {
    font-weight: bold; }

.pcp {
  position: absolute;
  cursor: pointer; }
  .pcp .title {
    font-size: 25px;
    font-weight: bold; }
  .pcp .expression-line {
    stroke-width: 6;
    stroke-opacity: 0.5;
    stroke-linecap: round;
    stroke-join: round;
    fill: none; }
    .pcp .expression-line.current {
      stroke-opacity: 1;
      stroke: #d62728; }
  .pcp .expression-text {
    font-weight: bold; }
  .pcp .expression-point {
    fill: white;
    stroke: black;
    stroke-width: 4px; }
  .pcp .axis path, .pcp .axis .tick {
    stroke: black;
    stroke-opacity: 1;
    stroke-width: 2; }
  .pcp .axis line {
    shape-rendering: crispEdges; }
  .pcp .axis.zero {
    stroke: black;
    stroke-width: 2;
    stroke-opacity: 1;
    stroke-dasharray: 8, 5;
    fill: none; }
  .pcp .axis text {
    font-weight: bold; }

.tag_name {
  fill: black; }
  .tag_name:hover {
    fill: #d62728; }
  .tag_name.current {
    fill: #d62728; }

.heatmap {
  float: left;
  cursor: pointer; }
  .heatmap .row rect {
    stroke: black;
    stroke-width: 1px; }
  .heatmap .row.current.clicked rect {
    stroke: black; }
  .heatmap .row.current text {
    font-weight: bold; }
  .heatmap .column text {
    font-weight: normal; }

#dashboard {
  position: fixed;
  top: 0;
  width: 100%;
  z-index: 9999;
  background-color: black;
  padding: 20px; }
  #dashboard h1 {
    line-height: 40px;
    color: white;
    margin: 0;
    font-family: Lobster, Helvetica, Arial, sans-serif;
    font-weight: bold; }
  #dashboard .clusters {
    float: right;
    width: 800px;
    padding: 0 40px 0 0;
    font-family: Lobster, Georgia, serif;
    font-weight: bold; }
    #dashboard .clusters span {
      cursor: pointer;
      -moz-user-select: -moz-none;
      -khtml-user-select: none;
      -webkit-user-select: none;
      -ms-user-select: none;
      user-select: none;
      color: white;
      width: 140px;
      float: left;
      font-size: 30px;
      margin: 0 5px 5px 0;
      padding: 5px 0;
      text-align: center;
      -webkit-border-radius: 10px;
      -moz-border-radius: 10px;
      border-radius: 10px; }
      #dashboard .clusters span.current {
        -webkit-box-shadow: 0px 4px 4px 0px #aaaaaa;
        -moz-box-shadow: 0px 4px 4px 0px #aaaaaa;
        box-shadow: 0px 4px 4px 0px #aaaaaa; }
      #dashboard .clusters span:active {
        position: relative;
        top: 3px; }

.tooltip {
  position: absolute;
  z-index: 1020;
  display: block;
  padding: 5px;
  font-size: 20px;
  transform: translate(12px, 0);
  opacity: 0;
  visibility: visible; }
  .tooltip.in {
    opacity: 0.8;
    filter: alpha(opacity=80); }
  .tooltip.top {
    margin-top: -2px; }
  .tooltip.right {
    margin-left: 2px; }
  .tooltip.bottom {
    margin-top: 2px; }
  .tooltip.left {
    margin-left: -2px; }
  .tooltip.top .tooltip-arrow {
    bottom: 0;
    left: 50%;
    margin-left: -5px;
    border-top: 5px solid black;
    border-right: 5px solid transparent;
    border-left: 5px solid transparent; }
  .tooltip.left .tooltip-arrow {
    top: 50%;
    right: 0;
    margin-top: -5px;
    border-top: 5px solid transparent;
    border-bottom: 5px solid transparent;
    border-left: 5px solid black; }
  .tooltip.bottom .tooltip-arrow {
    top: 0;
    left: 50%;
    margin-left: -5px;
    border-right: 5px solid transparent;
    border-bottom: 5px solid black;
    border-left: 5px solid transparent; }
  .tooltip.right .tooltip-arrow {
    top: 50%;
    left: 0;
    margin-top: -5px;
    border-top: 5px solid transparent;
    border-right: 5px solid black;
    border-bottom: 5px solid transparent; }

.tooltip-inner {
  max-width: 200px;
  padding: 3px 8px;
  color: white;
  font-weight: bold;
  text-align: center;
  text-decoration: none;
  background-color: black;
  -webkit-border-radius: 4px;
  -moz-border-radius: 4px;
  border-radius: 4px; }

.tooltip-arrow {
  position: absolute;
  width: 0;
  height: 0; }

.popover {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 1010;
  display: none;
  padding: 5px; }
  .popover.top {
    margin-top: -5px; }
  .popover.right {
    margin-left: 5px; }
  .popover.bottom {
    margin-top: 5px; }
  .popover.left {
    margin-left: -5px; }
  .popover.top .arrow {
    bottom: 0;
    left: 50%;
    margin-left: -5px;
    border-top: 5px solid black;
    border-right: 5px solid transparent;
    border-left: 5px solid transparent; }
  .popover.right .arrow {
    top: 50%;
    left: 0;
    margin-top: -5px;
    border-top: 5px solid transparent;
    border-right: 5px solid black;
    border-bottom: 5px solid transparent; }
  .popover.bottom .arrow {
    top: 0;
    left: 50%;
    margin-left: -5px;
    border-right: 5px solid transparent;
    border-bottom: 5px solid black;
    border-left: 5px solid transparent; }
  .popover.left .arrow {
    top: 50%;
    right: 0;
    margin-top: -5px;
    border-top: 5px solid transparent;
    border-bottom: 5px solid transparent;
    border-left: 5px solid black; }
  .popover .arrow {
    position: absolute;
    width: 0;
    height: 0; }

.popover-inner {
  width: 280px;
  padding: 3px;
  overflow: hidden;
  background: black;
  background: rgba(0, 0, 0, 0.8);
  -webkit-border-radius: 6px;
  -moz-border-radius: 6px;
  border-radius: 6px;
  -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
  -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
  box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); }

.popover-title {
  padding: 9px 15px;
  line-height: 1;
  background-color: #f5f5f5;
  border-bottom: 1px solid #eeeeee;
  -webkit-border-radius: 3px 3px 0 0;
  -moz-border-radius: 3px 3px 0 0;
  border-radius: 3px 3px 0 0; }

.popover-content {
  padding: 14px;
  background-color: white;
  -webkit-border-radius: 0 0 3px 3px;
  -moz-border-radius: 0 0 3px 3px;
  border-radius: 0 0 3px 3px;
  -webkit-background-clip: padding-box;
  -moz-background-clip: padding-box;
  background-clip: padding-box; }
  .popover-content p, .popover-content ul, .popover-content ol {
    margin-bottom: 0; }

longitudinal_model.js

// Generated by CoffeeScript 1.4.0
(function() {
  var __hasProp = {}.hasOwnProperty,
    __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };

  window.LongitudinalModel = (function(_super) {
    var defaults;

    __extends(LongitudinalModel, _super);

    function LongitudinalModel() {
      return LongitudinalModel.__super__.constructor.apply(this, arguments);
    }

    defaults = {
      currentRowId: null,
      clickedRowId: null,
      currentCluster: null,
      currentTag: null
    };

    LongitudinalModel.prototype.initialize = function(parsedData, userOptions) {
      var id;
      if (userOptions == null) {
        userOptions = {};
      }
      this.options = {
        rowNameColumn: userOptions.rowNameColumn || "id",
        yAxisName: userOptions.yAxisName || "log-ratio",
        specialColumnNames: userOptions.specialColumnNames || ["cluster"],
        rowType: userOptions.rowType,
        tagFile: userOptions.urlVars["tags"],
        showGroups: userOptions.urlVars["groups"],
        hideHeatmap: userOptions.urlVars["no_heatmap"],
        hidePCP: userOptions.urlVars["no_pcp"],
        hideRowNames: userOptions.urlVars["no_row_names"]
      };
      this.options.specialColumnNames = _.union(this.options.specialColumnNames, this.options.rowNameColumn);
      this.parsedData = parsedData;
      this.rowNames = _.pluck(this.parsedData, this.options.rowNameColumn);
      this.rowIds = (function() {
        var _i, _ref, _results;
        _results = [];
        for (id = _i = 1, _ref = this.parsedData.length; 1 <= _ref ? _i <= _ref : _i >= _ref; id = 1 <= _ref ? ++_i : --_i) {
          _results.push("_row_" + id);
        }
        return _results;
      }).call(this);
      this.columnNames = this.getColumnNames();
      this.columnGroups = this.getColumnGroups();
      this.longitudinalData = this.getLongitudinalData();
      this.groupedLongitudinalData = this.getGroupedLongitudinalData();
      this.clusters = _.pluck(this.parsedData, "cluster");
      this.options.showClusters = _.uniq(this.clusters).length > 1;
      return this.clusterNames = this.getClusterNames();
    };

    LongitudinalModel.prototype.getRowName = function(rowName) {
      return this.rowNames[this.rowIds.indexOf(rowName)];
    };

    LongitudinalModel.prototype.getColumnGroups = function() {
      return _.chain(this.columnNames).map(function(columnName) {
        return columnName.split("_")[0];
      }).uniq().value();
    };

    LongitudinalModel.prototype.getLongitudinalValueExtent = function() {
      var longitudinalValues;
      longitudinalValues = _.flatten(this.longitudinalData.map(function(longitudinalDataRow) {
        return longitudinalDataRow.map(function(item) {
          return item.value;
        });
      }));
      return d3.extent(longitudinalValues);
    };

    LongitudinalModel.prototype.getColumnNames = function() {
      var _this = this;
      return _.keys(this.parsedData[0]).filter(function(columnName) {
        return !_.include(_this.options.specialColumnNames, columnName);
      });
    };

    LongitudinalModel.prototype.getLongitudinalData = function() {
      var _this = this;
      return this.parsedData.map(function(row) {
        return _this.columnNames.map(function(columnName) {
          return {
            condition: columnName,
            value: +row[columnName]
          };
        });
      });
    };

    LongitudinalModel.prototype.getGroupedLongitudinalData = function() {
      var _this = this;
      return this.parsedData.map(function(row) {
        var columnGroups, currentGroup, longitudinalValues;
        columnGroups = _this.columnGroups.slice(0);
        currentGroup = columnGroups.shift();
        longitudinalValues = [];
        _this.columnNames.map(function(columnName) {
          if (!columnName.match(RegExp("^" + currentGroup + "_"))) {
            longitudinalValues.push({
              condition: "",
              value: null
            });
            currentGroup = columnGroups.shift();
          }
          return longitudinalValues.push({
            condition: columnName,
            value: +row[columnName]
          });
        });
        return longitudinalValues;
      });
    };

    LongitudinalModel.prototype.getClusterNames = function() {
      var clusterFrequencies;
      clusterFrequencies = window.getFrequencies(this.clusters);
      return _.chain(clusterFrequencies).keys().sortBy(function(x) {
        return clusterFrequencies[x];
      }).value().reverse();
    };

    return LongitudinalModel;

  })(Backbone.Model);

}).call(this);

pcp_view.js

// Generated by CoffeeScript 1.4.0
(function() {
  var __hasProp = {}.hasOwnProperty,
    __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };

  window.PcpView = (function(_super) {

    __extends(PcpView, _super);

    function PcpView() {
      return PcpView.__super__.constructor.apply(this, arguments);
    }

    PcpView.prototype.initialize = function() {
      var _this = this;
      if (!this.model.options.hidePCP) {
        this.render();
        this.model.on("change:currentRowId", function() {
          return _this.showCurrentRow();
        });
        this.model.on("change:clickedRowId", function() {
          return _this.showClickedRow();
        });
        this.model.on("change:currentCluster", function() {
          return _this.showCurrentCluster();
        });
        return this.model.on("change:currentTag", function() {
          return _this.showCurrentTag();
        });
      }
    };

    PcpView.prototype.events = function() {
      return {
        "mouseover .expression-line": "changeCurrentRowId",
        "mouseout .expression-line": "changeCurrentRowId",
        "click .expression-line": "changeClickedRowId",
        "click .expression-point": "showExpressionPointText"
      };
    };

    PcpView.prototype.render = function() {
      var columnName,
        _this = this;
      this.columnNamesMargin = d3.max((function() {
        var _i, _len, _ref, _results;
        _ref = this.model.columnNames;
        _results = [];
        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
          columnName = _ref[_i];
          _results.push(columnName.length);
        }
        return _results;
      }).call(this));
      this.margin = {
        top: 300,
        right: 50,
        bottom: this.columnNamesMargin * 30,
        left: 150
      };
      this.width = d3.max([400, this.model.columnNames.length * 40]);
      this.height = 300;
      this.pcp = d3.select(this.el).attr("width", this.width + this.margin.right + this.margin.left).attr("height", this.height + this.margin.top + this.margin.bottom).attr("class", "pcp").append("g").attr("transform", "translate(" + this.margin.left + "," + this.margin.top + ")");
      this.x = d3.scale.ordinal().domain(this.model.columnNames).rangePoints([0, this.width], .2);
      this.y = d3.scale.linear().domain(this.model.getLongitudinalValueExtent()).range([this.height, 0], .2);
      this.line = d3.svg.line().defined(function(d) {
        return _.isFinite(d.value);
      }).x(function(d) {
        return _this.x(d.condition);
      }).y(function(d) {
        return _this.y(d.value);
      });
      this.xAxis = d3.svg.axis().scale(this.x).tickSize(6, 0);
      this.renderedXAxis = this.pcp.append("g").attr("class", "x axis").attr("transform", "translate(0," + this.height + ")").call(this.xAxis);
      this.renderedXAxis.selectAll("text").attr("dx", "-.5em").attr("dy", ".3em").attr("text-anchor", "end").attr("transform", "rotate(-45)");
      this.yAxis = d3.svg.axis().scale(this.y).ticks(4).tickSize(6, 0).orient("left");
      this.renderedYAxis = this.pcp.append("g").attr("class", "y axis").attr("transform", "translate(0,0)").call(this.yAxis);
      this.renderedYAxis.append("text").attr("text-anchor", "middle").attr("dy", "-.5em").text(this.model.options.yAxisName);
      this.addLongitudinalDataLines();
      return this.pcp.append("line").attr("y1", this.y(0)).attr("y2", this.y(0)).attr("x2", this.width).attr("class", "axis zero");
    };

    PcpView.prototype.addLongitudinalDataLines = function() {
      var _this = this;
      this.pcp.selectAll(".expression-line").data(this.getLongitudinalData()).enter().append("path").attr("class", "expression-line").attr("row-id", function(d, i) {
        return _this.model.rowIds[i];
      }).attr("stroke", "steelblue").attr("stroke-opacity", .5).attr("d", this.line);
      if (this.model.options.showClusters) {
        return this.pcp.selectAll(".expression-line").attr("cluster", function(d, i) {
          return _this.model.clusters[i];
        }).attr("stroke", function(d, i) {
          return window.clusterColor(_this.model.clusters[i]);
        });
      }
    };

    PcpView.prototype.getLongitudinalData = function() {
      if (this.model.options.showGroups) {
        return this.model.groupedLongitudinalData;
      } else {
        return this.model.longitudinalData;
      }
    };

    PcpView.prototype.changeCurrentRowId = function(e) {
      e.stopPropagation();
      return this.model.set({
        currentRowId: e.type === "mouseover" ? d3.select(e.target).attr("row-id") : null
      });
    };

    PcpView.prototype.changeClickedRowId = function(e) {
      e.stopPropagation();
      return this.model.set({
        clickedRowId: d3.select(e.target).attr("row-id")
      });
    };

    PcpView.prototype.showExpressionPointText = function(e) {
      var d;
      e.stopPropagation();
      d = e.target.__data__;
      return this.pcp.append("text").attr("class", "expression-text").attr("x", this.x(d.condition)).attr("y", this.y(d.value)).attr("text-anchor", "middle").attr("dy", "-.8em").text(d3.round(d.value, 2));
    };

    PcpView.prototype.showCurrentRow = function() {
      var currentRow, currentRowId;
      this.pcp.selectAll(".expression-line").filter(":not(.clicked)").classed("current", 0);
      this.pcp.selectAll("text.title").filter(":not(.clicked)").remove();
      currentRowId = this.model.get("currentRowId");
      if (currentRowId != null) {
        currentRow = this.pcp.select("[row-id=" + currentRowId + "]");
        currentRow.classed("current", 1);
        if (this.pcp.select(".clicked").empty()) {
          currentRow.node().parentNode.appendChild(currentRow.node());
        }
        this.pcp.selectAll("text.title").filter(".clicked").style("display", "none");
        return this.pcp.append("text").attr("class", "title").attr("x", this.width / 2).attr("y", -40).attr("text-anchor", "middle").text(this.model.getRowName(currentRowId));
      } else {
        return this.pcp.selectAll("text.title").filter(".clicked").style("display", "block");
      }
    };

    PcpView.prototype.showClickedRow = function() {
      var clickedRow, clickedRowId, data;
      clickedRowId = this.model.get("clickedRowId");
      if (clickedRowId != null) {
        clickedRow = this.pcp.select("[row-id=" + clickedRowId + "]");
        clickedRow.node().parentNode.appendChild(clickedRow.node());
        this.removeClickedRow();
        clickedRow.classed("current clicked", 1);
        data = clickedRow.node().__data__;
        this.pcp.selectAll(".expression-point").data(data.filter(function(d) {
          return _.isFinite(d.value);
        })).enter().append("circle").attr("class", "expression-point").attr("cx", this.line.x()).attr("cy", this.line.y()).attr("r", 5);
        this.pcp.selectAll(".expression-text").data(data.filter(this.filterTextDatapoints)).enter().append("text").attr("class", "expression-text").attr("x", this.line.x()).attr("y", this.line.y()).attr("text-anchor", "middle").attr("dy", "-.8em").text(function(d, i) {
          return d3.round(d.value, 2);
        });
        return this.pcp.select("text.title").classed("clicked", 1).text(this.model.getRowName(clickedRowId));
      } else {
        return this.removeClickedRow();
      }
    };

    PcpView.prototype.filterTextDatapoints = function(item, i, arr) {
      var significantDifference;
      significantDifference = .5;
      return (i === 0 && _.isFinite(item.value)) || ((i === arr.length - 1) && _.isFinite(item.value)) || (arr[i - 1].value === null && _.isFinite(item.value)) || (arr[i + 1].value === null && _.isFinite(item.value)) || (_.isFinite(item.value) && item.value > arr[i - 1].value && item.value > arr[i + 1].value && (Math.abs(item.value - arr[i - 1].value) > significantDifference || Math.abs(item.value - arr[i + 1].value) > significantDifference)) || (_.isFinite(item.value) && item.value < arr[i - 1].value && item.value < arr[i + 1].value && (Math.abs(item.value - arr[i - 1].value) > significantDifference || Math.abs(item.value - arr[i + 1].value) > significantDifference));
    };

    PcpView.prototype.removeClickedRow = function() {
      this.pcp.selectAll(".expression-point").remove();
      this.pcp.selectAll(".expression-text").remove();
      this.pcp.selectAll(".expression-line").classed("current clicked", 0);
      return this.pcp.select("text.title").text("");
    };

    PcpView.prototype.showCurrentCluster = function() {
      var currentCluster,
        _this = this;
      currentCluster = this.model.get("currentCluster");
      d3.select("#pcp").style("top", d3.select("body").node().scrollTop);
      if (currentCluster) {
        this.pcp.selectAll(".expression-line").attr("stroke", "#999");
        return this.pcp.selectAll(".expression-line[cluster='" + currentCluster + "']").attr("stroke", function(d, i) {
          return window.clusterColor(currentCluster);
        }).each(function() {
          return this.parentNode.appendChild(this);
        });
      } else {
        this.pcp.selectAll(".expression-line").remove();
        return this.addLongitudinalDataLines();
      }
    };

    PcpView.prototype.showCurrentTag = function() {
      var currentTag, taggedRowIds;
      currentTag = this.model.get("currentTag");
      d3.select("#pcp").style("top", 0);
      scroll(0, 0);
      if (currentTag) {
        taggedRowIds = _(d3.selectAll("#heatmap .row")).chain().first().filter(function(d) {
          return d.style.display !== "none";
        }).map(function(d) {
          return d3.select(d).attr("row-id");
        }).value();
        return this.pcp.selectAll(".expression-line").attr("stroke", "#999").filter(function(d, i) {
          return _.include(taggedRowIds, d3.select(this).attr("row-id"));
        }).attr("stroke", function(d, i) {
          return window.clusterColor(d3.select(this).attr("cluster"));
        }).each(function() {
          return this.parentNode.appendChild(this);
        });
      } else {
        this.pcp.selectAll(".expression-line").remove();
        return this.addLongitudinalDataLines();
      }
    };

    return PcpView;

  })(Backbone.View);

}).call(this);