<commit_before>window.onload = function() {

    var canvas = document.getElementById("graph"),
        graph = {
            ctx: canvas.getContext('2d'),
            dim: canvas.height
        };
        graph.imageData = graph.ctx.getImageData(0, 0, graph.dim, graph.dim);
        graph.numPixels = graph.dim * graph.dim;

    var cmath = math,
        pi = Math.PI;
        // phase = 0;

    function pixelIndexToCoord(pixel) {
        return [pixel % graph.dim, Math.floor(pixel / graph.dim)];
    }
    function coordToPixelIndex(x, y) {
        return y * graph.dim + x;
    }

    function degToRad(angle) {
        return (angle / 180) * pi;
    }

    function isComplex(number) {
        if (typeof number === "number") {
            return false;
        } else if (typeof number === "object"){
            return true;
        } else {
            throw new TypeError("isComplex() only takes type `number` or `object`");
        }
    }

    function cosSnell(n1, n2, thetaI) {
        var cosSquaredThetaT = 1 - Math.pow((n1/n2) * Math.sin(thetaI), 2);
        if (cosSquaredThetaT < 0) {
            return cmath.sqrt(cosSquaredThetaT);
        } else {
            return Math.sqrt(cosSquaredThetaT);
        }
    }

    var Boundary = {
        init: function(angle, n1, n2, polarisation, interference) {
            this.theta = degToRad(angle);
            this.n1 = n1;
            this.n2 = n2;
            this.polarisation = polarisation;

            this.frame = 0;
            this.numFrames = 63; //126
        },

        createFrame: function() {
            this.data = new Float32Array(4*graph.numPixels);
            var incident = this.createWave(this.theta, amplitude=1, material=1);
            this.transmit();
            if (interference === true && this.n1 !== this.n2) {
                this.reflect();
            }
            this.convertToColorVals();
        },

        transmit: function() {
            var cosThetaI = Math.cos(this.theta),
                cosThetaT = cosSnell(this.n1, this.n2, this.theta),
                plotThetaT;
            if (isComplex(cosThetaT)) {
                console.log("Total internal reflection");
                plotThetaT = cmath.acos(cosThetaT);
                cosThetaT = 0;
            } else {
                plotThetaT = Math.acos(cosThetaT);
            }

            var t;
            if (this.polarisation === "s") {
                t = (2 * this.n1 * cosThetaI) / (this.n1 * cosThetaI + this.n2 * cosThetaT);
            } else {
                t = (2 * this.n1 * cosThetaI) / (this.n1 * cosThetaT + this.n2 * cosThetaI);
            }
            return this.createWave(theta=plotThetaT, amplitude=t, material=2);
        },

        reflect: function() {
            if (this.n1 === this.n2) {
                console.log("Refractive indices equal - no reflection");
                return;
            }
            var cosThetaI = Math.cos(this.theta),
                thetaR = this.theta,
                cosThetaT = cosSnell(this.n1, this.n2, this.theta),
                plotThetaR = -thetaR;

            if (isComplex(cosThetaT)) {
                cosThetaT = 0;
            }

            var r;
            if (this.polarisation === "s") {
                r = (this.n1 * cosThetaI - this.n2 * cosThetaT) / (this.n1 * cosThetaI + this.n2 * cosThetaT);
            } else {
                r = (this.n1 * cosThetaT - this.n2 * cosThetaI) / (this.n1 * cosThetaT + this.n2 * cosThetaI);
            }
            return this.createWave(theta=plotThetaR, amplitude=r, material=1, reversePhase=true);
        },

        createWave: function(theta, amplitude, material, reversePhase) {
            if (typeof reversePhase === "undefined") reversePhase = false;

            var xMin, xMax, n;
            if (material === 1) {
                xMin = 0;
                xMax = graph.dim / 2;
                n = this.n1;
            } else if (material === 2) {
                xMin = graph.dim / 2;
                xMax = graph.dim;
                n = this.n2;
            } else {
                throw new RangeError("material must be 1 or 2");
            }
            var yMin = 0,
                yMax = graph.dim;

                // xRange = np.range(xMin, xMax),
                // yRange = np.range(0, graph.dim),

            function waveFunc(x, y, phase) {
                x = 2*x/graph.dim - 1;
                y = -2*y/graph.dim - 1;

                if (isComplex(theta)) {
                    // Fast complex sin & cos:
                    var p = theta.re,
                        q = theta.im,

                        cosP = Math.cos(p),
                        sinP = Math.sin(p),
                        coshQ = Math.cosh(q),
                        sinhQ = Math.sinh(q),

                        a = cosP * coshQ,
                        b = -sinP * sinhQ,
                        c = -sinP * coshQ,
                        d = -cosP * sinhQ,
                        g = 8*pi*n;
                    return amplitude * Math.cos( g * (a*x + c*y - phase) ) * Math.exp( -g * (b*x + d*y) );
                } else {
                    var k_x = n * Math.cos(theta),
                        k_y = -n * Math.sin(theta);

                    if (reversePhase === false) {
                        return amplitude * Math.cos( 8*pi * (k_x*x + k_y*y - phase) );
                    } else if (reversePhase === true) {
                        return amplitude * Math.cos( 8*pi * (k_x*x + k_y*y + phase) );
                    } else {
                        throw new TypeError("`createWave()` arg `reversePhase` must be of type `bool`");
                    }
                }
            }

            // xy =
            var phase = 2*this.frame/graph.dim;
            for (var x = xMin; x < xMax; x++) {
                for (var y = yMin; y < yMax; y++) {
                    var pixel = coordToPixelIndex(x, y);
                    this.data[4*pixel] += waveFunc(x, y, phase);
                    // this.data[4*pixel + 1] += waveVal;
                    // this.data[4*pixel + 2] += waveVal;
                    this.data[4*pixel + 3] = 255;
                }
            }
            // console.log("Calculated frame data");
            return;
        },

        /** Return intensity value between 0-255 for a given pixel */
        convertToColorVals: function() {
            for (var pixel = 0; pixel < graph.numPixels; pixel++) {
                this.data[4*pixel] = this.data[4*pixel]*127.5 + 127.5;
            }
            // console.log("Converted colour values");
        },

        updatePlot: function() {
            this.createFrame();
            graph.imageData.data.set(this.data);
            // console.log(this.data[4000], graph.imageData.data[4000]);
            graph.ctx.putImageData(graph.imageData, 0, 0);
            this.frame++;
            if (this.frame === this.numFrames) {
                console.log("Reached end of frames");
                this.frame = 0;
            }
            // console.log("Updated plot;");
            // console.log(graph.imageData);
            // console.log(this.data);
        }

    };

    Boundary.init(angle=43, n1=1, n2=1.5, polarisation="s", interference=true);
    Boundary.updatePlot();

    var animIntervalID = window.setInterval(function() {
        Boundary.updatePlot();
    }, 50);
    setTimeout(function() {
        clearInterval(animIntervalID);
    }, 15000);

    // console.log(np.range(-2, 15));

};
<commit_msg>Refactor code slightly<commit_after>window.onload = function() {

    var canvas = document.getElementById("graph"),
        graph = {
            ctx: canvas.getContext('2d'),
            dim: canvas.height
        };
        graph.imageData = graph.ctx.getImageData(0, 0, graph.dim, graph.dim);
        graph.numPixels = graph.dim * graph.dim;

    var cmath = math,
        pi = Math.PI;
        // phase = 0;

    function pixelIndexToCoord(pixel) {
        return [pixel % graph.dim, Math.floor(pixel / graph.dim)];
    }
    function coordToPixelIndex(x, y) {
        return y * graph.dim + x;
    }

    function degToRad(angle) {
        return (angle / 180) * pi;
    }

    function isComplex(number) {
        if (typeof number === "number") {
            return false;
        } else if (typeof number === "object"){
            return true;
        } else {
            throw new TypeError("isComplex() only takes type `number` or `object`");
        }
    }

    function cosSnell(n1, n2, thetaI) {
        var cosSquaredThetaT = 1 - Math.pow((n1/n2) * Math.sin(thetaI), 2);
        if (cosSquaredThetaT < 0) {
            return cmath.sqrt(cosSquaredThetaT);
        } else {
            return Math.sqrt(cosSquaredThetaT);
        }
    }

    var Boundary = {
        init: function(angle, n1, n2, polarisation, interference) {
            this.theta = degToRad(angle);
            this.n1 = n1;
            this.n2 = n2;
            this.polarisation = polarisation;

            this.numSines = 2 * 4;

            this.frame = 0;
            this.numFrames = Math.round(graph.dim/this.numSines);//63; //126

            var animIntervalID = window.setInterval(function() {
                this.updatePlot();
            }.bind(this), 100);
            setTimeout(function() {
                clearInterval(animIntervalID);
            }, 15000);
        },

        createFrame: function() {
            this.data = new Float32Array(4*graph.numPixels);
            var incident = this.createWave(this.theta, amplitude=1, material=1);
            this.transmit();
            if (interference === true && this.n1 !== this.n2) {
                this.reflect();
            }
            this.convertToColorVals();
        },

        transmit: function() {
            var cosThetaI = Math.cos(this.theta),
                cosThetaT = cosSnell(this.n1, this.n2, this.theta),
                plotThetaT;
            if (isComplex(cosThetaT)) {
                console.log("Total internal reflection");
                plotThetaT = cmath.acos(cosThetaT);
                cosThetaT = 0;
            } else {
                plotThetaT = Math.acos(cosThetaT);
            }

            var t;
            if (this.polarisation === "s") {
                t = (2 * this.n1 * cosThetaI) / (this.n1 * cosThetaI + this.n2 * cosThetaT);
            } else {
                t = (2 * this.n1 * cosThetaI) / (this.n1 * cosThetaT + this.n2 * cosThetaI);
            }
            return this.createWave(theta=plotThetaT, amplitude=t, material=2);
        },

        reflect: function() {
            if (this.n1 === this.n2) {
                console.log("Refractive indices equal - no reflection");
                return;
            }
            var cosThetaI = Math.cos(this.theta),
                thetaR = this.theta,
                cosThetaT = cosSnell(this.n1, this.n2, this.theta),
                plotThetaR = -thetaR;

            if (isComplex(cosThetaT)) {
                cosThetaT = 0;
            }

            var r;
            if (this.polarisation === "s") {
                r = (this.n1 * cosThetaI - this.n2 * cosThetaT) / (this.n1 * cosThetaI + this.n2 * cosThetaT);
            } else {
                r = (this.n1 * cosThetaT - this.n2 * cosThetaI) / (this.n1 * cosThetaT + this.n2 * cosThetaI);
            }
            return this.createWave(theta=plotThetaR, amplitude=r, material=1, reversePhase=true);
        },

        createWave: function(theta, amplitude, material, reversePhase) {
            if (typeof reversePhase === "undefined") reversePhase = false;

            var xMin, xMax, n;
            if (material === 1) {
                xMin = 0;
                xMax = graph.dim / 2;
                n = this.n1;
            } else if (material === 2) {
                xMin = graph.dim / 2;
                xMax = graph.dim;
                n = this.n2;
            } else {
                throw new RangeError("material must be 1 or 2");
            }
            var yMin = 0,
                yMax = graph.dim,
                numSines = this.numSines;

                // xRange = np.range(xMin, xMax),
                // yRange = np.range(0, graph.dim),

            function waveFunc(x, y, phase) {
                x = 2*x/graph.dim - 1;
                y = -2*y/graph.dim - 1;
                // console.log(foo);
                if (isComplex(theta)) {
                    // Fast complex sin & cos:
                    var p = theta.re,
                        q = theta.im,

                        cosP = Math.cos(p),
                        sinP = Math.sin(p),
                        coshQ = Math.cosh(q),
                        sinhQ = Math.sinh(q),

                        a = cosP * coshQ,
                        b = -sinP * sinhQ,
                        c = -sinP * coshQ,
                        d = -cosP * sinhQ,
                        g = numSines*pi*n;
                    return amplitude * Math.cos( g * (a*x + c*y - phase) ) * Math.exp( -g * (b*x + d*y) );
                } else {
                    var k_x = n * Math.cos(theta),
                        k_y = -n * Math.sin(theta);

                    if (reversePhase === false) {
                        return amplitude * Math.cos( numSines*pi * (k_x*x + k_y*y - phase) );
                    } else if (reversePhase === true) {
                        return amplitude * Math.cos( numSines*pi * (k_x*x + k_y*y + phase) );
                    } else {
                        throw new TypeError("`createWave()` arg `reversePhase` must be of type `bool`");
                    }
                }
            }

            // xy =
            var phase = 2*this.frame/graph.dim;
            for (var x = xMin; x < xMax; x++) {
                for (var y = yMin; y < yMax; y++) {
                    var pixel = coordToPixelIndex(x, y);
                    this.data[4*pixel] += waveFunc(x, y, phase);
                    // this.data[4*pixel + 1] += waveVal;
                    // this.data[4*pixel + 2] += waveVal;
                    this.data[4*pixel + 3] = 255;
                }
            }
            // console.log("Calculated frame data");
            return;
        },

        /** Return intensity value between 0-255 for a given pixel */
        convertToColorVals: function() {
            for (var pixel = 0; pixel < graph.numPixels; pixel++) {
                this.data[4*pixel] = this.data[4*pixel]*127.5 + 127.5;
            }
            // console.log("Converted colour values");
        },

        updatePlot: function() {
            this.createFrame();
            graph.imageData.data.set(this.data);
            // console.log(this.data[4000], graph.imageData.data[4000]);
            graph.ctx.putImageData(graph.imageData, 0, 0);
            this.frame++;
            if (this.frame === this.numFrames) {
                console.log("Reached end of frames");
                this.frame = 0;
            }
            // console.log("Updated plot;");
            // console.log(graph.imageData);
            // console.log(this.data);
        }

    };

    Boundary.init(angle=50, n1=1.5, n2=1., polarisation="s", interference=true);
    // Boundary.updatePlot();

    // console.log(np.range(-2, 15));

};
<|endoftext|>


