<commit_before>coord = function(x, y) {
    return "" + x + "," + y;
}

function HouseBoat() {
    console.log("Created new HouseBoat");
}

HouseBoat.prototype.initialize = function() {
    this.svg = d3.select("svg");
    this.window_width = +this.svg.attr("width");
    this.window_height = +this.svg.attr("height");
    var wby2 = 0.5 * this.window_width, hby2 = 0.5 * this.window_height;
    // Set coordinate system with origin at center of the window,
    // x increasing to the right, and y increasing upwards.
    this.axes = this.svg.append("g")
        .attr("transform", "translate(512, 320) scale(1, -1)");
    // Initialize boat state.
    this.x = 0.0;
    this.y = 0.0;
    this.theta = 45.0;
    this.phi = 0.0;
    this.vx = 0.0;
    this.vy = 0.0;
    this.omega = 0.0;
    // Create boat's visual representation.
    this.boat = this.axes.append("g");
    this.boat.append("rect")
        .attr("width", "100")
        .attr("height", "60")
        .attr("transform", "translate(-50, -30)")
        .attr("fill", "gray")
        .attr("stroke", "black");
    var wake_group = this.boat.append("g")
        .attr("transform", "translate(-50, 0)");
    this.wake = wake_group.append("polyline")
        .attr("points", "-50,-15 0,0 -50,15")
        .attr("transform", "rotate(0) scale(-0.5)")
        .attr("stroke", "red")
        .attr("fill", "none");
    this.draw_boat();
    // Capture arrow keypress events.
    this.right = false;
    this.left = false;
    this.up = false;
    this.down = false;
    var self = this;
    d3.select("body")
        // up=38, dn=40
        .on("keydown", function() {
            var code = d3.event.keyCode;
            if(code == 39) {
                self.right = true;
            }
            else if(code == 37) {
                self.left = true;
            }
            else if(code == 38) {
                self.up = true;
            }
            else if(code == 40) {
                self.down = true;
            }
            if(self.right || self.left) {
                self.steering_display.attr("fill", "red");
            }
            if(self.up || self.down) {
                self.throttle_display.attr("fill", "red");
            }
        })
        .on("keyup", function() {
            var code = d3.event.keyCode;
            if(code == 39) {
                self.right = false;
            }
            else if(code == 37) {
                self.left = false;
            }
            else if(code == 38) {
                self.up = false;
            }
            else if(code == 40) {
                self.down = false;
            }
            if(!self.right && !self.left) {
                self.steering_display.attr("fill", "green");
            }
            if(!self.up && !self.down) {
                self.throttle_display.attr("fill", "green");
            }
        });
        // Initialize throttle and steering controls.
        this.throttle = 0.0;
        this.steering = 0.0;
        var radius = 16;
        this.throttle_display = this.svg.append("circle")
            .attr("cx", self.window_width - radius)
            .attr("cy", hby2 - radius)
            .attr("r", radius)
            .attr("stroke", "black")
            .attr("fill-opacity", "0.5")
            .attr("fill", "green");
        this.steering_display = this.svg.append("circle")
            .attr("cx", wby2 - radius)
            .attr("cy", self.window_height - radius)
            .attr("r", radius)
            .attr("stroke", "black")
            .attr("fill-opacity", "0.5")
            .attr("fill", "green");
        // Draw center marks for each control.
        var w = 0.2 * radius, h = 0.6 * radius;
        this.svg.append("polygon")
            .attr("points",
                coord(wby2 - w, this.window_height) + " " +
                coord(wby2, this.window_height - h) + " " +
                coord(wby2 + w, this.window_height))
            .attr("stroke", "black")
            .attr("fill", "blue");
            this.svg.append("polygon")
                .attr("points",
                    coord(this.window_width, hby2 - w) + " " +
                    coord(this.window_width - h, hby2) + " " +
                    coord(this.window_width, hby2 + w))
                .attr("stroke", "black")
                .attr("fill", "blue");
        this.throttle_max = self.window_height - 2 * radius;
        this.steering_max = self.window_width - 2 * radius;
        // Initialize constants.
        this.deg2rad = Math.PI / 180;
        this.max_thrust = 10.0;
        this.drag_p = 0.1;
        this.drag_t = 2.0;
        this.drag_r = 0.2;
        this.current_x = 0.0;
        this.current_y = 0.0;
        this.leverarm = 1.0;
}

HouseBoat.prototype.update_state = function() {
    dt = 0.1;
    // Initialize a unit vector aligned with the pontoons.
    var theta = this.theta * this.deg2rad;
    var nx = Math.cos(theta), ny = Math.sin(theta);
    // Split the velocity (vx,vy) into components parallel
    // and perpendicular to the pontoons.
    var dotprod = this.vx * nx + this.vy * ny;
    var vpx = dotprod * nx;
    var vpy = dotprod * ny;
    var vtx = this.vx - vpx;
    var vty = this.vy - vpy;
    // Calculate the combined drag force.
    var drag_x = -this.drag_p * vpx - this.drag_t * vtx;
    var drag_y = -this.drag_p * vpy - this.drag_t * vty;
    // Calculate the thrust force on the COM.
    var phi = this.phi * this.deg2rad;
    var angle = theta + phi;
    var thrust_mag = this.max_thrust * this.throttle;
    var thrust_x = thrust_mag * Math.cos(angle);
    var thrust_y = thrust_mag * Math.sin(angle);
    // Calculate the net force on the COM
    var Fx = thrust_x + drag_x + this.current_x;
    var Fy = thrust_y + drag_y + this.current_y;
    // Update the position and bearing using the present motion.
    this.x += this.vx * dt;
    this.y += this.vy * dt;
    this.theta += this.omega * dt;
    //Update the COM velocity (assuming mass=1).
    this.vx += Fx * dt;
    this.vy += Fy * dt;
    // Calculate the torque about the COM due to the thrust.
    var tprop = -thrust_mag * this.leverarm * Math.sin(phi);
    // Calculate the torque about the COM due to rotational drag.
    var tdrag = -this.omega * this.drag_r;
    // Calculate the net torque about the COM.
    var tnet = tprop + tdrag;
    // Update the angular velocity about the COM (assuming I=1).
    this.omega += tnet * dt;
}

HouseBoat.prototype.draw_boat = function() {
    this.boat
        .attr("transform",
            "translate(" + coord(this.x,this.y) + ") rotate(" +
            this.theta + ",0,0)");
    if(Math.abs(this.throttle) < 0.05) {
        wake_scale = 0.05;
    }
    else {
        wake_scale = this.throttle;
    }
    this.wake
        .attr("transform", "rotate(" + this.phi + ") scale(" +
            wake_scale + ")");
}

HouseBoat.prototype.run = function() {
    var ival = 50; // milliseconds
    this.trans = d3.transition().duration(ival).ease(d3.easeLinear);
    // Start the interval timer.
    var self = this;
    //this.interval_id = setInterval(function() {
    d3.interval(function(elapsed) {
        var throttle_adjust = +1. * self.up - 1. * self.down;
        self.throttle += 0.01 * throttle_adjust;
        if(self.throttle > 1.0) {
            self.throttle = 1.0;
        }
        else if(self.throttle < -1.0) {
            self.throttle = -1.0;
        }
        var steering_adjust = +1. * self.right - 1. * self.left;
        self.steering += 0.01 * steering_adjust;
        if(self.steering > 1.0) {
            self.steering = 1.0;
        }
        else if(self.steering < -1.0) {
            self.steering = -1.0;
        }
        self.throttle_display //.transition(self.trans)
            .attr("cy", 0.5 * (self.window_height - self.throttle_max * self.throttle));
        self.steering_display //.transition(self.trans)
            .attr("cx", 0.5 * (self.window_width + self.steering_max * self.steering));
        self.phi = 90.0 * self.steering;
        self.update_state();
        self.draw_boat();
    }, ival);
}

var hb = new HouseBoat();

$(function() {
    // DOM is ready.
    hb.initialize();
    hb.run();
});
<commit_msg>Refactor to create separate Simulator and HouseBoat classes<commit_after>coord = function(x, y) {
    return "" + x + "," + y;
}

function Boundary(coords) {
    this.coords = coords;
}

Boundary.prototype.draw = function(container) {
    var points_str = "";
    for(var i = 0; i < this.coords.length; i+=2) {
        if(i > 0) points_str += " ";
        points_str += coords(this.coords[i], this.coords[i + 1]);
    }
    container.append("polyline")
        .attr("points", points_str)
        .attr("stroke", "red")
        .attr("fill", "none");
}

function HouseBoat() { }

HouseBoat.prototype.initialize = function(axes) {
    // Initialize boat state.
    this.x = 0.0;
    this.y = 0.0;
    this.theta = 45.0;
    this.vx = 0.0;
    this.vy = 0.0;
    this.omega = 0.0;
    this.throttle = 0.0;
    this.steering = 0.0;
    this.phi = 0.0;
    // Create boat's visual representation.
    this.boat = axes.append("g");
    this.boat.append("rect")
        .attr("width", "100")
        .attr("height", "60")
        .attr("transform", "translate(-50, -30)")
        .attr("fill", "gray")
        .attr("stroke", "black");
    var wake_group = this.boat.append("g")
        .attr("transform", "translate(-50, 0)");
    this.wake = wake_group.append("polyline")
        .attr("points", "-50,-15 0,0 -50,15")
        .attr("transform", "rotate(0) scale(-0.5)")
        .attr("stroke", "red")
        .attr("fill", "none");
    this.draw();
    // Initialize model constants.
    this.deg2rad = Math.PI / 180;
    this.max_thrust = 10.0;
    this.drag_p = 0.1;
    this.drag_t = 2.0;
    this.drag_r = 0.2;
    this.current_x = 0.0;
    this.current_y = 0.0;
    this.leverarm = 1.0;
}

HouseBoat.prototype.update = function(throttle, steering) {
    dt = 0.1;
    this.throttle = throttle;
    this.steering = steering;
    this.phi = 90.0 * steering;
    // Initialize a unit vector aligned with the pontoons.
    var theta = this.theta * this.deg2rad;
    var nx = Math.cos(theta), ny = Math.sin(theta);
    // Split the velocity (vx,vy) into components parallel
    // and perpendicular to the pontoons.
    var dotprod = this.vx * nx + this.vy * ny;
    var vpx = dotprod * nx;
    var vpy = dotprod * ny;
    var vtx = this.vx - vpx;
    var vty = this.vy - vpy;
    // Calculate the combined drag force.
    var drag_x = -this.drag_p * vpx - this.drag_t * vtx;
    var drag_y = -this.drag_p * vpy - this.drag_t * vty;
    // Calculate the thrust force on the COM.
    var phi = this.phi * this.deg2rad;
    var angle = theta + phi;
    var thrust_mag = this.max_thrust * this.throttle;
    var thrust_x = thrust_mag * Math.cos(angle);
    var thrust_y = thrust_mag * Math.sin(angle);
    // Calculate the net force on the COM
    var Fx = thrust_x + drag_x + this.current_x;
    var Fy = thrust_y + drag_y + this.current_y;
    // Update the position and bearing using the present motion.
    this.x += this.vx * dt;
    this.y += this.vy * dt;
    this.theta += this.omega * dt;
    //Update the COM velocity (assuming mass=1).
    this.vx += Fx * dt;
    this.vy += Fy * dt;
    // Calculate the torque about the COM due to the thrust.
    var tprop = -thrust_mag * this.leverarm * Math.sin(phi);
    // Calculate the torque about the COM due to rotational drag.
    var tdrag = -this.omega * this.drag_r;
    // Calculate the net torque about the COM.
    var tnet = tprop + tdrag;
    // Update the angular velocity about the COM (assuming I=1).
    this.omega += tnet * dt;
}

