<commit_before>window.AdventOfCode.Day7 = function( originalInput )
{
	var input = originalInput.split( '\n' );

	// Pre-sort the input to avoid multiple iterations
	input.sort( function( a, b )
	{
		a = a.split( ' ' );
		b = b.split( ' ' );

		// Get the output wire
		a = a[ a.length - 1 ];
		b = b[ b.length - 1 ];

		// Wire 'a' must end at the end of instructions
		if( a === 'a' )
		{
			return 1;
		}
		else if( b === 'a' )
		{
			return -1;
		}

		if( a.length === b.length )
		{
			return a > b ? 1 : -1;
		}

		return a.length - b.length;
	} );

	var Solve = function( wires )
	{
		var GetValue = function( value )
		{
			if( value in wires )
			{
				return wires[ value ];
			}

			return +value;
		};

		var SetWire = function( wire, value )
		{
			// Can't assign same wire twice,
			// this becomes a problem when overriding b
			if( wire in wires )
			{
				return;
			}

			wires[ wire ] = value & 0xFFFF;
		};

		for( var i = 0; i < input.length; i++ )
		{
			var operation = input[ i ].split( ' ' );

			console.log(i,operation);

			switch( operation.length )
			{
				// 123 -> x means that the signal 123 is provided to wire x.
				case 3: // ["123", "->", "x"]
					SetWire( operation[ 2 ], GetValue( operation[ 0 ] ) );

					break;

				// NOT e -> f means that the bitwise complement of the value from wire e is provided to wire f.
				case 4: // ["NOT", "x", "->", "h"]
					SetWire( operation[ 3 ], ~GetValue( operation[ 1 ] ) );

					break;

				// x AND y -> z means that the bitwise AND of wire x and wire y is provided to wire z.
				case 5: // ["x", "AND", "y", "->", "d"]
					switch( operation[ 1 ] )
					{
						case 'AND'   : SetWire( operation[ 4 ], GetValue( operation[ 0 ] ) &  GetValue( operation[ 2 ] ) ); break;
						case 'OR'    : SetWire( operation[ 4 ], GetValue( operation[ 0 ] ) |  GetValue( operation[ 2 ] ) ); break;
						case 'LSHIFT': SetWire( operation[ 4 ], GetValue( operation[ 0 ] ) << GetValue( operation[ 2 ] ) ); break;
						case 'RSHIFT': SetWire( operation[ 4 ], GetValue( operation[ 0 ] ) >> GetValue( operation[ 2 ] ) ); break;
					}

					break;
			}
		}

		return wires[ 'a' ] || 0;
	};

	var solution1 = Solve( {} );
	var solution2 = Solve( { b: solution1 } );

	return [ solution1, solution2 ];
};
<commit_msg>Split operations once in the beginning<commit_after>window.AdventOfCode.Day7 = function( originalInput )
{
	var input = originalInput.split( '\n' );

	// Split all operations by space
	input = input.map( function( a )
	{
		return a.split( ' ' );
	} );

	// Pre-sort the input to avoid multiple iterations
	input.sort( function( a, b )
	{
		// Get the output wire
		a = a[ a.length - 1 ];
		b = b[ b.length - 1 ];

		// Wire 'a' must end at the end of instructions
		if( a === 'a' )
		{
			return 1;
		}
		else if( b === 'a' )
		{
			return -1;
		}

		if( a.length === b.length )
		{
			return a > b ? 1 : -1;
		}

		return a.length - b.length;
	} );

	var Solve = function( wires )
	{
		var GetValue = function( value )
		{
			if( value in wires )
			{
				return wires[ value ];
			}

			return +value;
		};

		var SetWire = function( wire, value )
		{
			// Can't assign same wire twice,
			// this becomes a problem when overriding b
			if( wire in wires )
			{
				return;
			}

			wires[ wire ] = value & 0xFFFF;
		};

		for( var i = 0; i < input.length; i++ )
		{
			var operation = input[ i ];

			switch( operation.length )
			{
				// 123 -> x means that the signal 123 is provided to wire x.
				case 3: // ["123", "->", "x"]
					SetWire( operation[ 2 ], GetValue( operation[ 0 ] ) );

					break;

				// NOT e -> f means that the bitwise complement of the value from wire e is provided to wire f.
				case 4: // ["NOT", "x", "->", "h"]
					SetWire( operation[ 3 ], ~GetValue( operation[ 1 ] ) );

					break;

				// x AND y -> z means that the bitwise AND of wire x and wire y is provided to wire z.
				case 5: // ["x", "AND", "y", "->", "d"]
					switch( operation[ 1 ] )
					{
						case 'AND'   : SetWire( operation[ 4 ], GetValue( operation[ 0 ] ) &  GetValue( operation[ 2 ] ) ); break;
						case 'OR'    : SetWire( operation[ 4 ], GetValue( operation[ 0 ] ) |  GetValue( operation[ 2 ] ) ); break;
						case 'LSHIFT': SetWire( operation[ 4 ], GetValue( operation[ 0 ] ) << GetValue( operation[ 2 ] ) ); break;
						case 'RSHIFT': SetWire( operation[ 4 ], GetValue( operation[ 0 ] ) >> GetValue( operation[ 2 ] ) ); break;
					}

					break;
			}
		}

		return wires[ 'a' ] || 0;
	};

	var solution1 = Solve( {} );
	var solution2 = Solve( { b: solution1 } );

	return [ solution1, solution2 ];
};
<|endoftext|>


<commit_before>// Constants
var leftMargin = 20;
var buttonWidth = 60;
var buttonHeight = 40;
var buttonSpacing = 20;
var smallMovement = 3;
var nodeSize = 5;

$(document).ready(function() {
    var ctx = $('#canvas').get(0).getContext('2d');
    ctx.textAlign = "center";

    // Scaling and panning
    var f = 30;
    var px = 0;
    var py = 0;

    // Mouse
    var mx = 0;
    var my = 0;
    var mdx = 0;
    var mdy = 0;
    var mouseDown = false;
    var selectedObjs = [];
    var movingGroup = false;

    // Keys
    var spaceDown = false;
    var shiftDown = false;

    // Button hover
    var leftIndex = -1;

    var leftButtons = [
      {label: "AND", action: newGate('and')},
      {label: "OR", action: newGate('or')},
      {label: "NOT", action: newGate('not')}
    ];

    function newGate(type) {
      return function() {
        circuit.gates.push({
          type: type,
          ins: [],
          outs: [],
          pos: [400 - px, 300 - py]
        })
      }
    }

    function draw_buttons() {
      ctx.font = "20px sans";
      var y = buttonSpacing;
      for (var i = 0; i < leftButtons.length; i++) {
        if (i === leftIndex) {
          ctx.fillStyle = "cyan";
          ctx.fillRect(leftMargin, y, buttonWidth, buttonHeight);
        }
        ctx.strokeRect(leftMargin, y, buttonWidth, buttonHeight);
        ctx.fillStyle = "black";
        ctx.fillText(leftButtons[i].label, leftMargin + buttonWidth / 2, y + 30);
        y += buttonHeight + buttonSpacing;
      }
    }

    function draw_and_gate(gate) {
      var x0 = px + gate.pos[0];
      var y0 = py + gate.pos[1];

      ctx.beginPath();

      //draw the circle part of the and gate
      ctx.arc(x0 + f, y0 + f/2, f/2, -0.5 * Math.PI, 0.5 * Math.PI);
      ctx.stroke();

      //draw the rectangle part of the and gate
      ctx.moveTo(x0 + f, y0 + f)
      ctx.lineTo(x0, y0 + f);
      ctx.lineTo(x0, y0);
      ctx.lineTo(x0 + f, y0);
      ctx.stroke();
    }

    function draw_or_gate(gate){
      var x0 = px + gate.pos[0];
      var y0 = py + gate.pos[1];

      ctx.beginPath();

      //front part
      ctx.moveTo(x0, y0);
      ctx.quadraticCurveTo(x0 + 3 * f, y0 + 0.5 * f, x0, y0 + f);

      //back part
      ctx.quadraticCurveTo(x0 + 0.5*f, y0 + 0.5 * f, x0, y0);
      ctx.stroke();
    }

    function draw_not_gate(gate){
      var x0 = px + gate.pos[0];
      var y0 = py + gate.pos[1];

      ctx.beginPath();

      //triangle part
      ctx.moveTo(x0, y0);
      ctx.lineTo(x0 + 1.3 * f, y0 + 0.5 * f);
      ctx.lineTo(x0, y0 + f);
      ctx.closePath();

      //circle part
      var r = 0.1 * f;
      ctx.moveTo(x0 + 1.3 * f + 2 * r, y0 + 0.5 * f)
      ctx.arc(x0 + 1.3 * f + r, y0 + 0.5 * f, r, 0, Math.PI*2);
      ctx.stroke();
    }

    function draw_generic_gate(gate) {
      var x0 = px + gate.pos[0];
      var y0 = py + gate.pos[1];

      ctx.font = Math.round(0.5 * f) + "px sans";
      ctx.strokeRect(x0, y0, 1.5 * f, f);
      ctx.fillText(gate.type, x0 + 0.75 * f, y0 + 0.8 * f);
    }

    function draw_gate(gate) {
      switch (gate.type) {
        case 'and':
          draw_and_gate(gate);
          break;
        case 'or':
          draw_or_gate(gate);
          break;
        case 'not':
          draw_not_gate(gate);
          break;
        default:
          draw_generic_gate(gate);
          break;
      }
    }

    function render() {
      ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
      for (var i = 0; i < circuit.gates.length; i++) {
        if (selectedObjs.indexOf(circuit.gates[i]) > -1) {
          ctx.strokeStyle = "magenta";
        }
        draw_gate(circuit.gates[i]);
        ctx.strokeStyle = "black";
      }

      if (mouseDown && !spaceDown && !movingGroup) {
        ctx.fillStyle = "rgba(0, 1, 1, 0.2)";
        ctx.fillRect(mdx, mdy, mx - mdx, my - mdy);
      }

      draw_buttons();
    }

    function simulate() {
        updateAgenda();
        for (var i = 0; i < agenda.length; i++) {
            render();
            var tasks = agenda[i];
            for (var j = 0; j < tasks.length; j++) {
              apply(tasks[j]);
            }
        }
    }

    function checkLeftButtons() {
      if (mx >= leftMargin && mx <= leftMargin + buttonWidth) {
        if (my % (buttonSpacing + buttonHeight) >= buttonSpacing) {
          var i = Math.floor(my / (buttonSpacing + buttonHeight));
          if (i < leftButtons.length) {
            leftIndex = i;
            return;
          }
        }
      }
      leftIndex = -1;
    }

    function objectsUnderCursor() {
      objs = [];
      for (var i = 0; i < circuit.gates.length; i++) {
        var x0 = px + circuit.gates[i].pos[0];
        var y0 = py + circuit.gates[i].pos[1];
        if (mx >= x0 && my >= y0 && mx <= x0 + 1.5 * f && my <= y0 + f) {
          objs.push(circuit.gates[i]);
        }
      }
      for (var i = 0; i < circuit.nodes.length; i++) {
        var x0 = px + circuit.nodes[i].pos[0];
        var y0 = px + circuit.nodes[i].pos[1];
        var size;
        if (circuit.nodes[i].type == undefined) {
          size = 0.2 * f;
        } else {
          size = 2 * f;
        }
        if (mx >= x0 && my >= y0 && mx <= x0 + nodeSize && my <= y0 + nodeSize) {
          objs.push(circuit.nodes[i]);
        }
      }
      return objs;
    }

    function objectsInMarquee() {
      objs = [];
      var xa = Math.min(mx, mdx);
      var xb = Math.max(mx, mdx);
      var ya = Math.min(my, mdy);
      var yb = Math.max(my, mdy);
      for (var i = 0; i < circuit.gates.length; i++) {
        var x0 = px + circuit.gates[i].pos[0];
        var y0 = py + circuit.gates[i].pos[1];
        if (x0 >= xa && y0 >= ya && x0 + 1.5 * f <= xb && y0 + f <= yb) {
          objs.push(circuit.gates[i]);
        }
      }
      for (var i = 0; i < circuit.nodes.length; i++) {
        var x0 = px + circuit.nodes[i].pos[0];
        var y0 = px + circuit.nodes[i].pos[1];
        var size;
        if (circuit.nodes[i].type == undefined) {
          size = 0.2 * f;
        } else {
          size = 2 * f;
        }
        if (x0 >= xa && y0 >= ya && x0 + size <= xb && y0 + size <= yb) {
          objs.push(circuit.nodes[i]);
        }
      }
      return objs;
    }

    $('#canvas').mousedown(function(e) {
      mouseDown = true;
      mdx = e.offsetX;
      mdy = e.offsetY;

      if (leftIndex !== -1) {
        leftButtons[leftIndex].action();
      }

      if (!spaceDown) {
        var objs = objectsUnderCursor();
        movingGroup = false;
        for (var i = 0; i < objs.length; i++) {
          for (var j = 0; j < selectedObjs.length; j++) {
            if (objs[i] == selectedObjs[j]) {
              movingGroup = true;
              break;
            }
          }
        }
      }

      render();
    });

    $('#canvas').mousemove(function(e) {
      if (mouseDown) {
        if (spaceDown) {
          px += e.offsetX - mx;
          py += e.offsetY - my;
        } else if (movingGroup) {
          for (var i = 0; i < selectedObjs.length; i++) {
            selectedObjs[i].pos[0] += e.offsetX - mx;
            selectedObjs[i].pos[1] += e.offsetY - my;
          }
        }
      }

      mx = e.offsetX;
      my = e.offsetY;

      var old = leftIndex;
      checkLeftButtons();
      if (old !== leftIndex || mouseDown) {
        render();
      }
    });

    $('#canvas').mouseup(function(e) {
      if (Math.abs(mx - mdx) <= smallMovement && Math.abs(my - mdy) <= smallMovement) {
        var objs = objectsUnderCursor();
        if (objs.length > 0) {
          if (shiftDown) {
            selectedObjs.push(objs[0]);
          } else {
            selectedObjs = [objs[0]];
          }
        } else {
          selectedObjs = [];
        }
      } else if (!spaceDown && !movingGroup) {
        selectedObjs = objectsInMarquee();
      }
      mouseDown = false;
      movingGroup = false;
      render();
    });

    $(document).keydown(function(e) {
      if (e.which === 32) {
        e.preventDefault();
        spaceDown = true;
      } else if (e.which == 16) {
        shiftDown = true;
      }
    });

    $(document).keyup(function(e) {
      if (e.which === 32) {
        e.preventDefault();
        spaceDown = false;
      } else if (e.which == 16) {
        shiftDown = false;
      }
    });

    render();
});
<commit_msg>select and drag in one movement<commit_after>// Constants
var leftMargin = 20;
var buttonWidth = 60;
var buttonHeight = 40;
var buttonSpacing = 20;
var smallMovement = 3;
var nodeSize = 5;