<commit_before>var numbers = require('../numbers');
var calculus = exports;

/**
 * Calculate point differential for a specified function at a
 * specified point.  For functions of one variable.
 *
 * @param {Function} math function to be evaluated
 * @param {Number} point to be evaluated
 * @return {Number} result
 */
calculus.pointDiff = function (func, point) {
  var a = func(point - Number.MIN_VALUE);
  var b = func(point + Number.MIN_VALUE);

  return (b - a) / (Number.MIN_VALUE*2);
};

/**
 * Calculate riemann sum for a specified, one variable, function
 * from a starting point, to a finishing point, with n divisions.
 *
 * @param {Function} math function to be evaluated
 * @param {Number} point to initiate evaluation
 * @param {Number} point to complete evaluation
 * @param {Number} quantity of divisions
 * @param {Function} (Optional) Function that returns which value
 * to sample on each interval; if none is provided, left endpoints
 * will be used.
 * @return {Number} result
 */
calculus.riemann = function (func, start, finish, n, sampler) {
  var inc = (finish - start) / n, totalHeight = 0, i;

  if (typeof sampler === 'function') {
    for (i = start; i < finish; i += inc) {
      totalHeight += func(sampler(i, i + inc));
    }
  } else {
    for (i = start; i < finish; i += inc) {
      totalHeight += func(i);
    }
  }

  return totalHeight * inc;
};

/**
 * Helper function in calculating integral of a function
 * from a to b using simpson quadrature.
 *
 * @param {Function} math function to be evaluated
 * @param {Number} point to initiate evaluation
 * @param {Number} point to complete evaluation
 * @return {Number} evaluation
 */
function simpsonDef (func, a, b) {
  var c = (a + b) / 2;
  var d = Math.abs(b - a) / 6;
  return d * (func(a) + 4 * func(c) + func(b));
}

/**
 * Helper function in calculating integral of a function
 * from a to b using simpson quadrature.  Manages recursive
 * investigation, handling evaluations within an error bound.
 *
 * @param {Function} math function to be evaluated
 * @param {Number} point to initiate evaluation
 * @param {Number} point to complete evaluation
 * @param {Number} total value
 * @param {Number} Error bound (epsilon)
 * @return {Number} recursive evaluation of left and right side
 */
function simpsonRecursive (func, a, b, whole, eps) {
  var c = a + b,
      left = simpsonDef(func, a, c),
      right = simpsonDef(func, c, b);

  if (Math.abs(left + right - whole) <= 15 * eps) {
    return left + right + (left + right - whole) / 15;
  } else {
    return simpsonRecursive(func, a, c, eps/2, left) + simpsonRecursive(func, c, b, eps / 2, right);
  }
}

/**
 * Evaluate area under a curve using adaptive simpson quadrature.
 *
 * @param {Function} math function to be evaluated
 * @param {Number} point to initiate evaluation
 * @param {Number} point to complete evaluation
 * @param {Number} Optional error bound (epsilon);
 * global error bound will be used as a fallback.
 * @return {Number} area underneath curve
 */
calculus.adaptiveSimpson = function (func, a, b, eps) {
  eps = (typeof eps === "undefined") ? numbers.EPSILON : eps;

  return simpsonRecursive(func, a, b, simpsonDef(func, a, b), eps);
};

/**
 * Calculate limit of a function at a given point.  Can approach from
 * left, middle, or right.
 *
 * @param {Function} math function to be evaluated
 * @param {Number} point to evaluate
 * @param {String} approach to limit
 * @return {Number} limit
 */
calculus.limit = function (func, point, approach) {
  if (approach === 'left') {
    return func(point - Number.MIN_VALUE);
  } else if (approach === 'right') {
    return func(point + Number.MIN_VALUE);
  } else if (approach === 'middle') {
    return (calculus.limit(func, point, 'left') + calculus.limit(func, point, 'right')) / 2;
  } else {
    throw new Error('Approach not provided');
  }
};
<commit_msg>Fix calculus.pointDiff test<commit_after>var numbers = require('../numbers');
var calculus = exports;

/**
 * Calculate point differential for a specified function at a
 * specified point.  For functions of one variable.
 *
 * @param {Function} math function to be evaluated
 * @param {Number} point to be evaluated
 * @return {Number} result
 */
calculus.pointDiff = function (func, point) {
  var a = func(point - 1e-15);
  var b = func(point + 1e-15);

  return (b - a) / (Number.MIN_VALUE*2);
};

/**
 * Calculate riemann sum for a specified, one variable, function
 * from a starting point, to a finishing point, with n divisions.
 *
 * @param {Function} math function to be evaluated
 * @param {Number} point to initiate evaluation
 * @param {Number} point to complete evaluation
 * @param {Number} quantity of divisions
 * @param {Function} (Optional) Function that returns which value
 * to sample on each interval; if none is provided, left endpoints
 * will be used.
 * @return {Number} result
 */
calculus.riemann = function (func, start, finish, n, sampler) {
  var inc = (finish - start) / n, totalHeight = 0, i;

  if (typeof sampler === 'function') {
    for (i = start; i < finish; i += inc) {
      totalHeight += func(sampler(i, i + inc));
    }
  } else {
    for (i = start; i < finish; i += inc) {
      totalHeight += func(i);
    }
  }

  return totalHeight * inc;
};

/**
 * Helper function in calculating integral of a function
 * from a to b using simpson quadrature.
 *
 * @param {Function} math function to be evaluated
 * @param {Number} point to initiate evaluation
 * @param {Number} point to complete evaluation
 * @return {Number} evaluation
 */
function simpsonDef (func, a, b) {
  var c = (a + b) / 2;
  var d = Math.abs(b - a) / 6;
  return d * (func(a) + 4 * func(c) + func(b));
}

/**
 * Helper function in calculating integral of a function
 * from a to b using simpson quadrature.  Manages recursive
 * investigation, handling evaluations within an error bound.
 *
 * @param {Function} math function to be evaluated
 * @param {Number} point to initiate evaluation
 * @param {Number} point to complete evaluation
 * @param {Number} total value
 * @param {Number} Error bound (epsilon)
 * @return {Number} recursive evaluation of left and right side
 */
function simpsonRecursive (func, a, b, whole, eps) {
  var c = a + b,
      left = simpsonDef(func, a, c),
      right = simpsonDef(func, c, b);

  if (Math.abs(left + right - whole) <= 15 * eps) {
    return left + right + (left + right - whole) / 15;
  } else {
    return simpsonRecursive(func, a, c, eps/2, left) + simpsonRecursive(func, c, b, eps / 2, right);
  }
}

/**
 * Evaluate area under a curve using adaptive simpson quadrature.
 *
 * @param {Function} math function to be evaluated
 * @param {Number} point to initiate evaluation
 * @param {Number} point to complete evaluation
 * @param {Number} Optional error bound (epsilon);
 * global error bound will be used as a fallback.
 * @return {Number} area underneath curve
 */
calculus.adaptiveSimpson = function (func, a, b, eps) {
  eps = (typeof eps === "undefined") ? numbers.EPSILON : eps;

  return simpsonRecursive(func, a, b, simpsonDef(func, a, b), eps);
};

/**
 * Calculate limit of a function at a given point.  Can approach from
 * left, middle, or right.
 *
 * @param {Function} math function to be evaluated
 * @param {Number} point to evaluate
 * @param {String} approach to limit
 * @return {Number} limit
 */
calculus.limit = function (func, point, approach) {
  if (approach === 'left') {
    return func(point - Number.MIN_VALUE);
  } else if (approach === 'right') {
    return func(point + Number.MIN_VALUE);
  } else if (approach === 'middle') {
    return (calculus.limit(func, point, 'left') + calculus.limit(func, point, 'right')) / 2;
  } else {
    throw new Error('Approach not provided');
  }
};
<|endoftext|>


<commit_before>/*
 * Each data file in the database consists of an array of entry records
 * with the following format:
 *
 *   Magic        2 bytes             Always equal to the value "ET"
 *   Data size    4 bytes             In big endian format.
 *   CRC32        4 bytes             Checksum of the data.
 *   Data         'Data size' bytes   The actual data.
 */

var fs   = require('fs');
var path = require('path');
var child_process = require('child_process');
var TimeEntry     = require('./time_entry.js');
var Group         = require('./group.js');
var BufferUtils   = require('./buffer_utils.js');
var CRC32         = require('./crc32.js');

const MAX_SIZE = 1024 * 1024;
const MAX_SIZE_DESCRIPTION = "1 MB";

const ENTRY_MAGIC       = new Buffer("ET");
const SIZE_ENTRY_SIZE   = 4;
const CRC32_BINARY_SIZE = 4;
const HEADER_SIZE       = ENTRY_MAGIC.length + SIZE_ENTRY_SIZE + CRC32_BINARY_SIZE;