HouseBoat.prototype.draw = function() {
    this.boat
        .attr("transform",
            "translate(" + coord(this.x,this.y) + ") rotate(" +
            this.theta + ",0,0)");
    if(Math.abs(this.throttle) < 0.05) {
        wake_scale = 0.05;
    }
    else {
        wake_scale = this.throttle;
    }
    this.wake
        .attr("transform", "rotate(" + this.phi + ") scale(" +
            wake_scale + ")");
}

function Simulator() {
}

Simulator.prototype.initialize = function() {
    this.svg = d3.select("svg");
    this.window_width = +this.svg.attr("width");
    this.window_height = +this.svg.attr("height");
    var wby2 = 0.5 * this.window_width, hby2 = 0.5 * this.window_height;
    // Set coordinate system with origin at center of the window,
    // x increasing to the right, and y increasing upwards.
    this.axes = this.svg.append("g")
        .attr("transform", "translate(" + coord(wby2, hby2) +
            ") scale(1, -1)");
    // Initialize throttle and steering controls.
    var radius = 16.0;
    this.throttle_display = this.svg.append("circle")
        .attr("cx", this.window_width - radius)
        .attr("cy", hby2)
        .attr("r", radius)
        .attr("stroke", "black")
        .attr("fill-opacity", "0.5")
        .attr("fill", "green");
    this.steering_display = this.svg.append("circle")
        .attr("cx", wby2)
        .attr("cy", this.window_height - radius)
        .attr("r", radius)
        .attr("stroke", "black")
        .attr("fill-opacity", "0.5")
        .attr("fill", "green");
    // Draw center marks for each control.
    var w = 0.2 * radius, h = 0.6 * radius;
    this.svg.append("polygon")
        .attr("points",
            coord(wby2 - w, this.window_height) + " " +
            coord(wby2, this.window_height - h) + " " +
            coord(wby2 + w, this.window_height))
        .attr("stroke", "black")
        .attr("fill", "blue");
    this.svg.append("polygon")
        .attr("points",
            coord(this.window_width, hby2 - w) + " " +
            coord(this.window_width - h, hby2) + " " +
            coord(this.window_width, hby2 + w))
        .attr("stroke", "black")
        .attr("fill", "blue");
    this.throttle_max = this.window_height - 2 * radius;
    this.steering_max = this.window_width - 2 * radius;

    // Capture arrow keypress events.
    this.right = false;
    this.left = false;
    this.up = false;
    this.down = false;
    var self = this;
    d3.select("body")
        .on("keydown", function() {
            var code = d3.event.keyCode;
            if(code == 39) {
                self.right = true;
            }
            else if(code == 37) {
                self.left = true;
            }
            else if(code == 38) {
                self.up = true;
            }
            else if(code == 40) {
                self.down = true;
            }
            if(self.right || self.left) {
                self.steering_display.attr("fill-opacity", "1.0");
            }
            if(self.up || self.down) {
                self.throttle_display.attr("fill-opacity", "1.0");
            }
        })
        .on("keyup", function() {
            var code = d3.event.keyCode;
            if(code == 39) {
                self.right = false;
            }
            else if(code == 37) {
                self.left = false;
            }
            else if(code == 38) {
                self.up = false;
            }
            else if(code == 40) {
                self.down = false;
            }
            if(!self.right && !self.left) {
                self.steering_display.attr("fill-opacity", "0.5");
            }
            if(!self.up && !self.down) {
                self.throttle_display.attr("fill-opacity", "0.5");
            }
        });

    this.houseboat = new HouseBoat();
    this.houseboat.initialize(this.axes);
}

Simulator.prototype.run = function() {
    var ival = 50; // milliseconds
    // Start the interval timer.
    var self = this;
    d3.interval(function(elapsed) {
        // Adjust the throttle.
        var throttle = self.houseboat.throttle;
        var throttle_adjust = +1. * self.up - 1. * self.down;
        throttle += 0.01 * throttle_adjust;
        if(throttle > 1.0) { throttle = 1.0; }
        else if(throttle < -1.0) { throttle = -1.0; }
        // Adjust the steering.
        var steering = self.houseboat.steering;
        var steering_adjust = +1. * self.right - 1. * self.left;
        steering += 0.01 * steering_adjust;
        if(steering > 1.0) { steering = 1.0; }
        else if(steering < -1.0) { steering = -1.0; }
        self.throttle_display //.transition(self.trans)
            .attr("cy",
            0.5 * (self.window_height - self.throttle_max * throttle));
        self.steering_display //.transition(self.trans)
            .attr("cx",
            0.5 * (self.window_width + self.steering_max * steering));
        self.houseboat.update(throttle, steering);
        self.houseboat.draw();
    }, ival);
}

var sim = new Simulator();

$(function() {
    // DOM is ready.
    sim.initialize();
    sim.run();
});
<|endoftext|>


<commit_before>//
//  Copyright (c) 2011 <redpill27@gmail.com>
//  This code is freely distributable under the MIT license
//

var hn = {
    generateChart: function() {
        var username = $('#username').val();
        var fields = [
            "filter[fields][username][]=" + username,
            "filter[fields][type][]=comment",
            "sortby=points desc",
            "limit=" + parseInt($("#limit").val(),10)
        ];
        var qs = fields.join('&');

        this.scriptNode = document.createElement('script');
        this.scriptNode.type = 'text/javascript';
        this.scriptNode.src = "http://api.thriftdb.com/api.hnsearch.com/items/_search?callback=hn.onSearchComplete&" + qs;
        $("body").append(this.scriptNode);
    },

    //
    // callback called from the search API
    //
    onSearchComplete: function(data) {
        // detach the map and jsonp-script nodes
        $(this.scriptNode).detach();
        $("#chart-map").detach();
        $("#comments-wrapper").empty();
        if (data.hits === 0) {
            $("#chart").css("display", "none");
            return;
        }
        // retrieve the img map from the server
        var imgQs = hncharts.commentLengthAndPoints(data);
        var imgUrl = "http://chart.googleapis.com/chart?" + imgQs
        var that = this;
        $.getJSON("/hn/chart-map.json?" + imgQs, function(data) {
            if (data.chartshape !== undefined) {
                $(that.imageMapTemplate({
                    data : _.select(data.chartshape, function(item) { return item.type === "CIRCLE";})
                })).appendTo($("#chart"));
            }
        });
        // update the document
        $("#chart a").attr("href", imgUrl);
        $("#chart img").attr("src", imgUrl);
        $("#chart").css("display", "block");
        $("#comments-wrapper").html(this.commentsTemplate(data));
    },

    init: function() {
        this.imageMapTemplate = _.template('<map name="chart-map" id="chart-map"> \
  <% _.each(data, function(r, i) { %> \
  <area name="<%= r.name %>" shape="CIRCLE" coords="<%= r.coords.join(",") %>" href="#c<%= i+1 %>"  title="#<%= i+1 %>"> \
  <% }); %> \
</map>');
        this.commentsTemplate = _.template('<% _.each(results, function(r, i) { %> \

 \

<a name="c<%= i+1 %>" /> \
        #<%= i+1 %>, <%= (r.item.points === null) ? "-" : r.item.points %> points \
        by <a href="http://news.ycombinator.com/user?id=<%= r.item.username %>"><%= r.item.username %> | \
        <%= r.item.create_ts.slice(0,10) %> | \
        <a href="http://news.ycombinator.com/item?id=<%= r.item.id %>">link | \
        <a href="http://news.ycombinator.com/item?id=<%= r.item.parent_id %>">parent | \
        <% if (r.item.discussion !== null) { %> \
        on: <a href="http://news.ycombinator.com/item?id=<%= r.item.discussion.id %>"><%= r.item.discussion.title %> \
        <% } %> \

 \

 \
        <%= r.item.text %> \

 \

 \
    <% }); %>');
    }
};

$(function() {
    _.bindAll(hn);
    hn.init();
});

<commit_msg>initial focus<commit_after>//
//  Copyright (c) 2011 <redpill27@gmail.com>
//  This code is freely distributable under the MIT license
//

var hn = {
    generateChart: function() {
        var username = $('#username').val();
        var fields = [
            "filter[fields][username][]=" + username,
            "filter[fields][type][]=comment",
            "sortby=points desc",
            "limit=" + parseInt($("#limit").val(),10)
        ];
        var qs = fields.join('&');

        this.scriptNode = document.createElement('script');
        this.scriptNode.type = 'text/javascript';
        this.scriptNode.src = "http://api.thriftdb.com/api.hnsearch.com/items/_search?callback=hn.onSearchComplete&" + qs;
        $("body").append(this.scriptNode);
    },

    //
    // callback called from the search API
    //
    onSearchComplete: function(data) {
        // detach the map and jsonp-script nodes
        $(this.scriptNode).detach();
        $("#chart-map").detach();
        $("#comments-wrapper").empty();
        if (data.hits === 0) {
            $("#chart").css("display", "none");
            return;
        }
        // retrieve the img map from the server
        var imgQs = hncharts.commentLengthAndPoints(data);
        var imgUrl = "http://chart.googleapis.com/chart?" + imgQs
        var that = this;
        $.getJSON("/hn/chart-map.json?" + imgQs, function(data) {
            if (data.chartshape !== undefined) {
                $(that.imageMapTemplate({
                    data : _.select(data.chartshape, function(item) { return item.type === "CIRCLE";})
                })).appendTo($("#chart"));
            }
        });
        // update the document
        $("#chart a").attr("href", imgUrl);
        $("#chart img").attr("src", imgUrl);
        $("#chart").css("display", "block");
        $("#comments-wrapper").html(this.commentsTemplate(data));
    },

    init: function() {
        this.imageMapTemplate = _.template('<map name="chart-map" id="chart-map"> \
  <% _.each(data, function(r, i) { %> \
  <area name="<%= r.name %>" shape="CIRCLE" coords="<%= r.coords.join(",") %>" href="#c<%= i+1 %>"  title="#<%= i+1 %>"> \
  <% }); %> \
</map>');
        this.commentsTemplate = _.template('<% _.each(results, function(r, i) { %> \

 \

<a name="c<%= i+1 %>" /> \
        #<%= i+1 %>, <%= (r.item.points === null) ? "-" : r.item.points %> points \
        by <a href="http://news.ycombinator.com/user?id=<%= r.item.username %>"><%= r.item.username %> | \
        <%= r.item.create_ts.slice(0,10) %> | \
        <a href="http://news.ycombinator.com/item?id=<%= r.item.id %>">link | \
        <a href="http://news.ycombinator.com/item?id=<%= r.item.parent_id %>">parent | \
        <% if (r.item.discussion !== null) { %> \
        on: <a href="http://news.ycombinator.com/item?id=<%= r.item.discussion.id %>"><%= r.item.discussion.title %> \
        <% } %> \

 \

 \
        <%= r.item.text %> \

 \

 \
    <% }); %>');
    }
};

$(function() {
    _.bindAll(hn);
    hn.init();
    $('#username').focus();
});

<|endoftext|>


<commit_before>/**
 * Processes and draws the TypeDNA diagram.
 *
 * Authors:
 *  - Michael Fiest
 *  - Eddie Antonio Santos
 *  - Ian Watts
 */

/**
 * From: http://colorbrewer2.org/?type=diverging&scheme=RdBu&n=3
 * ['#ef8a62','#f7f7f7','#67a9cf'];
 */
var COLOR_ADDITIONS = '#ef8a62';
var COLOR_DELETIONS = '#67a9cf';
var VALID_STEP_SIZES = d3.set(['hour', 'day', 'month', 'week']);

/** The set of edit kinds.  */
ASTDiff.EDIT_KIND = d3.set(['ADD', 'REMOVE']);

/* Shim the assert function in there! */
!window.assert ? (window.assert = console.assert.bind(console)) : undefined;

/* Run right after the page (and this script) finishes loading. */
//document.addEventListener('DOMContentLoaded', function () {
//  var container = document.getElementById('dna-table');
//
//  /* XXX: */
//  var processed = window.preprocessedData = preprocessData(window.DATA);
//  var filtered = window.filteredData = filterTypes(processed, {
//  });
//
//  /* Make this arbitrarily large. */
//  var height = 20 * filtered.types.length;
//
//  drawGraph(filtered, container.offsetWidth, height);
//});