$(document).ready(function() {
    var ctx = $('#canvas').get(0).getContext('2d');
    ctx.textAlign = "center";

    // Scaling and panning
    var f = 30;
    var px = 0;
    var py = 0;

    // Mouse
    var mx = 0;
    var my = 0;
    var mdx = 0;
    var mdy = 0;
    var mouseDown = false;
    var selectedObjs = [];
    var movingGroup = false;

    // Keys
    var spaceDown = false;
    var shiftDown = false;

    // Button hover
    var leftIndex = -1;

    var leftButtons = [
      {label: "AND", action: newGate('and')},
      {label: "OR", action: newGate('or')},
      {label: "NOT", action: newGate('not')}
    ];

    function newGate(type) {
      return function() {
        circuit.gates.push({
          type: type,
          ins: [],
          outs: [],
          pos: [400 - px, 300 - py]
        })
      }
    }

    function draw_buttons() {
      ctx.font = "20px sans";
      var y = buttonSpacing;
      for (var i = 0; i < leftButtons.length; i++) {
        if (i === leftIndex) {
          ctx.fillStyle = "cyan";
          ctx.fillRect(leftMargin, y, buttonWidth, buttonHeight);
        }
        ctx.strokeRect(leftMargin, y, buttonWidth, buttonHeight);
        ctx.fillStyle = "black";
        ctx.fillText(leftButtons[i].label, leftMargin + buttonWidth / 2, y + 30);
        y += buttonHeight + buttonSpacing;
      }
    }

    function draw_and_gate(gate) {
      var x0 = px + gate.pos[0];
      var y0 = py + gate.pos[1];

      ctx.beginPath();

      //draw the circle part of the and gate
      ctx.arc(x0 + f, y0 + f/2, f/2, -0.5 * Math.PI, 0.5 * Math.PI);
      ctx.stroke();

      //draw the rectangle part of the and gate
      ctx.moveTo(x0 + f, y0 + f)
      ctx.lineTo(x0, y0 + f);
      ctx.lineTo(x0, y0);
      ctx.lineTo(x0 + f, y0);
      ctx.stroke();
    }

    function draw_or_gate(gate){
      var x0 = px + gate.pos[0];
      var y0 = py + gate.pos[1];

      ctx.beginPath();

      //front part
      ctx.moveTo(x0, y0);
      ctx.quadraticCurveTo(x0 + 3 * f, y0 + 0.5 * f, x0, y0 + f);

      //back part
      ctx.quadraticCurveTo(x0 + 0.5*f, y0 + 0.5 * f, x0, y0);
      ctx.stroke();
    }

    function draw_not_gate(gate){
      var x0 = px + gate.pos[0];
      var y0 = py + gate.pos[1];

      ctx.beginPath();

      //triangle part
      ctx.moveTo(x0, y0);
      ctx.lineTo(x0 + 1.3 * f, y0 + 0.5 * f);
      ctx.lineTo(x0, y0 + f);
      ctx.closePath();

      //circle part
      var r = 0.1 * f;
      ctx.moveTo(x0 + 1.3 * f + 2 * r, y0 + 0.5 * f)
      ctx.arc(x0 + 1.3 * f + r, y0 + 0.5 * f, r, 0, Math.PI*2);
      ctx.stroke();
    }

    function draw_generic_gate(gate) {
      var x0 = px + gate.pos[0];
      var y0 = py + gate.pos[1];

      ctx.font = Math.round(0.5 * f) + "px sans";
      ctx.strokeRect(x0, y0, 1.5 * f, f);
      ctx.fillText(gate.type, x0 + 0.75 * f, y0 + 0.8 * f);
    }

    function draw_gate(gate) {
      switch (gate.type) {
        case 'and':
          draw_and_gate(gate);
          break;
        case 'or':
          draw_or_gate(gate);
          break;
        case 'not':
          draw_not_gate(gate);
          break;
        default:
          draw_generic_gate(gate);
          break;
      }
    }

    function render() {
      ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
      for (var i = 0; i < circuit.gates.length; i++) {
        if (selectedObjs.indexOf(circuit.gates[i]) > -1) {
          ctx.strokeStyle = "magenta";
        }
        draw_gate(circuit.gates[i]);
        ctx.strokeStyle = "black";
      }

      if (mouseDown && !spaceDown && !movingGroup) {
        ctx.fillStyle = "rgba(0, 1, 1, 0.2)";
        ctx.fillRect(mdx, mdy, mx - mdx, my - mdy);
      }

      draw_buttons();
    }

    function simulate() {
        updateAgenda();
        for (var i = 0; i < agenda.length; i++) {
            render();
            var tasks = agenda[i];
            for (var j = 0; j < tasks.length; j++) {
              apply(tasks[j]);
            }
        }
    }

    function checkLeftButtons() {
      if (mx >= leftMargin && mx <= leftMargin + buttonWidth) {
        if (my % (buttonSpacing + buttonHeight) >= buttonSpacing) {
          var i = Math.floor(my / (buttonSpacing + buttonHeight));
          if (i < leftButtons.length) {
            leftIndex = i;
            return;
          }
        }
      }
      leftIndex = -1;
    }

    function objectsUnderCursor() {
      objs = [];
      for (var i = 0; i < circuit.gates.length; i++) {
        var x0 = px + circuit.gates[i].pos[0];
        var y0 = py + circuit.gates[i].pos[1];
        if (mx >= x0 && my >= y0 && mx <= x0 + 1.5 * f && my <= y0 + f) {
          objs.push(circuit.gates[i]);
        }
      }
      for (var i = 0; i < circuit.nodes.length; i++) {
        var x0 = px + circuit.nodes[i].pos[0];
        var y0 = px + circuit.nodes[i].pos[1];
        var size;
        if (circuit.nodes[i].type == undefined) {
          size = 0.2 * f;
        } else {
          size = 2 * f;
        }
        if (mx >= x0 && my >= y0 && mx <= x0 + nodeSize && my <= y0 + nodeSize) {
          objs.push(circuit.nodes[i]);
        }
      }
      return objs;
    }

    function objectsInMarquee() {
      objs = [];
      var xa = Math.min(mx, mdx);
      var xb = Math.max(mx, mdx);
      var ya = Math.min(my, mdy);
      var yb = Math.max(my, mdy);
      for (var i = 0; i < circuit.gates.length; i++) {
        var x0 = px + circuit.gates[i].pos[0];
        var y0 = py + circuit.gates[i].pos[1];
        if (x0 >= xa && y0 >= ya && x0 + 1.5 * f <= xb && y0 + f <= yb) {
          objs.push(circuit.gates[i]);
        }
      }
      for (var i = 0; i < circuit.nodes.length; i++) {
        var x0 = px + circuit.nodes[i].pos[0];
        var y0 = px + circuit.nodes[i].pos[1];
        var size;
        if (circuit.nodes[i].type == undefined) {
          size = 0.2 * f;
        } else {
          size = 2 * f;
        }
        if (x0 >= xa && y0 >= ya && x0 + size <= xb && y0 + size <= yb) {
          objs.push(circuit.nodes[i]);
        }
      }
      return objs;
    }

    $('#canvas').mousedown(function(e) {
      mouseDown = true;
      mdx = e.offsetX;
      mdy = e.offsetY;

      if (leftIndex !== -1) {
        leftButtons[leftIndex].action();
        render();
        return;
      }

      if (!spaceDown) {
        var objs = objectsUnderCursor();
        if (objs.length > 0) {
          movingGroup = true;
          var existing = false
          for (var i = 0; i < objs.length; i++) {
            for (var j = 0; j < selectedObjs.length; j++) {
              if (objs[i] == selectedObjs[j]) {
                existing = true;
                break;
              }
            }
          }
          if (!existing) {
            if (shiftDown) {
              selectedObjs.push(objs[0]);
            } else {
              selectedObjs = [objs[0]];
            }
          }
        }
      }

      render();
    });

    $('#canvas').mousemove(function(e) {
      if (mouseDown) {
        if (spaceDown) {
          px += e.offsetX - mx;
          py += e.offsetY - my;
        } else if (movingGroup) {
          for (var i = 0; i < selectedObjs.length; i++) {
            selectedObjs[i].pos[0] += e.offsetX - mx;
            selectedObjs[i].pos[1] += e.offsetY - my;
          }
        }
      }

      mx = e.offsetX;
      my = e.offsetY;

      var old = leftIndex;
      checkLeftButtons();
      if (old !== leftIndex || mouseDown) {
        render();
      }
    });

    $('#canvas').mouseup(function(e) {
      if (leftIndex === -1) {
        if (Math.abs(mx - mdx) <= smallMovement && Math.abs(my - mdy) <= smallMovement) {
          var objs = objectsUnderCursor();
          if (objs.length > 0) {
            if (shiftDown) {
              selectedObjs.push(objs[0]);
            } else {
              selectedObjs = [objs[0]];
            }
          } else {
            selectedObjs = [];
          }
        } else if (!spaceDown && !movingGroup) {
          selectedObjs = objectsInMarquee();
        }
      }
      mouseDown = false;
      movingGroup = false;
      render();
    });

    $(document).keydown(function(e) {
      if (e.which === 32) {
        e.preventDefault();
        spaceDown = true;
      } else if (e.which == 16) {
        shiftDown = true;
      }
    });

    $(document).keyup(function(e) {
      if (e.which === 32) {
        e.preventDefault();
        spaceDown = false;
      } else if (e.which == 16) {
        shiftDown = false;
      }
    });

    render();
});
<|endoftext|>