function Database(dbpath) {
	this.dbpath = dbpath;
	this.groupCount = 0;
	this.groups = {};
	this.ntimeEntriesBeingCreated = 0;
	this.reloading = false;
	this.checksummer = new CRC32.CRC32();
}

Database.prototype.reload = function(callback) {
	var state = {
		self: this,
		aborted: false,
		operations: 1,
		groupCount: 0,
		groups: {}
	};

	function decOperations() {
		if (!state.aborted) {
			state.operations--;
			if (state.operations == 0) {
				state.self.groupCount = state.groupCount;
				state.self.groups = state.groups;
				state.self.reloading = false;
				callback();
			}
		}
	}

	function scanTimeDirectory(dayTimestamp, timePath, group, err, stats) {
		if (state.aborted) {
			return;
		} else if (err) {
			state.aborted = true;
			callback(new Error("Cannot stat directory " + timePath + ": " + err));
			return;
		}

		if (stats.isDirectory()) {
			var dataPath = path.join(timePath, "data");
			state.operations++;
			fs.stat(dataPath, function(err, stats) {
				var timeEntry, stream;
				if (state.aborted) {
					return;
				} else if (err) {
					if (err.code == 'ENOENT') {
						try {
							stream = openDataFile(dataPath);
						} catch (err) {
							state.aborted = true;
							callback(new Error("Cannot create data file " +
								dataPath + ": " + err));
							return;
						}
						timeEntry = new TimeEntry.TimeEntry(dayTimestamp,
							timePath, stream, 0);
						group.timeEntryCount++;
						group.timeEntries[dayTimestamp] = timeEntry;
						decOperations();
					} else {
						state.aborted = true;
						callback(new Error("Cannot stat data file " +
							dataPath + ": " + err.message));
					}
				} else if (!stats.isFile()) {
					state.aborted = true;
					callback(new Error("Data file " + dataPath + " is not a file"));
				} else {
					try {
						stream = openDataFile(dataPath);
					} catch (err) {
						state.aborted = true;
						callback(new Error("Cannot create data file " +
							dataPath + ": " + err));
						return;
					}
					timeEntry = new TimeEntry.TimeEntry(dayTimestamp,
						timePath, stream, stats.size);
					group.timeEntryCount++;
					group.timeEntries[dayTimestamp] = timeEntry;
					decOperations();
				}
			});
		}
		decOperations();
	}

	function scanGroupDirectory(groupPath, err, stats) {
		if (state.aborted) {
			return;
		} else if (err) {
			state.aborted = true;
			callback(new Error("Cannot stat " + groupPath + ": " + err));
			return;
		}

		if (stats.isDirectory()) {
			state.operations++;
			fs.readdir(groupPath, function(err, files) {
				if (state.aborted) {
					return;
				} else if (err) {
					state.aborted = true;
					callback(new Error("Cannot read directory " +
						groupPath + ": " + err));
					return;
				}

				var groupName = path.basename(groupPath);
				var group = new Group.Group(groupName, groupPath);
				state.groupCount++;
				state.groups[groupName] = group;

				var i, timePath;
				for (i = 0; i < files.length; i++) {
					if (files[i][0] == '.' || !looksLikeTimestamp(files[i])) {
						continue;
					}
					timePath = path.join(groupPath, files[i]);
					state.operations++;
					fs.stat(timePath, scanTimeDirectory.bind(state.this,
						parseInt(files[i]), timePath, group));
				}
				decOperations();
			});
		}
		decOperations();
	}

	this.reloading = true;
	this.operations++;
	fs.readdir(this.dbpath, function(err, files) {
		if (err) {
			callback(new Error("Cannot read directory " + state.self.dbpath +
				": " + err.message));
			return;
		}

		var i, groupPath;
		for (i = 0; i < files.length; i++) {
			if (files[i][0] == '.') {
				continue;
			}

			groupPath = path.join(state.self.dbpath, files[i]);
			state.operations++;
			fs.stat(groupPath, scanGroupDirectory.bind(state.self, groupPath));
		}
		decOperations();
	});
}

Database.prototype.findOrCreateGroup = function(groupName) {
	if (this.reloading) {
		// I don't want to write code for handling concurrency issues.
		// Just don't support this until there's a need for live reload.
		throw Error("Cannot perform this operation while reloading");
	} else if (!Group.validateGroupName(groupName)) {
		throw new Error('Invalid group name');
	}

	var group = this.groups[groupName];
	if (!group) {
		var groupPath = path.join(this.dbpath, groupName);
		try {
			fs.mkdirSync(groupPath, 0755);
		} catch (err) {
			if (err.code != 'EEXIST') {
				throw err;
			}
		}
		group = new Group.Group(groupName, groupPath);
		this.groups[groupName] = group;
	}
	return group;
}

Database.prototype.findOrCreateTimeEntry = function(groupName, dayTimestamp) {
	var group = this.findOrCreateGroup(groupName);
	var timeEntry = group.timeEntries[dayTimestamp];
	if (!timeEntry) {
		var timePath = path.join(this.dbpath, groupName, dayTimestamp + "");
		var dataPath = path.join(timePath, "data");
		var size;

		try {
			fs.mkdirSync(timePath, 0700);
			size = 0;
		} catch (err) {
			if (err.code == 'EEXIST') {
				try {
					size = fs.statSync(dataPath).size;
				} catch (err) {
					if (err.code == 'ENOENT') {
						size = 0;
					} else {
						throw err;
					}
				}
			} else {
				throw err;
			}
		}

		var stream = openDataFile(dataPath);
		group.timeEntryCount++;
		timeEntry = new TimeEntry.TimeEntry(dayTimestamp,
			timePath, stream, size);
		group.timeEntries[dayTimestamp] = timeEntry;
	}
	return timeEntry;
}

Database.prototype.get = function(groupName, dayTimestamp, offset, callback) {
	var timeEntry;
	try {
		timeEntry = this.findOrCreateTimeEntry(groupName, dayTimestamp);
	} catch (err) {
		callback(err);
		return;
	}

	if (timeEntry.dataFileSize < offset + HEADER_SIZE) {
		// Invalid offset.
		callback('not-found');
	} else {
		// Preallocate slightly larger buffer so that we don't have to allocate two
		// buffers in case the entry is < ~4 KB. The '- 3 * 8' is to account for
		// malloc overhead so that a single allocation fits in a single page.
		console.assert(HEADER_SIZE <= 1024 * 4 - 3 * 8);
		var self = this;
		var buf = new Buffer(1024 * 4 - 3 * 8);
		timeEntry.incFdUsage();
		fs.read(timeEntry.stream.fd, buf, 0, HEADER_SIZE, offset, function(err, bytesRead) {
			var i, size, storedChecksum;

			if (err) {
				timeEntry.decFdUsage();
				callback(err);
				return;
			} else if (bytesRead != HEADER_SIZE) {
				// Invalid offset or corrupt file.
				timeEntry.decFdUsage();
				callback('not-found');
				return;
			}

			// Check magic.
			for (i = 0; i < ENTRY_MAGIC.length; i++) {
				if (buf[i] != ENTRY_MAGIC[i]) {
					// Invalid offset or corrupt file.
					timeEntry.decFdUsage();
					callback('not-found');
					return;
				}
			}

			// Check size.
			size = BufferUtils.bufferToUint(buf, ENTRY_MAGIC.length);
			if (size > MAX_SIZE) {
				// Probably corrupt file..
				timeEntry.decFdUsage();
				callback('not-found');
				return;
			}

			storedChecksum = BufferUtils.bufferToUint(buf,
				ENTRY_MAGIC.length + SIZE_ENTRY_SIZE);

			// TODO: prevent TimeEntry.destroy() from closing
			// file descriptor until done.
			if (size > buf.length) {
				buf = new Buffer(size);
			}
			fs.read(timeEntry.stream.fd, buf, 0, size, offset + HEADER_SIZE,
				function(err, bytesRead)
			{
				timeEntry.decFdUsage();

				if (err) {
					callback(err);
					return;
				} else if (bytesRead != size) {
					// What's going on?
					callback('not-found');
					return;
				}

				self.checksummer.reset();
				self.checksummer.update(buf, 0, size);
				self.checksummer.finalize();

				if (self.checksummer.value == storedChecksum) {
					if (buf.length != size) {
						buf = buf.slice(0, size);
					}
					callback(undefined, buf);
				} else {
					// Probably corrupt file.
					callback('not-found');
				}
			});
		});
	}
}

Database.prototype.add = function(groupName, dayTimestamp, buffers, checksumBuffer, callback) {
	console.assert(checksumBuffer.length == CRC32_BINARY_SIZE);
	var timeEntry = this.findOrCreateTimeEntry(groupName, dayTimestamp);
	var flushed, i, totalSize = 0;
	var prevOffset = timeEntry.dataFileSize;

	for (i = 0; i < buffers.length; i++) {
		totalSize += buffers[i].length;
	}
	console.assert(totalSize < MAX_SIZE);

	function written(err) {
		if (err) {
			callback(err);
		} else {
			callback(undefined, prevOffset);
		}
	}

	var buf = new Buffer(HEADER_SIZE);
	ENTRY_MAGIC.copy(buf);
	BufferUtils.uintToBuffer(totalSize, buf, ENTRY_MAGIC.length);
	checksumBuffer.copy(buf, ENTRY_MAGIC.length + SIZE_ENTRY_SIZE);
	flushed = timeEntry.stream.write(buf);
	timeEntry.dataFileSize += HEADER_SIZE;
	console.assert(!flushed, "Code assumes that file I/O is never flushed immediately");

	for (i = 0; i < buffers.length; i++) {
		flushed = timeEntry.stream.write(buffers[i],
			(i == buffers.length - 1) ? written : undefined);
		timeEntry.dataFileSize += buffers[i].length;
		console.assert(!flushed, "Code assumes that file I/O is never flushed immediately");
	}
}

Database.prototype.remove = function(groupName, dayTimestamp, callback) {
	console.assert(Group.validateGroupName(groupName));
	var group = this.groups[groupName];
	if (!group) {
		callback();
		return;
	}

	var self = this;
	var newFilename;

	if (dayTimestamp) {
		var dayTimestampsToRemove = [];
		var dirsToRemove = [];
		var dts, i, timeEntry;

		for (dts in group.timeEntries) {
			if (parseInt(dts) < dayTimestamp) {
				dayTimestampsToRemove.push(dts);
			}
		}

		function deleteNextDir() {
			var dir = dirsToRemove.pop();
			if (dir) {
				var child = child_process.spawn('rm', ['-rf', dir]);
				child.on('exit', deleteNextDir);
			}
		}

		for (i = 0; i < dayTimestampsToRemove.length; i++) {
			dts = dayTimestampsToRemove[i];
			timeEntry = group.timeEntries[dts];
			try {
				newFilename = renameToHidden(timeEntry.path);
			} catch (err) {
				deleteNextDir();
				callback(err);
				return;
			}
			timeEntry.destroy();
			group.timeEntryCount--;
			delete group.timeEntries[dts];
			dirsToRemove.push(newFilename);
		}

		deleteNextDir();
		callback();

	} else {
		try {
			newFilename = renameToHidden(group.path);
		} catch (err) {
			callback(err);
			return;
		}
		this.groupCount--;
		delete this.groups[groupName];
		group.destroy();
		child_process.spawn('rm', ['-rf', newFilename]);
		callback();
	}
}

function looksLikeTimestamp(name) {
	return name[0] >= '0' && name[0] <= '9' && parseInt(name) == name;
}