/* Draw table given JSON */
function createTable(data, filter) {
  window.DATA = JSON.parse(data);
  createTable2(filter);
}

function createTable2(filter) {
  var container = document.getElementById('dna-table');

  /* Clear previous table */
  container.innerHTML = "";

  /* XXX: */
  var processed = window.preprocessedData = preprocessData(window.DATA);
  var filtered = window.filteredData = filterTypes(processed, filter);

  /* Make this arbitrarily large. */
  var height = 20 * filtered.types.length;

  drawGraph(filtered, container.offsetWidth, height);
}

/*=== Classes ===*/

/**
 * Class: ASTDiff
 *
 * Instantiated with an original data object.
 */
function ASTDiff(original) {
  /* Instantiate a new ASTDiff object if called without `new`. */
  if (!(this instanceof ASTDiff)) {
    return new ASTDiff(original);
  }

  var date = new Date(original.date);
  /* The date must be reasonable... */
  assert(date > new Date('1970-01-01') && date < new Date());
  assert(typeof original.type === 'string');
  assert(ASTDiff.EDIT_KIND.has(original.edit));
  assert(looksLikeAnEmail(original.author));

  this.date = date;
  this.type = original.type;
  this.author = original.author;
  this.edit = original.edit;
  this.commit = original.commitID;
}

/**
 * ASTDiff methods and computed properties.
 */
Object.defineProperties(ASTDiff.prototype, {
  isAdd: {
    get: function () {
      return this.edit === 'ADD';
    }
  },

  isRemove: {
    get: function () {
      return this.edit === 'REMOVE';
    }
  },

  /**
   * Delegate valueOf() to the internal date object. This makes relational
   * comparisons with ASTDiff objects equivillent to comparing their dates.
   * (i.e.,
   *    (ASTDiff(...) < ASTDiff(...)) ===
   *        (ASTDiff(...).date < ASTDiff(...))
   * )
   */
  valueOf: {
    value: function () {
      return this.date.valueOf();
    },
  }
});

/**
 * Class: Cell
 *
 * Represents the data for a single cell in the table.  The data is a number
 * of observations, which are either additions or deletions.
 */
function Cell(start, until, type) {
  /* Instantiate a new Cell object if called without `new`. */
  if (!(this instanceof Cell)) {
    return new Cell(start, until, type);
  }

  assert(start instanceof Date);
  assert(until instanceof Date);
  assert(typeof type === 'string');

  this.data = []
  this.startDate = start;
  this.endDate = until;
  this.type = type;
}

Object.defineProperties(Cell.prototype, {
  /**
   * Given a bunch of ASTDiffs,
   * sets this cell's data to the appropriate ASTDiff.
   */
  setDataFromSortedASTDiffs: {
    value: function (astDiffs) {
      assert(astDiffs instanceof Array);
      var typeName = this.type;

      var lowerIndex = d3.bisectLeft(astDiffs, this.startDate);
      var upperIndex = d3.bisectRight(astDiffs, this.endDate);

      this.data = astDiffs
        .slice(lowerIndex, upperIndex)
        .filter(function (diff) {
          return diff.type === typeName;
        });

      return this;
    },
  },

  /** True if the cell contains at least one AST diff.  */
  hasData: {
    get: function () {
      return this.data && (this.data.length > 0);
    }
  },

  /** Number of observations. */
  numberOfObservations: {
    get: function () {
      return this.data.length;
    }
  },

  /** Number of additions.  */
  numberOfAdds: {
    get: function () {
      return this.data.filter(function (diff) {
        return diff.isAdd;
      }).length;
    }
  },

  /** Number of deletions.  */
  numberOfDeletions: {
    get: function () {
      return this.data.filter(function (diff) {
        return diff.isRemove;
      }).length;
    }
  }
});

/**
 * Class: JavaType
 *
 * Represents a type in Java.
 */
function JavaType(name) {
  /* Instantiate a new Cell object if called without `new`. */
  if (!(this instanceof JavaType)) {
    return new JavaType(name);
  }

  /* TODO: primitive types? */
  /* TODO: generics? */
  var components = name.split('.');
  this.name = components.pop();
  this.package = components.join('.');
  this.cells = [];
}

Object.defineProperties(JavaType.prototype, {
  /**
   * Given a bunch of ASTDiffs,
   * sets this cell's data to the appropriate ASTDiff.
   */
  fullyQualifiedName: {
    get: function () {
      return this.package + '.' + this.name;
    }
  },

  addCell: {
    value: function (cell) {
      assert(cell instanceof Cell);
      return this.cells.push(cell);
    }
  },

  toString: {
    value: function () {
      return this.className;
    }
  }
});

/*=== Core functions ====*/

/**
 * Modifies original data.
 */
function preprocessData(data) {
  assert(data.types instanceof Array);
  assert(data.commits instanceof Array);
  assert(data.dates instanceof Array);

  return {
    /* A set of types. */
    types: d3.set(data.types),

    /* Mapping GitSha -> Commit MetaData. */
    commits: (function () {
      var commitMap = {};
      data.commits.forEach(function (commit) {
        assert(typeof commit.commitID  === 'string');
        var sha = commit.commitID;
        assert(looksLikeAGitSha(sha));

        commitMap[sha] = commit;
      });
      return commitMap;
    }()),

    /* A copy of AST Diff data, in asscending order of date. */
    astDiffs: (function (astDiffsByType) {
      return astDiffsByType
        .map(ASTDiff)
        .sort(function (a, b) {
          assert(a instanceof ASTDiff);
          return d3.ascending(a.date, b.date);
        });
    }(data.dates.slice()))
  };
}

/**
 * Takes preprocessed data, and returns an array of types, in ascending order
 * of popularity.
 *
 * Each type has its Cells, with ASTDiffs.
 */
function filterTypes(data, filters) {
  assert(filters);
  /* Either the date provided, or the first date attested. */
  var startDate = filters.start || first(data.astDiffs).date;
  /* Either the date provided or the last date attested. */
  var endDate = filters.end || last(data.astDiffs).date;
  var numberOfTypesUpperBound = filters.limit || Infinity;
  var cellSize = filters.stepSize || 'day';

  assert(startDate instanceof Date);
  assert(endDate instanceof Date);
  assert(startDate < endDate);
  assert(typeof numberOfTypesUpperBound === 'number');
  assert(VALID_STEP_SIZES.has(cellSize));

  /* Find the range of diffs to use. */
  var lowerIndex = d3.bisectLeft(data.astDiffs, startDate);
  var upperIndex = d3.bisectRight(data.astDiffs, endDate);

  var applicableDiffs = data.astDiffs.slice(lowerIndex, upperIndex);
  /* Filter types, first by the types that are ACTUALLY present in the
   * filtered ASTDiffs */
  var typesPresent = countTypeAbsoluteFrequency(applicableDiffs);
  var sortedTypeNames = Object.keys(typesPresent)
    .sort(function (a, b) {
      return d3.descending(typesPresent[a], typesPresent[b]);
    })
    .slice(0, numberOfTypesUpperBound);

  assert(sortedTypeNames.length > 0);

  /* Create an entry for each type. */
  var types = sortedTypeNames.map(JavaType);

  /* Create all cells applicable to display.  */
  forEachDateLimitsDescending(startDate, endDate, cellSize, function (start, end) {
    /* For each type... */
    types.forEach(function (type) {
      /* ...add all data cells. */
      type.addCell(
        new Cell(start, end, type.fullyQualifiedName)
          .setDataFromSortedASTDiffs(applicableDiffs)
      );
    });

  });

  /* The cells in each type are in DESCENDING order.
   * Therefore, take the END date of the FIRST cell to get the upper limit;
   * and take the START date of the LAST cell to get the lower limit. */
  var arbitraryCells = first(types).cells;
  var maxDate = first(arbitraryCells).endDate;
  var minDate = last(arbitraryCells).startDate;
  assert(minDate <= maxDate);

  return {
    types: types,
    numberOfColumns: arbitraryCells.length,
    minDate: minDate,
    maxDate: maxDate
  };
}

/**
 * Count all types in the given ASTDiff objects.
 */
function countTypeAbsoluteFrequency(astDiffs) {
  var typesFrequency = {};
  astDiffs.forEach(function (diff) {
    var freq = typesFrequency[diff.type] || 0;
    typesFrequency[diff.type] = freq + 1;
  });

  return typesFrequency;
}

/**
 * Given start and end dates, calls the given callback with the start and end
 * date.
 */
function forEachDateLimitsDescending(start, end, step, callback) {
  var currentStart;
  var currentEnd = end;

  while (start < currentEnd) {
    currentStart = moment(currentEnd);

    switch (step) {
        case 'hour':
            currentStart.subtract(1, 'hour');
            break;
        case 'day':
            currentStart.subtract(1, 'day');
            break;
        case 'week':
            /* TODO: Make this smarter? */
            currentStart.subtract(1, 'weeks');
            break;
        case 'month':
            currentStart.subtract(1, 'month');
    }

    callback(currentStart.toDate(), currentEnd);
    currentEnd = currentStart.toDate();
  }
}

/*=== Graph ===*/

function drawGraph(data, width, height) {
  var marginLeft = 120;

  /* Create a scale for the types i.e., the y-axis */
  var yScale = d3.scale.ordinal()
    .domain(data.types.map(function (type) { return type.name }))
    .rangeBands([0, height]);

  /* Create a scale for the dates i.e., the x-axis */
  var xScale = d3.time.scale()
    .domain([data.minDate, data.maxDate])
    .rangeRound([marginLeft, width]);

  /**
   * Given number of additions and deletions, returns an appropriate,
   * interpoloated colour.
   */
  var colorScale = (function () {
    var colorInterpolator = d3.interpolateHsl(COLOR_ADDITIONS, COLOR_DELETIONS);

    return function (additions, deletions) {
      assert(additions + deletions > 0);
      var score = additions / (additions + deletions);
      assert(0 <= score && score <= 1);
      return colorInterpolator(score);
    };
  }());

  var svg = d3.select('#dna-table').append('svg')
      .attr("width", width)
      .attr("height", height);

  var row = svg.selectAll('.row')
      .data(data.types)
    .enter().append('g')
      .classed('row', true)
      .attr('transform', function (type) {
        return 'translate(0, ' + yScale(type.name) + ')';
      })
      .each(createCellsForType);

  row.append('text')
      .attr('y', yScale.rangeBand() / 2)
      .attr('dy', '.32em')
      .attr('x', `${marginLeft - 10}px`)
      .attr('text-anchor', 'end')
      .text(function (type) { return type.name });

  function createCellsForType(type) {
    /* Create all the cells. */
    var cell = d3.select(this).selectAll('.cell')
        .data(type.cells)
      .enter().append('g')
        /* Only do cool things with cells that *HAVE* data! */
        .filter(function (cell) { return cell.hasData; })
        .classed('cell', true)
        .attr('transform', function (cell) {
          return 'translate(' + xScale(cell.startDate) + ', 0)';
        });

    cell.append('rect')
      .classed('cell-data', true)
      .attr('width', cellWidthFromScale(first(type.cells), xScale))
      .attr('transform', 'translate(0, 1)')
      .attr('height', yScale.rangeBand() - 2)
      .style('fill', function (cell) {
        assert(cell.hasData);
        /* Use a gradient that shows proportion of additions and deletions. */
        return colorScale(cell.numberOfAdds, cell.numberOfDeletions);
      });
  }
}

/*=== Utilties ===*/

function cellWidthFromScale(cell, scale) {
  var bigger = scale(cell.endDate);
  var smaller = scale(cell.startDate);
  assert(bigger > smaller);
  return bigger - smaller;
}

function first(array) {
  assert(array instanceof Array);
  return array[0];
}

function last(array) {
  assert(array instanceof Array);
  return array[array.length - 1];
}