<commit_before>define(function() {

    'use strict';

    var canvas, ctx, moon, IMAGE_SIZE, HALF_SIZE;

    (function init() {
        canvas = document.querySelector('canvas');
        ctx = canvas.getContext('2d');

        moon = document.querySelector('img#moon');

        if (!ctx.ellipse) {
            ctx.ellipse = function(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise) {
                /* for this project, we do not need: rotation, startAngle, endAngle, anticlockwise */
                x -= radiusX;
                y -= radiusY;
                radiusX *= 2;
                radiusY *= 2;

                var kappa = 0.5522848,
                    ox = (radiusX / 2) * kappa, // control point offset horizontal
                    oy = (radiusY / 2) * kappa, // control point offset vertical
                    xe = x + radiusX, // x-end
                    ye = y + radiusY, // y-end
                    xm = x + radiusX / 2, // x-middle
                    ym = y + radiusY / 2; // y-middle

                if (startAngle === Math.PI / 2 && endAngle === 3 * Math.PI / 2) {
                    ctx.moveTo(xm, ye);
                    ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); /* 2nd quarter */
                    ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); /* 3rd quarter */
                } else if (startAngle === 3 * Math.PI / 2 && endAngle === Math.PI / 2) {
                    ctx.moveTo(xm, y);
                    ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); /* 4th quarter */
                    ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); /* 1st quarter */
                } else if (startAngle === 0 && endAngle === 2 * Math.PI) {
                    ctx.moveTo(xe, ym);
                    ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); /* 1st quarter */
                    ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); /* 2nd quarter */
                    ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); /* 3rd quarter */
                    ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); /* 4th quarter */
                }
            };
        }
    })();

    function drawMoon(phase_of_moon, IMAGE_SIZE) {
        var HALF_SIZE = IMAGE_SIZE / 2;

        var phase_shadow_adjust = null;
        var arc_scale = null;

        ctx.strokeStyle = 'black';
        ctx.fillStyle = 'black';
        ctx.fillRect(0, 0, IMAGE_SIZE, IMAGE_SIZE);

        if (phase_of_moon < 0.25) {
            phase_shadow_adjust = phase_of_moon - Math.abs(Math.sin(phase_of_moon * Math.PI * 4) / 18.0);
            arc_scale = 1 - 4 * phase_shadow_adjust;

            ctx.fillStyle = 'white';
            ctx.fillRect(HALF_SIZE, 0, HALF_SIZE, IMAGE_SIZE);
            ctx.fillStyle = 'black';
            drawEllipse(HALF_SIZE - IMAGE_SIZE * arc_scale / 2, 0, IMAGE_SIZE * arc_scale, IMAGE_SIZE, 0, 3 * Math.PI / 2, Math.PI / 2);
            ctx.fill();

        } else if (phase_of_moon < 0.50) {
            phase_shadow_adjust = phase_of_moon + Math.abs(Math.sin(phase_of_moon * Math.PI * 4) / 18.0);
            arc_scale = 4 * (phase_shadow_adjust - 0.25);

            ctx.fillStyle = 'white';
            ctx.fillRect(HALF_SIZE, 0, HALF_SIZE, IMAGE_SIZE);
            ctx.fillStyle = 'white';
            drawEllipse(HALF_SIZE - IMAGE_SIZE * arc_scale / 2, 0, IMAGE_SIZE * arc_scale, IMAGE_SIZE, 0, Math.PI / 2, 3 * Math.PI / 2);
            ctx.fill();

        } else if (phase_of_moon < 0.75) {
            phase_shadow_adjust = phase_of_moon - Math.abs(Math.sin(phase_of_moon * Math.PI * 4) / 18.0);
            arc_scale = 1 - 4 * (phase_shadow_adjust - 0.5);

            ctx.fillStyle = 'white';
            ctx.fillRect(0, 0, HALF_SIZE, IMAGE_SIZE);
            ctx.fillStyle = 'white';
            drawEllipse(HALF_SIZE - IMAGE_SIZE * arc_scale / 2, 0, IMAGE_SIZE * arc_scale, IMAGE_SIZE, 0, 3 * Math.PI / 2, Math.PI / 2);
            ctx.fill();

        } else {
            phase_shadow_adjust = phase_of_moon + Math.abs(Math.sin(phase_of_moon * Math.PI * 4) / 18.0);
            arc_scale = 4 * (phase_shadow_adjust - 0.75);

            ctx.fillStyle = 'white';
            ctx.fillRect(0, 0, HALF_SIZE, IMAGE_SIZE);
            ctx.fillStyle = 'black';
            drawEllipse(HALF_SIZE - IMAGE_SIZE * arc_scale / 2, 0, IMAGE_SIZE * arc_scale, IMAGE_SIZE, 0, Math.PI / 2, 3 * Math.PI / 2);
            ctx.fill();
        }
        ctx.save();

        ctx.globalCompositeOperation = 'multiply';
        ctx.drawImage(moon, 0, 0, IMAGE_SIZE, IMAGE_SIZE);
        ctx.globalAlpha = 0.5;
        ctx.globalCompositeOperation = 'source-over';
        ctx.drawImage(moon, 0, 0, IMAGE_SIZE, IMAGE_SIZE);

        ctx.restore();
    }

    function drawGrid(compass_text, IMAGE_SIZE) {
        var HALF_SIZE = 0.5 * IMAGE_SIZE;
        var needleLength = 0.08 * IMAGE_SIZE;

        ctx.font = '16px Sans';
        ctx.lineWidth = 3;

        /* Latitude Labels */
        ctx.fillStyle = 'blue';
        drawLabel(HALF_SIZE + 1, HALF_SIZE, 26, 22, '0°');
        drawLabel(HALF_SIZE + 1, HALF_SIZE * 0.50, 36, 22, '30°');
        drawLabel(HALF_SIZE + 1, HALF_SIZE * 1.5, 36, 22, '30°');
        drawLabel(HALF_SIZE + 1, HALF_SIZE * 0.15, 36, 22, '60°');
        drawLabel(HALF_SIZE + 1, HALF_SIZE * 1.85, 36, 22, '60°');

        /* Longitude Labels */
        ctx.fillStyle = 'red';
        drawLabel((HALF_SIZE * 0.48), HALF_SIZE, 36, 22, '30°');
        drawLabel((HALF_SIZE * 1.52), HALF_SIZE, 36, 22, '30°');
        drawLabel((HALF_SIZE * 0.15), HALF_SIZE, 36, 22, '60°');
        drawLabel((HALF_SIZE * 1.85), HALF_SIZE, 36, 22, '60°');

        /* Latitude Lines*/
        ctx.strokeStyle = 'blue';
        drawLine(0, HALF_SIZE, IMAGE_SIZE, HALF_SIZE);
        drawLine(HALF_SIZE * 0.15, HALF_SIZE * 0.5, IMAGE_SIZE - HALF_SIZE * 0.15, HALF_SIZE * 0.5);
        drawLine(HALF_SIZE * 0.15, HALF_SIZE * 1.5, IMAGE_SIZE - HALF_SIZE * 0.15, HALF_SIZE * 1.5);
        drawLine(HALF_SIZE * 0.5, HALF_SIZE * 0.15, IMAGE_SIZE - HALF_SIZE * 0.5, HALF_SIZE * 0.15);
        drawLine(HALF_SIZE * 0.5, HALF_SIZE * 1.85, IMAGE_SIZE - HALF_SIZE * 0.5, HALF_SIZE * 1.85);

        /* Longitude Lines*/
        ctx.strokeStyle = 'red';
        drawLine(HALF_SIZE, 0, HALF_SIZE, IMAGE_SIZE);
        drawEllipse(HALF_SIZE * 0.15, 0, IMAGE_SIZE - IMAGE_SIZE * 0.15, IMAGE_SIZE, 0, 0, 2 * Math.PI);
        drawEllipse(HALF_SIZE * 0.48, 0, IMAGE_SIZE - IMAGE_SIZE * 0.48, IMAGE_SIZE, 0, 0, 2 * Math.PI);

        /* Compass */
        ctx.fillStyle = 'red';
        ctx.fillRect(0 + 16, needleLength, needleLength, 4);
        ctx.fillText(compass_text[3], 0, needleLength + 8);
        ctx.fillText(compass_text[2], needleLength + 16 + 4, needleLength + 8);
        ctx.fillText('Longitude', 0, IMAGE_SIZE - 16);

        ctx.fillStyle = 'blue';
        ctx.fillRect(0.5 * needleLength + 16, 0.5 * needleLength, 4, needleLength);
        ctx.fillText(compass_text[0], 0.5 * needleLength + 16 - 4, 0.5 * needleLength - 8);
        ctx.fillText(compass_text[1], 0.5 * needleLength + 16 - 4, 1.5 * needleLength + 16 + 4);
        ctx.fillText('Latitude', 0, IMAGE_SIZE - 40);
    }

    function drawEllipse(x, y, width, height, rotation, startAngle, endAngle, anticlockwise) {
        ctx.beginPath();
        x += width / 2;
        y += height / 2;
        var radiusX = width / 2;
        var radiusY = height / 2;
        ctx.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise);
        ctx.stroke();
    }

    function drawLine(x1, y1, x2, y2) {
        ctx.beginPath();
        ctx.moveTo(x1, y1);
        ctx.lineTo(x2, y2);
        ctx.stroke();
    }

    function drawLabel(x, y, width, height, text) {
        var labelColor = ctx.fillStyle;
        ctx.fillRect(x, y, width, height);
        ctx.fillStyle = 'white';
        ctx.fillText(text, x + 5, y + 18);
        ctx.fillStyle = labelColor;
    }

    return {
        moon: drawMoon,
        grid: drawGrid
    };
});<commit_msg>replace degree symbol '°' with hex-code<commit_after>define(function() {

    'use strict';

    var canvas, ctx, moon, IMAGE_SIZE, HALF_SIZE;

    (function init() {
        canvas = document.querySelector('canvas');
        ctx = canvas.getContext('2d');

        moon = document.querySelector('img#moon');

        if (!ctx.ellipse) {
            ctx.ellipse = function(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise) {
                /* for this project, we do not need: rotation, startAngle, endAngle, anticlockwise */
                x -= radiusX;
                y -= radiusY;
                radiusX *= 2;
                radiusY *= 2;

                var kappa = 0.5522848,
                    ox = (radiusX / 2) * kappa, // control point offset horizontal
                    oy = (radiusY / 2) * kappa, // control point offset vertical
                    xe = x + radiusX, // x-end
                    ye = y + radiusY, // y-end
                    xm = x + radiusX / 2, // x-middle
                    ym = y + radiusY / 2; // y-middle

                if (startAngle === Math.PI / 2 && endAngle === 3 * Math.PI / 2) {
                    ctx.moveTo(xm, ye);
                    ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); /* 2nd quarter */
                    ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); /* 3rd quarter */
                } else if (startAngle === 3 * Math.PI / 2 && endAngle === Math.PI / 2) {
                    ctx.moveTo(xm, y);
                    ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); /* 4th quarter */
                    ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); /* 1st quarter */
                } else if (startAngle === 0 && endAngle === 2 * Math.PI) {
                    ctx.moveTo(xe, ym);
                    ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); /* 1st quarter */
                    ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); /* 2nd quarter */
                    ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); /* 3rd quarter */
                    ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); /* 4th quarter */
                }
            };
        }
    })();

    function drawMoon(phase_of_moon, IMAGE_SIZE) {
        var HALF_SIZE = IMAGE_SIZE / 2;

        var phase_shadow_adjust = null;
        var arc_scale = null;

        ctx.strokeStyle = 'black';
        ctx.fillStyle = 'black';
        ctx.fillRect(0, 0, IMAGE_SIZE, IMAGE_SIZE);

        if (phase_of_moon < 0.25) {
            phase_shadow_adjust = phase_of_moon - Math.abs(Math.sin(phase_of_moon * Math.PI * 4) / 18.0);
            arc_scale = 1 - 4 * phase_shadow_adjust;

            ctx.fillStyle = 'white';
            ctx.fillRect(HALF_SIZE, 0, HALF_SIZE, IMAGE_SIZE);
            ctx.fillStyle = 'black';
            drawEllipse(HALF_SIZE - IMAGE_SIZE * arc_scale / 2, 0, IMAGE_SIZE * arc_scale, IMAGE_SIZE, 0, 3 * Math.PI / 2, Math.PI / 2);
            ctx.fill();

        } else if (phase_of_moon < 0.50) {
            phase_shadow_adjust = phase_of_moon + Math.abs(Math.sin(phase_of_moon * Math.PI * 4) / 18.0);
            arc_scale = 4 * (phase_shadow_adjust - 0.25);

            ctx.fillStyle = 'white';
            ctx.fillRect(HALF_SIZE, 0, HALF_SIZE, IMAGE_SIZE);
            ctx.fillStyle = 'white';
            drawEllipse(HALF_SIZE - IMAGE_SIZE * arc_scale / 2, 0, IMAGE_SIZE * arc_scale, IMAGE_SIZE, 0, Math.PI / 2, 3 * Math.PI / 2);
            ctx.fill();

        } else if (phase_of_moon < 0.75) {
            phase_shadow_adjust = phase_of_moon - Math.abs(Math.sin(phase_of_moon * Math.PI * 4) / 18.0);
            arc_scale = 1 - 4 * (phase_shadow_adjust - 0.5);

            ctx.fillStyle = 'white';
            ctx.fillRect(0, 0, HALF_SIZE, IMAGE_SIZE);
            ctx.fillStyle = 'white';
            drawEllipse(HALF_SIZE - IMAGE_SIZE * arc_scale / 2, 0, IMAGE_SIZE * arc_scale, IMAGE_SIZE, 0, 3 * Math.PI / 2, Math.PI / 2);
            ctx.fill();

        } else {
            phase_shadow_adjust = phase_of_moon + Math.abs(Math.sin(phase_of_moon * Math.PI * 4) / 18.0);
            arc_scale = 4 * (phase_shadow_adjust - 0.75);

            ctx.fillStyle = 'white';
            ctx.fillRect(0, 0, HALF_SIZE, IMAGE_SIZE);
            ctx.fillStyle = 'black';
            drawEllipse(HALF_SIZE - IMAGE_SIZE * arc_scale / 2, 0, IMAGE_SIZE * arc_scale, IMAGE_SIZE, 0, Math.PI / 2, 3 * Math.PI / 2);
            ctx.fill();
        }
        ctx.save();

        ctx.globalCompositeOperation = 'multiply';
        ctx.drawImage(moon, 0, 0, IMAGE_SIZE, IMAGE_SIZE);
        ctx.globalAlpha = 0.5;

        ctx.globalCompositeOperation = 'source-over';
        ctx.drawImage(moon, 0, 0, IMAGE_SIZE, IMAGE_SIZE);

        ctx.restore();
    }

    function drawGrid(compass_text, IMAGE_SIZE) {
        var HALF_SIZE = 0.5 * IMAGE_SIZE;
        var needleLength = 0.08 * IMAGE_SIZE;

        ctx.font = '16px Sans';
        ctx.lineWidth = 3;

        /* Latitude Labels */
        ctx.fillStyle = 'blue';
        drawLabel(HALF_SIZE + 1, HALF_SIZE, 26, 22, '0\xB0');
        drawLabel(HALF_SIZE + 1, HALF_SIZE * 0.50, 36, 22, '30\xB0');
        drawLabel(HALF_SIZE + 1, HALF_SIZE * 1.5, 36, 22, '30\xB0');
        drawLabel(HALF_SIZE + 1, HALF_SIZE * 0.15, 36, 22, '60\xB0');
        drawLabel(HALF_SIZE + 1, HALF_SIZE * 1.85, 36, 22, '60\xB0');

        /* Longitude Labels */
        ctx.fillStyle = 'red';
        drawLabel((HALF_SIZE * 0.48), HALF_SIZE, 36, 22, '30\xB0');
        drawLabel((HALF_SIZE * 1.52), HALF_SIZE, 36, 22, '30\xB0');
        drawLabel((HALF_SIZE * 0.15), HALF_SIZE, 36, 22, '60\xB0');
        drawLabel((HALF_SIZE * 1.85), HALF_SIZE, 36, 22, '60\xB0');

        /* Latitude Lines*/
        ctx.strokeStyle = 'blue';
        drawLine(0, HALF_SIZE, IMAGE_SIZE, HALF_SIZE);
        drawLine(HALF_SIZE * 0.15, HALF_SIZE * 0.5, IMAGE_SIZE - HALF_SIZE * 0.15, HALF_SIZE * 0.5);
        drawLine(HALF_SIZE * 0.15, HALF_SIZE * 1.5, IMAGE_SIZE - HALF_SIZE * 0.15, HALF_SIZE * 1.5);
        drawLine(HALF_SIZE * 0.5, HALF_SIZE * 0.15, IMAGE_SIZE - HALF_SIZE * 0.5, HALF_SIZE * 0.15);
        drawLine(HALF_SIZE * 0.5, HALF_SIZE * 1.85, IMAGE_SIZE - HALF_SIZE * 0.5, HALF_SIZE * 1.85);

        /* Longitude Lines*/
        ctx.strokeStyle = 'red';
        drawLine(HALF_SIZE, 0, HALF_SIZE, IMAGE_SIZE);
        drawEllipse(HALF_SIZE * 0.15, 0, IMAGE_SIZE - IMAGE_SIZE * 0.15, IMAGE_SIZE, 0, 0, 2 * Math.PI);
        drawEllipse(HALF_SIZE * 0.48, 0, IMAGE_SIZE - IMAGE_SIZE * 0.48, IMAGE_SIZE, 0, 0, 2 * Math.PI);

        /* Compass */
        ctx.fillStyle = 'red';
        ctx.fillRect(0 + 16, needleLength, needleLength, 4);
        ctx.fillText(compass_text[3], 0, needleLength + 8);
        ctx.fillText(compass_text[2], needleLength + 16 + 4, needleLength + 8);
        ctx.fillText('Longitude', 0, IMAGE_SIZE - 16);

        ctx.fillStyle = 'blue';
        ctx.fillRect(0.5 * needleLength + 16, 0.5 * needleLength, 4, needleLength);
        ctx.fillText(compass_text[0], 0.5 * needleLength + 16 - 4, 0.5 * needleLength - 8);
        ctx.fillText(compass_text[1], 0.5 * needleLength + 16 - 4, 1.5 * needleLength + 16 + 4);
        ctx.fillText('Latitude', 0, IMAGE_SIZE - 40);
    }

    function drawEllipse(x, y, width, height, rotation, startAngle, endAngle, anticlockwise) {
        ctx.beginPath();
        x += width / 2;
        y += height / 2;
        var radiusX = width / 2;
        var radiusY = height / 2;
        ctx.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise);
        ctx.stroke();
    }

    function drawLine(x1, y1, x2, y2) {
        ctx.beginPath();
        ctx.moveTo(x1, y1);
        ctx.lineTo(x2, y2);
        ctx.stroke();
    }

    function drawLabel(x, y, width, height, text) {
        var labelColor = ctx.fillStyle;
        ctx.fillRect(x, y, width, height);
        ctx.fillStyle = 'white';
        ctx.fillText(text, x + 5, y + 18);
        ctx.fillStyle = labelColor;
    }

    return {
        moon: drawMoon,
        grid: drawGrid
    };
});<|endoftext|>