function openDataFile(filename, callback) {
	return fs.createWriteStream(filename, {
		flags: 'a+',
		encoding: null,
		mode: 0600,
		fd: fs.openSync(filename, 'a+', 0600)
	});
}

function renameToHidden(filename, callback) {
	var dirname = path.dirname(filename);
	var basename = path.basename(filename);
	var newFilename;

	for (i = 0; i < 1000; i++) {
		newFilename = path.join(dirname, "._" + basename + '-' + i);
		try {
			fs.renameSync(filename, newFilename);
			return newFilename;
		} catch (err) {
			if (err.code != 'ENOTEMPTY') {
				throw err;
			}
		}
		return
	}
	// TODO: clear stale renames upon reloading
	throw Error("Cannot find a suitable filename to rename to");
}

exports.Database = Database;
exports.MAX_SIZE = MAX_SIZE;
exports.MAX_SIZE_DESCRIPTION = MAX_SIZE_DESCRIPTION;
<commit_msg>We should redirect child process output.<commit_after>/*
 * Each data file in the database consists of an array of entry records
 * with the following format:
 *
 *   Magic        2 bytes             Always equal to the value "ET"
 *   Data size    4 bytes             In big endian format.
 *   CRC32        4 bytes             Checksum of the data.
 *   Data         'Data size' bytes   The actual data.
 */

var fs   = require('fs');
var path = require('path');
var child_process = require('child_process');
var TimeEntry     = require('./time_entry.js');
var Group         = require('./group.js');
var BufferUtils   = require('./buffer_utils.js');
var CRC32         = require('./crc32.js');

const MAX_SIZE = 1024 * 1024;
const MAX_SIZE_DESCRIPTION = "1 MB";

const ENTRY_MAGIC       = new Buffer("ET");
const SIZE_ENTRY_SIZE   = 4;
const CRC32_BINARY_SIZE = 4;
const HEADER_SIZE       = ENTRY_MAGIC.length + SIZE_ENTRY_SIZE + CRC32_BINARY_SIZE;

function Database(dbpath) {
	this.dbpath = dbpath;
	this.groupCount = 0;
	this.groups = {};
	this.ntimeEntriesBeingCreated = 0;
	this.reloading = false;
	this.checksummer = new CRC32.CRC32();
}

Database.prototype.reload = function(callback) {
	var state = {
		self: this,
		aborted: false,
		operations: 1,
		groupCount: 0,
		groups: {}
	};

	function decOperations() {
		if (!state.aborted) {
			state.operations--;
			if (state.operations == 0) {
				state.self.groupCount = state.groupCount;
				state.self.groups = state.groups;
				state.self.reloading = false;
				callback();
			}
		}
	}

	function scanTimeDirectory(dayTimestamp, timePath, group, err, stats) {
		if (state.aborted) {
			return;
		} else if (err) {
			state.aborted = true;
			callback(new Error("Cannot stat directory " + timePath + ": " + err));
			return;
		}

		if (stats.isDirectory()) {
			var dataPath = path.join(timePath, "data");
			state.operations++;
			fs.stat(dataPath, function(err, stats) {
				var timeEntry, stream;
				if (state.aborted) {
					return;
				} else if (err) {
					if (err.code == 'ENOENT') {
						try {
							stream = openDataFile(dataPath);
						} catch (err) {
							state.aborted = true;
							callback(new Error("Cannot create data file " +
								dataPath + ": " + err));
							return;
						}
						timeEntry = new TimeEntry.TimeEntry(dayTimestamp,
							timePath, stream, 0);
						group.timeEntryCount++;
						group.timeEntries[dayTimestamp] = timeEntry;
						decOperations();
					} else {
						state.aborted = true;
						callback(new Error("Cannot stat data file " +
							dataPath + ": " + err.message));
					}
				} else if (!stats.isFile()) {
					state.aborted = true;
					callback(new Error("Data file " + dataPath + " is not a file"));
				} else {
					try {
						stream = openDataFile(dataPath);
					} catch (err) {
						state.aborted = true;
						callback(new Error("Cannot create data file " +
							dataPath + ": " + err));
						return;
					}
					timeEntry = new TimeEntry.TimeEntry(dayTimestamp,
						timePath, stream, stats.size);
					group.timeEntryCount++;
					group.timeEntries[dayTimestamp] = timeEntry;
					decOperations();
				}
			});
		}
		decOperations();
	}

	function scanGroupDirectory(groupPath, err, stats) {
		if (state.aborted) {
			return;
		} else if (err) {
			state.aborted = true;
			callback(new Error("Cannot stat " + groupPath + ": " + err));
			return;
		}

		if (stats.isDirectory()) {
			state.operations++;
			fs.readdir(groupPath, function(err, files) {
				if (state.aborted) {
					return;
				} else if (err) {
					state.aborted = true;
					callback(new Error("Cannot read directory " +
						groupPath + ": " + err));
					return;
				}

				var groupName = path.basename(groupPath);
				var group = new Group.Group(groupName, groupPath);
				state.groupCount++;
				state.groups[groupName] = group;

				var i, timePath;
				for (i = 0; i < files.length; i++) {
					if (files[i][0] == '.' || !looksLikeTimestamp(files[i])) {
						continue;
					}
					timePath = path.join(groupPath, files[i]);
					state.operations++;
					fs.stat(timePath, scanTimeDirectory.bind(state.this,
						parseInt(files[i]), timePath, group));
				}
				decOperations();
			});
		}
		decOperations();
	}

	this.reloading = true;
	this.operations++;
	fs.readdir(this.dbpath, function(err, files) {
		if (err) {
			callback(new Error("Cannot read directory " + state.self.dbpath +
				": " + err.message));
			return;
		}

		var i, groupPath;
		for (i = 0; i < files.length; i++) {
			if (files[i][0] == '.') {
				continue;
			}

			groupPath = path.join(state.self.dbpath, files[i]);
			state.operations++;
			fs.stat(groupPath, scanGroupDirectory.bind(state.self, groupPath));
		}
		decOperations();
	});
}

Database.prototype.findOrCreateGroup = function(groupName) {
	if (this.reloading) {
		// I don't want to write code for handling concurrency issues.
		// Just don't support this until there's a need for live reload.
		throw Error("Cannot perform this operation while reloading");
	} else if (!Group.validateGroupName(groupName)) {
		throw new Error('Invalid group name');
	}

	var group = this.groups[groupName];
	if (!group) {
		var groupPath = path.join(this.dbpath, groupName);
		try {
			fs.mkdirSync(groupPath, 0755);
		} catch (err) {
			if (err.code != 'EEXIST') {
				throw err;
			}
		}
		group = new Group.Group(groupName, groupPath);
		this.groups[groupName] = group;
	}
	return group;
}

Database.prototype.findOrCreateTimeEntry = function(groupName, dayTimestamp) {
	var group = this.findOrCreateGroup(groupName);
	var timeEntry = group.timeEntries[dayTimestamp];
	if (!timeEntry) {
		var timePath = path.join(this.dbpath, groupName, dayTimestamp + "");
		var dataPath = path.join(timePath, "data");
		var size;

		try {
			fs.mkdirSync(timePath, 0700);
			size = 0;
		} catch (err) {
			if (err.code == 'EEXIST') {
				try {
					size = fs.statSync(dataPath).size;
				} catch (err) {
					if (err.code == 'ENOENT') {
						size = 0;
					} else {
						throw err;
					}
				}
			} else {
				throw err;
			}
		}

		var stream = openDataFile(dataPath);
		group.timeEntryCount++;
		timeEntry = new TimeEntry.TimeEntry(dayTimestamp,
			timePath, stream, size);
		group.timeEntries[dayTimestamp] = timeEntry;
	}
	return timeEntry;
}

Database.prototype.get = function(groupName, dayTimestamp, offset, callback) {
	var timeEntry;
	try {
		timeEntry = this.findOrCreateTimeEntry(groupName, dayTimestamp);
	} catch (err) {
		callback(err);
		return;
	}

	if (timeEntry.dataFileSize < offset + HEADER_SIZE) {
		// Invalid offset.
		callback('not-found');
	} else {
		// Preallocate slightly larger buffer so that we don't have to allocate two
		// buffers in case the entry is < ~4 KB. The '- 3 * 8' is to account for
		// malloc overhead so that a single allocation fits in a single page.
		console.assert(HEADER_SIZE <= 1024 * 4 - 3 * 8);
		var self = this;
		var buf = new Buffer(1024 * 4 - 3 * 8);
		timeEntry.incFdUsage();
		fs.read(timeEntry.stream.fd, buf, 0, HEADER_SIZE, offset, function(err, bytesRead) {
			var i, size, storedChecksum;

			if (err) {
				timeEntry.decFdUsage();
				callback(err);
				return;
			} else if (bytesRead != HEADER_SIZE) {
				// Invalid offset or corrupt file.
				timeEntry.decFdUsage();
				callback('not-found');
				return;
			}

			// Check magic.
			for (i = 0; i < ENTRY_MAGIC.length; i++) {
				if (buf[i] != ENTRY_MAGIC[i]) {
					// Invalid offset or corrupt file.
					timeEntry.decFdUsage();
					callback('not-found');
					return;
				}
			}

			// Check size.
			size = BufferUtils.bufferToUint(buf, ENTRY_MAGIC.length);
			if (size > MAX_SIZE) {
				// Probably corrupt file..
				timeEntry.decFdUsage();
				callback('not-found');
				return;
			}

			storedChecksum = BufferUtils.bufferToUint(buf,
				ENTRY_MAGIC.length + SIZE_ENTRY_SIZE);

			// TODO: prevent TimeEntry.destroy() from closing
			// file descriptor until done.
			if (size > buf.length) {
				buf = new Buffer(size);
			}
			fs.read(timeEntry.stream.fd, buf, 0, size, offset + HEADER_SIZE,
				function(err, bytesRead)
			{
				timeEntry.decFdUsage();

				if (err) {
					callback(err);
					return;
				} else if (bytesRead != size) {
					// What's going on?
					callback('not-found');
					return;
				}

				self.checksummer.reset();
				self.checksummer.update(buf, 0, size);
				self.checksummer.finalize();

				if (self.checksummer.value == storedChecksum) {
					if (buf.length != size) {
						buf = buf.slice(0, size);
					}
					callback(undefined, buf);
				} else {
					// Probably corrupt file.
					callback('not-found');
				}
			});
		});
	}
}

Database.prototype.add = function(groupName, dayTimestamp, buffers, checksumBuffer, callback) {
	console.assert(checksumBuffer.length == CRC32_BINARY_SIZE);
	var timeEntry = this.findOrCreateTimeEntry(groupName, dayTimestamp);
	var flushed, i, totalSize = 0;
	var prevOffset = timeEntry.dataFileSize;

	for (i = 0; i < buffers.length; i++) {
		totalSize += buffers[i].length;
	}
	console.assert(totalSize < MAX_SIZE);

	function written(err) {
		if (err) {
			callback(err);
		} else {
			callback(undefined, prevOffset);
		}
	}

	var buf = new Buffer(HEADER_SIZE);
	ENTRY_MAGIC.copy(buf);
	BufferUtils.uintToBuffer(totalSize, buf, ENTRY_MAGIC.length);
	checksumBuffer.copy(buf, ENTRY_MAGIC.length + SIZE_ENTRY_SIZE);
	flushed = timeEntry.stream.write(buf);
	timeEntry.dataFileSize += HEADER_SIZE;
	console.assert(!flushed, "Code assumes that file I/O is never flushed immediately");

	for (i = 0; i < buffers.length; i++) {
		flushed = timeEntry.stream.write(buffers[i],
			(i == buffers.length - 1) ? written : undefined);
		timeEntry.dataFileSize += buffers[i].length;
		console.assert(!flushed, "Code assumes that file I/O is never flushed immediately");
	}
}