/*=== Predicates used in assertions and checks ===*/

function looksLikeAnEmail(thing) {
  if (typeof thing !== 'string') {
    return false;
  }

  /* Emails are actually really complicated, but let's... do this: */
  return thing.match(/^.+@.+$/);
}

function looksLikeAGitSha(thing) {
  if (typeof thing !== 'string') {
    return false;
  }

  return thing.match(/^[0-9a-f]{5,}$/i);
}

/*globals d3, moment*/
<commit_msg>Changed margin and text size. (Still some types get cut off)<commit_after>/**
 * Processes and draws the TypeDNA diagram.
 *
 * Authors:
 *  - Michael Fiest
 *  - Eddie Antonio Santos
 *  - Ian Watts
 */

/**
 * From: http://colorbrewer2.org/?type=diverging&scheme=RdBu&n=3
 * ['#ef8a62','#f7f7f7','#67a9cf'];
 */
var COLOR_ADDITIONS = '#ef8a62';
var COLOR_DELETIONS = '#67a9cf';
var VALID_STEP_SIZES = d3.set(['hour', 'day', 'month', 'week']);

/** The set of edit kinds.  */
ASTDiff.EDIT_KIND = d3.set(['ADD', 'REMOVE']);

/* Shim the assert function in there! */
!window.assert ? (window.assert = console.assert.bind(console)) : undefined;

/* Run right after the page (and this script) finishes loading. */
//document.addEventListener('DOMContentLoaded', function () {
//  var container = document.getElementById('dna-table');
//
//  /* XXX: */
//  var processed = window.preprocessedData = preprocessData(window.DATA);
//  var filtered = window.filteredData = filterTypes(processed, {
//  });
//
//  /* Make this arbitrarily large. */
//  var height = 20 * filtered.types.length;
//
//  drawGraph(filtered, container.offsetWidth, height);
//});

/* Draw table given JSON */
function createTable(data, filter) {
  window.DATA = JSON.parse(data);
  createTable2(filter);
}

function createTable2(filter) {
  var container = document.getElementById('dna-table');

  /* Clear previous table */
  container.innerHTML = "";

  /* XXX: */
  var processed = window.preprocessedData = preprocessData(window.DATA);
  var filtered = window.filteredData = filterTypes(processed, filter);

  /* Make this arbitrarily large. */
  var height = 20 * filtered.types.length;

  drawGraph(filtered, container.offsetWidth, height);
}

/*=== Classes ===*/

/**
 * Class: ASTDiff
 *
 * Instantiated with an original data object.
 */
function ASTDiff(original) {
  /* Instantiate a new ASTDiff object if called without `new`. */
  if (!(this instanceof ASTDiff)) {
    return new ASTDiff(original);
  }

  var date = new Date(original.date);
  /* The date must be reasonable... */
  assert(date > new Date('1970-01-01') && date < new Date());
  assert(typeof original.type === 'string');
  assert(ASTDiff.EDIT_KIND.has(original.edit));
  assert(looksLikeAnEmail(original.author));

  this.date = date;
  this.type = original.type;
  this.author = original.author;
  this.edit = original.edit;
  this.commit = original.commitID;
}

/**
 * ASTDiff methods and computed properties.
 */
Object.defineProperties(ASTDiff.prototype, {
  isAdd: {
    get: function () {
      return this.edit === 'ADD';
    }
  },

  isRemove: {
    get: function () {
      return this.edit === 'REMOVE';
    }
  },

  /**
   * Delegate valueOf() to the internal date object. This makes relational
   * comparisons with ASTDiff objects equivillent to comparing their dates.
   * (i.e.,
   *    (ASTDiff(...) < ASTDiff(...)) ===
   *        (ASTDiff(...).date < ASTDiff(...))
   * )
   */
  valueOf: {
    value: function () {
      return this.date.valueOf();
    },
  }
});

/**
 * Class: Cell
 *
 * Represents the data for a single cell in the table.  The data is a number
 * of observations, which are either additions or deletions.
 */
function Cell(start, until, type) {
  /* Instantiate a new Cell object if called without `new`. */
  if (!(this instanceof Cell)) {
    return new Cell(start, until, type);
  }

  assert(start instanceof Date);
  assert(until instanceof Date);
  assert(typeof type === 'string');

  this.data = []
  this.startDate = start;
  this.endDate = until;
  this.type = type;
}

Object.defineProperties(Cell.prototype, {
  /**
   * Given a bunch of ASTDiffs,
   * sets this cell's data to the appropriate ASTDiff.
   */
  setDataFromSortedASTDiffs: {
    value: function (astDiffs) {
      assert(astDiffs instanceof Array);
      var typeName = this.type;

      var lowerIndex = d3.bisectLeft(astDiffs, this.startDate);
      var upperIndex = d3.bisectRight(astDiffs, this.endDate);

      this.data = astDiffs
        .slice(lowerIndex, upperIndex)
        .filter(function (diff) {
          return diff.type === typeName;
        });

      return this;
    },
  },

  /** True if the cell contains at least one AST diff.  */
  hasData: {
    get: function () {
      return this.data && (this.data.length > 0);
    }
  },

  /** Number of observations. */
  numberOfObservations: {
    get: function () {
      return this.data.length;
    }
  },

  /** Number of additions.  */
  numberOfAdds: {
    get: function () {
      return this.data.filter(function (diff) {
        return diff.isAdd;
      }).length;
    }
  },

  /** Number of deletions.  */
  numberOfDeletions: {
    get: function () {
      return this.data.filter(function (diff) {
        return diff.isRemove;
      }).length;
    }
  }
});

/**
 * Class: JavaType
 *
 * Represents a type in Java.
 */
function JavaType(name) {
  /* Instantiate a new Cell object if called without `new`. */
  if (!(this instanceof JavaType)) {
    return new JavaType(name);
  }

  /* TODO: primitive types? */
  /* TODO: generics? */
  var components = name.split('.');
  this.name = components.pop();
  this.package = components.join('.');
  this.cells = [];
}

Object.defineProperties(JavaType.prototype, {
  /**
   * Given a bunch of ASTDiffs,
   * sets this cell's data to the appropriate ASTDiff.
   */
  fullyQualifiedName: {
    get: function () {
      return this.package + '.' + this.name;
    }
  },

  addCell: {
    value: function (cell) {
      assert(cell instanceof Cell);
      return this.cells.push(cell);
    }
  },

  toString: {
    value: function () {
      return this.className;
    }
  }
});

/*=== Core functions ====*/

/**
 * Modifies original data.
 */
function preprocessData(data) {
  assert(data.types instanceof Array);
  assert(data.commits instanceof Array);
  assert(data.dates instanceof Array);

  return {
    /* A set of types. */
    types: d3.set(data.types),

    /* Mapping GitSha -> Commit MetaData. */
    commits: (function () {
      var commitMap = {};
      data.commits.forEach(function (commit) {
        assert(typeof commit.commitID  === 'string');
        var sha = commit.commitID;
        assert(looksLikeAGitSha(sha));

        commitMap[sha] = commit;
      });
      return commitMap;
    }()),

    /* A copy of AST Diff data, in asscending order of date. */
    astDiffs: (function (astDiffsByType) {
      return astDiffsByType
        .map(ASTDiff)
        .sort(function (a, b) {
          assert(a instanceof ASTDiff);
          return d3.ascending(a.date, b.date);
        });
    }(data.dates.slice()))
  };
}

/**
 * Takes preprocessed data, and returns an array of types, in ascending order
 * of popularity.
 *
 * Each type has its Cells, with ASTDiffs.
 */
function filterTypes(data, filters) {
  assert(filters);
  /* Either the date provided, or the first date attested. */
  var startDate = filters.start || first(data.astDiffs).date;
  /* Either the date provided or the last date attested. */
  var endDate = filters.end || last(data.astDiffs).date;
  var numberOfTypesUpperBound = filters.limit || Infinity;
  var cellSize = filters.stepSize || 'day';

  assert(startDate instanceof Date);
  assert(endDate instanceof Date);
  assert(startDate < endDate);
  assert(typeof numberOfTypesUpperBound === 'number');
  assert(VALID_STEP_SIZES.has(cellSize));

  /* Find the range of diffs to use. */
  var lowerIndex = d3.bisectLeft(data.astDiffs, startDate);
  var upperIndex = d3.bisectRight(data.astDiffs, endDate);

  var applicableDiffs = data.astDiffs.slice(lowerIndex, upperIndex);
  /* Filter types, first by the types that are ACTUALLY present in the
   * filtered ASTDiffs */
  var typesPresent = countTypeAbsoluteFrequency(applicableDiffs);
  var sortedTypeNames = Object.keys(typesPresent)
    .sort(function (a, b) {
      return d3.descending(typesPresent[a], typesPresent[b]);
    })
    .slice(0, numberOfTypesUpperBound);

  assert(sortedTypeNames.length > 0);

  /* Create an entry for each type. */
  var types = sortedTypeNames.map(JavaType);

  /* Create all cells applicable to display.  */
  forEachDateLimitsDescending(startDate, endDate, cellSize, function (start, end) {
    /* For each type... */
    types.forEach(function (type) {
      /* ...add all data cells. */
      type.addCell(
        new Cell(start, end, type.fullyQualifiedName)
          .setDataFromSortedASTDiffs(applicableDiffs)
      );
    });

  });

  /* The cells in each type are in DESCENDING order.
   * Therefore, take the END date of the FIRST cell to get the upper limit;
   * and take the START date of the LAST cell to get the lower limit. */
  var arbitraryCells = first(types).cells;
  var maxDate = first(arbitraryCells).endDate;
  var minDate = last(arbitraryCells).startDate;
  assert(minDate <= maxDate);

  return {
    types: types,
    numberOfColumns: arbitraryCells.length,
    minDate: minDate,
    maxDate: maxDate
  };
}

/**
 * Count all types in the given ASTDiff objects.
 */
function countTypeAbsoluteFrequency(astDiffs) {
  var typesFrequency = {};
  astDiffs.forEach(function (diff) {
    var freq = typesFrequency[diff.type] || 0;
    typesFrequency[diff.type] = freq + 1;
  });

  return typesFrequency;
}

/**
 * Given start and end dates, calls the given callback with the start and end
 * date.
 */
function forEachDateLimitsDescending(start, end, step, callback) {
  var currentStart;
  var currentEnd = end;

  while (start < currentEnd) {
    currentStart = moment(currentEnd);

    switch (step) {
        case 'hour':
            currentStart.subtract(1, 'hour');
            break;
        case 'day':
            currentStart.subtract(1, 'day');
            break;
        case 'week':
            /* TODO: Make this smarter? */
            currentStart.subtract(1, 'weeks');
            break;
        case 'month':
            currentStart.subtract(1, 'month');
    }

    callback(currentStart.toDate(), currentEnd);
    currentEnd = currentStart.toDate();
  }
}

/*=== Graph ===*/