<commit_before>// Create a Paper.js Path to draw a line into it:
var line = new Path();
line.style = {
    strokeColor: 'black',
    strokeWidth: 4
}

var start = new Point(300, 100);
// Move to start and draw a line from there
line.moveTo(start);
// Note the plus operator on Point objects.
line.lineTo(start + [200, -50]);

// if view is resized and then redrawn, pathCircle will recenter itself
var pathCircle = new Path.Circle({
    center: view.center,
    radius: 30,
    fillColor: new Color(0.9, 0.5, 0.5),
    strokeColor: 'red',
    strokeWidth: 10
});

// Create player object with a name, rounded rectangle and text showing name.
function Player(name, origin, rectColor, textColor)
{
    this.name = name;

    // rounded rectangle
    var rectSize = new Size(200, 30);
    var rectOrigin = origin + [100, 0];
    var rect = new Rectangle(rectOrigin, rectSize);

    var cornerSize = new Size(10, 10);
    this.roundRect = new Path.RoundRectangle(rect, cornerSize);
    this.roundRect.style = {
        fillColor: rectColor,
        strokeColor: 'brown',
        strokeWidth: 4
    };

        //point: [this.position.x + 20, this.position.y + rectSize.height/2],
    this.text = new PointText({
        point: [origin.x, origin.y + 0.75*rectSize.height],
        content: this.name,
        fillColor: textColor,
        fontSize: 20
    });

    //return new Group([this.roundRect, this.text]);
}