Database.prototype.remove = function(groupName, dayTimestamp, callback) {
	console.assert(Group.validateGroupName(groupName));
	var group = this.groups[groupName];
	if (!group) {
		callback();
		return;
	}

	var self = this;
	var newFilename;

	if (dayTimestamp) {
		var dayTimestampsToRemove = [];
		var dirsToRemove = [];
		var dts, i, timeEntry;

		for (dts in group.timeEntries) {
			if (parseInt(dts) < dayTimestamp) {
				dayTimestampsToRemove.push(dts);
			}
		}

		function deleteNextDir() {
			var dir = dirsToRemove.pop();
			if (dir) {
				var child = child_process.spawn('rm', ['-rf', dir]);
				child.stdin.end();
				child.stdout.pipe(process.stdout);
				child.stderr.pipe(process.stderr);
				child.on('exit', deleteNextDir);
			}
		}

		for (i = 0; i < dayTimestampsToRemove.length; i++) {
			dts = dayTimestampsToRemove[i];
			timeEntry = group.timeEntries[dts];
			try {
				newFilename = renameToHidden(timeEntry.path);
			} catch (err) {
				deleteNextDir();
				callback(err);
				return;
			}
			timeEntry.destroy();
			group.timeEntryCount--;
			delete group.timeEntries[dts];
			dirsToRemove.push(newFilename);
		}

		deleteNextDir();
		callback();

	} else {
		try {
			newFilename = renameToHidden(group.path);
		} catch (err) {
			callback(err);
			return;
		}
		this.groupCount--;
		delete this.groups[groupName];
		group.destroy();
		child_process.spawn('rm', ['-rf', newFilename]);
		callback();
	}
}

function looksLikeTimestamp(name) {
	return name[0] >= '0' && name[0] <= '9' && parseInt(name) == name;
}

function openDataFile(filename, callback) {
	return fs.createWriteStream(filename, {
		flags: 'a+',
		encoding: null,
		mode: 0600,
		fd: fs.openSync(filename, 'a+', 0600)
	});
}

function renameToHidden(filename, callback) {
	var dirname = path.dirname(filename);
	var basename = path.basename(filename);
	var newFilename;

	for (i = 0; i < 1000; i++) {
		newFilename = path.join(dirname, "._" + basename + '-' + i);
		try {
			fs.renameSync(filename, newFilename);
			return newFilename;
		} catch (err) {
			if (err.code != 'ENOTEMPTY') {
				throw err;
			}
		}
		return
	}
	// TODO: clear stale renames upon reloading
	throw Error("Cannot find a suitable filename to rename to");
}

exports.Database = Database;
exports.MAX_SIZE = MAX_SIZE;
exports.MAX_SIZE_DESCRIPTION = MAX_SIZE_DESCRIPTION;
<|endoftext|>


<commit_before>/**
 * Copyright 2012 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Definitions for the mouse lock API.
 * @see https://developer.mozilla.org/en/API/Mouse_Lock_API
 * @externs
 */

/** @constructor */
function PointerLock() {}
/** @type {boolean} */
PointerLock.prototype.isLocked;
/**
 * @param {!Element} target
 * @param {(function(): void)=} opt_successCallback
 * @param {(function(): void)=} opt_failureCallback
 */
PointerLock.prototype.lock =
    function(target, opt_successCallback, opt_failureCallback) {};
PointerLock.prototype.unlock = function() {};

/** @type {number} */
Event.prototype.movementX;
/** @type {number} */
Event.prototype.movementY;
/** @type {number} */
Event.prototype.mozMovementX;
/** @type {number} */
Event.prototype.mozMovementY;
/** @type {number} */
Event.prototype.webkitMovementX;
/** @type {number} */
Event.prototype.webkitMovementY;

/** @type {PointerLock|undefined} */
Navigator.prototype.pointer;
/** @type {PointerLock|undefined} */
Navigator.prototype.mozPointer;
/** @type {PointerLock|undefined} */
Navigator.prototype.webkitPointer;
<commit_msg>Adding the future mouse lock API externs, although Chrome isn't shipping with it yet<commit_after>/**
 * Copyright 2012 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Definitions for the mouse lock API.
 * @see https://developer.mozilla.org/en/API/Mouse_Lock_API
 * @externs
 */

// TODO(benvanik): swap for the new API when Chrome gets it
// /** @return {Element} */
// Document.prototype.pointerLockElement = function() {};
// /** @return {Element} */
// Document.prototype.mozPointerLockElement = function() {};
// /** @return {Element} */
// Document.prototype.webkitPointerLockElement = function() {};
// Document.prototype.exitPointerLock = function() {};
// Element.prototype.requestPointerLock = function() {};
// Element.prototype.mozRequestPointerLock = function() {};
// Element.prototype.webkitRequestPointerLock = function() {};

/** @constructor */
function PointerLock() {}
/** @type {boolean} */
PointerLock.prototype.isLocked;
/**
 * @param {!Element} target
 * @param {(function(): void)=} opt_successCallback
 * @param {(function(): void)=} opt_failureCallback
 */
PointerLock.prototype.lock =
    function(target, opt_successCallback, opt_failureCallback) {};
PointerLock.prototype.unlock = function() {};

/** @type {number} */
Event.prototype.movementX;
/** @type {number} */
Event.prototype.movementY;
/** @type {number} */
Event.prototype.mozMovementX;
/** @type {number} */
Event.prototype.mozMovementY;
/** @type {number} */
Event.prototype.webkitMovementX;
/** @type {number} */
Event.prototype.webkitMovementY;

/** @type {PointerLock|undefined} */
Navigator.prototype.pointer;
/** @type {PointerLock|undefined} */
Navigator.prototype.mozPointer;
/** @type {PointerLock|undefined} */
Navigator.prototype.webkitPointer;
<|endoftext|>


<commit_before>/*global requirejs:true*/
requirejs(
    [
        'json!../data.json',
        'moment',
        'goog!visualization,1,packages:[corechart]'
    ],
    // google doesn't have AMD so we need to access it via window.google
    function (raw_data, moment) {
        'use strict';
        var points = formatData(raw_data);
        var data = new window.google.visualization.DataTable();
        var dates = points.map(function(p){return p[0].toDate();});
        var weights = points.map(function(p){return p[1];});
        var avgs = rollingAverage(points);
        var minDate = getMinDate(points);
        var maxDate = getMaxDate(points);
        var DAYS_EITHER_SIDE = 10;

        function weightFormat(kg) {
            var lbs = kg * 2.20462;
            var st = Math.floor(lbs / 14);
            lbs = lbs % 14;
            return '' + st + 'st, ' + lbs.toFixed(1) + 'lb.';
        }

        function formatData(raw_data) {
            var data = raw_data.point.map(function(point) {
                // get it out of nanoseconds
                var val = point.value[0].fpVal;

                // start time is in nanos, date accepts timestamps in millis, so convert for tat
                var time = new Date(+point.startTimeNanos / 1e6);
                return [
                    moment(time),
                    {
                        v: val,
                        f: weightFormat(val)
                    }
                ];
            });
            return data.sort(function(a, b) {
                return a[0] - b[0];
            });
        }

        function rollingAverage(points) {
            var avgs = [];
            points.forEach(function(point){
                // using every for its shortcut functionality
                var startDate = point[0].clone().subtract(10, 'days');
                var avg = null;
                var nearbyPts = points.reduce(function(nearbyPts, altPt) {
                    var afterStart = altPt[0] >= startDate;
                    var beforeEnd = altPt[0] <= point[0];
                    // if it's within 10 days it's useful for rolling avg
                    if (afterStart && beforeEnd) {
                        nearbyPts.push(altPt[1].v);
                    }
                    return nearbyPts;
                }, []);
                avg = nearbyPts.length ? nearbyPts.reduce(function(accumulator, val) {
                    return accumulator + val;
                }, 0) / nearbyPts.length : null;
                // if there are no nearby pts be null

                avgs.push(avg);
            });
            return avgs;
        }

        function getMinDate(points) {
            return points[0][0].clone().subtract(DAYS_EITHER_SIDE, 'days');
        }
        function getMaxDate(points) {
            return points.slice(-1)[0][0].clone().add(DAYS_EITHER_SIDE, 'days');
        }
        data.addColumn('datetime', 'Date');
        data.addColumn('number', 'Weight');
        data.addColumn('number', 'rollingAvg');

        for (var i = 0; i < dates.length; i++) {
            data.addRow([dates[i], weights[i], avgs[i]]);
        }

        // Set chart options
        var options = {
            title: 'Weight',
            vAxis: {
                title: 'Weight (kg)'
            },
            hAxis: {
                title: 'Date',
                gridlines: {count: -1},
                viewWindowMode: 'explicit',
                viewWindow: {
                    // dates are zero indexed just because
                    min: minDate.toDate(),
                    max: maxDate.toDate(),
                },
            },
            series: {
                0: {
                    type: 'scatter'
                },
                1: {
                    type: 'line',
                },
            },
            trendlines: {
                0: {
                    // type: 'polynomial',
                    // degree: 2,
                    lineWidth: 5,
                    opacity: 0.5,
                    color: 'green',
                }
            },
            legend: {position: 'none'},
        };

        // Instantiate and draw our chart, passing in some options.
        var chart = new window.google.visualization.ComboChart(
            document.getElementById('chart_div')
        );
        chart.draw(data, options);
});
<commit_msg>removed tooltips for computed data<commit_after>/*global requirejs:true*/
requirejs(
    [
        'json!../data.json',
        'moment',
        'goog!visualization,1,packages:[corechart]'
    ],
    // google doesn't have AMD so we need to access it via window.google
    function (raw_data, moment) {
        'use strict';
        var points = formatData(raw_data);
        var data = new window.google.visualization.DataTable();
        var dates = points.map(function(p){return p[0].toDate();});
        var weights = points.map(function(p){return p[1];});
        var avgs = rollingAverage(points);
        var minDate = getMinDate(points);
        var maxDate = getMaxDate(points);
        var DAYS_EITHER_SIDE = 10;

        function weightFormat(kg) {
            var lbs = kg * 2.20462;
            var st = Math.floor(lbs / 14);
            lbs = lbs % 14;
            return '' + st + 'st, ' + lbs.toFixed(1) + 'lb.';
        }

        function formatData(raw_data) {
            var data = raw_data.point.map(function(point) {
                // get it out of nanoseconds
                var val = point.value[0].fpVal;

                // start time is in nanos, date accepts timestamps in millis, so convert for tat
                var time = new Date(+point.startTimeNanos / 1e6);
                return [
                    moment(time),
                    {
                        v: val,
                        f: weightFormat(val)
                    }
                ];
            });
            return data.sort(function(a, b) {
                return a[0] - b[0];
            });
        }

        function rollingAverage(points) {
            var avgs = [];
            points.forEach(function(point){
                // using every for its shortcut functionality
                var startDate = point[0].clone().subtract(10, 'days');
                var avg = null;
                var nearbyPts = points.reduce(function(nearbyPts, altPt) {
                    var afterStart = altPt[0] >= startDate;
                    var beforeEnd = altPt[0] <= point[0];
                    // if it's within 10 days it's useful for rolling avg
                    if (afterStart && beforeEnd) {
                        nearbyPts.push(altPt[1].v);
                    }
                    return nearbyPts;
                }, []);
                avg = nearbyPts.length ? nearbyPts.reduce(function(accumulator, val) {
                    return accumulator + val;
                }, 0) / nearbyPts.length : null;
                // if there are no nearby pts be null

                avgs.push(avg);
            });
            return avgs;
        }

        function getMinDate(points) {
            return points[0][0].clone().subtract(DAYS_EITHER_SIDE, 'days');
        }
        function getMaxDate(points) {
            return points.slice(-1)[0][0].clone().add(DAYS_EITHER_SIDE, 'days');
        }
        data.addColumn('datetime', 'Date');
        data.addColumn('number', 'Weight');
        data.addColumn('number', 'rollingAvg');

        for (var i = 0; i < dates.length; i++) {
            data.addRow([dates[i], weights[i], avgs[i]]);
        }

        // Set chart options
        var options = {
            title: 'Weight',
            vAxis: {
                title: 'Weight (kg)'
            },
            hAxis: {
                title: 'Date',
                gridlines: {count: -1},
                viewWindowMode: 'explicit',
                viewWindow: {
                    // dates are zero indexed just because
                    min: minDate.toDate(),
                    max: maxDate.toDate(),
                },
            },
            series: {
                0: {
                    type: 'scatter'
                },
                1: {
                    type: 'line',
                    tooltip: false
                },
            },
            trendlines: {
                0: {
                    lineWidth: 5,
                    opacity: 0.5,
                    color: 'green',
                    tooltip: false
                }
            },
            legend: {position: 'none'},
        };

        // Instantiate and draw our chart, passing in some options.
        var chart = new window.google.visualization.ComboChart(
            document.getElementById('chart_div')
        );
        chart.draw(data, options);
});
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014 Juniper Networks, Inc. All rights reserved.
 */