function drawGraph(data, width, height) {
  var marginLeft = 150;

  /* Create a scale for the types i.e., the y-axis */
  var yScale = d3.scale.ordinal()
    .domain(data.types.map(function (type) { return type.name }))
    .rangeBands([0, height]);

  /* Create a scale for the dates i.e., the x-axis */
  var xScale = d3.time.scale()
    .domain([data.minDate, data.maxDate])
    .rangeRound([marginLeft, width]);

  /**
   * Given number of additions and deletions, returns an appropriate,
   * interpoloated colour.
   */
  var colorScale = (function () {
    var colorInterpolator = d3.interpolateHsl(COLOR_ADDITIONS, COLOR_DELETIONS);

    return function (additions, deletions) {
      assert(additions + deletions > 0);
      var score = additions / (additions + deletions);
      assert(0 <= score && score <= 1);
      return colorInterpolator(score);
    };
  }());

  var svg = d3.select('#dna-table').append('svg')
      .attr("width", width)
      .attr("height", height);

  var row = svg.selectAll('.row')
      .data(data.types)
    .enter().append('g')
      .classed('row', true)
      .attr('transform', function (type) {
        return 'translate(0, ' + yScale(type.name) + ')';
      })
      .each(createCellsForType);

  row.append('text')
      .attr('y', yScale.rangeBand() / 2)
      .attr('dy', '.22em')
      .attr('x', `${marginLeft - 10}px`)
      .attr('text-anchor', 'end')
      .text(function (type) { return type.name });

  function createCellsForType(type) {
    /* Create all the cells. */
    var cell = d3.select(this).selectAll('.cell')
        .data(type.cells)
      .enter().append('g')
        /* Only do cool things with cells that *HAVE* data! */
        .filter(function (cell) { return cell.hasData; })
        .classed('cell', true)
        .attr('transform', function (cell) {
          return 'translate(' + xScale(cell.startDate) + ', 0)';
        });

    cell.append('rect')
      .classed('cell-data', true)
      .attr('width', cellWidthFromScale(first(type.cells), xScale))
      .attr('transform', 'translate(0, 1)')
      .attr('height', yScale.rangeBand() - 2)
      .style('fill', function (cell) {
        assert(cell.hasData);
        /* Use a gradient that shows proportion of additions and deletions. */
        return colorScale(cell.numberOfAdds, cell.numberOfDeletions);
      });
  }
}

/*=== Utilties ===*/

function cellWidthFromScale(cell, scale) {
  var bigger = scale(cell.endDate);
  var smaller = scale(cell.startDate);
  assert(bigger > smaller);
  return bigger - smaller;
}

function first(array) {
  assert(array instanceof Array);
  return array[0];
}

function last(array) {
  assert(array instanceof Array);
  return array[array.length - 1];
}

/*=== Predicates used in assertions and checks ===*/

function looksLikeAnEmail(thing) {
  if (typeof thing !== 'string') {
    return false;
  }

  /* Emails are actually really complicated, but let's... do this: */
  return thing.match(/^.+@.+$/);
}

function looksLikeAGitSha(thing) {
  if (typeof thing !== 'string') {
    return false;
  }

  return thing.match(/^[0-9a-f]{5,}$/i);
}

/*globals d3, moment*/
<|endoftext|>


<commit_before>var state_running = function(game) {
    return {
        create: function(game) {
            this.graphics = game.add.graphics(0, 0);
            this.firstTouchY = null;
            this.dragY = null;

            game.input.onDown.add(this.onDown, this);
            game.input.onUp.add(this.onUp, this);
            game.input.onUp.add(function() {
                if (objects.runner.onGround() && objects.runner.vspeed === 0) {
                    objects.runner.color = colorPalette.runner;
                }
            });

            var textStyle = {
                fill: colorPalette.text,
                boundsAlignH: "center",
                boundsAlignV: "middle"
            };
            objects.scoreDisplay = game.add.text(game.world.centerX - constants.runnerSize, constants.groundHeight - (constants.runnerSize / 2), gameState.score, textStyle);
            objects.scoreDisplay.anchor.set(0.5);
            objects.scoreDisplay.setShadow(1, 1, colorPalette.textShadow);
            objects.highScoreDisplay = game.add.text(constants.runnerSize / 2, constants.groundHeight + (constants.runnerSize / 2), "", textStyle);
            objects.highScoreDisplay.anchor.y = 0.5;
            objects.highScoreDisplay.setShadow(1, 1, colorPalette.black);

            setupGame();
        },

        update: function(game) {
            if (gameState.state == states.dying) {
                if (this.colorStep <= 50) {
                    objects.runner.color = Phaser.Color.interpolateColor(this.runnerOriginalColor, colorPalette.background, 50, this.colorStep++);
                }
                if (game.time.time > this.timeToLeave) {
                    var highScore = localStorage.getItem('squareSquared-highScore');
                    localStorage.setItem('squareSquared-highScore', Math.max(gameState.score, highScore ? highScore : 0));
                    objects.runner.color = colorPalette.runner;
                    setupGame();
                    this.gameIsLost = false;
                }
            } else {
                objects.runner.applyGravity();

                var obstacleIndex;
                for (obstacleIndex = 0; obstacleIndex < objects.obstacles.length; ++obstacleIndex) {
                    var obstacle = objects.obstacles[obstacleIndex];
                    obstacle.applyGravity();
                    obstacle.movePolygonBy(constants.hspeed);

                    if (objects.runner.intersects(obstacle)) {
                        // Game over
                        loseGame(this);
                        return;
                    }

                    if (runnerHasPassedObstacle(obstacle, objects.runner)) {
                        this.scorePoint(obstacle);
                    }

                }
                if (objects.obstacles[0] && objects.obstacles[0].findRightmostPoint() < 0) {
                    var removed = objects.obstacles.shift();
                    objects.runner.onLand.remove(removed.runnerLandCallback);
                }
                if (objects.obstacles[objects.obstacles.length - 1] && objects.obstacles[objects.obstacles.length - 1].findRightmostPoint() < game.world.width) {
                    obstacleGenerator.addObstacleToBack();
                }
                this.dragY = this.firstTouchY - game.input.activePointer.worldY;
                var touchIsDown = this.firstTouchY !== null;
                var charge = chargeLevel(this.dragY, game.world.height, touchIsDown);
                objects.runner.updateBeforeDraw(charge, touchIsDown);
                objects.leftJumpLine.moveToX(objects.leftJumpLine.xPos);
                objects.rightJumpLine.moveToX(objects.rightJumpLine.xPos);

                if (this.dragY > 0) {
                    objects.dragLine.setHeight(this.dragY);
                }
                objects.dragLine.color = constants.chargeColors[charge];
            }
        },

        scorePoint: function(obstacle) {
            ++gameState.score;
            objects.scoreDisplay.visible = true;
            objects.scoreDisplay.text = (gameState.score);
            obstacle.hasScored = true;
            var loseSound = game.sound.play('score', 0.1);
            loseSound._sound.playbackRate.value = 0.8 + Math.max(0.05 * gameState.score, 2);
        },

        render: function() {
            this.graphics.clear();
            objects.leftJumpLine.draw(this.graphics);
            objects.rightJumpLine.draw(this.graphics);
            objects.runner.draw(this.graphics);
            var obstacleIndex;
            for (obstacleIndex = 0; obstacleIndex < objects.obstacles.length; ++obstacleIndex) {
                var obstacle = objects.obstacles[obstacleIndex];
                obstacle.draw(this.graphics);
            }
            objects.ground.draw(this.graphics);
            objects.dragLine.draw(this.graphics);
        },

        onDown: function(pointer, mouseEvent) {
            if (mouseEvent.identifier === 0) {
                if (this.firstTouchY === null) {
                    this.firstTouchY = pointer.worldY;
                    objects.dragLine.setLowerLeftTo(pointer.worldX + constants.runnerSize / 2, pointer.worldY);
                    objects.dragLine.visible = true;
                    objects.dragLine.setHeight(1);
                }
            }
        },

        onUp: function(pointer, mouseEvent) {
            // pointer.identifier === 0 Prevents 'mouse leaving the game world' from firing this, too
            if (mouseEvent.identifier === 0) {
                var charge = chargeLevel(this.dragY, game.world.height, false);
                objects.runner.jump(charge);
                this.firstTouchY = null;
                this.dragY = null;
                objects.dragLine.visible = false;
                if (gameState.state == states.waiting && charge === constants.chargeLevels.length - 1) {
                    startGame();
                }
            }
        }
    };
};

var runnerHasPassedObstacle = function(obstacle, runner) {
    return !obstacle.hasScored && Math.round(obstacle.findRightmostPoint()) < runner.findLeftmostPoint() - 1;
};
var loseGame = function(context) {
    gameState.state = states.dying;
    context.timeToLeave = game.time.time + constants.timeOnDyingScreen;
    context.colorStep = 0;
    context.runnerOriginalColor = objects.runner.color;
    var loseSound = game.sound.play('lose', 0.1);
    loseSound._sound.playbackRate.value = 0.5;
};

var startGame = function() {
    gameState.state = states.running;
    obstacleGenerator.addObstacleToBack();
    gameState.score = 0;
    var leftTween = game.add.tween(objects.leftJumpLine);
    leftTween.to({
        xPos: game.world.width / -2
    }, 1000, Phaser.Easing.Bounce.Out);
    leftTween.start();

    var rightTween = game.add.tween(objects.rightJumpLine);
    rightTween.to({
        xPos: game.world.width
    }, 1000, Phaser.Easing.Bounce.Out);
    rightTween.start();
};

var setupGame = function() {
    gameState.state = states.waiting;
    objects.obstacles = [];
    var highScore = localStorage.getItem('squareSquared-highScore');
    if (highScore && highScore > 0) {
        objects.highScoreDisplay.text = "High Score: " + highScore;
        objects.highScoreDisplay.visible = true;
    } else {
        objects.highScoreDisplay.visible = false;
    }
    objects.scoreDisplay.visible = false;

    game.add.bitmapText(Math.max(game.world.centerX - 150, 0), 75, 'titleGreen', 'Square', 64);
    game.add.bitmapText(game.world.centerX - 75, 25, 'titlePurple', 'Squared', 64);

    var leftTween = game.add.tween(objects.leftJumpLine);
    leftTween.to({
        xPos: 0
    }, 1000, Phaser.Easing.Bounce.Out);
    leftTween.start();

    var rightTween = game.add.tween(objects.rightJumpLine);
    rightTween.to({
        xPos: game.world.width / 2
    }, 1000, Phaser.Easing.Bounce.Out);
    rightTween.start();
};
<commit_msg>polish - moving score and high score to be on the same line but opposite sides<commit_after>var state_running = function(game) {
    return {
        create: function(game) {
            this.graphics = game.add.graphics(0, 0);
            this.firstTouchY = null;
            this.dragY = null;

            game.input.onDown.add(this.onDown, this);
            game.input.onUp.add(this.onUp, this);
            game.input.onUp.add(function() {
                if (objects.runner.onGround() && objects.runner.vspeed === 0) {
                    objects.runner.color = colorPalette.runner;
                }
            });

            var textStyle = {
                fill: colorPalette.text,
                boundsAlignH: "center",
                boundsAlignV: "middle"
            };
            objects.scoreDisplay = game.add.text(20, constants.groundHeight + (constants.runnerSize / 2), gameState.score, textStyle);
            objects.scoreDisplay.anchor.y = 0.5;
            objects.scoreDisplay.anchor.x = 0;
            objects.scoreDisplay.setShadow(1, 1, colorPalette.textShadow);
            objects.highScoreDisplay = game.add.text(game.world.width - 20, constants.groundHeight + (constants.runnerSize / 2), "", textStyle);
            objects.highScoreDisplay.anchor.y = 0.5;
            objects.highScoreDisplay.anchor.x = 1;
            objects.highScoreDisplay.setShadow(1, 1, colorPalette.black);

            setupGame();
        },

        update: function(game) {
            if (gameState.state == states.dying) {
                if (this.colorStep <= 50) {
                    objects.runner.color = Phaser.Color.interpolateColor(this.runnerOriginalColor, colorPalette.background, 50, this.colorStep++);
                }
                if (game.time.time > this.timeToLeave) {
                    var highScore = localStorage.getItem('squareSquared-highScore');
                    localStorage.setItem('squareSquared-highScore', Math.max(gameState.score, highScore ? highScore : 0));
                    objects.runner.color = colorPalette.runner;
                    setupGame();
                    this.gameIsLost = false;
                }
            } else {
                objects.runner.applyGravity();

                var obstacleIndex;
                for (obstacleIndex = 0; obstacleIndex < objects.obstacles.length; ++obstacleIndex) {
                    var obstacle = objects.obstacles[obstacleIndex];
                    obstacle.applyGravity();
                    obstacle.movePolygonBy(constants.hspeed);

                    if (objects.runner.intersects(obstacle)) {
                        // Game over
                        loseGame(this);
                        return;
                    }

                    if (runnerHasPassedObstacle(obstacle, objects.runner)) {
                        this.scorePoint(obstacle);
                    }

                }
                if (objects.obstacles[0] && objects.obstacles[0].findRightmostPoint() < 0) {
                    var removed = objects.obstacles.shift();
                    objects.runner.onLand.remove(removed.runnerLandCallback);
                }
                if (objects.obstacles[objects.obstacles.length - 1] && objects.obstacles[objects.obstacles.length - 1].findRightmostPoint() < game.world.width) {
                    obstacleGenerator.addObstacleToBack();
                }
                this.dragY = this.firstTouchY - game.input.activePointer.worldY;
                var touchIsDown = this.firstTouchY !== null;
                var charge = chargeLevel(this.dragY, game.world.height, touchIsDown);
                objects.runner.updateBeforeDraw(charge, touchIsDown);
                objects.leftJumpLine.moveToX(objects.leftJumpLine.xPos);
                objects.rightJumpLine.moveToX(objects.rightJumpLine.xPos);

                if (this.dragY > 0) {
                    objects.dragLine.setHeight(this.dragY);
                }
                objects.dragLine.color = constants.chargeColors[charge];
            }
        },

        scorePoint: function(obstacle) {
            ++gameState.score;
            objects.scoreDisplay.visible = true;
            objects.scoreDisplay.text = (gameState.score);
            obstacle.hasScored = true;
            var loseSound = game.sound.play('score', 0.1);
            loseSound._sound.playbackRate.value = 0.8 + Math.max(0.05 * gameState.score, 2);
        },

        render: function() {
            this.graphics.clear();
            objects.leftJumpLine.draw(this.graphics);
            objects.rightJumpLine.draw(this.graphics);
            objects.runner.draw(this.graphics);
            var obstacleIndex;
            for (obstacleIndex = 0; obstacleIndex < objects.obstacles.length; ++obstacleIndex) {
                var obstacle = objects.obstacles[obstacleIndex];
                obstacle.draw(this.graphics);
            }
            objects.ground.draw(this.graphics);
            objects.dragLine.draw(this.graphics);
        },

        onDown: function(pointer, mouseEvent) {
            if (mouseEvent.identifier === 0) {
                if (this.firstTouchY === null) {
                    this.firstTouchY = pointer.worldY;
                    objects.dragLine.setLowerLeftTo(pointer.worldX + constants.runnerSize / 2, pointer.worldY);
                    objects.dragLine.visible = true;
                    objects.dragLine.setHeight(1);
                }
            }
        },

        onUp: function(pointer, mouseEvent) {
            // pointer.identifier === 0 Prevents 'mouse leaving the game world' from firing this, too
            if (mouseEvent.identifier === 0) {
                var charge = chargeLevel(this.dragY, game.world.height, false);
                objects.runner.jump(charge);
                this.firstTouchY = null;
                this.dragY = null;
                objects.dragLine.visible = false;
                if (gameState.state == states.waiting && charge === constants.chargeLevels.length - 1) {
                    startGame();
                }
            }
        }
    };
};