// must use new Point, not [50, 140]
playerOrigin = new Point(50, 80);

// when Player() returns new Group and instantiate as = Player, set position works
// when player() doesn't return group and instantiate as = new Player, set position doesn't work??
// for group or other 'item'?
//playerA.position = [300, 340];

// when playerA returns new Group and instantiate as = Player, set color doesn't work
// when playerA doesn't return new Group and instantiate as = new Player, set color works
//playerA.text.fillColor = 'red';

// return color that varies with index and alternates semi-complementary
function oddEvenColor(index)
{
    // http://stackoverflow.com/questions/4228356/integer-division-in-javascript
    // complementary hue = +180 degrees?
    var oddEvenColor = {hue: 40.*Math.floor(index/2) + ((index%2)*180.),
        saturation: 0.4,
        brightness: 0.8};
    return oddEvenColor;
}

// Loop through array of players and draw them all.
var playerNames = ['Bill', 'Joe', 'Sally', 'Fred', 'John', 'Zoe', 'Cathy', 'Luis', 'Hector', 'Om',
'Pinto', 'Ray', 'Hiro', 'Yi', 'Boo', 'Arfu'];
var offset = 40;
for(var i = 0; i < playerNames.length; i++) {
    var player = new Player(playerNames[i],
                            playerOrigin,
                            oddEvenColor(i),
                            'black');
    // TODO: translate as a Group or using a function
    player.roundRect.translate(0, offset * i);
    player.text.translate(0, offset * i);
};
<commit_msg>Change player name color from black to white.<commit_after>// Create a Paper.js Path to draw a line into it:
var line = new Path();
line.style = {
    strokeColor: 'black',
    strokeWidth: 4
}

var start = new Point(300, 100);
// Move to start and draw a line from there
line.moveTo(start);
// Note the plus operator on Point objects.
line.lineTo(start + [200, -50]);

// if view is resized and then redrawn, pathCircle will recenter itself
var pathCircle = new Path.Circle({
    center: view.center,
    radius: 30,
    fillColor: new Color(0.9, 0.5, 0.5),
    strokeColor: 'red',
    strokeWidth: 10
});

// Create player object with a name, rounded rectangle and text showing name.
function Player(name, origin, rectColor, textColor)
{
    this.name = name;

    // rounded rectangle
    var rectSize = new Size(200, 30);
    var rectOrigin = origin + [100, 0];
    var rect = new Rectangle(rectOrigin, rectSize);

    var cornerSize = new Size(10, 10);
    this.roundRect = new Path.RoundRectangle(rect, cornerSize);
    this.roundRect.style = {
        fillColor: rectColor,
        strokeColor: 'brown',
        strokeWidth: 4
    };

        //point: [this.position.x + 20, this.position.y + rectSize.height/2],
    this.text = new PointText({
        point: [origin.x, origin.y + 0.75*rectSize.height],
        content: this.name,
        fillColor: textColor,
        fontSize: 20
    });

    //return new Group([this.roundRect, this.text]);
}

// must use new Point, not [50, 140]
playerOrigin = new Point(50, 80);

// when Player() returns new Group and instantiate as = Player, set position works
// when player() doesn't return group and instantiate as = new Player, set position doesn't work??
// for group or other 'item'?
//playerA.position = [300, 340];

// when playerA returns new Group and instantiate as = Player, set color doesn't work
// when playerA doesn't return new Group and instantiate as = new Player, set color works
//playerA.text.fillColor = 'red';

// return color that varies with index and alternates semi-complementary
function oddEvenColor(index)
{
    // http://stackoverflow.com/questions/4228356/integer-division-in-javascript
    // complementary hue = +180 degrees?
    var oddEvenColor = {hue: 40.*Math.floor(index/2) + ((index%2)*180.),
        saturation: 0.4,
        brightness: 0.8};
    return oddEvenColor;
}

// Loop through array of players and draw them all.
var playerNames = ['Bill', 'Joe', 'Sally', 'Fred', 'John', 'Zoe', 'Cathy', 'Luis', 'Hector', 'Om',
'Pinto', 'Ray', 'Hiro', 'Yi', 'Boo', 'Arfu'];
var offset = 40;
for(var i = 0; i < playerNames.length; i++) {
    var player = new Player(playerNames[i],
                            playerOrigin,
                            oddEvenColor(i),
                            {hue: 0, saturation: 0.0, brightness: 0.8});
    // TODO: translate as a Group or using a function
    player.roundRect.translate(0, offset * i);
    player.text.translate(0, offset * i);
};
<|endoftext|>


<commit_before>/**
 * Created by huiji-001 on 2015/12/15.
 */
function addCategory(category){
    var api = new mw.Api()
    api.postWithToken( "edit", {
        action: "edit",
        title: mw.config.get( "wgPageName" ),
        summary: "快速添加分类",
        appendtext: category
    } ).done( function( result, jqXHR ) {
        var a ='';
        var b = category.split(']][[Category:');
        b[0] = b[0].substring(11);
        b[b.length-1] = b[b.length-1].substring(0,b[b.length-1].length-2);
        b.forEach(function(i){
            a += '
'+i+'
'
        })
        $('#catlinks .last').before(a);
        $('#add-category-btn').popover('hide');
    } ).fail( function( code, result ) {
        if ( code === "http" ) {
            console.log( "HTTP error: " + result.textStatus ); // result.xhr contains the jqXHR object
        } else if ( code === "ok-but-empty" ) {
            mw.log( "Got an empty response from the server" );
        } else {
            mw.log( "API error: " + code );
        }
    } );
}
function addSpan(){
    var val = $('.edit-field input').val();
    var html = '
'+val+'
';
    if(val == ''||val == ' '){
        mw.notification.notify('分类不能为空，请重新输入');
        $('.edit-field input').val('');
        return;
    }
    $('#mw-normal-catlinks>ul>li:not(.last)>a,.edit-span').each(function(){
        if($(this).text()==val){
            mw.notification.notify('分类不能重复，请重新输入');
            $('.edit-field input').val('');
            return;
        }
    });
    $('.edit-field').before(html);
    $('.edit-field input').val('');
}
function getData(){
    var data='';
    $('.edit-choices .edit-span').each(function(){
        data += '[[Category:'+$(this).text()+']]';
    });
    return data;
}
$(function(){
    var addbtn = '
添加分类
';
    var content = '
' +
        '
'+
        '
'+
        '
<input type="text">
'+
        '
'+
        '
'+
        '
' +
        '<button class="btn btn-primary edit-submit"></button>' +
        '<button type="button" class="btn edit-cancel"></button>' +
        '
' +
        '
';
    $('#mw-normal-catlinks>ul').append(addbtn);
    $('#add-category-btn').popover({title:'添加分类',content:content,html:true});
    $('#mw-normal-catlinks').on('keyup','.edit-field input',function(event){
       if(event.keyCode == 13){
           addSpan();
       }
    }).on('click','.edit-input i',function(){
        $(this).parents('.edit-choices li').remove();
    }).on('click','.edit-input',function(){
        $('.edit-field input').focus();
    }).on('click','.edit-submit',function(e){
        e.stopPropagation();
        var data = getData();
        addCategory(data);
    }).on('click','.edit-cancel',function(){
        $('#add-category-btn').popover('hide');
    });
});
<commit_msg>Update edit.js<commit_after>/**
 * Created by huiji-001 on 2015/12/15.
 */
function addCategory(category){
    var api = new mw.Api()
    api.postWithToken( "edit", {
        action: "edit",
        title: mw.config.get( "wgPageName" ),
        summary: "快速添加分类",
        appendtext: category
    } ).done( function( result, jqXHR ) {
        var a ='';
        var b = category.split(']][[Category:');
        b[0] = b[0].substring(11);
        b[b.length-1] = b[b.length-1].substring(0,b[b.length-1].length-2);
        b.forEach(function(i){
            a += '
'+i+'
'
        })
        $('#catlinks .last').before(a);
        $('#add-category-btn').popover('hide');
    } ).fail( function( code, result ) {
        if ( code === "http" ) {
            console.log( "HTTP error: " + result.textStatus ); // result.xhr contains the jqXHR object
        } else if ( code === "ok-but-empty" ) {
            mw.log( "Got an empty response from the server" );
        } else {
            mw.log( "API error: " + code );
        }
    } );
}
function addSpan(){
    var val = $('.edit-field input').val();
    var html = '
'+val+'
';
    if(val == ''||val == ' '){
        mw.notification.notify('分类不能为空，请重新输入');
        $('.edit-field input').val('');
        return;
    }
    $('#mw-normal-catlinks>ul>li:not(.last)>a,.edit-span').each(function(){
        if($(this).text()==val){
            mw.notification.notify('分类不能重复，请重新输入');
            $('.edit-field input').val('');
            return;
        }
    });
    $('.edit-field').before(html);
    $('.edit-field input').val('');
}
function getData(){
    var data='';
    $('.edit-choices .edit-span').each(function(){
        data += '[[Category:'+$(this).text()+']]';
    });
    return data;
}
$(function(){
    if (mw.config.get('wgUserName')==''){
        return;
    }
    var addbtn = '
添加分类
';
    var content = '
' +
        '
'+
        '
'+
        '
<input type="text">
'+
        '
'+
        '
'+
        '
' +
        '<button class="btn btn-primary edit-submit"></button> ' +
        '<button type="button" class="btn edit-cancel"></button>' +
        '
' +
        '
';
    $('#mw-normal-catlinks>ul').append(addbtn);
    $('#add-category-btn').popover({title:'添加分类',content:content,html:true});
    $('#mw-normal-catlinks').on('keyup','.edit-field input',function(event){
       if(event.keyCode == 13){
           addSpan();
       }
    }).on('click','.edit-input i',function(){
        $(this).parents('.edit-choices li').remove();
    }).on('click','.edit-input',function(){
        $('.edit-field input').focus();
    }).on('click','.edit-submit',function(e){
        e.stopPropagation();
        var data = getData();
        addCategory(data);
    }).on('click','.edit-cancel',function(){
        $('#add-category-btn').popover('hide');
    });
});
<|endoftext|>