var assert = require('assert'),
    path = require('path'),
    fs = require('fs'),
    contrailConfig;

var fileReadOptions = {encoding: "utf8"};
var defaultMotdFilePath = "/etc/contrail/contrail-webui-motd";

/* Function: compareAndMergeDefaultConfig
   This function is used to compare and merge missing configuration from
   default.config.global.js with config file
 */
function compareAndMergeDefaultConfig (confFile)
{
    if (null == confFile) {
        confFile = __dirname + '/../../../config/config.global.js';
    }
    var confFile = path.normalize(confFile);
    var defConfFile = __dirname + '/../../../config/default.config.global.js';
    return compareAndMergeFiles(confFile, defConfFile);
}

/* Function: mergeObjects
    This function is used to merge default config with actual config, so any
    config is missed in actual config, that is added from default config.
 */
function mergeObjects (defaults, configs)
{
    if (null == configs) {
        return defaults;
    }
    if (null == defaults) {
        return configs;
    }
    Object.keys(defaults).forEach(function(keyDefault) {
        if (typeof configs[keyDefault] == "undefined") {
            configs[keyDefault] = defaults[keyDefault];
        } else if (isObject(defaults[keyDefault]) && isObject(configs[keyDefault])) {
            mergeObjects(defaults[keyDefault], configs[keyDefault]);
        }
    });

    function isObject(object) {
        return Object.prototype.toString.call(object) === '[object Object]';
    }

    return configs;
}

function updateMOTD (msg)
{
    if (null == msg) {
        delete contrailConfig.motd_string;
    } else {
        msg = msg.replace(/^\s+|\s+$/g, '');
        contrailConfig.motd_string= msg;
    }
}

/* Function: compareAndMergeFiles
   This function is used to mege two files line by line comparing the left
   porton of splitter of fileToCmp with fileWithCmp.

   args:
    fileToCmp: The file to which comparison is done, finally the extra string in
               fileWithCmp gets copied in fileToCmp.
    fileWithCmp: The file with with comparison is done
 */
function compareAndMergeFiles (fileToCmp, fileWithCmp)
{
    var oldConfig = require(fileToCmp);
    var defConfig = require(fileWithCmp);
    var config = mergeObjects(defConfig, oldConfig);
    return config;
}

function subscribeMOTDFileChange ()
{
    var commonUtils = require("../utils/common.utils");
    var logutils = require('../utils/log.utils');
    var config = getConfig();
    var contents = null;
    var filePath = null;
    var motdFilePath =
        commonUtils.getValueByJsonPath(config, "motd;file_path", "");
    fs.readFile(motdFilePath, fileReadOptions, function(error, contents) {
        if ((null != error) && (null != error.errno)) {
            fs.readFile(defaultMotdFilePath, fileReadOptions, function(error, contents) {
                if ((null != error) && (null != error.errno)) {
                    logutils.logger.error("Configured/Default MOTD file " +
                                          "read error code", error.errno);
                    updateAndWatchMOTDFileChange(null, null);
                    return;
                }
                updateAndWatchMOTDFileChange(contents, defaultMotdFilePath);
                return;
            });
        }
        updateAndWatchMOTDFileChange(contents, motdFilePath);
    });
    return;
}

function updateAndWatchMOTDFileChange (contents, filePath)
{
    updateMOTD(contents);
    if (null == filePath) {
        return;
    }
    fs.watchFile(filePath, function(curr, prev) {
        fs.readFile(filePath, fileReadOptions, function(error, contents) {
            if ((null != error) && (null != error.errno)) {
                logutils.logger.error("Configured/Default MOTD file " +
                        "read error code", error.errno);
            }
            if ((null == error) && (null != contents)) {
                updateMOTD(contents);
            } else if (defaultMotdFilePath == filePath) {
                /* No MOTD Message to display */
                updateMOTD(null);
                return;
            } else {
                /* Try with default one now */
                fs.readFile(defaultMotdFilePath, fileReadOptions,
                            function(error, contents) {
                    if ((null == error) && (null != contents)) {
                        updateMOTD(contents);
                    } else {
                        /* No MOTD Message to display */
                        updateMOTD(null);
                    }
                });
            }
        });
    });
}

/* Function: subscribeAutoDetectConfig, implements fs.watchFile
 * This function detects changes in config.global.js
 * and updates contrailConfig variable
 */
function subscribeAutoDetectConfig(confFile)
{
    fs.watchFile(confFile, function(curr, prev){
        getActualPath(confFile, function(err, actPath){
            if(err){
                console.error("subscribeAutoDetectConfig " + err);
                return;
            }
            delete require.cache[actPath];
            updateConfig(actPath);
            var contrailServ = require('./contrailservice.api');
            contrailServ.getContrailServices();
            contrailServ.startWatchContrailServiceRetryList();
            subscribeMOTDFileChange();
        });
    });
}

/* Function: getActualPath
 * handling symlinks
*/
function getActualPath(path, callback) {
    // Check if it's a link
    fs.lstat(path, function(err, stats) {
        if(err) {
            // log errors
            callback(err, null);
            return;
        } else if(stats.isSymbolicLink()) {
            // Read symlink
            fs.readlink(path, function(err, realPath) {
                // Handle errors
                if(err) {
                    callback(err, null);
                    return;
                }
                // the real file
                callback(null, realPath);
            });
        } else {
            // It's not a symlink
            callback(null, path);
        }
    });
}

function updateConfig(confFile)
{
    contrailConfig = compareAndMergeDefaultConfig(confFile);
}

/* Function: getConfig
 * This function gets the latest config
 */
function getConfig()
{
    if (null == contrailConfig) {
        updateConfig("../../../config/config.global.js");
    }
  return contrailConfig;
}

/* Function:getConfigFile
 * It gets configFile with full path if it is provided through
 * command line arguments else from default path.
 */

function getConfigFile(args)
{
    var configFile = null,
        argsCnt = args.length;
    for (var i = 0; i < argsCnt; i++) {
        if (('--c' == args[i]) || ('--conf_file' == args[i])) {
            if (null == args[i + 1]) {
                console.error('Config file not provided');
                assert(0);
            } else {
                configFile = args[i + 1];
                try {
                    var tmpConfig = require(configFile);
                    if ((null == tmpConfig) ||
                            (typeof tmpConfig !== 'object')) {
                        console.error('Config file ' +
                                configFile + ' is not valid');
                        assert(0);
                    }
                    break;
                } catch(e) {
                    console.error('Config file ' + configFile + ' not found');
                    assert(0);
                }
            }
        }
    }
    if(configFile === null){
        configFile =
            path.normalize(__dirname + '/../../../config/config.global.js');
    }
    return configFile;
}

exports.compareAndMergeDefaultConfig = compareAndMergeDefaultConfig;
exports.mergeObjects = mergeObjects;
exports.subscribeAutoDetectConfig = subscribeAutoDetectConfig;
exports.updateConfig = updateConfig;
exports.getConfig = getConfig;
exports.getConfigFile = getConfigFile;
exports.subscribeMOTDFileChange = subscribeMOTDFileChange;

<commit_msg>Added the missing variable definition in updateAndWatchMOTDFileChange()<commit_after>/*
 * Copyright (c) 2014 Juniper Networks, Inc. All rights reserved.
 */

var assert = require('assert'),
    path = require('path'),
    fs = require('fs'),
    contrailConfig;

var fileReadOptions = {encoding: "utf8"};
var defaultMotdFilePath = "/etc/contrail/contrail-webui-motd";

/* Function: compareAndMergeDefaultConfig
   This function is used to compare and merge missing configuration from
   default.config.global.js with config file
 */
function compareAndMergeDefaultConfig (confFile)
{
    if (null == confFile) {
        confFile = __dirname + '/../../../config/config.global.js';
    }
    var confFile = path.normalize(confFile);
    var defConfFile = __dirname + '/../../../config/default.config.global.js';
    return compareAndMergeFiles(confFile, defConfFile);
}

/* Function: mergeObjects
    This function is used to merge default config with actual config, so any
    config is missed in actual config, that is added from default config.
 */
function mergeObjects (defaults, configs)
{
    if (null == configs) {
        return defaults;
    }
    if (null == defaults) {
        return configs;
    }
    Object.keys(defaults).forEach(function(keyDefault) {
        if (typeof configs[keyDefault] == "undefined") {
            configs[keyDefault] = defaults[keyDefault];
        } else if (isObject(defaults[keyDefault]) && isObject(configs[keyDefault])) {
            mergeObjects(defaults[keyDefault], configs[keyDefault]);
        }
    });

    function isObject(object) {
        return Object.prototype.toString.call(object) === '[object Object]';
    }

    return configs;
}

function updateMOTD (msg)
{
    if (null == msg) {
        delete contrailConfig.motd_string;
    } else {
        msg = msg.replace(/^\s+|\s+$/g, '');
        contrailConfig.motd_string= msg;
    }
}

/* Function: compareAndMergeFiles
   This function is used to mege two files line by line comparing the left
   porton of splitter of fileToCmp with fileWithCmp.

   args:
    fileToCmp: The file to which comparison is done, finally the extra string in
               fileWithCmp gets copied in fileToCmp.
    fileWithCmp: The file with with comparison is done
 */
function compareAndMergeFiles (fileToCmp, fileWithCmp)
{
    var oldConfig = require(fileToCmp);
    var defConfig = require(fileWithCmp);
    var config = mergeObjects(defConfig, oldConfig);
    return config;
}

