<commit_before>/*
Copyright 2014 Google Inc. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

var readgraph = new function() {
  var cigarMatcher = /([0-9]+[MIDNSHP=X])/gi

  var width = 0;
  var height = 800;
  var margin = 40;

  var textHeight, textWidth = 0;

  var y = d3.scale.linear().range([margin, height - margin*2]);
  var x = d3.scale.linear();
  var xAxis = d3.svg.axis().ticks(5).scale(x);
  var xFormat = d3.format(',f');

  var zoom = null;
  var maxZoom = 1;
  var zoomLevelChange = 1;
  var minRange = 0;

  var opacity = d3.scale.linear().domain([0, 93]).range([.2, 1]);
  var unsupportedMessage = null;

  // Current state
  // Format: {id, type, backend, sequences}
  var setObjects = [];
  var mergedSequences = [];
  var currentSequence = null;
  var readStats = {}; // Map from position to stat information
  var xhrTimeout = null;

  var readTrackLength = 0;
  var callsetTrackLength = 0;

  // Dom elements
  var svg, axisGroup, readGroup, readDiv, variantDiv, spinner = null;
  var hoverline, positionIndicator, positionIndicatorBg, positionIndicatorText;

  var updateHeight = function() {
    var totalTracks = readTrackLength + callsetTrackLength;
    height = totalTracks * textHeight + 100;
    height = Math.max(height, 450);

    y.range([margin, height - margin*2]).domain([totalTracks, -1]);
    $('#graph').height(height);

    // TODO: Reduce duplicate height setting code
    axisGroup.attr('transform', 'translate(0,' + (height - margin) + ')');
    positionIndicatorBg.attr('height', height - margin);
    positionIndicatorText.attr('y', height - margin - textHeight);
    hoverline.attr("y2", height);

    zoom.size([width, height]);

    return totalTracks;
  };

  var getScaleLevel = function() {
    return Math.floor(Math.log(zoom.scale()) / Math.log(zoomLevelChange) + .1);
  };

  var handleZoom = function() {
    var tx = zoom.translate()[0];
    // TODO: This isn't strict enough
    tx = Math.max(tx, (1 - zoom.scale()) * width);
    tx = Math.min(tx, 0);
    zoom.translate([tx, 0]);
    svg.select(".axis").call(xAxis);

    // Update scale bar
    d3.select('.zoomLevel').attr('y', (6 - getScaleLevel()) * 24 + 38);
    updateDisplay();
  };

  var moveToSequencePosition = function(position) {
    position = Math.max(0, position);
    position = Math.min(currentSequence['length'], position);

    var newX = x(position);
    newX = zoom.translate()[0] - newX + width / 2;
    zoom.translate([newX, 0]);
    handleZoom();
  };

  var setupRun = false;
  var setup = function() {
    setupRun = true;

    // Measurements
    svg = d3.select("#graph");
    var text = addText(svg, 'G', 0, 0);
    var bbox = text.node().getBBox();
    textWidth = bbox.width;
    textHeight = bbox.height;
    text.remove();

    width = $('#graph').width();
    x.rangeRound([margin, width - margin]);
    minRange = (width / textWidth / 2); // Twice the zoom of individual bases

    readDiv = $('#readDiv');
    variantDiv = $('#variantDiv');


    // Svg init
    // Reads Axis
    axisGroup = svg.append('g')
        .attr('transform', 'translate(0,' + (height - margin) + ')')
        .attr('class', 'axis');

    // Unsupported message
    unsupportedMessage = addText(svg, 'This zoom level is coming soon!',
        width/2, height/4);

    // Hover line
    hoverline = svg.append("line")
        .attr("class", "hover hoverline")
        .attr("x1", 0).attr("x2", 0)
        .attr("y1", 0).attr("y2", height);

    var hovertext = svg.append('text')
        .attr("class", "hover hovertext")
        .attr('y', textHeight);

    svg.on("mousemove", function() {
      var mouseX = d3.mouse(this)[0];
      mouseX = Math.max(margin, mouseX);
      mouseX = Math.min(width - margin, mouseX);

      if (mouseX > width * 2/3) {
        hovertext.attr('x', mouseX - 3).style('text-anchor', 'end');
      } else {
        hovertext.attr('x', mouseX + 3).style('text-anchor', 'start');
      }

      var position = Math.floor(x.invert(mouseX));
      hovertext.selectAll('tspan').remove();
      hovertext.append('tspan').text(xFormat(position));

      if (readStats[position]) {
        var counts = _.countBy(readStats[position]);
        hovertext.append('tspan')
            .attr('y', textHeight*2).attr('x', hovertext.attr('x'))
            .text(_.reduce(counts, function(memo, num, key) {
              return memo + num + key + " ";
            }, ""));
      }

      hoverline.attr("x1", mouseX).attr("x2", mouseX)
    });

    // Position indicator
    positionIndicator = svg.append('g')
        .attr('transform', 'translate(0,0)')
        .attr('class', 'axis');
    positionIndicatorBg = positionIndicator.append('rect')
        .attr('class', 'positionIndicator background')
        .attr('x', 0).attr('y', 0)
        .attr('width', textWidth * 1.5).attr('height', height - margin);
    positionIndicatorText = positionIndicator.append('text')
        .attr('class', 'positionIndicator text')
        .attr('x', 3)
        .attr('y', height - margin - textHeight);
    toggleVisibility(positionIndicator, false);

    // Groups
    readGroup = svg.append('g').attr('class', 'readGroup');
    var zoomGroup = svg.append('g').attr('class', 'zoomGroup');

    // Zooming
    var changeZoomLevel = function(levelChange) {
      var newZoom = zoom.scale();
      // Keep the graph centered on the middle position
      var middleX = x.invert(width / 2);

      if (levelChange > 0) {
        newZoom = zoom.scale() * zoomLevelChange;
      } else {
        newZoom = zoom.scale() / zoomLevelChange;
      }
      newZoom = Math.max(1, newZoom);
      newZoom = Math.min(maxZoom, newZoom);
      zoom.scale(newZoom);

      handleZoom();
      moveToSequencePosition(middleX);
    };

    zoom = d3.behavior.zoom().size([width, height]).on("zoom", handleZoom);
    svg.call(zoom);

    // Zoom background
    zoomGroup.append('rect')
        .attr('x', 23).attr('y', 35)
        .attr('width', 66).attr('height', 170);

    addImage(zoomGroup, 'zoom-bar.png', 10, 201, 7, 10);
    addImage(zoomGroup, 'zoom-level.png', 22, 15, 2, 183, null, 'zoomLevel');
    addImage(zoomGroup, 'zoom-plus.png', 25, 25, 0, 10, function() {
      changeZoomLevel(1);
    });
    addImage(zoomGroup, 'zoom-minus.png', 25, 25, 0, 200, function() {
      changeZoomLevel(-1);
    });
    var zoomTextX = 23;
    addText(zoomGroup, 'Bases', zoomTextX, 50);
    addText(zoomGroup, 'Reads', zoomTextX, 98);
    addText(zoomGroup, 'Coverage', zoomTextX, 147);
    addText(zoomGroup, 'Summary', zoomTextX, 195);

    // Spinner
    spinner = addImage(readGroup, 'spinner.gif', 16, 16, width - 16, 0);
    spinner.style('display', 'none');
  };

  var chrLocation = /^(.*):(\d*)$/;

  // Jumps the graph to the given user-entered position
  // Returns a bookmarkable position value
  this.jumpGraph = function(location) {
    var jumpResults = $("#jumpResults").empty();

    // Locations of the form chr:position
    if (chrLocation.test(location)) {
      var matches = chrLocation.exec(location);
      jumpToPosition(parseInt(matches[2].replace(/,/g, '')), matches[1], true);
      return location;
    }

    var position = parseInt(location.replace(/,/g, ''));
    // Numbered locations
    if (position > 0) {
      jumpToPosition(position, null, true);
      return currentSequence.name + ":" + position;
    }

    // Queried locations
    showMessage('Looking up location: ' + location);

    $.getJSON('api/snps', {snp: location}).done(function(res) {
      if (res.snps.length == 0) {
        showMessage('Could not find location: ' + location);

      } else {
        $.each(res.snps, function(i, snp) {
          var listItem = $('<a/>', {'href': '#', 'class': 'list-group-item'})
              .appendTo(jumpResults).click(function() {
                if (snp.position) {
                  jumpToPosition(snp.position, snp.chr, true, snp.name);
                } else {
                  showMessage('Could not find a position for this snp.' +
                      ' Check SNPedia for more information.');
                }
                return false;
              });
          $('<span>', {'class': 'title'}).text(snp.name + ' ')
              .appendTo(listItem);
          $('<a>', {'href': snp.link, 'target': '_blank'}).text('SNPedia')
              .appendTo(listItem).click(function() {
                window.open(snp.link);
                return false;
              });
          if (snp.position) {
            $('<div>').text('chr ' + snp.chr + ' at ' + xFormat(snp.position))
                .appendTo(listItem);
          }
        });

        $("#jumpResults .list-group-item").click();
      }
    });
    return location;
  };

  var fuzzyFindSequence = function(chr) {
    var actualNames = _.pluck(mergedSequences, 'name');
    var possibleNames = [chr, "chr" + chr];
    possibleNames = _.intersection(actualNames, possibleNames);

    if (possibleNames.length > 0) {
      return _.findWhere(mergedSequences, {name: possibleNames[0]});
    }
    return null;
  };

  var jumpToPosition = function(position, chr, baseView, snp) {
    if (chr) {
      // Update our sequence
      var sequence = fuzzyFindSequence(chr);
      if (!sequence) {
        showError('This data doesn\'t have the sequence ' + chr +
          '. Please try a different position.');
        return;
      }
<commit_msg>Stop complaining when the seq name doesn't match<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

var readgraph = new function() {
  var cigarMatcher = /([0-9]+[MIDNSHP=X])/gi

  var width = 0;
  var height = 800;
  var margin = 40;

  var textHeight, textWidth = 0;

  var y = d3.scale.linear().range([margin, height - margin*2]);
  var x = d3.scale.linear();
  var xAxis = d3.svg.axis().ticks(5).scale(x);
  var xFormat = d3.format(',f');

  var zoom = null;
  var maxZoom = 1;
  var zoomLevelChange = 1;
  var minRange = 0;

  var opacity = d3.scale.linear().domain([0, 93]).range([.2, 1]);
  var unsupportedMessage = null;

  // Current state
  // Format: {id, type, backend, sequences}
  var setObjects = [];
  var mergedSequences = [];
  var currentSequence = null;
  var readStats = {}; // Map from position to stat information
  var xhrTimeout = null;

  var readTrackLength = 0;
  var callsetTrackLength = 0;

  // Dom elements
  var svg, axisGroup, readGroup, readDiv, variantDiv, spinner = null;
  var hoverline, positionIndicator, positionIndicatorBg, positionIndicatorText;

  var updateHeight = function() {
    var totalTracks = readTrackLength + callsetTrackLength;
    height = totalTracks * textHeight + 100;
    height = Math.max(height, 450);

    y.range([margin, height - margin*2]).domain([totalTracks, -1]);
    $('#graph').height(height);

    // TODO: Reduce duplicate height setting code
    axisGroup.attr('transform', 'translate(0,' + (height - margin) + ')');
    positionIndicatorBg.attr('height', height - margin);
    positionIndicatorText.attr('y', height - margin - textHeight);
    hoverline.attr("y2", height);

    zoom.size([width, height]);

    return totalTracks;
  };

  var getScaleLevel = function() {
    return Math.floor(Math.log(zoom.scale()) / Math.log(zoomLevelChange) + .1);
  };

  var handleZoom = function() {
    var tx = zoom.translate()[0];
    // TODO: This isn't strict enough
    tx = Math.max(tx, (1 - zoom.scale()) * width);
    tx = Math.min(tx, 0);
    zoom.translate([tx, 0]);
    svg.select(".axis").call(xAxis);

    // Update scale bar
    d3.select('.zoomLevel').attr('y', (6 - getScaleLevel()) * 24 + 38);
    updateDisplay();
  };

  var moveToSequencePosition = function(position) {
    position = Math.max(0, position);
    position = Math.min(currentSequence['length'], position);

    var newX = x(position);
    newX = zoom.translate()[0] - newX + width / 2;
    zoom.translate([newX, 0]);
    handleZoom();
  };

  var setupRun = false;
  var setup = function() {
    setupRun = true;

    // Measurements
    svg = d3.select("#graph");
    var text = addText(svg, 'G', 0, 0);
    var bbox = text.node().getBBox();
    textWidth = bbox.width;
    textHeight = bbox.height;
    text.remove();

    width = $('#graph').width();
    x.rangeRound([margin, width - margin]);
    minRange = (width / textWidth / 2); // Twice the zoom of individual bases

    readDiv = $('#readDiv');
    variantDiv = $('#variantDiv');


    // Svg init
    // Reads Axis
    axisGroup = svg.append('g')
        .attr('transform', 'translate(0,' + (height - margin) + ')')
        .attr('class', 'axis');

    // Unsupported message
    unsupportedMessage = addText(svg, 'This zoom level is coming soon!',
        width/2, height/4);

    // Hover line
    hoverline = svg.append("line")
        .attr("class", "hover hoverline")
        .attr("x1", 0).attr("x2", 0)
        .attr("y1", 0).attr("y2", height);

    var hovertext = svg.append('text')
        .attr("class", "hover hovertext")
        .attr('y', textHeight);

    svg.on("mousemove", function() {
      var mouseX = d3.mouse(this)[0];
      mouseX = Math.max(margin, mouseX);
      mouseX = Math.min(width - margin, mouseX);

      if (mouseX > width * 2/3) {
        hovertext.attr('x', mouseX - 3).style('text-anchor', 'end');
      } else {
        hovertext.attr('x', mouseX + 3).style('text-anchor', 'start');
      }

      var position = Math.floor(x.invert(mouseX));
      hovertext.selectAll('tspan').remove();
      hovertext.append('tspan').text(xFormat(position));

      if (readStats[position]) {
        var counts = _.countBy(readStats[position]);
        hovertext.append('tspan')
            .attr('y', textHeight*2).attr('x', hovertext.attr('x'))
            .text(_.reduce(counts, function(memo, num, key) {
              return memo + num + key + " ";
            }, ""));
      }

      hoverline.attr("x1", mouseX).attr("x2", mouseX)
    });

    // Position indicator
    positionIndicator = svg.append('g')
        .attr('transform', 'translate(0,0)')
        .attr('class', 'axis');
    positionIndicatorBg = positionIndicator.append('rect')
        .attr('class', 'positionIndicator background')
        .attr('x', 0).attr('y', 0)
        .attr('width', textWidth * 1.5).attr('height', height - margin);
    positionIndicatorText = positionIndicator.append('text')
        .attr('class', 'positionIndicator text')
        .attr('x', 3)
        .attr('y', height - margin - textHeight);
    toggleVisibility(positionIndicator, false);

    // Groups
    readGroup = svg.append('g').attr('class', 'readGroup');
    var zoomGroup = svg.append('g').attr('class', 'zoomGroup');

    // Zooming
    var changeZoomLevel = function(levelChange) {
      var newZoom = zoom.scale();
      // Keep the graph centered on the middle position
      var middleX = x.invert(width / 2);

      if (levelChange > 0) {
        newZoom = zoom.scale() * zoomLevelChange;
      } else {
        newZoom = zoom.scale() / zoomLevelChange;
      }
      newZoom = Math.max(1, newZoom);
      newZoom = Math.min(maxZoom, newZoom);
      zoom.scale(newZoom);

      handleZoom();
      moveToSequencePosition(middleX);
    };

    zoom = d3.behavior.zoom().size([width, height]).on("zoom", handleZoom);
    svg.call(zoom);

    // Zoom background
    zoomGroup.append('rect')
        .attr('x', 23).attr('y', 35)
        .attr('width', 66).attr('height', 170);

    addImage(zoomGroup, 'zoom-bar.png', 10, 201, 7, 10);
    addImage(zoomGroup, 'zoom-level.png', 22, 15, 2, 183, null, 'zoomLevel');
    addImage(zoomGroup, 'zoom-plus.png', 25, 25, 0, 10, function() {
      changeZoomLevel(1);
    });
    addImage(zoomGroup, 'zoom-minus.png', 25, 25, 0, 200, function() {
      changeZoomLevel(-1);
    });
    var zoomTextX = 23;
    addText(zoomGroup, 'Bases', zoomTextX, 50);
    addText(zoomGroup, 'Reads', zoomTextX, 98);
    addText(zoomGroup, 'Coverage', zoomTextX, 147);
    addText(zoomGroup, 'Summary', zoomTextX, 195);

    // Spinner
    spinner = addImage(readGroup, 'spinner.gif', 16, 16, width - 16, 0);
    spinner.style('display', 'none');
  };

  var chrLocation = /^(.*):(\d*)$/;

  // Jumps the graph to the given user-entered position
  // Returns a bookmarkable position value
  this.jumpGraph = function(location) {
    var jumpResults = $("#jumpResults").empty();

    // Locations of the form chr:position
    if (chrLocation.test(location)) {
      var matches = chrLocation.exec(location);
      jumpToPosition(parseInt(matches[2].replace(/,/g, '')), matches[1], true);
      return location;
    }

    var position = parseInt(location.replace(/,/g, ''));
    // Numbered locations
    if (position > 0) {
      jumpToPosition(position, null, true);
      return currentSequence.name + ":" + position;
    }

    // Queried locations
    showMessage('Looking up location: ' + location);

    $.getJSON('api/snps', {snp: location}).done(function(res) {
      if (res.snps.length == 0) {
        showMessage('Could not find location: ' + location);

      } else {
        $.each(res.snps, function(i, snp) {
          var listItem = $('<a/>', {'href': '#', 'class': 'list-group-item'})
              .appendTo(jumpResults).click(function() {
                if (snp.position) {
                  jumpToPosition(snp.position, snp.chr, true, snp.name);
                } else {
                  showMessage('Could not find a position for this snp.' +
                      ' Check SNPedia for more information.');
                }
                return false;
              });
          $('<span>', {'class': 'title'}).text(snp.name + ' ')
              .appendTo(listItem);
          $('<a>', {'href': snp.link, 'target': '_blank'}).text('SNPedia')
              .appendTo(listItem).click(function() {
                window.open(snp.link);
                return false;
              });
          if (snp.position) {
            $('<div>').text('chr ' + snp.chr + ' at ' + xFormat(snp.position))
                .appendTo(listItem);
          }
        });

        $("#jumpResults .list-group-item").click();
      }
    });
    return location;
  };

  var fuzzyFindSequence = function(chr) {
    var actualNames = _.pluck(mergedSequences, 'name');
    var possibleNames = [chr, "chr" + chr];
    possibleNames = _.intersection(actualNames, possibleNames);

    if (possibleNames.length > 0) {
      return _.findWhere(mergedSequences, {name: possibleNames[0]});
    }
    return null;
  };

  var jumpToPosition = function(position, chr, baseView, snp) {
    if (chr) {
      // Update our sequence
      var sequence = fuzzyFindSequence(chr);
      if (!sequence) {
        return;
      }

{'fraction_non_alphanumeric': 0.16115576355682165, 'fraction_numerical': 0.01627835995523451, 'mean_word_length': 2.6821502153961414, 'pattern_counts': {'":': 2, '<': 11, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6923108', 'n_tokens_mistral': 6402, 'n_tokens_neox': 6023, 'n_words': 1507}