<commit_before>/* Model */

function fix_paipu(paipu) {

    var format = {
        title:  true,
        player: true,
        qijia:  true,
        log:    true,
        defen:  true,
        rank:   true,
        point:  true
    };

    for (var p of [].concat(paipu)) {
        for (var key in format) {
            if (p[key] == undefined) throw new Error(key+': '+p[key]);
        }
        for (var key in p) {
            if (! format[key]) delete p[key];
        }
    }
    return paipu;
}

Majiang.PaipuFile = function(storage) {

    this._paipu = [];

    if (storage && localStorage) {
        if (localStorage.getItem(storage)) {
            this._paipu = fix_paipu(JSON.parse(localStorage.getItem(storage)));
        }
        this._storage = storage;
    }
}

Majiang.PaipuFile.prototype.length = function() {
    return this._paipu.length;
}

Majiang.PaipuFile.prototype.stringify = function(idx) {
    if (idx == null) return JSON.stringify(this._paipu);
    else             return JSON.stringify(this._paipu[idx]);
}

Majiang.PaipuFile.prototype.add_paipu = function(paipu) {
    this._paipu = this._paipu.concat(fix_paipu(paipu));
    if (this._storage) {
        localStorage.setItem(this._storage, this.stringify())
    }
}

Majiang.PaipuFile.prototype.get_paipu = function(idx) {
    return this._paipu[idx];
}

Majiang.PaipuFile.prototype.del_paipu = function(idx) {
    this._paipu.splice(idx, 1);
    if (this._storage) {
        localStorage.setItem(this._storage, this.stringify())
    }
}

/* View */

Majiang.View.PaipuFile = function(node, paipu_file) {

    this._node  = node;
    this._model = paipu_file;

    this._max_idx = 0;

    this._row = this._node.find('.list > div');

    node.find('.error').on('click', function(){
        var self = this;
        $(this).fadeOut(500, function(){ $(self).empty(); });
    });
}

Majiang.View.PaipuFile.prototype.redraw = function() {

    var node =  this._node.find('.list');
    node.empty();

    for (var i = 0; i < this._model.length(); i++) {
        var paipu = this._model._paipu[i];
        var player = ['','','',''];
        for (var l = 0; l < 4; l++) {
            player[paipu.rank[l] - 1]
                = paipu.player[l]
                    + '(' + (paipu.point[l] > 0 ? '+' : '')
                            + paipu.point[l] + ')';
        }
        player = player.join(', ');

        var row = this._row.clone();
        row.find('.title').text(paipu.title);
        row.find('.player').text(player);
        node.append(row);
        if (i < this._max_idx) row.show();
    }
    this._max_idx = this._model.length();

    if (this._model.length() == 0) this._node.find('.download').hide();
    else                           this._node.find('.download').show();

    this._node.find('.list > div').fadeIn();
}