var runnerHasPassedObstacle = function(obstacle, runner) {
    return !obstacle.hasScored && Math.round(obstacle.findRightmostPoint()) < runner.findLeftmostPoint() - 1;
};
var loseGame = function(context) {
    gameState.state = states.dying;
    context.timeToLeave = game.time.time + constants.timeOnDyingScreen;
    context.colorStep = 0;
    context.runnerOriginalColor = objects.runner.color;
    var loseSound = game.sound.play('lose', 0.1);
    loseSound._sound.playbackRate.value = 0.5;
};

var startGame = function() {
    gameState.state = states.running;
    obstacleGenerator.addObstacleToBack();
    gameState.score = 0;
    var leftTween = game.add.tween(objects.leftJumpLine);
    leftTween.to({
        xPos: game.world.width / -2
    }, 1000, Phaser.Easing.Bounce.Out);
    leftTween.start();

    var rightTween = game.add.tween(objects.rightJumpLine);
    rightTween.to({
        xPos: game.world.width
    }, 1000, Phaser.Easing.Bounce.Out);
    rightTween.start();
};

var setupGame = function() {
    gameState.state = states.waiting;
    objects.obstacles = [];
    var highScore = localStorage.getItem('squareSquared-highScore');
    if (highScore && highScore > 0) {
        objects.highScoreDisplay.text = "High Score: " + highScore;
        objects.highScoreDisplay.visible = true;
    } else {
        objects.highScoreDisplay.visible = false;
    }
    objects.scoreDisplay.visible = false;

    game.add.bitmapText(Math.max(game.world.centerX - 150, 0), 75, 'titleGreen', 'Square', 64);
    game.add.bitmapText(game.world.centerX - 75, 25, 'titlePurple', 'Squared', 64);

    var leftTween = game.add.tween(objects.leftJumpLine);
    leftTween.to({
        xPos: 0
    }, 1000, Phaser.Easing.Bounce.Out);
    leftTween.start();

    var rightTween = game.add.tween(objects.rightJumpLine);
    rightTween.to({
        xPos: game.world.width / 2
    }, 1000, Phaser.Easing.Bounce.Out);
    rightTween.start();
};
<|endoftext|>


<commit_before>var listVirtualManufacturers;

function initListVirtualManufacturers() {
    listVirtualManufacturers = myApp.virtualList('.lstmanufacturers', {
        items: [
        ],
        searchAll: function (query, items) {
            var foundItems = [];
            for (var i = 0; i < items.length; i++) {
                // Check if title contains query string
                var found=false;

                if (items[i].name.toLowerCase().indexOf(query.trim()) >= 0) found=true;
               //Locationda gelebilir.
               // if (items[i].description_short.toLowerCase().indexOf(query.trim()) >= 0) found=true;
               // if (items[i].description.toLowerCase().indexOf(query.trim()) >= 0) found=true;

                if(found) foundItems.push(i);;
            }
            // Return array with indexes of matched items
            return foundItems;
        },
        height: 61,
        template: '
' +
        '' +
        '
' +
        //'' +
        '' +
        '
' +
        '
' +
        '
' +
        '
{{name}}
' +
        '
lorem ipsum dolor sit amet...
' +
        '
' +
        '
' +
        '' +
        '
'
    });
}

function showManufacturerMenu(manufacturer_name,id_manufacturer){
    selectedManufacturerId=id_manufacturer;
    selectedManufacturerName=manufacturer_name;
    setContextParameter("manufacturers_menu","selectedManufacturerName",selectedManufacturerName);
    loadPageWithLang("manufacturers_menu");
}

var listProductResult;

function initlistProduct() {
       listProductResult = myApp.virtualList('.lstproduct', {
        items: [

        ],
        searchAll: function (query, items) {
            var foundItems = [];
            for (var i = 0; i < items.length; i++) {
                // Check if title contains query string
                var found=false;

                if (items[i].name.toLowerCase().indexOf(query.trim()) >= 0) found=true;
                if (items[i].description_short.toLowerCase().indexOf(query.trim()) >= 0) found=true;
                if (items[i].description.toLowerCase().indexOf(query.trim()) >= 0) found=true;

                if(found) foundItems.push(i);;
            }
            // Return array with indexes of matched items
            return foundItems;
        },
        height: 80,
        template: '
' +
        '' +
        '
' +
        '' +
        '
' +
        '
' +
        '
' +
        '
{{name}}
' +
        '
{{reducedprice}} €
' +
        '
' +
        '
{{description_short}}
' +
        '
{{description}}
' +
        '
' +
        '' +
        '
'
    });
}

var listManufacturersMenu;

function initListManufacturersMenu() {
    listManufacturersMenu = myApp.virtualList('.lstManufacturersMenu', {
        items: [

        ],
        height: 124,
        template: '
' +
        '' +
        '
' +
        '' +
        '
' +
        '
' +
        '
' +
        '
{{name}}
' +
        '
{{reducedprice}} €
' +
        '
' +
        '
{{description_short}}
' +
        '
{{description}}
' +
        '
' +
        '' +
        '
'
    });
}

var listVirtualUserAddresses;

function initListVirtualUserAddresses() {
    listVirtualUserAddresses = myApp.virtualList('.lstUserAddresses', {
        items: [

        ],
        height: 105,
        template: '
' +
                  '
'+
                  '
'+
                  '
' +
                  '
{{alias}}
' +
                  '

' +
                  '
' +
                  '
{{postcode}}, {{city}}/{{name}}
' +
                  '
{{address1}} {{address2}}
'+
                  '

' +
                  '

' +
                  '
'

    });
}

function deleteAddress(addressAlias,addressId){
    var userId = window.localStorage.getItem("customerId");
    deleteaddress(userId, addressAlias, addressId);
}

var listVirtualCategories;

function initListVirtualCategories() {
    listVirtualCategories = myApp.virtualList('.lstCategories', {
        items: [

        ],
        cols:2,
        height: 60,
        template: '
' +
        '
' +
        '
' +
        '
' +
        '

' +
        '
' +
        '
' +
        '
' +
        '
' +
        '
' +
        '
' +
        '

' +
        '
' +
        '
' +
        '
' +
        '
'
    });
}<commit_msg>asdasd<commit_after>var listVirtualManufacturers;

function initListVirtualManufacturers() {
    listVirtualManufacturers = myApp.virtualList('.lstmanufacturers', {
        items: [
        ],
        searchAll: function (query, items) {
            var foundItems = [];
            for (var i = 0; i < items.length; i++) {
                // Check if title contains query string
                var found=false;

                if (items[i].name.toLowerCase().indexOf(query.trim()) >= 0) found=true;
               //Locationda gelebilir.
               // if (items[i].description_short.toLowerCase().indexOf(query.trim()) >= 0) found=true;
               // if (items[i].description.toLowerCase().indexOf(query.trim()) >= 0) found=true;

                if(found) foundItems.push(i);;
            }
            // Return array with indexes of matched items
            return foundItems;
        },
        height: 61,
        template: '
' +
        '' +
        '
' +
        //'' +
        '' +
        '
' +
        '
' +
        '
' +
        '
{{name}}
' +
        '
lorem ipsum dolor sit amet...
' +
        '
' +
        '
' +
        '' +
        '
'
    });
}

function showManufacturerMenu(manufacturer_name,id_manufacturer){
    selectedManufacturerId=id_manufacturer;
    selectedManufacturerName=manufacturer_name;
    setContextParameter("manufacturers_menu","selectedManufacturerName",selectedManufacturerName);
    loadPageWithLang("manufacturers_menu");
}

var listProductResult;

function initlistProduct() {
       listProductResult = myApp.virtualList('.lstproduct', {
        items: [

        ],
        rowsBefore:200,
        rowsAfter:200,
        searchAll: function (query, items) {
            var foundItems = [];
            for (var i = 0; i < items.length; i++) {
                // Check if title contains query string
                var found=false;

                if (items[i].name.toLowerCase().indexOf(query.trim()) >= 0) found=true;
                if (items[i].description_short.toLowerCase().indexOf(query.trim()) >= 0) found=true;
                if (items[i].description.toLowerCase().indexOf(query.trim()) >= 0) found=true;

                if(found) foundItems.push(i);;
            }
            // Return array with indexes of matched items
            return foundItems;
        },
        height: 124,
        template: '
' +
        '' +
        '
' +
        '' +
        '
' +
        '
' +
        '
' +
        '
{{name}}
' +
        '
{{reducedprice}} €
' +
        '
' +
        '
{{description_short}}
' +
        '
{{description}}
' +
        '
' +
        '' +
        '
'
    });
}

var listManufacturersMenu;

function initListManufacturersMenu() {
    listManufacturersMenu = myApp.virtualList('.lstManufacturersMenu', {
        items: [

        ],
        height: 124,
        template: '
' +
        '' +
        '
' +
        '' +
        '
' +
        '
' +
        '
' +
        '
{{name}}
' +
        '
{{reducedprice}} €
' +
        '
' +
        '
{{description_short}}
' +
        '
{{description}}
' +
        '
' +
        '' +
        '
'
    });
}