function subscribeMOTDFileChange ()
{
    var commonUtils = require("../utils/common.utils");
    var logutils = require('../utils/log.utils');
    var config = getConfig();
    var contents = null;
    var filePath = null;
    var motdFilePath =
        commonUtils.getValueByJsonPath(config, "motd;file_path", "");
    fs.readFile(motdFilePath, fileReadOptions, function(error, contents) {
        if ((null != error) && (null != error.errno)) {
            fs.readFile(defaultMotdFilePath, fileReadOptions, function(error, contents) {
                if ((null != error) && (null != error.errno)) {
                    logutils.logger.error("Configured/Default MOTD file " +
                                          "read error code", error.errno);
                    updateAndWatchMOTDFileChange(null, null);
                    return;
                }
                updateAndWatchMOTDFileChange(contents, defaultMotdFilePath);
                return;
            });
        }
        updateAndWatchMOTDFileChange(contents, motdFilePath);
    });
    return;
}

function updateAndWatchMOTDFileChange (contents, filePath)
{
    var logutils = require('../utils/log.utils');
    updateMOTD(contents);
    if (null == filePath) {
        return;
    }
    fs.watchFile(filePath, function(curr, prev) {
        fs.readFile(filePath, fileReadOptions, function(error, contents) {
            if ((null != error) && (null != error.errno)) {
                logutils.logger.error("Configured/Default MOTD file " +
                        "read error code", error.errno);
            }
            if ((null == error) && (null != contents)) {
                updateMOTD(contents);
            } else if (defaultMotdFilePath == filePath) {
                /* No MOTD Message to display */
                updateMOTD(null);
                return;
            } else {
                /* Try with default one now */
                fs.readFile(defaultMotdFilePath, fileReadOptions,
                            function(error, contents) {
                    if ((null == error) && (null != contents)) {
                        updateMOTD(contents);
                    } else {
                        /* No MOTD Message to display */
                        updateMOTD(null);
                    }
                });
            }
        });
    });
}

/* Function: subscribeAutoDetectConfig, implements fs.watchFile
 * This function detects changes in config.global.js
 * and updates contrailConfig variable
 */
function subscribeAutoDetectConfig(confFile)
{
    fs.watchFile(confFile, function(curr, prev){
        getActualPath(confFile, function(err, actPath){
            if(err){
                console.error("subscribeAutoDetectConfig " + err);
                return;
            }
            delete require.cache[actPath];
            updateConfig(actPath);
            var contrailServ = require('./contrailservice.api');
            contrailServ.getContrailServices();
            contrailServ.startWatchContrailServiceRetryList();
            subscribeMOTDFileChange();
        });
    });
}

/* Function: getActualPath
 * handling symlinks
*/
function getActualPath(path, callback) {
    // Check if it's a link
    fs.lstat(path, function(err, stats) {
        if(err) {
            // log errors
            callback(err, null);
            return;
        } else if(stats.isSymbolicLink()) {
            // Read symlink
            fs.readlink(path, function(err, realPath) {
                // Handle errors
                if(err) {
                    callback(err, null);
                    return;
                }
                // the real file
                callback(null, realPath);
            });
        } else {
            // It's not a symlink
            callback(null, path);
        }
    });
}

function updateConfig(confFile)
{
    contrailConfig = compareAndMergeDefaultConfig(confFile);
}

/* Function: getConfig
 * This function gets the latest config
 */
function getConfig()
{
    if (null == contrailConfig) {
        updateConfig("../../../config/config.global.js");
    }
  return contrailConfig;
}

/* Function:getConfigFile
 * It gets configFile with full path if it is provided through
 * command line arguments else from default path.
 */

function getConfigFile(args)
{
    var configFile = null,
        argsCnt = args.length;
    for (var i = 0; i < argsCnt; i++) {
        if (('--c' == args[i]) || ('--conf_file' == args[i])) {
            if (null == args[i + 1]) {
                console.error('Config file not provided');
                assert(0);
            } else {
                configFile = args[i + 1];
                try {
                    var tmpConfig = require(configFile);
                    if ((null == tmpConfig) ||
                            (typeof tmpConfig !== 'object')) {
                        console.error('Config file ' +
                                configFile + ' is not valid');
                        assert(0);
                    }
                    break;
                } catch(e) {
                    console.error('Config file ' + configFile + ' not found');
                    assert(0);
                }
            }
        }
    }
    if(configFile === null){
        configFile =
            path.normalize(__dirname + '/../../../config/config.global.js');
    }
    return configFile;
}

exports.compareAndMergeDefaultConfig = compareAndMergeDefaultConfig;
exports.mergeObjects = mergeObjects;
exports.subscribeAutoDetectConfig = subscribeAutoDetectConfig;
exports.updateConfig = updateConfig;
exports.getConfig = getConfig;
exports.getConfigFile = getConfigFile;
exports.subscribeMOTDFileChange = subscribeMOTDFileChange;

<|endoftext|>


<commit_before>/**********
 * The source code for my snake clone-, er, I mean, original game.
 * Author: Bronzdragon
 * Date: May 25th, 2017
 **********/

/**
 * Initialises/starts our game object
 */

var game = new Game();

function init(){
	if(game.init())
		game.start();
}

/**
 * Repository for all the images used in our game. Using a repository like this
 * means we only need to store each image once.
 */

var imageRepository = new function() {
	// this.empty = null;
	this.background = new Image();

	this.background.src = "images/background.png";
}

/**
 * Creates the Drawable object, which will serve as the base class for all
 * drawable objects in the game.
 */

function Drawable() {
	this.init = function(x, y) {
		this.x = x;
		this.y = y;
	}
	this.speed = 0;
	this.canvasWidth = 0;
	this.canvasHeight = 0;

	this.draw = function() {};
}

/**
 * Child of Drawable. This will pan, thus creating the illusion of movement
 */

// Set our back-ground object to inherit from drawable.
function Background() {
	this.speed = 1;

	this.draw = function() {  // implement the earlier absctract function
		// Pan the background
		this.y += this.speed;

		this.context.drawImage(imageRepository.background, this.x, this.y);
		// Draw a duplicate, so that if the previous image scrolls too
		// far, we can still see the background.
		this.context.drawImage(imageRepository.background, this.x, this.y - this.canvasHeight);
		if (this.y >= this.canvasHeight)
			this.y = 0;
	};
}
Background.prototype = new Drawable();

/**
 * Definition of our game object
 */
function Game() {
	// See if the canvas object is supported
	this.init = function(){
		this.bgCanvas = document.getElementById('background');

		if (this.bgCanvas.getContext){
			this.bgContext = this.bgCanvas.getContext('2d');

			Background.prototype.context = this.bgContext;
			Background.prototype.canvasWidth = this.bgCanvas.width;
			Background.prototype.canvasHeight = this.bgCanvas.height;

			this.background = new Background();
			this.background.init(0,0);
			return true;
		} else {
			return false;
		}
	};

	this.start = function () {
		animate();
	};
}

/**
 * requestAnim shim layer by Paul Irish
 * Finds the first API that works to optimize the animation loop,
 * otherwise defaults to setTimeout().
 */

function animate() {
	requestAnimFrame(animate);
	game.background.draw();
}

/**
 * requestAnim shim layer by Paul Irish
 * Finds the first API that works to optimize the animation loop,
 * otherwise defaults to setTimeout().
 */
window.requestAnimFrame = (function(){
	return  window.requestAnimationFrame       ||
		window.webkitRequestAnimationFrame ||
		window.mozRequestAnimationFrame    ||
		window.oRequestAnimationFrame      ||
		window.msRequestAnimationFrame     ||
		function(/* function */ callback, /* DOMElement */ element){
			window.setTimeout(callback, 1000 / 60);
		};
})();
<commit_msg>Scrolling achieved... somehow<commit_after>/**********
 * The source code for my snake clone-, er, I mean, original game.
 * Author: Bronzdragon
 * Date: May 25th, 2017
 **********/

/**
 * Initialises/starts our game object
 */

var game = new Game();

function init(){
	if(game.init())
		game.start();
}

/**
 * Repository for all the images used in our game. Using a repository like this
 * means we only need to store each image once.
 */

var imageRepository = new function() {
	this.empty = null;
	this.background = new Image();

	this.background.src = "images/background.png";
}

/**
 * Creates the Drawable object, which will serve as the base class for all
 * drawable objects in the game.
 */

function Drawable() {
	this.init = function(x, y) {
		this.x = x;
		this.y = y;
	}
	this.speed = 0;
	this.canvasWidth = 0;
	this.canvasHeight = 0;

	this.draw = function() {};
}

/**
 * Child of Drawable. This will pan, thus creating the illusion of movement
 */

// Set our back-ground object to inherit from drawable.
function Background() {
	this.speed = 1;

	this.draw = function() {  // implement the earlier absctract function
		// Pan background
		this.y += this.speed;

		this.context.drawImage(imageRepository.background, this.x, this.y);
		// Draw a duplicate, so that if the previous image scrolls too
		// far, we can still see the background.
		this.context.drawImage(imageRepository.background, this.x, this.y - this.canvasHeight);
		if (this.y >= this.canvasHeight)
			this.y = 0;
	};
}
Background.prototype = new Drawable();

/**
 * Definition of our game object
 */
function Game() {
	// See if the canvas object is supported
	this.init = function(){
		this.bgCanvas = document.getElementById('background');

		if (this.bgCanvas.getContext){
			this.bgContext = this.bgCanvas.getContext('2d');

			Background.prototype.context = this.bgContext;
			Background.prototype.canvasWidth = this.bgCanvas.width;
			Background.prototype.canvasHeight = this.bgCanvas.height;

			this.background = new Background();
			this.background.init(0,0);
			return true;
		} else {
			return false;
		}
	};

	this.start = function () {
		animate();
	};
}

/**
 * requestAnim shim layer by Paul Irish
 * Finds the first API that works to optimize the animation loop,
 * otherwise defaults to setTimeout().
 */

function animate() {
	requestAnimFrame(animate);
	game.background.draw();
}

/**
 * requestAnim shim layer by Paul Irish
 * Finds the first API that works to optimize the animation loop,
 * otherwise defaults to setTimeout().
 */
window.requestAnimFrame = (function(){
	return  window.requestAnimationFrame       ||
		window.webkitRequestAnimationFrame ||
		window.mozRequestAnimationFrame    ||
		window.oRequestAnimationFrame      ||
		window.msRequestAnimationFrame     ||
		function(/* function */ callback, /* DOMElement */ element){
			window.setTimeout(callback, 1000 / 60);
		};
})();

<|endoftext|>


<commit_before>import stringTraceUseValue from "./stringTraceUseValue"
import StringTraceString, {makeTraceObject} from "./FromJSString"
import Origin from "../origin"
import mapInnerHTMLAssignment from "./mapInnerHTMLAssignment"

function untrackedString(value){
    return makeTraceObject({
        value: value,
        origin: new Origin({
            action: "Untracked String",
            value: value,
            inputValues: []
        }),
    })
}