Majiang.View.PaipuFile.prototype.error = function(msg) {
    var self = this;
    this._node.find('.error').append($('

').text(msg)).fadeIn();
    setTimeout(function(){ self._node.find('.error').click() }, 5000);
}

/* Controller */

Majiang.PaipuEditor = function(storage) {

    var self = this;

    this._model  = new Majiang.PaipuFile(storage);
    this._view   = {
        paipu_file: new Majiang.View.PaipuFile(
                        $('#editor .paipu_file'), this._model),
    };

    $('#editor .paipu_file .upload input').on('change', function(){
        for (var i = 0; i < this.files.length; i++) {
            var file = this.files[i];
            if (! file.type.match(/application\/json/i)
                && ! file.name.match(/\.json/i))
            {
                self._view.paipu_file.error('不正なファイル: ' + file.name);
                continue;
            }
            var reader = new FileReader();
            reader.onload = (function(filename){
                return function(event){
                    var paipu;
                    try {
                        self._model.add_paipu(JSON.parse(event.target.result));
                    }
                    catch(e) {
                        console.log(filename + ': ' + e);           // for DEBUG
                        self._view.paipu_file.error('不正なファイル: ' + filename);
                        return;
                    }
                    self._view.paipu_file.redraw();
                    self.set_handler();
                };
            })(file.name);
            reader.readAsText(file);
        }
        $(this).val(null);
    });
}

Majiang.PaipuEditor.prototype.start = function(paipu) {

    $('body').removeClass('game').addClass('editor');
    $('#game').hide();
    $('#editor').fadeIn();

    if (paipu) this._model.add_paipu(paipu);
    this._view.paipu_file.redraw();
    this.set_handler();
}

Majiang.PaipuEditor.prototype.set_handler = function(paipu) {

    var self = this;

    if (this._model.length() == 0) return;

    var list = $('#editor .paipu_file .list > div');
    for (var i = 0; i < this._model.length(); i++) {

        list.eq(i).find('.delete').on('click', i, function(event){
            self._model.del_paipu(event.data);
            self._view.paipu_file.redraw();
            self.set_handler();
        });

        var title = this._model._paipu[i].title.replace(/[ \\\/\:]/g,'_');
        var blob = new Blob([ this._model.stringify(i) ],
                            { type: 'application/json' });
        list.eq(i).find('.download')
                        .attr('href', URL.createObjectURL(blob))
                        .attr('download', '牌譜(' + title + ').json');

        list.eq(i).find('.replay').on('click', i, function(event){
            var paipu = self._model.get_paipu(event.data);
            var game  = new Majiang.Game.Paipu(paipu);
            game._callback = function(){ self.start() };
            $('body').removeClass('editor').addClass('game');
            $('#editor').hide();
            $('#game').show();
            game.next();
        });
    }

    var title = this._model._paipu[0].title.replace(/[ \\\/\:]/g,'_');

    var blob = new Blob([ this._model.stringify() ],
                        { type: 'application/json' });
    $('#editor .paipu_file > .download')
                    .attr('href', URL.createObjectURL(blob))
                    .attr('download', '牌譜(' + title + ').json');

    var ua = navigator.userAgent;
    if (! ua.match(/\bChrome\b/) && ua.match(/\bSafari\b/)) {
        $('.download').hide();
    }
}
<commit_msg>#5 デバッグ文を削除<commit_after>/* Model */

function fix_paipu(paipu) {

    var format = {
        title:  true,
        player: true,
        qijia:  true,
        log:    true,
        defen:  true,
        rank:   true,
        point:  true
    };

    for (var p of [].concat(paipu)) {
        for (var key in format) {
            if (p[key] == undefined) throw new Error(key+': '+p[key]);
        }
        for (var key in p) {
            if (! format[key]) delete p[key];
        }
    }
    return paipu;
}

Majiang.PaipuFile = function(storage) {

    this._paipu = [];

    if (storage && localStorage) {
        if (localStorage.getItem(storage)) {
            this._paipu = fix_paipu(JSON.parse(localStorage.getItem(storage)));
        }
        this._storage = storage;
    }
}

Majiang.PaipuFile.prototype.length = function() {
    return this._paipu.length;
}

Majiang.PaipuFile.prototype.stringify = function(idx) {
    if (idx == null) return JSON.stringify(this._paipu);
    else             return JSON.stringify(this._paipu[idx]);
}

Majiang.PaipuFile.prototype.add_paipu = function(paipu) {
    this._paipu = this._paipu.concat(fix_paipu(paipu));
    if (this._storage) {
        localStorage.setItem(this._storage, this.stringify())
    }
}

Majiang.PaipuFile.prototype.get_paipu = function(idx) {
    return this._paipu[idx];
}

Majiang.PaipuFile.prototype.del_paipu = function(idx) {
    this._paipu.splice(idx, 1);
    if (this._storage) {
        localStorage.setItem(this._storage, this.stringify())
    }
}

/* View */

Majiang.View.PaipuFile = function(node, paipu_file) {

    this._node  = node;
    this._model = paipu_file;

    this._max_idx = 0;

    this._row = this._node.find('.list > div');

    node.find('.error').on('click', function(){
        var self = this;
        $(this).fadeOut(500, function(){ $(self).empty(); });
    });
}

Majiang.View.PaipuFile.prototype.redraw = function() {

    var node =  this._node.find('.list');
    node.empty();

    for (var i = 0; i < this._model.length(); i++) {
        var paipu = this._model._paipu[i];
        var player = ['','','',''];
        for (var l = 0; l < 4; l++) {
            player[paipu.rank[l] - 1]
                = paipu.player[l]
                    + '(' + (paipu.point[l] > 0 ? '+' : '')
                            + paipu.point[l] + ')';
        }
        player = player.join(', ');

        var row = this._row.clone();
        row.find('.title').text(paipu.title);
        row.find('.player').text(player);
        node.append(row);
        if (i < this._max_idx) row.show();
    }
    this._max_idx = this._model.length();

    if (this._model.length() == 0) this._node.find('.download').hide();
    else                           this._node.find('.download').show();

    this._node.find('.list > div').fadeIn();
}

Majiang.View.PaipuFile.prototype.error = function(msg) {
    var self = this;
    this._node.find('.error').append($('

').text(msg)).fadeIn();
    setTimeout(function(){ self._node.find('.error').click() }, 5000);
}

/* Controller */

Majiang.PaipuEditor = function(storage) {

    var self = this;

    this._model  = new Majiang.PaipuFile(storage);
    this._view   = {
        paipu_file: new Majiang.View.PaipuFile(
                        $('#editor .paipu_file'), this._model),
    };

    $('#editor .paipu_file .upload input').on('change', function(){
        for (var i = 0; i < this.files.length; i++) {
            var file = this.files[i];
            if (! file.type.match(/application\/json/i)
                && ! file.name.match(/\.json/i))
            {
                self._view.paipu_file.error('不正なファイル: ' + file.name);
                continue;
            }
            var reader = new FileReader();
            reader.onload = (function(filename){
                return function(event){
                    var paipu;
                    try {
                        self._model.add_paipu(JSON.parse(event.target.result));
                    }
                    catch(e) {
                        self._view.paipu_file.error('不正なファイル: ' + filename);
                        return;
                    }
                    self._view.paipu_file.redraw();
                    self.set_handler();
                };
            })(file.name);
            reader.readAsText(file);
        }
        $(this).val(null);
    });
}

Majiang.PaipuEditor.prototype.start = function(paipu) {

    $('body').removeClass('game').addClass('editor');
    $('#game').hide();
    $('#editor').fadeIn();

    if (paipu) this._model.add_paipu(paipu);
    this._view.paipu_file.redraw();
    this.set_handler();
}

Majiang.PaipuEditor.prototype.set_handler = function(paipu) {

    var self = this;

    if (this._model.length() == 0) return;

    var list = $('#editor .paipu_file .list > div');
    for (var i = 0; i < this._model.length(); i++) {

        list.eq(i).find('.delete').on('click', i, function(event){
            self._model.del_paipu(event.data);
            self._view.paipu_file.redraw();
            self.set_handler();
        });

        var title = this._model._paipu[i].title.replace(/[ \\\/\:]/g,'_');
        var blob = new Blob([ this._model.stringify(i) ],
                            { type: 'application/json' });
        list.eq(i).find('.download')
                        .attr('href', URL.createObjectURL(blob))
                        .attr('download', '牌譜(' + title + ').json');

        list.eq(i).find('.replay').on('click', i, function(event){
            var paipu = self._model.get_paipu(event.data);
            var game  = new Majiang.Game.Paipu(paipu);
            game._callback = function(){ self.start() };
            $('body').removeClass('editor').addClass('game');
            $('#editor').hide();
            $('#game').show();
            game.next();
        });
    }

    var title = this._model._paipu[0].title.replace(/[ \\\/\:]/g,'_');

    var blob = new Blob([ this._model.stringify() ],
                        { type: 'application/json' });
    $('#editor .paipu_file > .download')
                    .attr('href', URL.createObjectURL(blob))
                    .attr('download', '牌譜(' + title + ').json');

    var ua = navigator.userAgent;
    if (! ua.match(/\bChrome\b/) && ua.match(/\bSafari\b/)) {
        $('.download').hide();
    }
}
<|endoftext|>


<commit_before>(function($) {
  $(document).ready(function() {
    $(".signupForm").submit(function(event) {
      var formData = {
        'phoneNumber': $('input[name=phoneNumber]').val()
      }

      $.ajax({
        type: 'POST',
        url: 'https://autolotto-backend-production.herokuapp.com/api/v1/pres',
        data: formData,
        dataType: 'json',
        crossDomain: true,
        encode: true
      }).done(function(res) {
        var redirectTo = 'http://web.autolotto.com/giveaway/home/' + res.data.pre._id;
        window.parent.location.href = 'https://autolotto.com/redirect?url=' + redirectTo;
      }).fail(function() {
        alert('Please enter a valid US phone number!');
      });

      event.preventDefault();
    });
  });
})(jQuery);
<commit_msg>remove user id from redirect url<commit_after>(function($) {
  $(document).ready(function() {
    $(".signupForm").submit(function(event) {
      var formData = {
        'phoneNumber': $('input[name=phoneNumber]').val()
      }

      $.ajax({
        type: 'POST',
        url: 'https://autolotto-backend-production.herokuapp.com/api/v1/pres',
        data: formData,
        dataType: 'json',
        crossDomain: true,
        encode: true
      }).done(function(res) {
        var redirectTo = 'http://web.autolotto.com/giveaway/home/';
        window.parent.location.href = 'https://autolotto.com/redirect?url=' + redirectTo;
      }).fail(function() {
        alert('Please enter a valid US phone number!');
      });

      event.preventDefault();
    });
  });
})(jQuery);
<|endoftext|>


<commit_before>var Game = {};
var light = false;
var timerset = false;
var timersetflash = false;
var fading = false;
Game.fps = 50;

Game.initialize = function() {
  this.entities = [];
  this.context = document.getElementById("viewport").getContext("2d");

};

Game.fadeOutRectange = function(x, y, w, h, r, g, b) {

  };

Game.draw = function() {

  this.context.clearRect(0, 0, 512, 288);
  this.context.fillStyle = "#1F3D5C";
  this.context.fillRect(0, 0, 512, 288);

  /*if(timersetflash == false){ //flash timer
    setTimeout(function(){
      light = false;
      timersetflash = false;
    }, 200);
    timersetflash = true;
  }*/

  if(light == true && fading == false){
    this.context.fillStyle = "#FFFFFF";
    this.context.fillRect(0, 0, 512, 288);

    if(fading == false){
      fading = true;
      //fadeOutRectange(0, 0, 512, 288, 31, 61, 92);
      var r = 255, g = 255, b = 255;
      var self = document.getElementById("viewport").getContext("2d");
      var steps = 50;
      var dr = (31 - r) / steps; // how much red should be added each time
      var dg = (61 - g) / steps; // green
      var db = (92 - b) / steps; // blue
      var count = 0; // step counter
      var interval = setInterval(function() {
          self.fillStyle = 'rgb(' + Math.round(r + dr * count) + ',' + Math.round(g + dg * count) + ',' + Math.round(b + db * count) + ')';
          self.fillRect(0, 0, 512, 288); // will redraw the area each time
          count++;
          if(count === steps) { // stop if done
              clearInterval(interval);
              fading = false;
              light = false;
          }
      }, 30);
    }

    /*setTimeout(function() {
      light = false;
      timerset = false;
      flash = 0;
    }, 3000);*/
  }

  if(timerset == false){ //if we haven't set a timer yet, set one
    setTimeout(function(){
      light = true;
    }, 5000);
    timerset = true;
  }

  for (var i=0; i < this.entities.length; i++) {
    this.entities[i].draw(this.context);
  }
};

Game.update = function() {
  for (var i=0; i < this.entities.length; i++) {
    this.entities[i].update();
  }
};

Game.addRect = function(initX) {
  Game.entities.push(new Rect(initX));
};
Game.addRain = function() {
  Game.entities.push(new Rain());
};

Game.addPlayer = function(){
  Game.entites.push(new Player());
};
<commit_msg>Update game.js<commit_after>var Game = {};
var light = false;
var timerset = false;
var timersetflash = false;
var fading = false;
Game.fps = 50;

Game.initialize = function() {
  this.entities = [];
  this.context = document.getElementById("viewport").getContext("2d");

};

Game.fadeOutRectange = function(x, y, w, h, r, g, b) {

  };

Game.draw = function() {

  this.context.clearRect(0, 0, 512, 288);
  this.context.fillStyle = "#1F3D5C";
  this.context.fillRect(0, 0, 512, 288);

  if(light == true && fading == false){
    this.context.fillStyle = "#FFFFFF";
    this.context.fillRect(0, 0, 512, 288);
    fading = true;
    var r = 255, g = 255, b = 255; // starting color
    var self = document.getElementById("viewport").getContext("2d");
    var steps = 50;
    var dr = (31 - r) / steps; // how much red should be added each time
    var dg = (61 - g) / steps; // green
    var db = (92 - b) / steps; // blue
    var count = 0; // step counter
    var interval = setInterval(function() {
        self.fillStyle = 'rgb(' + Math.round(r + dr * count) + ',' + Math.round(g + dg * count) + ',' + Math.round(b + db * count) + ')';
        self.fillRect(0, 0, 512, 288); // will redraw the area each time
        count++;
        if(count === steps) { // stop if done
            clearInterval(interval);
            fading = false;
            light = false;
        }
    }, 30);
  }

  setTimer(function(){
    light = true;
  }, 5000);

  for (var i=0; i < this.entities.length; i++) {
    this.entities[i].draw(this.context);
  }
};

Game.update = function() {
  for (var i=0; i < this.entities.length; i++) {
    this.entities[i].update();
  }
};

Game.addRect = function(initX) {
  Game.entities.push(new Rect(initX));
};
Game.addRain = function() {
  Game.entities.push(new Rain());
};

Game.addPlayer = function(){
  Game.entites.push(new Player());
};
<|endoftext|>


<commit_before>(function(window, document, $) {
	var canvas = document.getElementById("game"),
		context = canvas.getContext("2d"),
		defaultColour = "#444",
		backgroundColour = "#eee",
		bits = [],
		cacti = [];

	function generateBits() {
		for (y = canvas.height - 10; y <= canvas.height; y += 8) {
			for (x = 0 + rand(0, 100); x <= canvas.width; x += rand(100, 200)) {
				bits.push({
					x: x,
					y: y,
					width: rand(2, 4)
				});
			}
		}
	}

	function updateCacti(offset) {
		var count = 0,
			buffer = 50; // used to force cacti off the screen
		while (cacti.length > count && cacti[count].x < offset - buffer) {
			count++;
		}

		cacti.splice(0, count);

		if (cacti.length === 0) {
			count = Math.floor(rand(1, 3.9));
			var scale = rand(0.8, 1.5);

			while (count--) {
				cacti.push({
					x: canvas.width + offset + buffer + (count * 20 * scale),
					scale: scale,
					leftSize: rand(0.5, 1.5),
					rightSize: rand(0.5, 1.5),
					centerSize: rand(0.5, 1.5)
				});
			}
		}
	}

	function rand(min, max) {
  		return Math.random() * (max - min) + min;
	}

	function drawDinosaur(options) {
		var context = options.context,
			x = options.left,
			y = options.bottom;

		// background spacer
		context.fillStyle = backgroundColour;
		context.fillRect(x + 7, y - 14, 22, 18);

		// Dino!
		context.fillStyle = defaultColour;

		// tail
		context.fillRect(x, y - 36, 2, 16);
		context.fillRect(x + 2, y - 32, 2, 16);
		context.fillRect(x + 4, y - 30, 2, 16);
		context.fillRect(x + 6, y - 28, 2, 16);
		context.fillRect(x + 8, y - 28, 2, 18);
		context.fillRect(x + 10, y - 30, 2, 22);
		context.fillRect(x + 12, y - 32, 4, 26);
		context.fillRect(x + 16, y - 34, 4, 26);
		context.fillRect(x + 20, y - 36, 4, 30);
		context.fillRect(x + 24, y - 38, 2, 30);
		context.fillRect(x + 26, y - 38, 2, 28);
		context.fillRect(x + 28, y - 52, 2, 40);

		if (options.wideEyed) {
			context.fillRect(x + 30, y - 54, 6, 2);
			context.fillRect(x + 32, y - 50, 2, 2);
			context.fillRect(x + 30, y - 46, 2, 32);
			context.fillRect(x + 32, y - 46, 2, 30);
			context.fillRect(x + 34, y - 46, 2, 28);
			// context.fillRect(x + 32, y - 54, 2, 4);
			// context.fillRect(x + 32, y - 48, 2, 32);
			// context.fillRect(x + 34, y - 54, 2, 36);
		} else {
			context.fillRect(x + 30, y - 54, 2, 40);
			context.fillRect(x + 32, y - 54, 2, 4);
			context.fillRect(x + 32, y - 48, 2, 32);
			context.fillRect(x + 34, y - 54, 2, 36);
		}

		context.fillRect(x + 36, y - 54, 2, 34);
		context.fillRect(x + 38, y - 54, 2, 20);
		context.fillRect(x + 40, y - 54, 12, 16);
		context.fillRect(x + 52, y - 52, 2, 14);

		if (options.wideEyed) {
			context.fillRect(x + 38, y - 34, 8, 2);
		} else {
			context.fillRect(x + 40, y - 36, 8, 2);
		}

		// arm (singular)
		context.fillRect(x + 36, y - 26, 4, 2);
		context.fillRect(x + 40, y - 26, 2, 4);

		y = options.bottom;
		if (options.backLegUp) {
			y -= 4;
		}
		// back leg
		context.fillRect(x + 12, y, 4, 2);
		context.fillRect(x + 12, y - 6, 2, 8);
		context.fillRect(x + 14, y - 6, 2, 3);
		context.fillRect(x + 16, y - 8, 2, 3);

		y = options.bottom;
		if (options.frontLegUp) {
			y -= 6;
		}

		// front leg
		context.fillRect(x + 22, y, 4, 2);
		context.fillRect(x + 22, y - 6, 2, 8);
	}

	function drawBackground(options) {
		var context = options.context,
			x = 0,
			y = options.height;

		context.fillStyle = defaultColour;

		context.fillRect(x, y - 20, options.width, 1);

		for (var i = bits.length - 1; i >= 0; i--) {
			context.fillRect(options.width - ((bits[i].x + options.offset) % options.width), bits[i].y, bits[i].width, 1);
		}
	}

	function drawCactus(options) {
		var context = options.context,
			x = options.left,
			y = options.bottom,
			scale = options.scale;

		context.fillStyle = defaultColour;

		// center
		var height = 15 * options.centerSize;
		context.fillRect(x + 6 * scale, y - (20 + height) * scale, 6 * scale, height * scale);
		context.fillRect(x + 7 * scale, y - (20 + height + 1) * scale, 4 * scale, 1 * scale);
		context.fillRect(x + 6 * scale, y - 20 * scale, 6 * scale, 20 * scale);

		// left
		height = 15 * options.leftSize;
		context.fillRect(x, y - (15 + height) * scale, 4 * scale, height * scale);
		context.fillRect(x + 1 * scale, y - (15 + height + 1) * scale, 2 * scale, 1 * scale);
		context.fillRect(x + 4 * scale, y - 19 * scale, 4 * scale, 4 * scale);

		// right
		height = 15 * options.rightSize;
		context.fillRect(x + 14 * scale, y - (15 + height) * scale, 4 * scale, height * scale);
		context.fillRect(x + 15 * scale, y - (15 + height + 1) * scale, 2 * scale, 1 * scale);
		context.fillRect(x + 12 * scale, y - 19 * scale, 4 * scale, 4 * scale);
	}

	var lastTick = null,
		jumping = false,
		score = 0,
		ground = canvas.height - 10,
		speed = 0.02; // 10 score per second

	function step(timestamp) {
		if (lastTick) {
			score += (timestamp - lastTick) * speed;

			context.clearRect ( 0, 0, canvas.width, canvas.height);

			var offset = score * 10;

			drawBackground({
				context: context,
				width: canvas.width,
				height: canvas.height,
				offset: offset,
			});

			updateCacti(offset);

			for (var i = 0; i < cacti.length; i++) {
				drawCactus({
					context: context,
					left: cacti[i].x - score * 10,
					bottom: canvas.height - 10,
					scale: cacti[i].scale,
					leftSize: cacti[i].leftSize,
					rightSize: cacti[i].rightSize,
					centerSize: cacti[i].centerSize
				});
			}

			drawDinosaur({
				context: context,
				left: 10,
				bottom: ground,
				backLegUp: Math.floor(score) % 3 === 0,
				frontLegUp: Math.floor(score) % 3 === 1
			});
		}
		lastTick = timestamp;
		window.requestAnimationFrame(step);
	}

	generateBits();
	window.requestAnimationFrame(step);

})(window, document, $);<commit_msg>added better cactus intervals<commit_after>(function(window, document, $) {
	var canvas = document.getElementById("game"),
		context = canvas.getContext("2d"),
		defaultColour = "#444",
		backgroundColour = "#eee",
		bits = [],
		cacti = [],
		nextCactus = 0;

	function generateBits() {
		for (y = canvas.height - 10; y <= canvas.height; y += 8) {
			for (x = 0 + rand(0, 100); x <= canvas.width; x += rand(100, 200)) {
				bits.push({
					x: x,
					y: y,
					width: rand(2, 4)
				});
			}
		}
	}

	function updateCacti(offset) {
		var count = 0,
			buffer = 50; // used to force cacti off the screen

		while (cacti.length > count && cacti[count].x < offset - buffer) {
			count++;
		}

		cacti.splice(0, count);

		if (offset > nextCactus) {
			count = Math.floor(rand(1, 3.9));
			var scale = rand(0.8, 1.5),
				x = canvas.width + offset + buffer;

			while (count--) {
				cacti.push({
					x: x + (count * 20 * scale),
					scale: scale,
					leftSize: rand(0.5, 1.5),
					rightSize: rand(0.5, 1.5),
					centerSize: rand(0.5, 1.5)
				});
			}

			nextCactus = offset + rand(200, canvas.width);
		}
	}

	function rand(min, max) {
  		return Math.random() * (max - min) + min;
	}

	function drawDinosaur(options) {
		var context = options.context,
			x = options.left,
			y = options.bottom;

		// background spacer
		context.fillStyle = backgroundColour;
		context.fillRect(x + 7, y - 14, 22, 18);

		// Dino!
		context.fillStyle = defaultColour;

		// tail
		context.fillRect(x, y - 36, 2, 16);
		context.fillRect(x + 2, y - 32, 2, 16);
		context.fillRect(x + 4, y - 30, 2, 16);
		context.fillRect(x + 6, y - 28, 2, 16);
		context.fillRect(x + 8, y - 28, 2, 18);
		context.fillRect(x + 10, y - 30, 2, 22);
		context.fillRect(x + 12, y - 32, 4, 26);
		context.fillRect(x + 16, y - 34, 4, 26);
		context.fillRect(x + 20, y - 36, 4, 30);
		context.fillRect(x + 24, y - 38, 2, 30);
		context.fillRect(x + 26, y - 38, 2, 28);
		context.fillRect(x + 28, y - 52, 2, 40);

		if (options.wideEyed) {
			context.fillRect(x + 30, y - 54, 6, 2);
			context.fillRect(x + 32, y - 50, 2, 2);
			context.fillRect(x + 30, y - 46, 2, 32);
			context.fillRect(x + 32, y - 46, 2, 30);
			context.fillRect(x + 34, y - 46, 2, 28);
			// context.fillRect(x + 32, y - 54, 2, 4);
			// context.fillRect(x + 32, y - 48, 2, 32);
			// context.fillRect(x + 34, y - 54, 2, 36);
		} else {
			context.fillRect(x + 30, y - 54, 2, 40);
			context.fillRect(x + 32, y - 54, 2, 4);
			context.fillRect(x + 32, y - 48, 2, 32);
			context.fillRect(x + 34, y - 54, 2, 36);
		}

		context.fillRect(x + 36, y - 54, 2, 34);
		context.fillRect(x + 38, y - 54, 2, 20);
		context.fillRect(x + 40, y - 54, 12, 16);
		context.fillRect(x + 52, y - 52, 2, 14);

		if (options.wideEyed) {
			context.fillRect(x + 38, y - 34, 8, 2);
		} else {
			context.fillRect(x + 40, y - 36, 8, 2);
		}

		// arm (singular)
		context.fillRect(x + 36, y - 26, 4, 2);
		context.fillRect(x + 40, y - 26, 2, 4);

		y = options.bottom;
		if (options.backLegUp) {
			y -= 4;
		}
		// back leg
		context.fillRect(x + 12, y, 4, 2);
		context.fillRect(x + 12, y - 6, 2, 8);
		context.fillRect(x + 14, y - 6, 2, 3);
		context.fillRect(x + 16, y - 8, 2, 3);

		y = options.bottom;
		if (options.frontLegUp) {
			y -= 6;
		}

		// front leg
		context.fillRect(x + 22, y, 4, 2);
		context.fillRect(x + 22, y - 6, 2, 8);
	}

	function drawBackground(options) {
		var context = options.context,
			x = 0,
			y = options.height;

		context.fillStyle = defaultColour;

		context.fillRect(x, y - 20, options.width, 1);

		for (var i = bits.length - 1; i >= 0; i--) {
			context.fillRect(options.width - ((bits[i].x + options.offset) % options.width), bits[i].y, bits[i].width, 1);
		}
	}

	function drawCactus(options) {
		var context = options.context,
			x = options.left,
			y = options.bottom,
			scale = options.scale;

		context.fillStyle = defaultColour;

		// center
		var height = 15 * options.centerSize;
		context.fillRect(x + 6 * scale, y - (20 + height) * scale, 6 * scale, height * scale);
		context.fillRect(x + 7 * scale, y - (20 + height + 1) * scale, 4 * scale, 1 * scale);
		context.fillRect(x + 6 * scale, y - 20 * scale, 6 * scale, 20 * scale);

		// left
		height = 15 * options.leftSize;
		context.fillRect(x, y - (15 + height) * scale, 4 * scale, height * scale);
		context.fillRect(x + 1 * scale, y - (15 + height + 1) * scale, 2 * scale, 1 * scale);
		context.fillRect(x + 4 * scale, y - 19 * scale, 4 * scale, 4 * scale);

		// right
		height = 15 * options.rightSize;
		context.fillRect(x + 14 * scale, y - (15 + height) * scale, 4 * scale, height * scale);
		context.fillRect(x + 15 * scale, y - (15 + height + 1) * scale, 2 * scale, 1 * scale);
		context.fillRect(x + 12 * scale, y - 19 * scale, 4 * scale, 4 * scale);
	}

	var lastTick = null,
		jumping = false,
		score = 0,
		ground = canvas.height - 10,
		speed = 0.02; // 10 score per second

	function step(timestamp) {
		if (lastTick) {
			score += (timestamp - lastTick) * speed;

			context.clearRect ( 0, 0, canvas.width, canvas.height);

			var offset = score * 10;

			drawBackground({
				context: context,
				width: canvas.width,
				height: canvas.height,
				offset: offset,
			});

			updateCacti(offset);

			for (var i = 0; i < cacti.length; i++) {
				drawCactus({
					context: context,
					left: cacti[i].x - score * 10,
					bottom: canvas.height - 10,
					scale: cacti[i].scale,
					leftSize: cacti[i].leftSize,
					rightSize: cacti[i].rightSize,
					centerSize: cacti[i].centerSize
				});
			}

			drawDinosaur({
				context: context,
				left: 10,
				bottom: ground,
				backLegUp: Math.floor(score) % 3 === 0,
				frontLegUp: Math.floor(score) % 3 === 1
			});
		}
		lastTick = timestamp;
		window.requestAnimationFrame(step);
	}

	generateBits();
	window.requestAnimationFrame(step);

})(window, document, $);<|endoftext|>