var listVirtualUserAddresses;

function initListVirtualUserAddresses() {
    listVirtualUserAddresses = myApp.virtualList('.lstUserAddresses', {
        items: [

        ],
        height: 105,
        template: '
' +
                  '
'+
                  '
'+
                  '
' +
                  '
{{alias}}
' +
                  '

' +
                  '
' +
                  '
{{postcode}}, {{city}}/{{name}}
' +
                  '
{{address1}} {{address2}}
'+
                  '

' +
                  '

' +
                  '
'

    });
}

function deleteAddress(addressAlias,addressId){
    var userId = window.localStorage.getItem("customerId");
    deleteaddress(userId, addressAlias, addressId);
}

var listVirtualCategories;

function initListVirtualCategories() {
    listVirtualCategories = myApp.virtualList('.lstCategories', {
        items: [

        ],
        cols:2,
        height: 60,
        template: '
' +
        '
' +
        '
' +
        '
' +
        '

' +
        '
' +
        '
' +
        '
' +
        '
' +
        '
' +
        '
' +
        '

' +
        '
' +
        '
' +
        '
' +
        '
'
    });
}<|endoftext|>


<commit_before>define(function(require) {

  var Backbone = require('backbone');
  var AppView = require('views/app');
  var docs = require('collections/docs');
  var cache = require('utils/cache');
  var settings = require('models/settings');
  var router = require('utils/router');

  function litewrite() {
    if ( _.isUndefined(settings.get('openDocId')) ) {
      settings.save('openDocId', docs.first().id);
    }

    remoteStorage.displayWidget('remotestorage-connect')

    setOpenDoc();
    setWindowTitle();

    settings
      .on('change:openDocId', setOpenDoc)
      .on('change:openDocId', setWindowTitle)
      .on('change:openDocId', setUrl)
      .on('change:openDocId', docs.deleteEmpty, docs);

    docs
      .on('change:title', setUrl)
      .on('change:title', setWindowTitle)
      .on('add', function(doc) {
        settings.save('openDocId', doc.id);
      });

    //Load on DOM-ready
    $(function() {
      new AppView();
      Backbone.history.start();
      setUrl();
    });

  }

  function setOpenDoc() {
    cache.openDoc = docs.get( settings.get('openDocId') );
  }

  function setWindowTitle() {
    document.title = (
      cache.openDoc ? cache.openDoc.get('title') : null
    ) || 'Litewrite';
  }

  function setUrl() {
    router.navigate(cache.openDoc.get('url'));
  }

  return litewrite;
});
<commit_msg>silence loggers as suggested by @nilclass in #41<commit_after>define(function(require) {

  var Backbone = require('backbone');
  var AppView = require('views/app');
  var docs = require('collections/docs');
  var cache = require('utils/cache');
  var settings = require('models/settings');
  var router = require('utils/router');

  function litewrite() {
    if ( _.isUndefined(settings.get('openDocId')) ) {
      settings.save('openDocId', docs.first().id);
    }

    remoteStorage.displayWidget('remotestorage-connect');
    remoteStorage.util.silenceAllLoggers();

    setOpenDoc();
    setWindowTitle();

    settings
      .on('change:openDocId', setOpenDoc)
      .on('change:openDocId', setWindowTitle)
      .on('change:openDocId', setUrl)
      .on('change:openDocId', docs.deleteEmpty, docs);

    docs
      .on('change:title', setUrl)
      .on('change:title', setWindowTitle)
      .on('add', function(doc) {
        settings.save('openDocId', doc.id);
      });

    //Load on DOM-ready
    $(function() {
      new AppView();
      Backbone.history.start();
      setUrl();
    });

  }

  function setOpenDoc() {
    cache.openDoc = docs.get( settings.get('openDocId') );
  }

  function setWindowTitle() {
    document.title = (
      cache.openDoc ? cache.openDoc.get('title') : null
    ) || 'Litewrite';
  }

  function setUrl() {
    router.navigate(cache.openDoc.get('url'));
  }

  return litewrite;
});
<|endoftext|>


<commit_before>/*eslint-env browser */
/*global require: false, module: false */

'use strict';

var _ = require('../underscore_ext');
var multi = require('../multi');
var {colorScale} = require('../heatmapColors');
var km = require('../km'); // move km down?

var MAX = 10; // max number of groups to display.

function average(data) {
	return data[0].map((v, s) => _.meannull(data.map(p => p[s])));
}

function codedVals({heatmap, colors, fields}, {codes}) {
	var field = fields[0],
		groups = _.range(Math.min(codes[field].length, MAX)),
		colorfn = _.first(colors.map(colorScale));
	return {
		warning: codes[field].length > MAX ? `Limited drawing to ${MAX} categories` : undefined,
		groups: groups,
		colors: groups.map(colorfn),
		labels: codes[field].slice(0, groups.length),
		values: heatmap[0]
	};
}

var saveNull = fn => v => v == null ? v : fn(v);

// XXX Here we include samples that might not have tte and ev, when
// picking the range. Ideal would be to partition not including
// samples w/o survival data.
function partitionedVals(avg, uniq, colorfn) {
	let vals = _.without(avg, null, undefined).sort((a, b) => a - b),
		min = _.min(vals),
		max = _.max(vals),
		low = vals[Math.round(vals.length / 3)].toPrecision(4),
		high = vals[Math.round(2 * vals.length / 3)].toPrecision(4);
	return {
		values: _.map(avg, saveNull(v => v <= low ? 'low' :
							(v <= high ? 'middle' : 'high'))),
		groups: ['low', 'middle', 'high'],
		colors: [colorfn(min), colorfn((min + max) / 2), colorfn(max)],
		labels: [`< ${low}`, `${low} to ${high}`, `> ${high}`]
	};
}

function floatVals(avg, uniq, colorfn) {
	return {
		values: avg,
		groups: uniq,
		colors: uniq.map(colorfn),
		labels: uniq.map(v => v.toPrecision(6))
	};
}

// We use data.display so the values reflect normalization settings.
// We average 1st, then see how many unique values there are, then decide
// whether to partition or not.
function floatOrPartitionVals({heatmap, colors}) {
	var warning = heatmap.length > 1 ? 'gene-level average' : undefined,
		avg = average(heatmap),
		uniq = _.without(_.uniq(avg), null, undefined),
		colorfn = _.first(colors.map(colorScale));
	return {warning, ...(uniq.length > MAX ? partitionedVals : floatVals)(avg, uniq, colorfn)};
}

function featureType({dataType, fields}, data) {
	// XXX We have a too many ad hoc checks in the code trying to decide if
	// something is coded or not, phenotype or not, etc. We need to fix this
	// across the code.
	var feature = _.getIn(data, ['req', 'probes', 0], _.get(fields, 0));
	var coded = _.getIn(data, ['codes', feature]);
	return (dataType === 'mutationVector') ? 'mutation' : (coded ? 'coded' : 'float');
}

function mutationVals(column, data, {bySample}, sortedSamples) {
	var mutCode = _.mapObject(bySample, vs => vs.length > 0 ? 1 : 0);
	return {
		values: _.map(sortedSamples, s => mutCode[s]),
		groups: [0, 1],
		colors: [
			"#ffffff", // white
			"#9467bd"  // dark purple
		],
		labels: [
			'No Mutation',
			'Has Mutation'
		]
	};
}

var toCoded = multi(featureType);
toCoded.add('float', floatOrPartitionVals);
toCoded.add('coded', codedVals);
toCoded.add('mutation', mutationVals);

var has = (obj, v) => obj[v] != null;

// Return indices of arr for which fn is true. fn is passed the value and index.
var filterIndices = (arr, fn) => _.range(arr.length).filter(i => fn(arr[i], i));

// Give tte and ev for each subgroup, compute p-value.
// Not sure why we recombine the data after splitting by group.
function pValue(groupsTte, groupsEv) {
	var allTte = _.flatten(groupsTte),
		allEv = _.flatten(groupsEv);

	return km.logranktest(
			// Only use points with an event
			km.compute(allTte, allEv).filter(point => point.e),
			groupsTte,
			groupsEv);
}

function warnDupPatients(usableSamples, samples, patient) {
	const getPatient = i => patient[samples[i]],
		havePatient = usableSamples.filter(getPatient),
		dups = _.difference(havePatient, _.uniq(havePatient, false, getPatient));

	return dups.length ? `Some individuals' survival data are used more than once in the KM plot. Affected samples are: ${dups.map(i => samples[i]).join(', ')}. For more information and how to remove such duplications: https://goo.gl/TSQt6z.` : null;
}

function makeGroups(column, data, index, survival, samples) {
	let {tte: {data: tte}, ev: {data: ev}, patient: {data: patient}} = survival,
		// Convert field to coded.
		{labels, colors, groups, values, warning} = toCoded(column, data, index, samples),
		usableSamples = filterIndices(samples, (s, i) =>
			has(tte, s) && has(ev, s) && has(values, i)),
		patientWarning = warnDupPatients(usableSamples, samples, patient),
		groupedIndices = _.groupBy(usableSamples, i => values[i]),
		gtte = groups.map(g => groupedIndices[g].map(i => tte[samples[i]])),
		gev = groups.map(g => groupedIndices[g].map(i => ev[samples[i]])),
		curves = groups.map((g, i)=> km.compute(gtte[i], gev[i])),
		pV = pValue(gtte, gev);

	return {
		colors,
		labels,
		curves,
		warning,
		patientWarning,
		...pV
	};
}

var featureID = (dsID, feature) => ({
	dsID: dsID,
	name: _.getIn(feature, ['name'])
});

function pickSurvivalVars(featuresByDataset, user) {
	var allFeatures = _.flatmap(featuresByDataset,
			(features, dsID) => _.map(features, f => featureID(dsID, f))),
		ev = _.find(allFeatures, ({name}) => name === '_EVENT'),
		tte = _.find(allFeatures, ({name}) => name === '_TIME_TO_EVENT'),
		patient = _.find(allFeatures, ({name}) => name === '_PATIENT');

	return {
		ev: _.getIn(user, ['ev'], ev),
		tte: _.getIn(user, ['tte'], tte),
		patient: _.getIn(user, ['patient'], patient)
	};
}

module.exports = {makeGroups, pickSurvivalVars};
<commit_msg>Fix km partition bounds.<commit_after>/*eslint-env browser */
/*global require: false, module: false */

'use strict';

var _ = require('../underscore_ext');
var multi = require('../multi');
var {colorScale} = require('../heatmapColors');
var km = require('../km'); // move km down?

var MAX = 10; // max number of groups to display.

function average(data) {
	return data[0].map((v, s) => _.meannull(data.map(p => p[s])));
}

function codedVals({heatmap, colors, fields}, {codes}) {
	var field = fields[0],
		groups = _.range(Math.min(codes[field].length, MAX)),
		colorfn = _.first(colors.map(colorScale));
	return {
		warning: codes[field].length > MAX ? `Limited drawing to ${MAX} categories` : undefined,
		groups: groups,
		colors: groups.map(colorfn),
		labels: codes[field].slice(0, groups.length),
		values: heatmap[0]
	};
}

var saveNull = fn => v => v == null ? v : fn(v);

// XXX Here we include samples that might not have tte and ev, when
// picking the range. Ideal would be to partition not including
// samples w/o survival data.
function partitionedVals(avg, uniq, colorfn) {
	let vals = _.without(avg, null, undefined).sort((a, b) => a - b),
		min = _.min(vals),
		max = _.max(vals),
		low = vals[Math.round(vals.length / 3)],
		high = vals[Math.round(2 * vals.length / 3)],
		labelLow = low.toPrecision(4),
		labelHigh = high.toPrecision(4);
	return {
		values: _.map(avg, saveNull(v => v < low ? 'low' :
							(v < high ? 'middle' : 'high'))),
		groups: ['low', 'middle', 'high'],
		colors: [colorfn(min), colorfn((min + max) / 2), colorfn(max)],
		labels: [`< ${labelLow}`, `${labelLow} to ${labelHigh}`, `>= ${labelHigh}`]
	};
}