var babelFunctions = {
    f__StringLiteral(value){
        return makeTraceObject({
            value: value,
            origin: new Origin({
                action: "String Literal",
                value: value,
                inputValues: []
            }),
        })
    },
    f__typeof(a){
        if (a && a.isStringTraceString) {
            return "string"
        }
        return typeof a
    },
    f__useValue(thing){
        return stringTraceUseValue(thing)
    },
    f__add(a, b){
        var stack = new Error().stack.split("\n")
        if (a == null){
            a = ""
        }
        if (b==null){
            b = ""
        }
        if (!a.isStringTraceString && typeof a === "string"){
            console.log("untracked string", a)

            a = untrackedString(a);

        }
        if (!b.isStringTraceString && typeof b === "string"){
            console.log("untracked string", b)
            // if (b.indexOf("completed") !== -1)debugger
            b = untrackedString(b);
        }
        if (!a.isStringTraceString) {
            return a + b;// not a string operation i think, could still be inferred to a stirng tho
        }

        var newValue = a.toString() + b.toString();
        return makeTraceObject({
            value: newValue,
            origin: new Origin({
                action: "Concat",
                value: newValue,
                inputValues: [a, b]
            })
        })
    },
    f__notTripleEqual(a,b){
        if (a && a.isStringTraceString) {
            a = a.toString()
        }
        if(b && b.isStringTraceString) {
            b = b.toString();
        }
        return a !== b;
    },
    f__tripleEqual(a,b){
        return !babelFunctions.f__notTripleEqual(a,b)
    },
    t__setInnerHTML(el, innerHTML){
        el.innerHTML = innerHTML

        mapInnerHTMLAssignment(el, innerHTML, "Assign InnerHTML")
    }
}

export default babelFunctions
<commit_msg>Fix typeof.<commit_after>import stringTraceUseValue from "./stringTraceUseValue"
import StringTraceString, {makeTraceObject} from "./FromJSString"
import Origin from "../origin"
import mapInnerHTMLAssignment from "./mapInnerHTMLAssignment"

function untrackedString(value){
    return makeTraceObject({
        value: value,
        origin: new Origin({
            action: "Untracked String",
            value: value,
            inputValues: []
        }),
    })
}

var babelFunctions = {
    f__StringLiteral(value){
        return makeTraceObject({
            value: value,
            origin: new Origin({
                action: "String Literal",
                value: value,
                inputValues: []
            }),
        })
    },
    f__typeof(a){
        if (a && a.isStringTraceString) {
            // rather confusingly a FromJSString can have a non string value...
            return typeof a.value
        }
        return typeof a
    },
    f__useValue(thing){
        return stringTraceUseValue(thing)
    },
    f__add(a, b){
        var stack = new Error().stack.split("\n")
        if (a == null){
            a = ""
        }
        if (b==null){
            b = ""
        }
        if (!a.isStringTraceString && typeof a === "string"){
            console.log("untracked string", a)

            a = untrackedString(a);

        }
        if (!b.isStringTraceString && typeof b === "string"){
            console.log("untracked string", b)
            // if (b.indexOf("completed") !== -1)debugger
            b = untrackedString(b);
        }
        if (!a.isStringTraceString) {
            return a + b;// not a string operation i think, could still be inferred to a stirng tho
        }

        var newValue = a.toString() + b.toString();
        return makeTraceObject({
            value: newValue,
            origin: new Origin({
                action: "Concat",
                value: newValue,
                inputValues: [a, b]
            })
        })
    },
    f__notTripleEqual(a,b){
        if (a && a.isStringTraceString) {
            a = a.toString()
        }
        if(b && b.isStringTraceString) {
            b = b.toString();
        }
        return a !== b;
    },
    f__tripleEqual(a,b){
        return !babelFunctions.f__notTripleEqual(a,b)
    },
    t__setInnerHTML(el, innerHTML){
        el.innerHTML = innerHTML

        mapInnerHTMLAssignment(el, innerHTML, "Assign InnerHTML")
    }
}

export default babelFunctions
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2014 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

MODEL({
  name: 'Physical',
  properties: [
    { model_: 'FloatProperty', name: 'vx',   defaultValue: 0 },
    { model_: 'FloatProperty', name: 'vy',   defaultValue: 0 },
    {
      model_: 'FloatProperty',
      name: 'velocity',
      getter: function() { return Movement.distance(this.vx, this.vy); },
      setter: function(v) { this.setVelocityAndAngle(v, this.angleOfVelocity); }
    },
    {
      model_: 'FloatProperty',
      name: 'angleOfVelocity',
      getter: function() { return Math.atan2(this.vy, this.vx); },
      setter: function(a) { this.setVelocityAndAngle(this.velocity, a); }
    },
    { model_: 'FloatProperty', name: 'mass', defaultValue: 1 }
  ],
  methods: {
    INFINITE_MASS: -1,

    applyEnergy: function(e, a) {
      this.vx += e * Math.cos(a);
      this.vy += e * Math.sin(a);
    },
    velocityAtAngle: function(a) { return Math.cos(a-this.angleOfVelocity) * this.velocity; },
    setVelocityAndAngle: function(v, a) {
      this.vx = v * Math.cos(a);
      this.vy = v * Math.sin(a);
    }
  }
});

/** Collision detection manager. **/
MODEL({
  name: 'Collider',
  properties: [
    { name: 'children', factory: function() { return []; } }
  ],
  listeners: [
    {
      name: 'tick',
      code: function () {
        this.detectCollisions();
        this.start();
      }
    }
  ],
  methods: {
    start: function() {
      this.X.requestAnimationFrame(this.tick);
    },
    detectCollisions: function() {
      var cs = this.children;
      for ( var i = 0 ; i < cs.length ; i++ ) {
        var c1 = cs[i];
        for ( var j = i+1 ; j < cs.length ; j++ ) {
          var c2 = cs[j];
          var dx = c1.x - c2.x;
          if ( dx > 70 || dx < -70 ) continue;
          var dy = c1.y - c2.y;
          if ( dy > 70 || dy < -70 ) continue;

          var ds = dx*dx + dy*dy;
          var rs = c1.r + c2.r;
          if ( ds < rs*rs ) this.collide(c1, c2);
        }
      }
    },
    collide: function(c1, c2) {
      var vx1 = c1.vx;
      var vy1 = c1.vy;
      var vx2 = c2.vx;
      var vy2 = c2.vy;

      var a = Math.atan2(c2.y-c1.y, c2.x-c1.x);

      function forceRatio(c1, c2) {
        if ( c1.mass == c1.INFINITE_MASS ) return 0;
        if ( c2.mass == c2.INFINITE_MASS ) return 1;
        return c2.mass / ( c1.mass + c2.mass );
      }

      var e1 =  c1.velocityAtAngle(a);
      var e2 = -c2.velocityAtAngle(a);
      var e  = e1 + e2;

      if ( e > 0 ) {
        c1.applyEnergy(-e1 - forceRatio(c1, c2) * e, a);
        c2.applyEnergy( e2 + forceRatio(c2, c1) * e, a);
      }

      // If objects intersect, then move them away from each other
      var d = Movement.distance(c1.y-c2.y, c1.x-c2.x);
      var m = (d - (c1.r+c2.r) ) / 2;

      if ( c1.mass !== c1.INFINITE_MASS ) {
        c1.x += m * Math.cos(a);
        c1.y += m * Math.sin(a);
      }
      if ( c2.mass !== c1.INFINITE_MASS ) {
        c2.x += m * Math.cos(a+Math.PI);
        c2.y += m * Math.sin(a+Math.PI);
      }
    },
    // add one one more components to be monitored for collisions
    add: function() {
      for ( var i = 0 ; i < arguments.length ; i++ )
        this.children.push(arguments[i]);
    }
  }
});

<commit_msg>Fix to collider mass support.<commit_after>/**
 * @license
 * Copyright 2014 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

Math.sign = function(n) { return n > 0 ? 1 : -1; };

MODEL({
  name: 'Physical',
  properties: [
    { model_: 'FloatProperty', name: 'vx',   defaultValue: 0 },
    { model_: 'FloatProperty', name: 'vy',   defaultValue: 0 },
    {
      model_: 'FloatProperty',
      name: 'velocity',
      getter: function() { return Movement.distance(this.vx, this.vy); },
      setter: function(v) { this.setVelocityAndAngle(v, this.angleOfVelocity); }
    },
    {
      model_: 'FloatProperty',
      name: 'angleOfVelocity',
      getter: function() { return Math.atan2(this.vy, this.vx); },
      setter: function(a) { this.setVelocityAndAngle(this.velocity, a); }
    },
    { model_: 'FloatProperty', name: 'mass', defaultValue: 1 }
  ],
  methods: {
    INFINITE_MASS: -1,

    applyEnergy: function(e, a) {
      this.vx += (e * Math.cos(a) / this.mass);
      this.vy += (e * Math.sin(a) / this.mass);
    },
    velocityAtAngle: function(a) {
      if ( this.mass == this.INFINITE_MASS ) return 0;
      return Math.cos(a-this.angleOfVelocity) * this.velocity;
    },
    energyAtAngle: function(a) {
      if ( this.mass == this.INFINITE_MASS ) return 0;
      var v = this.velocityAtAngle(a);
      return Math.sign(v) * v * this.mass;
    },
    setVelocityAndAngle: function(v, a) {
      this.vx = v * Math.cos(a);
      this.vy = v * Math.sin(a);
    }
  }
});

/** Collision detection manager. **/
MODEL({
  name: 'Collider',
  properties: [
    { name: 'children', factory: function() { return []; } }
  ],
  listeners: [
    {
      name: 'tick',
      code: function () {
        this.detectCollisions();
        this.start();
      }
    }
  ],
  methods: {
    start: function() {
      this.X.requestAnimationFrame(this.tick);
    },
    detectCollisions: function() {
      var cs = this.children;
      for ( var i = 0 ; i < cs.length ; i++ ) {
        var c1 = cs[i];
        for ( var j = i+1 ; j < cs.length ; j++ ) {
          var c2 = cs[j];
          var dx = c1.x - c2.x;
          if ( dx > 70 || dx < -70 ) continue;
          var dy = c1.y - c2.y;
          if ( dy > 70 || dy < -70 ) continue;

          var ds = dx*dx + dy*dy;
          var rs = c1.r + c2.r;
          if ( ds < rs*rs ) this.collide(c1, c2);
        }
      }
    },
    collide: function(c1, c2) {
      var vx1 = c1.vx;
      var vy1 = c1.vy;
      var vx2 = c2.vx;
      var vy2 = c2.vy;

      var a = Math.atan2(c2.y-c1.y, c2.x-c1.x);

      function energyRatio(c1, c2) {
        if ( c1.mass == c1.INFINITE_MASS ) return 0;
        if ( c2.mass == c2.INFINITE_MASS ) return 1;
        return c2.mass / ( c1.mass + c2.mass );
      }

      var r1 = energyRatio(c1, c2);
      var r2 = energyRatio(c2, c1);

      var e1 =  c1.energyAtAngle(a);
      var e2 = -c2.energyAtAngle(a);
      var e  = e1 + e2;

      if ( e > 0 ) {
        c1.applyEnergy(-e1 - r1 * e, a);
        c2.applyEnergy( e2 + r2 * e, a);
      }

      // If objects intersect, then move them away from each other
      var d = Movement.distance(c1.y-c2.y, c1.x-c2.x);
      var m = (d - (c1.r+c2.r) ) / 2;

      if ( c1.mass !== c1.INFINITE_MASS ) {
        c1.x += r1 * m * Math.cos(a);
        c1.y += r1 * m * Math.sin(a);
      }
      if ( c2.mass !== c1.INFINITE_MASS ) {
        c2.x += r2 * m * Math.cos(a+Math.PI);
        c2.y += r2 *m * Math.sin(a+Math.PI);
      }
    },

    // add one or more components to be monitored for collisions
    add: function() {
      for ( var i = 0 ; i < arguments.length ; i++ )
        this.children.push(arguments[i]);
    }
  }
});

<|endoftext|>