function floatVals(avg, uniq, colorfn) {
	return {
		values: avg,
		groups: uniq,
		colors: uniq.map(colorfn),
		labels: uniq.map(v => v.toPrecision(6))
	};
}

// We use data.display so the values reflect normalization settings.
// We average 1st, then see how many unique values there are, then decide
// whether to partition or not.
function floatOrPartitionVals({heatmap, colors}) {
	var warning = heatmap.length > 1 ? 'gene-level average' : undefined,
		avg = average(heatmap),
		uniq = _.without(_.uniq(avg), null, undefined),
		colorfn = _.first(colors.map(colorScale));
	return {warning, ...(uniq.length > MAX ? partitionedVals : floatVals)(avg, uniq, colorfn)};
}

function featureType({dataType, fields}, data) {
	// XXX We have a too many ad hoc checks in the code trying to decide if
	// something is coded or not, phenotype or not, etc. We need to fix this
	// across the code.
	var feature = _.getIn(data, ['req', 'probes', 0], _.get(fields, 0));
	var coded = _.getIn(data, ['codes', feature]);
	return (dataType === 'mutationVector') ? 'mutation' : (coded ? 'coded' : 'float');
}

function mutationVals(column, data, {bySample}, sortedSamples) {
	var mutCode = _.mapObject(bySample, vs => vs.length > 0 ? 1 : 0);
	return {
		values: _.map(sortedSamples, s => mutCode[s]),
		groups: [0, 1],
		colors: [
			"#ffffff", // white
			"#9467bd"  // dark purple
		],
		labels: [
			'No Mutation',
			'Has Mutation'
		]
	};
}

var toCoded = multi(featureType);
toCoded.add('float', floatOrPartitionVals);
toCoded.add('coded', codedVals);
toCoded.add('mutation', mutationVals);

var has = (obj, v) => obj[v] != null;

// Return indices of arr for which fn is true. fn is passed the value and index.
var filterIndices = (arr, fn) => _.range(arr.length).filter(i => fn(arr[i], i));

// Give tte and ev for each subgroup, compute p-value.
// Not sure why we recombine the data after splitting by group.
function pValue(groupsTte, groupsEv) {
	var allTte = _.flatten(groupsTte),
		allEv = _.flatten(groupsEv);

	return km.logranktest(
			// Only use points with an event
			km.compute(allTte, allEv).filter(point => point.e),
			groupsTte,
			groupsEv);
}

function warnDupPatients(usableSamples, samples, patient) {
	const getPatient = i => patient[samples[i]],
		havePatient = usableSamples.filter(getPatient),
		dups = _.difference(havePatient, _.uniq(havePatient, false, getPatient));

	return dups.length ? `Some individuals' survival data are used more than once in the KM plot. Affected samples are: ${dups.map(i => samples[i]).join(', ')}. For more information and how to remove such duplications: https://goo.gl/TSQt6z.` : null;
}

function makeGroups(column, data, index, survival, samples) {
	let {tte: {data: tte}, ev: {data: ev}, patient: {data: patient}} = survival,
		// Convert field to coded.
		{labels, colors, groups, values, warning} = toCoded(column, data, index, samples),
		usableSamples = filterIndices(samples, (s, i) =>
			has(tte, s) && has(ev, s) && has(values, i)),
		patientWarning = warnDupPatients(usableSamples, samples, patient),
		groupedIndices = _.groupBy(usableSamples, i => values[i]),
		gtte = groups.map(g => groupedIndices[g].map(i => tte[samples[i]])),
		gev = groups.map(g => groupedIndices[g].map(i => ev[samples[i]])),
		curves = groups.map((g, i)=> km.compute(gtte[i], gev[i])),
		pV = pValue(gtte, gev);

	return {
		colors,
		labels,
		curves,
		warning,
		patientWarning,
		...pV
	};
}

var featureID = (dsID, feature) => ({
	dsID: dsID,
	name: _.getIn(feature, ['name'])
});

function pickSurvivalVars(featuresByDataset, user) {
	var allFeatures = _.flatmap(featuresByDataset,
			(features, dsID) => _.map(features, f => featureID(dsID, f))),
		ev = _.find(allFeatures, ({name}) => name === '_EVENT'),
		tte = _.find(allFeatures, ({name}) => name === '_TIME_TO_EVENT'),
		patient = _.find(allFeatures, ({name}) => name === '_PATIENT');

	return {
		ev: _.getIn(user, ['ev'], ev),
		tte: _.getIn(user, ['tte'], tte),
		patient: _.getIn(user, ['patient'], patient)
	};
}

module.exports = {makeGroups, pickSurvivalVars};
<|endoftext|>


<commit_before>var UI = require('./ui')

var buffer

module.exports = {
	buildURL: buildOpenFiscaQueryURL,
	get: get,
	update: update,
	getLastResults: function() { return buffer },
}

function serialize(form) {
	var result = [],
		elements = form.elements

	Array.prototype.forEach.call(elements, function(element) {
		if (! element.name)
			return

		var value = element.value

		if (element.type == 'number')
			value = Number(element.value.replace(',', '.'))	// IE doesn't support locale number formats

		/* We are simulating a recruitment,
		hence requesting salaries with the new size of the entreprise */
		if (element.name == 'effectif_entreprise')
			value ++

		result.push(encodeURI(element.name + '=' + value))
	})

	return result.join('&')
}

var BOOLEAN_PARAMETERS = {
	employee: [ 'stagiaire', 'apprenti' ],
}

function getAdditionalParameters() {
	var result = {}

	for (var provider in BOOLEAN_PARAMETERS) {
		var key = document.querySelector('[data-provides="' + provider + '"]').value

		if (BOOLEAN_PARAMETERS[provider].indexOf(key) > -1)
			result[key] = true
	}

	return result
}

/** Serializes a shallow object into a series of query string parameters.
* A naive and shallow implementation.
*
*@param		{Object}	source
*@returns	{String}	The source object as a query string (with no leading '?').
*@private
*/
function serializeObject(source) {
	var result = []

	for (var key in source) {
		if (key && source.hasOwnProperty(key))
			result.push(encodeURI(key + '=' + source[key]))
	}

	return result.join('&')
}

/** Creates an OpenFisca URL to the /formula endpoint, based on the current main form state and the given additional parameters.
*
*@param		{Object}	[additionalParameters]	An object whose properties will be appended to the URL as query-string parameters.
*@returns	{String}	The URL for the OpenFisca query.
*/
function buildOpenFiscaQueryURL(additionalParameters) {
	var form = document.querySelector('#input form'),
		queryStringBlocks = [
			serialize(form),
			serializeObject(getAdditionalParameters()),
			serializeObject(additionalParameters),
		].filter(function(element) {
			return element !== ''}
		)

	return form.action + '?' + queryStringBlocks.join('&')
}

/** Computes values based on the current main form state and the given additional parameters.
*
*@param	{Object}	[additionalParameters]	An object whose properties will be appended to the URL as query-string parameters.
*@param	{Function<[XMLHttpRequest|SyntaxError], Object, Object>}	callback	A callback that will be called with three parameters: an optional error if something went wrong, the OpenFisca-computed values, and the full OpenFisca response if you want everything it sends back.
*/
function get(additionalParameters, callback) {
	if (! callback) {
		callback = additionalParameters
		additionalParameters = null
	}

	var request = new XMLHttpRequest()

	request.onload = function openFiscaOnloadHandler() {
		try {
			var data = JSON.parse(request.responseText)
		} catch (err) {
			callback(err)
		}

		callback(request.status != 200 && request, data.values, data)
	}

	request.onerror = callback.bind(null, request)

	request.open('GET', buildOpenFiscaQueryURL(additionalParameters))
	request.send()
}

/** Updates the displayed values.
*/
function update() {
	var today = new Date()

	get({
		contrat_de_travail_debut: today.getFullYear() + '-' + (today.getMonth() + 1),
	}, function(error, values, response) {
		if (error) {
			if (response && response.error)
				return UI.showError(response.error)

			UI.showError({ message: error })
			throw error
		}

		buffer = values
		UI.display(values)
	})
}
<commit_msg>Correct month in contrat_de_travail_debut<commit_after>var UI = require('./ui')

var buffer

module.exports = {
	buildURL: buildOpenFiscaQueryURL,
	get: get,
	update: update,
	getLastResults: function() { return buffer },
}

function serialize(form) {
	var result = [],
		elements = form.elements

	Array.prototype.forEach.call(elements, function(element) {
		if (! element.name)
			return

		var value = element.value

		if (element.type == 'number')
			value = Number(element.value.replace(',', '.'))	// IE doesn't support locale number formats

		/* We are simulating a recruitment,
		hence requesting salaries with the new size of the entreprise */
		if (element.name == 'effectif_entreprise')
			value ++

		result.push(encodeURI(element.name + '=' + value))
	})

	return result.join('&')
}

var BOOLEAN_PARAMETERS = {
	employee: [ 'stagiaire', 'apprenti' ],
}

function getAdditionalParameters() {
	var result = {}

	for (var provider in BOOLEAN_PARAMETERS) {
		var key = document.querySelector('[data-provides="' + provider + '"]').value

		if (BOOLEAN_PARAMETERS[provider].indexOf(key) > -1)
			result[key] = true
	}

	return result
}

/** Serializes a shallow object into a series of query string parameters.
* A naive and shallow implementation.
*
*@param		{Object}	source
*@returns	{String}	The source object as a query string (with no leading '?').
*@private
*/
function serializeObject(source) {
	var result = []

	for (var key in source) {
		if (key && source.hasOwnProperty(key))
			result.push(encodeURI(key + '=' + source[key]))
	}

	return result.join('&')
}

/** Creates an OpenFisca URL to the /formula endpoint, based on the current main form state and the given additional parameters.
*
*@param		{Object}	[additionalParameters]	An object whose properties will be appended to the URL as query-string parameters.
*@returns	{String}	The URL for the OpenFisca query.
*/
function buildOpenFiscaQueryURL(additionalParameters) {
	var form = document.querySelector('#input form'),
		queryStringBlocks = [
			serialize(form),
			serializeObject(getAdditionalParameters()),
			serializeObject(additionalParameters),
		].filter(function(element) {
			return element !== ''}
		)

	return form.action + '?' + queryStringBlocks.join('&')
}

/** Computes values based on the current main form state and the given additional parameters.
*
*@param	{Object}	[additionalParameters]	An object whose properties will be appended to the URL as query-string parameters.
*@param	{Function<[XMLHttpRequest|SyntaxError], Object, Object>}	callback	A callback that will be called with three parameters: an optional error if something went wrong, the OpenFisca-computed values, and the full OpenFisca response if you want everything it sends back.
*/
function get(additionalParameters, callback) {
	if (! callback) {
		callback = additionalParameters
		additionalParameters = null
	}

	var request = new XMLHttpRequest()

	request.onload = function openFiscaOnloadHandler() {
		try {
			var data = JSON.parse(request.responseText)
		} catch (err) {
			callback(err)
		}

		callback(request.status != 200 && request, data.values, data)
	}

	request.onerror = callback.bind(null, request)

	request.open('GET', buildOpenFiscaQueryURL(additionalParameters))
	request.send()
}

/** Updates the displayed values.
*/
function update() {
	var today = new Date(),
		mm = ('0' + (today.getMonth() + 1)).slice(-2)

	get({
		contrat_de_travail_debut: today.getFullYear() + '-' + mm,
	}, function(error, values, response) {
		if (error) {
			if (response && response.error)
				return UI.showError(response.error)

			UI.showError({ message: error })
			throw error
		}

		buffer = values
		UI.display(values)
	})
}
<|endoftext|>