<commit_before>var OrbitCamera = pc.createScript('orbitCamera');

OrbitCamera.attributes.add('distanceMax', {type: 'number', default: 0, title: 'Distance Max', description: 'Setting this at 0 will give an infinite distance limit'});
OrbitCamera.attributes.add('distanceMin', {type: 'number', default: 2, title: 'Distance Min'});
OrbitCamera.attributes.add('pitchAngleMax', {type: 'number', default: 90, title: 'Pitch Angle Max (degrees)'});
OrbitCamera.attributes.add('pitchAngleMin', {type: 'number', default: -90, title: 'Pitch Angle Min (degrees)'});

OrbitCamera.attributes.add('inertiaFactor', {
    type: 'number',
    default: 0,
    title: 'Inertia Factor',
    description: 'Higher value means that the camera will continue moving after the user has stopped dragging. 0 is fully responsive.'
});

OrbitCamera.attributes.add('focusEntity', {
    type: 'entity',
    title: 'Focus Entity',
    description: 'Entity for the camera to focus on. If blank, then the camera will use the whole scene'
});

OrbitCamera.attributes.add('frameOnStart', {
    type: 'boolean',
    default: true,
    title: 'Frame on Start',
    description: 'Frames the entity or scene at the start of the application."'
});

// Property to get and set the distance between the pivot point and camera
// Clamped between this.distanceMin and this.distanceMax
Object.defineProperty(OrbitCamera.prototype, "distance", {
    get: function() {
        return this._targetDistance;
    },

    set: function(value) {
        this._targetDistance = this._clampDistance(value);
    }
});

// Property to get and set the pitch of the camera around the pivot point (degrees)
// Clamped between this.pitchAngleMin and this.pitchAngleMax
// When set at 0, the camera angle is flat, looking along the horizon
Object.defineProperty(OrbitCamera.prototype, "pitch", {
    get: function() {
        return this._targetPitch;
    },

    set: function(value) {
        this._targetPitch = this._clampPitchAngle(value);
    }
});

// Property to get and set the yaw of the camera around the pivot point (degrees)
Object.defineProperty(OrbitCamera.prototype, "yaw", {
    get: function() {
        return this._targetYaw;
    },

    set: function(value) {
        this._targetYaw = value;

        // Ensure that the yaw takes the shortest route by making sure that
        // the difference between the targetYaw and the actual is 180 degrees
        // in either direction
        var diff = this._targetYaw - this._yaw;
        var reminder = diff % 360;
        if (reminder > 180) {
            this._targetYaw = this._yaw - (360 - reminder);
        } else if (reminder < -180) {
            this._targetYaw = this._yaw + (360 + reminder);
        } else {
            this._targetYaw = this._yaw + reminder;
        }
    }
});

// Property to get and set the world position of the pivot point that the camera orbits around
Object.defineProperty(OrbitCamera.prototype, "pivotPoint", {
    get: function() {
        return this._pivotPoint;
    },

    set: function(value) {
        this._pivotPoint.copy(value);
    }
});

// Moves the camera to look at an entity and all its children so they are all in the view
OrbitCamera.prototype.focus = function (focusEntity) {
    // Calculate an bounding box that encompasses all the models to frame in the camera view
    this._buildAabb(focusEntity, 0);

    var halfExtents = this._modelsAabb.halfExtents;

    var distance = Math.max(halfExtents.x, Math.max(halfExtents.y, halfExtents.z));
    distance = (distance / Math.tan(0.5 * this.entity.camera.fov * pc.math.DEG_TO_RAD));
    distance = (distance * 2);

    this.distance = distance;

    this._removeInertia();

    this._pivotPoint.copy(this._modelsAabb.center);
};

OrbitCamera.distanceBetween = new pc.Vec3();

// Set the camera position to a world position and look at a world position
// Useful if you have multiple viewing angles to swap between in a scene
OrbitCamera.prototype.resetAndLookAtPoint = function (resetPoint, lookAtPoint) {
    this.pivotPoint.copy(lookAtPoint);
    this.entity.setPosition(resetPoint);

    this.entity.lookAt(lookAtPoint);

    var distance = OrbitCamera.distanceBetween;
    distance.sub2(lookAtPoint, resetPoint);
    this.distance = distance.length();

    this.pivotPoint.copy(lookAtPoint);

    var cameraQuat = this.entity.getRotation();
    this.yaw = this._calcYaw(cameraQuat);
    this.pitch = this._calcPitch(cameraQuat, this.yaw);

    this._removeInertia();
    this._updatePosition();
};

// Set camera position to a world position and look at an entity in the scene
// Useful if you have multiple models to swap between in a scene
OrbitCamera.prototype.resetAndLookAtEntity = function (resetPoint, entity) {
    this._buildAabb(entity, 0);
    this.resetAndLookAtPoint(resetPoint, this._modelsAabb.center);
};

// Set the camera at a specific, yaw, pitch and distance without inertia (instant cut)
OrbitCamera.prototype.reset = function (yaw, pitch, distance) {
    this.pitch = pitch;
    this.yaw = yaw;
    this.distance = distance;

    this._removeInertia();
};

/////////////////////////////////////////////////////////////////////////////////////////////
// Private methods

OrbitCamera.prototype.initialize = function () {
    var self = this;
    var onWindowResize = function () {
        self._checkAspectRatio();
    };

    window.addEventListener('resize', onWindowResize, false);

    this._checkAspectRatio();

    // Find all the models in the scene that are under the focused entity
    this._modelsAabb = new pc.BoundingBox();
    this._buildAabb(this.focusEntity || this.app.root, 0);

    this.entity.lookAt(this._modelsAabb.center);

    this._pivotPoint = new pc.Vec3();
    this._pivotPoint.copy(this._modelsAabb.center);

    // Calculate the camera euler angle rotation around x and y axes
    // This allows us to place the camera at a particular rotation to begin with in the scene
    var cameraQuat = this.entity.getRotation();

    // Preset the camera
    this._yaw = this._calcYaw(cameraQuat);
    this._pitch = this._clampPitchAngle(this._calcPitch(cameraQuat, this._yaw));
    this.entity.setLocalEulerAngles(this._pitch, this._yaw, 0);

    this._distance = 0;

    this._targetYaw = this._yaw;
    this._targetPitch = this._pitch;

    // If we have ticked focus on start, then attempt to position the camera where it frames
    // the focused entity and move the pivot point to entity's position otherwise, set the distance
    // to be between the camera position in the scene and the pivot point
    if (this.frameOnStart) {
        this.focus(this.focusEntity || this.app.root);
    } else {
        var distanceBetween = new pc.Vec3();
        distanceBetween.sub2(this.entity.getPosition(), this._pivotPoint);
        this._distance = this._clampDistance(distanceBetween.length());
    }

    this._targetDistance = this._distance;

    // Reapply the clamps if they are changed in the editor
    this.on('attr:distanceMin', function (value, prev) {
        this._distance = this._clampDistance(this._distance);
    });

    this.on('attr:distanceMax', function (value, prev) {
        this._distance = this._clampDistance(this._distance);
    });

    this.on('attr:pitchAngleMin', function (value, prev) {
        this._pitch = this._clampPitchAngle(this._pitch);
    });

    this.on('attr:pitchAngleMax', function (value, prev) {
        this._pitch = this._clampPitchAngle(this._pitch);
    });

    // Focus on the entity if we change the focus entity
    this.on('attr:focusEntity', function (value, prev) {
        if (this.frameOnStart) {
            this.focus(value || this.app.root);
        } else {
            this.resetAndLookAtEntity(this.entity.getPosition(), value || this.app.root);
        }
    });

    this.on('attr:frameOnStart', function (value, prev) {
        if (value) {
            this.focus(this.focusEntity || this.app.root);
        }
    });

    this.on('destroy', function() {
        window.removeEventListener('resize', onWindowResize, false);
    });
};

OrbitCamera.prototype.update = function(dt) {
    // Add inertia, if any
    var t = this.inertiaFactor === 0 ? 1 : Math.min(dt / this.inertiaFactor, 1);
    this._distance = pc.math.lerp(this._distance, this._targetDistance, t);
    this._yaw = pc.math.lerp(this._yaw, this._targetYaw, t);
    this._pitch = pc.math.lerp(this._pitch, this._targetPitch, t);

    this._updatePosition();
};

OrbitCamera.prototype._updatePosition = function () {
    // Work out the camera position based on the pivot point, pitch, yaw and distance
    this.entity.setLocalPosition(0,0,0);
    this.entity.setLocalEulerAngles(this._pitch, this._yaw, 0);

    var position = this.entity.getPosition();
    position.copy(this.entity.forward);
    position.scale(-this._distance);
    position.add(this.pivotPoint);
    this.entity.setPosition(position);
};

OrbitCamera.prototype._removeInertia = function () {
    this._yaw = this._targetYaw;
    this._pitch = this._targetPitch;
    this._distance = this._targetDistance;
};

OrbitCamera.prototype._checkAspectRatio = function () {
    var height = this.app.graphicsDevice.height;
    var width = this.app.graphicsDevice.width;

    // Match the axis of FOV to match the aspect ratio of the canvas so
    // the focused entities is always in frame
    this.entity.camera.horizontalFov = height > width;
};

OrbitCamera.prototype._buildAabb = function (entity, modelsAdded) {
    var i = 0;

    if (entity.model) {
        var mi = entity.model.meshInstances;
        for (i = 0; i < mi.length; i++) {
            if (modelsAdded === 0) {
                this._modelsAabb.copy(mi[i].aabb);
            } else {
                this._modelsAabb.add(mi[i].aabb);
            }

            modelsAdded += 1;
        }
    }

    for (i = 0; i < entity.children.length; ++i) {
        modelsAdded += this._buildAabb(entity.children[i], modelsAdded);
    }

    return modelsAdded;
};

OrbitCamera.prototype._calcYaw = function (quat) {
    var transformedForward = new pc.Vec3();
    quat.transformVector(pc.Vec3.FORWARD, transformedForward);

    return Math.atan2(-transformedForward.x, -transformedForward.z) * pc.math.RAD_TO_DEG;
};

OrbitCamera.prototype._clampDistance = function (distance) {
    if (this.distanceMax > 0) {
        return pc.math.clamp(distance, this.distanceMin, this.distanceMax);
    } else {
        return Math.max(distance, this.distanceMin);
    }
};

OrbitCamera.prototype._clampPitchAngle = function (pitch) {
    // Negative due as the pitch is inversed since the camera is orbiting the entity
    return pc.math.clamp(pitch, -this.pitchAngleMax, -this.pitchAngleMin);
};

OrbitCamera.quatWithoutYaw = new pc.Quat();
OrbitCamera.yawOffset = new pc.Quat();

OrbitCamera.prototype._calcPitch = function(quat, yaw) {
    var quatWithoutYaw = OrbitCamera.quatWithoutYaw;
    var yawOffset = OrbitCamera.yawOffset;

    yawOffset.setFromEulerAngles(0, -yaw, 0);
    quatWithoutYaw.mul2(yawOffset, quat);

    var transformedForward = new pc.Vec3();

    quatWithoutYaw.transformVector(pc.Vec3.FORWARD, transformedForward);

    return Math.atan2(transformedForward.y, -transformedForward.z) * pc.math.RAD_TO_DEG;
};<commit_msg>Reverted default value of distanceMin to 0<commit_after>var OrbitCamera = pc.createScript('orbitCamera');

OrbitCamera.attributes.add('distanceMax', {type: 'number', default: 0, title: 'Distance Max', description: 'Setting this at 0 will give an infinite distance limit'});
OrbitCamera.attributes.add('distanceMin', {type: 'number', default: 0, title: 'Distance Min'});
OrbitCamera.attributes.add('pitchAngleMax', {type: 'number', default: 90, title: 'Pitch Angle Max (degrees)'});
OrbitCamera.attributes.add('pitchAngleMin', {type: 'number', default: -90, title: 'Pitch Angle Min (degrees)'});

OrbitCamera.attributes.add('inertiaFactor', {
    type: 'number',
    default: 0,
    title: 'Inertia Factor',
    description: 'Higher value means that the camera will continue moving after the user has stopped dragging. 0 is fully responsive.'
});

OrbitCamera.attributes.add('focusEntity', {
    type: 'entity',
    title: 'Focus Entity',
    description: 'Entity for the camera to focus on. If blank, then the camera will use the whole scene'
});

OrbitCamera.attributes.add('frameOnStart', {
    type: 'boolean',
    default: true,
    title: 'Frame on Start',
    description: 'Frames the entity or scene at the start of the application."'
});

// Property to get and set the distance between the pivot point and camera
// Clamped between this.distanceMin and this.distanceMax
Object.defineProperty(OrbitCamera.prototype, "distance", {
    get: function() {
        return this._targetDistance;
    },

    set: function(value) {
        this._targetDistance = this._clampDistance(value);
    }
});

// Property to get and set the pitch of the camera around the pivot point (degrees)
// Clamped between this.pitchAngleMin and this.pitchAngleMax
// When set at 0, the camera angle is flat, looking along the horizon
Object.defineProperty(OrbitCamera.prototype, "pitch", {
    get: function() {
        return this._targetPitch;
    },

    set: function(value) {
        this._targetPitch = this._clampPitchAngle(value);
    }
});

// Property to get and set the yaw of the camera around the pivot point (degrees)
Object.defineProperty(OrbitCamera.prototype, "yaw", {
    get: function() {
        return this._targetYaw;
    },

    set: function(value) {
        this._targetYaw = value;

        // Ensure that the yaw takes the shortest route by making sure that
        // the difference between the targetYaw and the actual is 180 degrees
        // in either direction
        var diff = this._targetYaw - this._yaw;
        var reminder = diff % 360;
        if (reminder > 180) {
            this._targetYaw = this._yaw - (360 - reminder);
        } else if (reminder < -180) {
            this._targetYaw = this._yaw + (360 + reminder);
        } else {
            this._targetYaw = this._yaw + reminder;
        }
    }
});

// Property to get and set the world position of the pivot point that the camera orbits around
Object.defineProperty(OrbitCamera.prototype, "pivotPoint", {
    get: function() {
        return this._pivotPoint;
    },

    set: function(value) {
        this._pivotPoint.copy(value);
    }
});

// Moves the camera to look at an entity and all its children so they are all in the view
OrbitCamera.prototype.focus = function (focusEntity) {
    // Calculate an bounding box that encompasses all the models to frame in the camera view
    this._buildAabb(focusEntity, 0);

    var halfExtents = this._modelsAabb.halfExtents;

    var distance = Math.max(halfExtents.x, Math.max(halfExtents.y, halfExtents.z));
    distance = (distance / Math.tan(0.5 * this.entity.camera.fov * pc.math.DEG_TO_RAD));
    distance = (distance * 2);

    this.distance = distance;

    this._removeInertia();

    this._pivotPoint.copy(this._modelsAabb.center);
};

OrbitCamera.distanceBetween = new pc.Vec3();

// Set the camera position to a world position and look at a world position
// Useful if you have multiple viewing angles to swap between in a scene
OrbitCamera.prototype.resetAndLookAtPoint = function (resetPoint, lookAtPoint) {
    this.pivotPoint.copy(lookAtPoint);
    this.entity.setPosition(resetPoint);

    this.entity.lookAt(lookAtPoint);

    var distance = OrbitCamera.distanceBetween;
    distance.sub2(lookAtPoint, resetPoint);
    this.distance = distance.length();

    this.pivotPoint.copy(lookAtPoint);

    var cameraQuat = this.entity.getRotation();
    this.yaw = this._calcYaw(cameraQuat);
    this.pitch = this._calcPitch(cameraQuat, this.yaw);

    this._removeInertia();
    this._updatePosition();
};

// Set camera position to a world position and look at an entity in the scene
// Useful if you have multiple models to swap between in a scene
OrbitCamera.prototype.resetAndLookAtEntity = function (resetPoint, entity) {
    this._buildAabb(entity, 0);
    this.resetAndLookAtPoint(resetPoint, this._modelsAabb.center);
};

// Set the camera at a specific, yaw, pitch and distance without inertia (instant cut)
OrbitCamera.prototype.reset = function (yaw, pitch, distance) {
    this.pitch = pitch;
    this.yaw = yaw;
    this.distance = distance;

    this._removeInertia();
};

/////////////////////////////////////////////////////////////////////////////////////////////
// Private methods

OrbitCamera.prototype.initialize = function () {
    var self = this;
    var onWindowResize = function () {
        self._checkAspectRatio();
    };

    window.addEventListener('resize', onWindowResize, false);

    this._checkAspectRatio();

    // Find all the models in the scene that are under the focused entity
    this._modelsAabb = new pc.BoundingBox();
    this._buildAabb(this.focusEntity || this.app.root, 0);

    this.entity.lookAt(this._modelsAabb.center);

    this._pivotPoint = new pc.Vec3();
    this._pivotPoint.copy(this._modelsAabb.center);

    // Calculate the camera euler angle rotation around x and y axes
    // This allows us to place the camera at a particular rotation to begin with in the scene
    var cameraQuat = this.entity.getRotation();

    // Preset the camera
    this._yaw = this._calcYaw(cameraQuat);
    this._pitch = this._clampPitchAngle(this._calcPitch(cameraQuat, this._yaw));
    this.entity.setLocalEulerAngles(this._pitch, this._yaw, 0);

    this._distance = 0;

    this._targetYaw = this._yaw;
    this._targetPitch = this._pitch;

    // If we have ticked focus on start, then attempt to position the camera where it frames
    // the focused entity and move the pivot point to entity's position otherwise, set the distance
    // to be between the camera position in the scene and the pivot point
    if (this.frameOnStart) {
        this.focus(this.focusEntity || this.app.root);
    } else {
        var distanceBetween = new pc.Vec3();
        distanceBetween.sub2(this.entity.getPosition(), this._pivotPoint);
        this._distance = this._clampDistance(distanceBetween.length());
    }

    this._targetDistance = this._distance;

    // Reapply the clamps if they are changed in the editor
    this.on('attr:distanceMin', function (value, prev) {
        this._distance = this._clampDistance(this._distance);
    });

    this.on('attr:distanceMax', function (value, prev) {
        this._distance = this._clampDistance(this._distance);
    });

    this.on('attr:pitchAngleMin', function (value, prev) {
        this._pitch = this._clampPitchAngle(this._pitch);
    });

    this.on('attr:pitchAngleMax', function (value, prev) {
        this._pitch = this._clampPitchAngle(this._pitch);
    });

    // Focus on the entity if we change the focus entity
    this.on('attr:focusEntity', function (value, prev) {
        if (this.frameOnStart) {
            this.focus(value || this.app.root);
        } else {
            this.resetAndLookAtEntity(this.entity.getPosition(), value || this.app.root);
        }
    });

    this.on('attr:frameOnStart', function (value, prev) {
        if (value) {
            this.focus(this.focusEntity || this.app.root);
        }
    });

    this.on('destroy', function() {
        window.removeEventListener('resize', onWindowResize, false);
    });
};

OrbitCamera.prototype.update = function(dt) {
    // Add inertia, if any
    var t = this.inertiaFactor === 0 ? 1 : Math.min(dt / this.inertiaFactor, 1);
    this._distance = pc.math.lerp(this._distance, this._targetDistance, t);
    this._yaw = pc.math.lerp(this._yaw, this._targetYaw, t);
    this._pitch = pc.math.lerp(this._pitch, this._targetPitch, t);

    this._updatePosition();
};

OrbitCamera.prototype._updatePosition = function () {
    // Work out the camera position based on the pivot point, pitch, yaw and distance
    this.entity.setLocalPosition(0,0,0);
    this.entity.setLocalEulerAngles(this._pitch, this._yaw, 0);

    var position = this.entity.getPosition();
    position.copy(this.entity.forward);
    position.scale(-this._distance);
    position.add(this.pivotPoint);
    this.entity.setPosition(position);
};

OrbitCamera.prototype._removeInertia = function () {
    this._yaw = this._targetYaw;
    this._pitch = this._targetPitch;
    this._distance = this._targetDistance;
};

OrbitCamera.prototype._checkAspectRatio = function () {
    var height = this.app.graphicsDevice.height;
    var width = this.app.graphicsDevice.width;

    // Match the axis of FOV to match the aspect ratio of the canvas so
    // the focused entities is always in frame
    this.entity.camera.horizontalFov = height > width;
};

OrbitCamera.prototype._buildAabb = function (entity, modelsAdded) {
    var i = 0;

    if (entity.model) {
        var mi = entity.model.meshInstances;
        for (i = 0; i < mi.length; i++) {
            if (modelsAdded === 0) {
                this._modelsAabb.copy(mi[i].aabb);
            } else {
                this._modelsAabb.add(mi[i].aabb);
            }

            modelsAdded += 1;
        }
    }

    for (i = 0; i < entity.children.length; ++i) {
        modelsAdded += this._buildAabb(entity.children[i], modelsAdded);
    }

    return modelsAdded;
};

OrbitCamera.prototype._calcYaw = function (quat) {
    var transformedForward = new pc.Vec3();
    quat.transformVector(pc.Vec3.FORWARD, transformedForward);

    return Math.atan2(-transformedForward.x, -transformedForward.z) * pc.math.RAD_TO_DEG;
};

OrbitCamera.prototype._clampDistance = function (distance) {
    if (this.distanceMax > 0) {
        return pc.math.clamp(distance, this.distanceMin, this.distanceMax);
    } else {
        return Math.max(distance, this.distanceMin);
    }
};

OrbitCamera.prototype._clampPitchAngle = function (pitch) {
    // Negative due as the pitch is inversed since the camera is orbiting the entity
    return pc.math.clamp(pitch, -this.pitchAngleMax, -this.pitchAngleMin);
};

OrbitCamera.quatWithoutYaw = new pc.Quat();
OrbitCamera.yawOffset = new pc.Quat();

OrbitCamera.prototype._calcPitch = function(quat, yaw) {
    var quatWithoutYaw = OrbitCamera.quatWithoutYaw;
    var yawOffset = OrbitCamera.yawOffset;

    yawOffset.setFromEulerAngles(0, -yaw, 0);
    quatWithoutYaw.mul2(yawOffset, quat);

    var transformedForward = new pc.Vec3();

    quatWithoutYaw.transformVector(pc.Vec3.FORWARD, transformedForward);

    return Math.atan2(transformedForward.y, -transformedForward.z) * pc.math.RAD_TO_DEG;
};<|endoftext|>


<commit_before>const express = require('express');
const expressNunjucks = require('express-nunjucks');
const winston = require('winston');

const cloudData = require('./cloud_data');
const { getScriptUrls } = require('./assets');

const logger = new (winston.Logger)({
  transports: [
    new (winston.transports.Console)({ timestamp: true, colorize: true }),
  ],
});

const app = express();
const isDev = app.get('env') === 'development';

app.enable('trust proxy');
app.set('views', `${__dirname}/templates`);
app.use(express.static('public'));

expressNunjucks(app, {
  watch: isDev,
  noCache: isDev,
});

app.get('/', (req, res) => {
  res.render('index', {
    isDev,
    scriptUrls: getScriptUrls(),
    canonicalUrl: 'https://www.meteorshowers.org/',

    cloudDataJson: JSON.stringify(cloudData),
  });
});

app.get('/view/:shower', (req, res) => {
  let showerId = req.params.shower;
  if (showerId.startsWith('iau-')) {
    Object.keys(cloudData).map(key => {
      const cloud = cloudData[key];
      if (showerId.endsWith('-' + cloud.iau_number)) {
        showerId = cloud.name;
      }
    });
  }

  res.render('index', {
    shower: showerId,

    isDev,
    scriptUrls: getScriptUrls(),

    canonicalUrl: `https://www.meteorshowers.org/view/${showerId}`,

    showerCloud: cloudData[showerId],
    cloudDataJson: JSON.stringify(cloudData),
  });
});

const port = process.env.PORT || 8988;
const server = app.listen(port);
logger.info('NODE_ENV:', process.env.NODE_ENV);
logger.info('Running on port', port);

const gracefulShutdown = function gracefulShutdown() {
  logger.info('Received kill signal, shutting down gracefully.');
  server.close(() => {
    logger.info('Closed out remaining connections.');
    process.exit();
  });
  setTimeout(() => {
    logger.error('Could not close connections in time, forcefully shutting down');
    process.exit();
  }, 10 * 1000);
};

// Listen for TERM signal .e.g. kill
process.on('SIGTERM', gracefulShutdown);

// Listen for INT signal e.g. Ctrl-C
process.on('SIGINT', gracefulShutdown);

module.exports = app;
<commit_msg>Default to Perseids<commit_after>const express = require('express');
const expressNunjucks = require('express-nunjucks');
const winston = require('winston');

const cloudData = require('./cloud_data');
const { getScriptUrls } = require('./assets');

const logger = new (winston.Logger)({
  transports: [
    new (winston.transports.Console)({ timestamp: true, colorize: true }),
  ],
});

const app = express();
const isDev = app.get('env') === 'development';

app.enable('trust proxy');
app.set('views', `${__dirname}/templates`);
app.use(express.static('public'));

expressNunjucks(app, {
  watch: isDev,
  noCache: isDev,
});

app.get('/', (req, res) => {
  res.render('index', {
    isDev,
    scriptUrls: getScriptUrls(),
    canonicalUrl: 'https://www.meteorshowers.org/',

    // Default to Perseids - Javascript will override to next shower.
    showerCloud: cloudData['Perseids'],
    cloudDataJson: JSON.stringify(cloudData),
  });
});

app.get('/view/:shower', (req, res) => {
  let showerId = req.params.shower;
  if (showerId.startsWith('iau-')) {
    Object.keys(cloudData).map(key => {
      const cloud = cloudData[key];
      if (showerId.endsWith('-' + cloud.iau_number)) {
        showerId = cloud.name;
      }
    });
  }

  res.render('index', {
    shower: showerId,

    isDev,
    scriptUrls: getScriptUrls(),

    canonicalUrl: `https://www.meteorshowers.org/view/${showerId}`,

    showerCloud: cloudData[showerId],
    cloudDataJson: JSON.stringify(cloudData),
  });
});

const port = process.env.PORT || 8988;
const server = app.listen(port);
logger.info('NODE_ENV:', process.env.NODE_ENV);
logger.info('Running on port', port);

const gracefulShutdown = function gracefulShutdown() {
  logger.info('Received kill signal, shutting down gracefully.');
  server.close(() => {
    logger.info('Closed out remaining connections.');
    process.exit();
  });
  setTimeout(() => {
    logger.error('Could not close connections in time, forcefully shutting down');
    process.exit();
  }, 10 * 1000);
};

// Listen for TERM signal .e.g. kill
process.on('SIGTERM', gracefulShutdown);

// Listen for INT signal e.g. Ctrl-C
process.on('SIGINT', gracefulShutdown);

module.exports = app;
<|endoftext|>


<commit_before>$(document).ready(function() {

  for (var key in localStorage) {
    var favParkInfo = "
" + key + "	NPS Webite Link	<button type='button' class='remove waves-effect waves-light btn'>Remove</button>
"
    $('table tbody').append(favParkInfo)
  }

  $('.remove').on('click', function() {
    var tableBody = $('tbody')[0]
    var tableRow = $('tr')[0]
    tableRow.parentElement.removeChild(tableRow)

    // localStorage.removeItem(key, localStorage[key])
  })

})
<commit_msg>remove button works<commit_after>$(document).ready(function() {

  for (var key in localStorage) {
    var favParkInfo = "
" + key + "	NPS Webite Link	<button type='button' class='remove waves-effect waves-light btn'>Remove</button>
"
    $('table tbody').append(favParkInfo)
  }

  $('.remove').on('click', function() {
    var tableRow = $('tr')[1]
    tableRow.parentElement.removeChild(tableRow)
    localStorage.removeItem(key, localStorage[key])
  })

})
<|endoftext|>


<commit_before>Drac.Game = function(game) {
    score = 0;
    lives = 0;
};

/**
 * No preload function required here as that is handled in the Drac.Loader state.
 *
 * @type {Object}
 */
Drac.Game.prototype = {
    create: function() {
        this.game.physics.startSystem(Phaser.Physics.ARCADE);
        //this.createFloor();

        //MAP
        this.map = this.game.add.tilemap('map');
        this.map.addTilesetImage('level');
        this.layer = this.map.createLayer('Tile Layer 1');
        this.map.setCollisionBetween(1, 5);
        //this.layer.resizeWorld();

        // Player
        this.player = this.game.add.sprite(32, this.game.world.height-100, 'you');
        this.game.physics.arcade.enable(this.player);
        this.player.body.bounce.y = 0.1;
        this.player.body.gravity.y = 9.5;
        this.player.body.collideWorldBounds = true;

        this.key_space = this.game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);

        this.key_c = this.game.input.keyboard.addKey(Phaser.Keyboard.C);
        this.key_c.onDown.add(this.castSpell, this);

        this.setupAnimations();

        this.cursors = this.game.input.keyboard.createCursorKeys();

    },
    update: function() {
        this.game.physics.arcade.collide(this.player, this.map);

        this.player.body.velocity.x = 0;

        if (this.cursors.left.isDown){
            this.player.body.velocity.x = -100;
            this.player.animations.play('left');
        }else if(this.cursors.right.isDown){
            this.player.body.velocity.x = 100;
            this.player.animations.play('right');
        }else if(this.cursors.up.isDown){
            this.player.animations.play('cast-large-right');
        }else if(this.key_c.isDown){
            this.player.animations.play('cast-large-left');
        }else{
            this.player.animations.stop();
            this.player.frame = 78;
        }
    },
    touchDrac: function() {

    },
    hitDrac: function() {

    },
    quitGame: function(){
        this.state.start('Menu');
    },
    createFloor: function(){
        // Init vector shape
        this.ground = this.game.add.graphics(0, 0);
        this.ground.lineStyle(2, 0x663300, 1);
        this.ground.beginFill(0x331900, 1);

        // Draw shape
        this.ground.drawRect(-2, this.game.world.height-20, this.game.world.width+20, 20);
        this.ground.endFill();
        this.game.physics.enable(this.ground);
        this.ground.enableBody = true;
        //This doesn't work - :(
        this.ground.body.immovable = true;
    },
    setupAnimations: function() {
        // Animations
        this.player.animations.add('left', [117, 118, 119, 120, 121, 122, 123, 124, 125], 10, true); // Row J
        this.player.animations.add('right', [143, 144, 145, 146, 147, 148, 149, 150, 151], 10, true); // Row L

        this.player.animations.add('cast-small-left', [65, 66, 67, 68, 69, 70, 71, 72], 10, false); // Row F
        this.player.animations.add('cast-small-right', [91, 92, 93, 94, 95, 96, 97, 98], 10, false); // Row H

        this.player.animations.add('cast-large-left', [221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233], 10, false); // Row R
        this.player.animations.add('cast-large-right', [247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259], 10, false); // Row T0

        this.player.animations.add('castout-right', [39, 40, 41, 42, 43, 44, 45], 5, false); // Row D
        this.player.animations.add('castout-left', [13, 14, 15, 16, 17, 18, 19], 5, false); // Row B

        this.player.animations.add('slash-left', [169,170, 171, 172, 173, 174, 175], 10, true); //Row N
        this.player.animations.add('slash-right', [195, 196, 197, 198, 199, 200], 10, true); // Row P

        this.player.animations.add('player-die', [260, 261, 262, 263, 264, 265], 5, false); // Row U
    },
    castSpell: function(){
        this.player.animations.stop();
        this.player.animations.play('castout-right');
    }
};<commit_msg>Adding up and left as keydown to see if poss to work in unison<commit_after>Drac.Game = function(game) {
    score = 0;
    lives = 0;
};

/**
 * No preload function required here as that is handled in the Drac.Loader state.
 *
 * @type {Object}
 */
Drac.Game.prototype = {
    create: function() {
        this.game.physics.startSystem(Phaser.Physics.ARCADE);
        //this.createFloor();

        //MAP
        this.map = this.game.add.tilemap('map');
        this.map.addTilesetImage('level');
        this.layer = this.map.createLayer('Tile Layer 1');
        this.map.setCollisionBetween(1, 5);
        //this.layer.resizeWorld();

        // Player
        this.player = this.game.add.sprite(32, this.game.world.height-100, 'you');
        this.game.physics.arcade.enable(this.player);
        this.player.body.bounce.y = 0.1;
        this.player.body.gravity.y = 9.5;
        this.player.body.collideWorldBounds = true;

        this.key_space = this.game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);

        this.key_c = this.game.input.keyboard.addKey(Phaser.Keyboard.C);
        this.key_c.onDown.add(this.castSpell, this);

        this.setupAnimations();

        this.cursors = this.game.input.keyboard.createCursorKeys();

    },
    update: function() {
        this.game.physics.arcade.collide(this.player, this.map);

        this.player.body.velocity.x = 0;

        if (this.cursors.left.isDown){
            this.player.body.velocity.x = -100;
            this.player.animations.play('left');
        }else if(this.cursors.right.isDown){
            this.player.body.velocity.x = 100;
            this.player.animations.play('right');
        }else if(this.cursors.up.isDown && this.cursors.left.isDown){
            this.player.animations.play('cast-large-right');
        }else if(this.key_c.isDown){
            this.player.animations.play('cast-large-left');
        }else{
            this.player.animations.stop();
            this.player.frame = 78;
        }
    },
    touchDrac: function() {

    },
    hitDrac: function() {

    },
    quitGame: function(){
        this.state.start('Menu');
    },
    createFloor: function(){
        // Init vector shape
        this.ground = this.game.add.graphics(0, 0);
        this.ground.lineStyle(2, 0x663300, 1);
        this.ground.beginFill(0x331900, 1);

        // Draw shape
        this.ground.drawRect(-2, this.game.world.height-20, this.game.world.width+20, 20);
        this.ground.endFill();
        this.game.physics.enable(this.ground);
        this.ground.enableBody = true;
        //This doesn't work - :(
        this.ground.body.immovable = true;
    },
    setupAnimations: function() {
        // Animations
        this.player.animations.add('left', [117, 118, 119, 120, 121, 122, 123, 124, 125], 10, true); // Row J
        this.player.animations.add('right', [143, 144, 145, 146, 147, 148, 149, 150, 151], 10, true); // Row L

        this.player.animations.add('cast-small-left', [65, 66, 67, 68, 69, 70, 71, 72], 10, false); // Row F
        this.player.animations.add('cast-small-right', [91, 92, 93, 94, 95, 96, 97, 98], 10, false); // Row H

        this.player.animations.add('cast-large-left', [221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233], 10, false); // Row R
        this.player.animations.add('cast-large-right', [247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259], 10, false); // Row T0

        this.player.animations.add('castout-right', [39, 40, 41, 42, 43, 44, 45], 5, false); // Row D
        this.player.animations.add('castout-left', [13, 14, 15, 16, 17, 18, 19], 5, false); // Row B

        this.player.animations.add('slash-left', [169,170, 171, 172, 173, 174, 175], 10, true); //Row N
        this.player.animations.add('slash-right', [195, 196, 197, 198, 199, 200], 10, true); // Row P

        this.player.animations.add('player-die', [260, 261, 262, 263, 264, 265], 5, false); // Row U
    },
    castSpell: function(){
        this.player.animations.stop();
        this.player.animations.play('castout-right');
    }
};<|endoftext|>


<commit_before>/**
 * Created by JarvisWalker on 17/12/16.
 */

// I have been following this tutorial
// http://www.williammalone.com/articles/create-html5-canvas-javascript-sprite-animation/

var canvas = document.getElementById("ctx");
var context = canvas.getContext("2d");
var sprites = new Image();
sprites.src = "sprites/spritesheet.png";

function sprite(options) {
    var self = {};
    var frameIndex = 0;
    var tickCount = 0;
    ticksPerFrame = options.ticksPerFrame || 0;
    numberOfFrames = options.numberOfFrames || 1;

    self.context = options.context;
    self.width = options.width;
    self.height = options.height;
    self.image = options.image;
    self.togglePosition = options.togglePosition;

    self.render = function() {
        self.context.clearRect(15, 0, self.width, self.height);
        self.context.drawImage(
            self.image,
            self.width, // x position
            self.height - self.height * frameIndex, // the next sprite is 16 pixels down
            self.width -1, // width on spritesheet
            self.height -1,// height on spritesheet
            15,
            0,
            self.width -1, // width on canvas
            self.height -1// height on canvas
        )
    };

    self.update = function() {
        tickCount++;

        if (tickCount > ticksPerFrame) {
            tickCount = 0;
            if (frameIndex < numberOfFrames - 1)
                frameIndex++;
            else {
                frameIndex = 0;
            }
        }
    };

    return self;
}

// animation relies on image loading

// same with the screens

var pet = sprite({
    context: context,
    width: 16,
    height: 16,
    image: sprites,
    togglePosition: {x: 15, y: 0},
    ticksPerFrame: 20,
    numberOfFrames: 2
});

sprites.onload = function() {
    loopPet();
};

var animationFrame = null;

function loopPet() {
    animationFrame = requestAnimationFrame(loopPet);
    pet.update();
    pet.render();
}

function screen(options) {
    var self = {};
    self.home = options.home;
    self.left = options.left;
    self.right = options.right;
    self.screenPos = options.screenPos;
    self.image = options.image;
    self.context = options.context;

    self.processKeyDown = function(event) {
        switch(event.keyCode) {
            case 37: // left
                self.left();
                self.render();
                break;
            case 38: // up
                //self.home.update();
                self.render();
                break;
            case 39: // right
                self.right();
                self.render();
                break;
            case 40: // down
                self.context.clearRect(0, 0, self.context.canvas.width, self.context.canvas.height);
                console.log(self.context);
                loopPet();
                break;
            default:
                console.log("TEST");
        }
    };

    self.render = function() {
        self.context.clearRect(0, 0, self.context.canvas.width, self.context.canvas.height);
        self.context.drawImage(self.image, 0, 0);
    };

    self.image.onload = function(){
        console.log("LOADED MAP");
        console.log(this);
    };

    return self;
}

var mapIMG = new Image();
mapIMG.src = "sprites/map-screen.png";

var mapScreen = screen({
    home: this,
    left: function(){console.log("Hello")},
    right: function(){console.log("World")},
    screenPos: {x: 3, y: 3},
    image: mapIMG,
    context: context
});

// need onkeydown for arrow keys left:37, up:38, right:39, down:40
function processKeyDown(event) {
    //console.log(event.keyCode);
    cancelAnimationFrame(animationFrame);
    //context.clearRect(0, 0, self.context.width, self.context.height);
    //mapScreen.render();
    mapScreen.processKeyDown(event);

}

//console.log(pet);

// http://jsfiddle.net/Q98xZ/16/?utm_source=website&utm_medium=embed&utm_campaign=Q98xZ
// full description of canvas and how to use it well
// https://codepo8.github.io/canvas-images-and-pixels/<commit_msg>Starting to optimise the screens<commit_after>/**
 * Created by JarvisWalker on 17/12/16.
 */

// I have been following this tutorial
// http://www.williammalone.com/articles/create-html5-canvas-javascript-sprite-animation/

var canvas = document.getElementById("ctx");
var context = canvas.getContext("2d");
var sprites = new Image();
sprites.src = "sprites/spritesheet.png";

function sprite(options) {
    var self = {};
    var frameIndex = 0;
    var tickCount = 0;
    ticksPerFrame = options.ticksPerFrame || 0;
    numberOfFrames = options.numberOfFrames || 1;

    self.context = options.context;
    self.width = options.width;
    self.height = options.height;
    self.image = options.image;
    self.togglePosition = options.togglePosition;

    self.render = function() {
        self.context.clearRect(15, 0, self.width, self.height);
        self.context.drawImage(
            self.image,
            self.width, // x position
            self.height - self.height * frameIndex, // the next sprite is 16 pixels down
            self.width -1, // width on spritesheet
            self.height -1,// height on spritesheet
            15,
            0,
            self.width -1, // width on canvas
            self.height -1// height on canvas
        )
    };

    self.update = function() {
        tickCount++;

        if (tickCount > ticksPerFrame) {
            tickCount = 0;
            if (frameIndex < numberOfFrames - 1)
                frameIndex++;
            else {
                frameIndex = 0;
            }
        }
    };

    return self;
}

// animation relies on image loading

// same with the screens

var pet = sprite({
    context: context,
    width: 16,
    height: 16,
    image: sprites,
    togglePosition: {x: 15, y: 0},
    ticksPerFrame: 20,
    numberOfFrames: 2
});

sprites.onload = function() {
    loopPet();
};

var animationFrame = null;

function loopPet() {
    animationFrame = requestAnimationFrame(loopPet);
    pet.update();
    pet.render();
}

function screen(options) {
    var self = {};
    self.up = options.up;
    self.left = options.left;
    self.right = options.right;
    self.down = options.down;
    self.screenPos = options.screenPos;
    self.image = options.image;
    self.context = options.context;

    self.processKeyDown = function(event) {
        switch(event.keyCode) {
            case 37: // left
                return self.left();
            case 38: // up
                return self.up();
            case 39: // right
                return self.right();
            case 40: // down
                return self.down();
            default:
                return false;
        }
    };

    self.render = function() {
        self.context.clearRect(0, 0, self.context.canvas.width, self.context.canvas.height);

        self.context.drawImage(self.image, 0, 0);
    };

    return self;
}

var mapIMG = new Image();
mapIMG.src = "sprites/map-screen.png";

var mapScreen = screen({
    up: function(){
        clearScreen();
        loopPet();
        return false;
    },
    left: function(){console.log("Hello"); return true;},
    right: function(){console.log("World"); return true;},
    down: function() { return true; },
    screenPos: {x: 3, y: 3},
    image: mapIMG,
    context: context
});

function clearScreen() {
    console.log(context.canvas.width, context.canvas.height);
    context.clearRect(0, 0, context.canvas.width, context.canvas.height);
}

// need onkeydown for arrow keys left:37, up:38, right:39, down:40
function processKeyDown(event) {
    //console.log(event.keyCode);
    cancelAnimationFrame(animationFrame);
    var updateScreen = mapScreen.processKeyDown(event);
    console.log(updateScreen);
    if (updateScreen)
        mapScreen.render();

}

//console.log(pet);

// http://jsfiddle.net/Q98xZ/16/?utm_source=website&utm_medium=embed&utm_campaign=Q98xZ
// full description of canvas and how to use it well
// https://codepo8.github.io/canvas-images-and-pixels/<|endoftext|>


<commit_before>/*global window:false*/
import _ from 'lodash';

import {EPSILON} from './const';
import {maxIterations, animationStepDuration, tileSize} from './config';
import * as print from './print';

const velocityI = {
  '>': 1,
  '^': 0,
  '<': -1,
  'v': 0,
};
const velocityJ = {
  '>': 0,
  '^': -1, // TODO when changing (i,j) to cartesian, change it to 1
  '<': 0,
  'v': 1, // TODO when changing (i,j) to cartesian, change it to -1
};

class Particle {
  constructor(i, j, to, re, im) {
    this.i = i;
    this.j = j;
    this.to = to;
    this.re = re;
    this.im = im;
  }
  get startX() {
    return tileSize * this.i + tileSize / 2;
  }
  get endX() {
    return tileSize * (this.i + velocityI[this.to[0]]) + tileSize / 2;
  }
  get startY() {
    return tileSize * this.j + tileSize / 2;
  }
  get endY() {
    return tileSize * (this.j + velocityJ[this.to[0]]) + tileSize / 2;
  }
}

class ParticleAnimation {
  constructor(history, board) {
    this.history = history;
    this.board = board;
    this.stepNo = 0;
    this.particleGroup = this.board.svg
      .select('.particles');
    this.currentTimeout = 0;
  }
  stop() {
    window.clearTimeout(this.currentTimeout);
  }
  play() {
    this.nextFrame();
  }
  nextFrame() {
    this.updateParticles();
    this.stepNo++;
    if (this.stepNo < this.history.length - 1) {
      this.currentTimeout = window.setTimeout(
        this.nextFrame.bind(this),
        animationStepDuration
      );
    }
  }
  updateParticles() {
    const particles = this.particleGroup
      .selectAll('.particle')
      .data(this.history[this.stepNo]);
    particles
      .exit()
      .remove();
    particles
      .enter()
      .append('use')
      .attr({
        'xlink:href': '#particle',
        'class': 'particle'
      });
    particles
      .attr({
        transform: (d) => `translate(${d.startX},${d.startY})`
      })
      .style({
        opacity: (d) => d.re * d.re + d.im * d.im
      });
    particles
      .transition()
      .ease([0, 1])
      .duration(animationStepDuration)
      .attr('transform', (d) => `translate(${d.endX},${d.endY})`);
  }
}

export class Particles {
  constructor(board) {
    this.board = board;
    this.history = [];
  }

  /**
   * Clear history and make it one-element list
   * containing initial particles state.
   */
  initialize() {
    const initialState =
      _.reduce(_.range(this.board.level.width), (accI, i) => {
        return _.reduce(_.range(this.board.level.height), (accJ, j) => {
          // Recognize generating tiles by having 'generation' method
          if (!this.board.tileMatrix[i][j].type.generation) {
            return accJ;
          }
          const emissions =
            this.board.tileMatrix[i][j].type.generation(
              this.board.tileMatrix[i][j].rotation
            );
          _.forEach(emissions, (emission) => {
            accJ.push(new Particle(i, j, emission.to, emission.re, emission.im));
          });
          return accJ;
        }, accI);
      }, []);
    this.history = [initialState];
  }

  /**
   * Make one propagation step and save it in history.
   * Additionally, return it.
   */
  propagate() {
    const lastState = _.last(this.history);
    const newState = this.interact(this.displace(lastState));
    this.history.push(newState);
    return newState;
  }

  /**
   * Creates a new state basing on input state, with particles
   * moved according to their directions.
   */
  displace(state) {
    return _.map(state, (entry) => {
      // 'to' value = direction + polarization
      const dir = entry.to[0];
      const newI = entry.i + velocityI[dir];
      const newJ = entry.j + velocityJ[dir];
      return new Particle(newI, newJ, entry.to, entry.re, entry.im);
    });
  }

  /**
   * Creates a new state basing on input state, applying probability
   * function changes from tiles' interactions.
   */
  interact(state) {
    // Collect all transitions into bins. Each bin will be labeled
    // with position (i, j) and momentum direction.
    const bins = _.reduce(state, (acc, entry) => {
      // Check if particle is out of bound
      if (
           entry.i < 0 || entry.i >= this.board.level.width
        || entry.j < 0 || entry.j >= this.board.level.height
      ) {
        return acc;
      }
      const tile = this.board.tileMatrix[entry.i][entry.j];
      const transition = tile.transitionAmplitudes;
      _.each(transition[entry.to], (change) => {
        const binKey = [entry.i, entry.j, change.to].join('_');
        // (a + bi)(c + di) = (ac - bd) + i(ad + bc)
        const re = entry.re * change.re - entry.im * change.im;
        const im = entry.re * change.im + entry.im * change.re;
        // Add to bin
        if (_.has(acc, binKey)) {
          acc[binKey].re += re;
          acc[binKey].im += im;
        } else {
          acc[binKey] = new Particle(entry.i, entry.j, change.to, re, im);
        }
      });
      return acc;
    }, {});
    // Remove keys; filter out zeroes
    return _.values(bins).filter((entry) =>
      entry.re * entry.re + entry.im * entry.im > EPSILON
    );
  }

  /**
   * Propagate until:
   * - all probabilities go to 0
   * - iteration limit is reached
   */
  propagateToEnd() {
    let stepNo, lastStep;
    for (stepNo = 0; stepNo < maxIterations; ++stepNo) {
      lastStep = this.propagate();
      if (!lastStep.length) {
        break;
      }
    }
  }

  /**
   * Generate history and play animation.
   */
  play() {
    this.initialize();
    this.propagateToEnd();

    this.history.forEach((state) => {
      console.log(print.stateToStr(state));
    });

    if (this.particleAnimation) {
      this.particleAnimation.stop();
    }
    this.particleAnimation = new ParticleAnimation(this.history, this.board);
    this.particleAnimation.play();
  }
}
<commit_msg>feat(particles): stopping removes particles from svg<commit_after>/*global window:false*/
import _ from 'lodash';

import {EPSILON} from './const';
import {maxIterations, animationStepDuration, tileSize} from './config';
import * as print from './print';

const velocityI = {
  '>': 1,
  '^': 0,
  '<': -1,
  'v': 0,
};
const velocityJ = {
  '>': 0,
  '^': -1, // TODO when changing (i,j) to cartesian, change it to 1
  '<': 0,
  'v': 1, // TODO when changing (i,j) to cartesian, change it to -1
};

class Particle {
  constructor(i, j, to, re, im) {
    this.i = i;
    this.j = j;
    this.to = to;
    this.re = re;
    this.im = im;
  }
  get startX() {
    return tileSize * this.i + tileSize / 2;
  }
  get endX() {
    return tileSize * (this.i + velocityI[this.to[0]]) + tileSize / 2;
  }
  get startY() {
    return tileSize * this.j + tileSize / 2;
  }
  get endY() {
    return tileSize * (this.j + velocityJ[this.to[0]]) + tileSize / 2;
  }
}

class ParticleAnimation {
  constructor(history, board) {
    this.history = history;
    this.board = board;
    this.stepNo = 0;
    this.particleGroup = this.board.svg
      .select('.particles');
    this.currentTimeout = 0;
  }
  stop() {
    window.clearTimeout(this.currentTimeout);
    this.particleGroup
      .selectAll('.particle')
      .remove();
  }
  play() {
    this.nextFrame();
  }
  nextFrame() {
    this.updateParticles();
    this.stepNo++;
    if (this.stepNo < this.history.length - 1) {
      this.currentTimeout = window.setTimeout(
        this.nextFrame.bind(this),
        animationStepDuration
      );
    }
  }
  updateParticles() {
    const particles = this.particleGroup
      .selectAll('.particle')
      .data(this.history[this.stepNo]);
    particles
      .exit()
      .remove();
    particles
      .enter()
      .append('use')
      .attr({
        'xlink:href': '#particle',
        'class': 'particle'
      });
    particles
      .attr({
        transform: (d) => `translate(${d.startX},${d.startY})`
      })
      .style({
        opacity: (d) => d.re * d.re + d.im * d.im
      });
    particles
      .transition()
      .ease([0, 1])
      .duration(animationStepDuration)
      .attr('transform', (d) => `translate(${d.endX},${d.endY})`);
  }
}

export class Particles {
  constructor(board) {
    this.board = board;
    this.history = [];
  }

  /**
   * Clear history and make it one-element list
   * containing initial particles state.
   */
  initialize() {
    const initialState =
      _.reduce(_.range(this.board.level.width), (accI, i) => {
        return _.reduce(_.range(this.board.level.height), (accJ, j) => {
          // Recognize generating tiles by having 'generation' method
          if (!this.board.tileMatrix[i][j].type.generation) {
            return accJ;
          }
          const emissions =
            this.board.tileMatrix[i][j].type.generation(
              this.board.tileMatrix[i][j].rotation
            );
          _.forEach(emissions, (emission) => {
            accJ.push(new Particle(i, j, emission.to, emission.re, emission.im));
          });
          return accJ;
        }, accI);
      }, []);
    this.history = [initialState];
  }

  /**
   * Make one propagation step and save it in history.
   * Additionally, return it.
   */
  propagate() {
    const lastState = _.last(this.history);
    const newState = this.interact(this.displace(lastState));
    this.history.push(newState);
    return newState;
  }

  /**
   * Creates a new state basing on input state, with particles
   * moved according to their directions.
   */
  displace(state) {
    return _.map(state, (entry) => {
      // 'to' value = direction + polarization
      const dir = entry.to[0];
      const newI = entry.i + velocityI[dir];
      const newJ = entry.j + velocityJ[dir];
      return new Particle(newI, newJ, entry.to, entry.re, entry.im);
    });
  }

  /**
   * Creates a new state basing on input state, applying probability
   * function changes from tiles' interactions.
   */
  interact(state) {
    // Collect all transitions into bins. Each bin will be labeled
    // with position (i, j) and momentum direction.
    const bins = _.reduce(state, (acc, entry) => {
      // Check if particle is out of bound
      if (
           entry.i < 0 || entry.i >= this.board.level.width
        || entry.j < 0 || entry.j >= this.board.level.height
      ) {
        return acc;
      }
      const tile = this.board.tileMatrix[entry.i][entry.j];
      const transition = tile.transitionAmplitudes;
      _.each(transition[entry.to], (change) => {
        const binKey = [entry.i, entry.j, change.to].join('_');
        // (a + bi)(c + di) = (ac - bd) + i(ad + bc)
        const re = entry.re * change.re - entry.im * change.im;
        const im = entry.re * change.im + entry.im * change.re;
        // Add to bin
        if (_.has(acc, binKey)) {
          acc[binKey].re += re;
          acc[binKey].im += im;
        } else {
          acc[binKey] = new Particle(entry.i, entry.j, change.to, re, im);
        }
      });
      return acc;
    }, {});
    // Remove keys; filter out zeroes
    return _.values(bins).filter((entry) =>
      entry.re * entry.re + entry.im * entry.im > EPSILON
    );
  }

  /**
   * Propagate until:
   * - all probabilities go to 0
   * - iteration limit is reached
   */
  propagateToEnd() {
    let stepNo, lastStep;
    for (stepNo = 0; stepNo < maxIterations; ++stepNo) {
      lastStep = this.propagate();
      if (!lastStep.length) {
        break;
      }
    }
  }

  /**
   * Generate history and play animation.
   */
  play() {
    this.initialize();
    this.propagateToEnd();

    this.history.forEach((state) => {
      console.log(print.stateToStr(state));
    });

    if (this.particleAnimation) {
      this.particleAnimation.stop();
    }
    this.particleAnimation = new ParticleAnimation(this.history, this.board);
    this.particleAnimation.play();
  }
}
<|endoftext|>


<commit_before>// scene object variables
var renderer, scene, camera;

var geometry, mesh;

// field variables
var fieldWidth = 400, fieldHeight = 200;

function setup()
{

	// set up all the 3D objects in the scene
	createScene();

	// and let's get cracking!
	draw();
}

function createScene()
{
	// set the scene size
	var WIDTH = window.innerWidth*0.98,
	  HEIGHT = window.innerHeight*0.98;

	// set some camera attributes
	var VIEW_ANGLE = 75,
	  ASPECT = WIDTH / HEIGHT,
	  NEAR = 1,
	  FAR = 1000;

	var c = document.getElementById("gameCanvas");

	// create a WebGL renderer, camera
	// and a scene
	renderer = new THREE.WebGLRenderer();
	camera =
	  new THREE.PerspectiveCamera(
		VIEW_ANGLE,
		ASPECT,
		NEAR,
		FAR);
	camera.position.z = 400;
	camera.position.y = 400;
	camera.position.x = -45 * Math.PI/180;

	scene = new THREE.Scene();

	scene.add(camera);
	geometry = new THREE.IcosahedronGeometry(200,1);

	var strangeTexture = THREE.ImageUtils.loadTexture('textures/strange.jpg');
	var strangeMaterial = new THREE.MeshBasicMaterial({map:strangeTexture});

	var spotsTexture = THREE.ImageUtils.loadTexture('textures/spots.jpg');
	var spotsMaterial = new THREE.MeshBasicMaterial({map:spotsTexture});

	var blueTexture = THREE.ImageUtils.loadTexture('textures/blue.jpg');
	var blueMaterial = new THREE.MeshBasicMaterial({map:blueTexture});

	var watercolorTexture = THREE.ImageUtils.loadTexture('textures/watercolor.jpg');
	var watercolorMaterial = new THREE.MeshBasicMaterial({map:watercolorTexture});
	//material = new THREE.MeshBasicMaterial({map:image} );

	//mesh = new THREE.Mesh(geometry, material);
	//scene.add(mesh);

	renderer.setSize(WIDTH, HEIGHT);

	c.appendChild(renderer.domElement);

	var materials = [strangeMaterial, spotsMaterial, blueMaterial, watercolorMaterial];

	materials.forEach(function(material){
		var city = generateCity(100, material);
		scene.add(city);
	});

}
function generateCity(buildingCount, material){

	var geo = new THREE.BoxGeometry(1,1,1);
	geo.applyMatrix(new THREE.Matrix4().makeTranslation(0,0.5,0));
	var cityGeometry = new THREE.Geometry();

	_.range(0,buildingCount).forEach(function(index){

		var building = generateBuilding(geo);
		THREE.GeometryUtils.merge(cityGeometry, building);
	});

	return new THREE.Mesh(cityGeometry, material);

}

function generateBuilding(geo){
	var floor = Math.floor;
	var rnd = Math.random;
	var building = new THREE.Mesh(geo.clone());
	var pos = building.position;
	var scale = building.scale;

	pos.x = floor(rnd()*200 - 100) * 4;
	pos.y = floor(rnd()*200 - 100) * 4;
	scale.x = rnd()*50 +10;
	scale.y = rnd()*scale.x*8 + 8;
	scale.z = scale.x;
	return building;

}

function draw()
{
	requestAnimationFrame(draw);

	renderer.render(scene, camera);

}

<commit_msg>added forg and light source<commit_after>// scene object variables
var renderer, scene, camera;

var geometry, mesh;

// field variables
var fieldWidth = 400, fieldHeight = 200;

function setup()
{

	// set up all the 3D objects in the scene
	createScene();

	// and let's get cracking!
	draw();
}

function createScene()
{
	// set the scene size
	var WIDTH = window.innerWidth*0.98,
	  HEIGHT = window.innerHeight*0.98;

	// set some camera attributes
	var VIEW_ANGLE = 75,
	  ASPECT = WIDTH / HEIGHT,
	  NEAR = 1,
	  FAR = 1000;

	var c = document.getElementById("gameCanvas");

	// create a WebGL renderer, camera
	// and a scene
	renderer = new THREE.WebGLRenderer();
	camera =
	  new THREE.PerspectiveCamera(
		VIEW_ANGLE,
		ASPECT,
		NEAR,
		FAR);
	camera.position.z = 400;
	camera.position.y = 400;
	camera.position.x = -45 * Math.PI/180;

	scene = new THREE.Scene();

	scene.add(camera);
	geometry = new THREE.IcosahedronGeometry(200,1);

	var strangeTexture = THREE.ImageUtils.loadTexture('textures/strange.jpg');
	var strangeMaterial = new THREE.MeshBasicMaterial({map:strangeTexture});

	var spotsTexture = THREE.ImageUtils.loadTexture('textures/spots.jpg');
	var spotsMaterial = new THREE.MeshBasicMaterial({map:spotsTexture});

	var blueTexture = THREE.ImageUtils.loadTexture('textures/blue.jpg');
	var blueMaterial = new THREE.MeshBasicMaterial({map:blueTexture});

	var watercolorTexture = THREE.ImageUtils.loadTexture('textures/watercolor.jpg');
	var watercolorMaterial = new THREE.MeshBasicMaterial({map:watercolorTexture});
	//material = new THREE.MeshBasicMaterial({map:image} );

	//mesh = new THREE.Mesh(geometry, material);
	//scene.add(mesh);

	renderer.setSize(WIDTH, HEIGHT);

	c.appendChild(renderer.domElement);

	var materials = [strangeMaterial, spotsMaterial, blueMaterial, watercolorMaterial];

	materials.forEach(function(material){
		var city = generateCity(100, material);
		scene.add(city);
	});

	var light = new THREE.DirectionalLight(0xee44bb, 1)	;
	light.position.set(1,3,2);
	scene.add(light);

	scene.fog = new THREE.Fog(0x99bbbb, 0.002);

}
function generateCity(buildingCount, material){

	var geo = new THREE.BoxGeometry(1,1,1);
	geo.applyMatrix(new THREE.Matrix4().makeTranslation(0,0.5,0));
	var cityGeometry = new THREE.Geometry();

	_.range(0,buildingCount).forEach(function(index){

		var building = generateBuilding(geo);
		THREE.GeometryUtils.merge(cityGeometry, building);
	});

	return new THREE.Mesh(cityGeometry, material);

}

function generateBuilding(geo){
	var floor = Math.floor;
	var rnd = Math.random;
	var building = new THREE.Mesh(geo.clone());
	var pos = building.position;
	var scale = building.scale;

	pos.x = floor(rnd()*200 - 100) * 4;
	pos.y = floor(rnd()*200 - 100) * 4;
	scale.x = rnd()*50 +10;
	scale.y = rnd()*scale.x*8 + 8;
	scale.z = scale.x;
	return building;

}

function draw()
{
	requestAnimationFrame(draw);

	renderer.render(scene, camera);

}

<|endoftext|>


<commit_before>var frontPage,
    page,
    login,
    USER = require('../utils/USER'),
    Auth = require("../auth");

page = function(req,res){
    res.render('home.html');
}

login = function(req,res){
    var username = req.body.username;
    USER.findUSER(username).then(function(User){
        var user = User[0];
        if (!user) {
            res.render("error.html",{ success: false, message: 'Authentication failed. User not found.' });
        }
        else if (user) {
            // check if password matches
            if (user.password != req.body.password) {
                res.render("error.html",{ success: false, message: 'Authentication failed. Wrong password.' });
            }
            else {
                // if user is found and password is right
                // create a token
                var token = Auth.generateToken(user)
                // return the information including token as JSON
                // res.json({
                //     success: true,
                //     message: 'Enjoy your token!',
                //     token: token
                // });
                res.cookie('auth',token);
                res.json(user.username);
            }
        }
    });
}

frontPage = {
    page : page,
    login : login
}

module.exports = frontPage;<commit_msg>added line29 res.redirect to /Collection/Username<commit_after>var frontPage,
    page,
    login,
    USER = require('../utils/USER'),
    Auth = require("../auth");

page = function(req,res){
    res.render('home.html');
}

login = function(req,res){
    var username = req.body.username;
    USER.findUSER(username).then(function(User){
        var user = User[0];
        if (!user) {
            res.render("error.html",{ success: false, message: 'Authentication failed. User not found.' });
        }
        else if (user) {
            // check if password matches
            if (user.password != req.body.password) {
                res.render("error.html",{ success: false, message: 'Authentication failed. Wrong password.' });
            }
            else {
                // if user is found and password is right
                // create a token
                var token = Auth.generateToken(user)
                res.cookie('auth',token);
                res.redirect('/Collection/'+user.username);
            }
        }
    });
}

frontPage = {
    page : page,
    login : login
}

module.exports = frontPage;<|endoftext|>


<commit_before>$( document ).ready(function(){
	new GameManager();
});

function GameManager(){
	var self = this;
	$(document).keydown(function(event){ if(event.which!=13) self.down(); });
	$('.button').mousedown(function(event){ self.down(); });
	$('.button').on("tap",function(event){ self.down(); });
	$(document).keyup(function(event){
		if(event.which==13){
			self.pause = !self.pause;
			if(self.pause) $('#beep')[0].pause();
		}else{
			self.up();
		}
	});
	$('.button').mouseup(function(event){ self.up(); });
	this.ctx = $('#game')[0].getContext('2d');
	this.ctx.imageSmoothingEnabled= false;
	this.score = 0;
	this.highscore = 0;
	this.speed = 6.0;
	this.pause = false;
	this.track = new morseTrack();
	while(this.track.queue.length<16){
		this.track.randomChar();
	}
	setInterval(function(){ self.gameloop(); }, 1000/30);
}

GameManager.prototype.down = function(){
	if(!this.isDown){
		this.isDown = true;
		$('.button').addClass("active");
	}
};

GameManager.prototype.up = function(){
	this.isDown = false;
	$('.button').removeClass("active");
};

GameManager.prototype.gameloop = function(){
	if(!this.pause){
		this.update();
	}
	this.render();
};

GameManager.prototype.update = function(){
	while(this.track.queue.length<16){
		this.track.randomChar();
	}
	var sig = this.track.queue[0];
	//start/stop beep sound
	var beep = $('#beep')[0];
	if(sig.hidden){
		beep.pause();
	}else if(beep.paused){
		beep.currentTime = 0;
		beep.play();
	}
	//update score
	if((!sig.hidden)&&this.isDown){
		this.score += 10;
	}else if(((!sig.hidden)||this.isDown)&&this.score>=20){
		this.score -= 20;
	}
	if(this.score > this.highscore) this.highscore = this.score;
	$('#score').text(this.score);
	$('#highscore').text(this.highscore);
	sig.time -= this.speed/30;
	console.log(sig.time);
	if(sig.time<0){
		this.track.queue.shift();
	}
};

GameManager.prototype.render = function(){
	this.ctx.clearRect(280, 0, 40, 500);
	this.ctx.fillStyle = "#b4b7b8";
	var qu = this.track.queue;
	for(p = 360,i = 0; i < qu.length&&p >= 0; i++){
		p -= Math.round(qu[i].time*40);
		if(!qu[i].hidden){
			if(qu[i] instanceof dot){
				this.ctx.beginPath();
				this.ctx.arc(300,p+20, 20, 0, Math.PI*2, false);
				this.ctx.fill();
			}else{
				this.ctx.fillRect(280,p,40,qu[i].time*40);
			}
		}
	}
};
<commit_msg>More prevent double-tap<commit_after>$( document ).ready(function(){
	$( document ).on("dbltap", function(event) {
		event.gesture.preventDefault();
	});
	new GameManager();
});

function GameManager(){
	var self = this;
	$(document).keydown(function(event){ if(event.which!=13) self.down(); });
	$('.button').mousedown(function(event){ self.down(); });
	$('.button').on("tap",function(event){ self.down(); });
	$(document).keyup(function(event){
		if(event.which==13){
			self.pause = !self.pause;
			if(self.pause) $('#beep')[0].pause();
		}else{
			self.up();
		}
	});
	$('.button').mouseup(function(event){ self.up(); });
	this.ctx = $('#game')[0].getContext('2d');
	this.ctx.imageSmoothingEnabled= false;
	this.score = 0;
	this.highscore = 0;
	this.speed = 6.0;
	this.pause = false;
	this.track = new morseTrack();
	while(this.track.queue.length<16){
		this.track.randomChar();
	}
	setInterval(function(){ self.gameloop(); }, 1000/30);
}

GameManager.prototype.down = function(){
	if(!this.isDown){
		this.isDown = true;
		$('.button').addClass("active");
	}
};

GameManager.prototype.up = function(){
	this.isDown = false;
	$('.button').removeClass("active");
};

GameManager.prototype.gameloop = function(){
	if(!this.pause){
		this.update();
	}
	this.render();
};

GameManager.prototype.update = function(){
	while(this.track.queue.length<16){
		this.track.randomChar();
	}
	var sig = this.track.queue[0];
	//start/stop beep sound
	var beep = $('#beep')[0];
	if(sig.hidden){
		beep.pause();
	}else if(beep.paused){
		beep.currentTime = 0;
		beep.play();
	}
	//update score
	if((!sig.hidden)&&this.isDown){
		this.score += 10;
	}else if(((!sig.hidden)||this.isDown)&&this.score>=20){
		this.score -= 20;
	}
	if(this.score > this.highscore) this.highscore = this.score;
	$('#score').text(this.score);
	$('#highscore').text(this.highscore);
	sig.time -= this.speed/30;
	console.log(sig.time);
	if(sig.time<0){
		this.track.queue.shift();
	}
};

GameManager.prototype.render = function(){
	this.ctx.clearRect(280, 0, 40, 500);
	this.ctx.fillStyle = "#b4b7b8";
	var qu = this.track.queue;
	for(p = 360,i = 0; i < qu.length&&p >= 0; i++){
		p -= Math.round(qu[i].time*40);
		if(!qu[i].hidden){
			if(qu[i] instanceof dot){
				this.ctx.beginPath();
				this.ctx.arc(300,p+20, 20, 0, Math.PI*2, false);
				this.ctx.fill();
			}else{
				this.ctx.fillRect(280,p,40,qu[i].time*40);
			}
		}
	}
};
<|endoftext|>


<commit_before>var gameScreen = {
  width: 800,
  height: 600
};

var block = {
  width: 64,
  height: 32,
  blockPerRow: function() {
    return parseInt(gameScreen.width / this.width);
  }
};

var blocks;
var platform;
var game = new Phaser.Game(gameScreen.width, gameScreen.height, Phaser.AUTO, '', { preload: preload, create: create, update: update });
var cursor;

function preload() {
  game.load.image("blueBlock" , "assets/blueBlock.png");
  game.load.image("redBlock","assets/redBlock.png");
  game.load.image("greenBlock","assets/greenBlock.png");
  game.load.spritesheet("mediumPlatform","assets/mediumPlatform.png");
}

function create() {
  cursor = game.input.keyboard.createCursorKeys();
  game.physics.startSystem(Phaser.Physics.ARCADE);
  blocks = game.add.group();
  createBlocks("green",24,30);
  //createBlockss("red",15,98);
  //createBlocks("blue",15,130);
  //createBlocks("green",15,162);
  platform = game.add.sprite(20,500,"mediumPlatform");
  game.physics.arcade.enable([platform]);

  platform.body.immovable = true;
}

function update() {

  platform.body.velocity.x = 0;
  if(cursor.left.isDown){
    platform.body.velocity.x = -150;
  }else if(cursor.right.isDown){
    platform.body.velocity.x = 150;
  }
}

function createBlocks(color,number,startHeight,distanceBetweenRow = block.height + 2 ){
  console.log(distanceBetweenRow);
  var height = startHeight;
  var width = 0;
  var blockPerRow = block.blockPerRow();
  for(var i = 1; i <= number ; i++){
      //Check if it need to create a new row
      if((i - 1) != 0  && ((i - 1) % blockPerRow) === 0){
        height += distanceBetweenRow;
        width = 0;
      }

      blocks.create(width,height,color + "Block");
      width += (block.width + 2);
  }
}
<commit_msg>The Ball was added: - Now it bounces around the game world - It collide with the platform and the block<commit_after>var gameScreen = {
  width: 800,
  height: 600
};

var block = {
  width: 64,
  height: 32,
  blockPerRow: function() {
    return parseInt(gameScreen.width / this.width);
  }
};

var blocks;
var platform;
var ball;
var game = new Phaser.Game(gameScreen.width, gameScreen.height, Phaser.AUTO, '', { preload: preload, create: create, update: update });
var cursor;

function preload() {
  game.load.image("blueBlock" , "assets/blueBlock.png");
  game.load.image("redBlock","assets/redBlock.png");
  game.load.image("greenBlock","assets/greenBlock.png");
  game.load.spritesheet("mediumPlatform","assets/mediumPlatform.png");
  game.load.spritesheet("redBall","assets/redBall.png");
}

function create() {
  game.physics.startSystem(Phaser.Physics.ARCADE);
  cursor = game.input.keyboard.createCursorKeys();

  blocks = game.add.group();
  createBlocks("green",24,30);

  platform = game.add.sprite(20,500,"mediumPlatform");

  ball = game.add.sprite(20,500,"redBall");

  game.physics.arcade.enable([blocks,ball,platform]);

  platform.body.collideWorldBounds = true;
  platform.body.immovable = true;
  blocks.enableBody = true;

  ball.body.velocity.setTo(200, 200);
  ball.body.collideWorldBounds = true;
  ball.body.bounce.setTo(1, 1);

}

function update() {
    game.physics.arcade.collide(platform,ball);
    game.physics.arcade.collide(ball,blocks);
  platform.body.velocity.x = 0;
  if(cursor.left.isDown){
    platform.body.velocity.x = -250;
  }else if(cursor.right.isDown){
    platform.body.velocity.x = 250;
  }
}

function createBlocks(color,number,startHeight,distanceBetweenRow = block.height + 2 ){
  console.log(distanceBetweenRow);
  var height = startHeight;
  var width = 0;
  var blockPerRow = block.blockPerRow();
  for(var i = 1; i <= number ; i++){
      //Check if it need to create a new row
      if((i - 1) != 0  && ((i - 1) % blockPerRow) === 0){
        height += distanceBetweenRow;
        width = 0;
      }

      blocks.create(width,height,color + "Block");
      width += (block.width + 2);
  }
}
<|endoftext|>


<commit_before>//====================================================//
//==== Playlist: enter the file names here: ==========//

var myVidsH264=[
   "videos/01-16s_h264.mp4",
   "videos/02-18s_h264.mp4",
   "videos/03-13s_h264.mp4",
   "videos/04-13s_h264.mp4",
   "videos/05-22s_h264.mp4",
   "videos/magenta_test.mp4",
   "videos/magenta_test2.mp4",
]

// var myVidsH264=[
//    "videos/01-16s_h264.mov",
//    "videos/02-18s_h264.mov",
//    "videos/03-13s_h264.mov",
//    "videos/04-13s_h264.mov",
//    "videos/05-22s_h264.mov",
// ]

var myVidsWebm=[
   "videos/01-16s_h264.webm",
   "videos/02-18s_h264.webm",
   "videos/03-13s_h264.webm",
   "videos/04-13s_h264.webm",
   "videos/05-22s_h264.webm",
]

var myVidsOgg=[
   "videos/01-16s_h264.ogv",
   "videos/02-18s_h264.ogv",
   "videos/03-13s_h264.ogv",
   "videos/04-13s_h264.ogv",
   "videos/05-22s_h264.ogv",
]

var randomOrder = true; // done - set to false if playlist order is non-random

///TBD
var autoplayOn = false;  /// what is behavior of autoplay? this doesnt seem like a UI feature
var doubleplay = false; // play each video twice

//=== UI control toggles - set to false to remove from UI
var playOn = true;
var ffOn = true; // uhh
var rwOn = true; // wat
var skipOn = true;
var volumeOn =  true;
var muteOn = true;
var durationOn = true;
var seekOn = true;
var fullscOn = true;

//====================================================//
//===================================================//

// html5 video elements
var vidElements = [];

console.log("modernizr video h264 support? " + Modernizr.video.h264);
console.log("modernizr video webm support? " + Modernizr.video.webm);
console.log("modernizr video ogg support? " + Modernizr.video.ogg);

// this function will randomly shuffle the list of videos upon page load
// from http://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array
function shuffle(array) {
  var currentIndex = array.length, temporaryValue, randomIndex ;

  // While there remain elements to shuffle...
  while (0 !== currentIndex) {

    // Pick a remaining element...
    randomIndex = Math.floor(Math.random() * currentIndex);
    currentIndex -= 1;

    // And swap it with the current element.
    temporaryValue = array[currentIndex];
    array[currentIndex] = array[randomIndex];
    array[randomIndex] = temporaryValue;
  }

  return array;
}

// check the file sizes n the server
function checkSizes(files){
  var fileBytes;
  // from: http://stackoverflow.com/questions/17416274/ajax-get-size-of-file-before-downloading
  for (var i = 0; i < files.length; i++){
    get_filesize(files[i], function(filesize){
      console.log("file of size: "+filesize);
      fileBytes += filesize;
    });

  }
  console.log("total fileBytes: "+fileBytes);

  function get_filesize(url, callback) {
    var xhr = new XMLHttpRequest();
    xhr.open("HEAD", url, true); // Notice "HEAD" instead of "GET",
                                 //  to get only the header
    xhr.onreadystatechange = function() {
        if (this.readyState == this.DONE) {
            callback(parseInt(xhr.getResponseHeader("Content-Length")));
        }
    };
    xhr.send();

    // return fileBytes;
  }

}

// check the video loading progress and update the progress bar
function checkLoad(){
  var percentLoaded = 0;
  var timeoutID = setInterval(function(){
    console.log("videlements.length: "+vidElements.length);
    console.log("myVidsH264.length: "+myVidsH264.length);
    if(percentLoaded < 100){
      percentLoaded = (vidElements.length+1) / (myVidsH264.length+1) * 100;
      $('progress').val(percentLoaded);
    }
    else{
      console.log("percentLoaded: "+percentLoaded);
      clearTimeout(timeoutID);
      $('progress').val(percentLoaded);
      document.getElementById("loading").style.display="none";
      player();
    }
  }, 400);}

// initialize video player and its UI
var player = function(){
  // add videojs player to each video
  for (var i = 0; i < vidElements.length; i++){
    var options = {
      "width": $("#video"+i).prop('videoWidth'),
      "height": $("#video"+i).prop('videoHeight'),
      controlBar: {
        playToggle: (playOn ? true : false),
        fullscreenToggle: (fullscOn ? true : false),
        currentTimeDisplay: (durationOn ? true : false),
        durationDisplay: (durationOn ? true : false),
        remainingTimeDisplay: (durationOn ? true : false),
        timeDivider: (durationOn ? true : false),
        progressControl: {
          seekBar: (seekOn ? true : false),
        },
        volumeControl: (volumeOn ? true : false),
        muteToggle: (muteOn ? true : false),
      },
    };
    // add options for controls based on toggled options or whatever

    addVjs("video"+i, options);

  }

  // video elements
  var vindex = 0;
  var videoPlayer, theVideo;

  // buttons - so they can access each other
  var prevBtn, nextBtn, playBtn;

  // player state
  var playing, speed, ffing, rwing, rwinterval;

  videoPlayer = document.getElementById("video_player");
  theVideo = document.getElementById("video"+vindex);

  videoPlayer.style.display="table";
  theVideo.style.display="block";
  showVjs("video"+vindex);

  // UI controls
  // if(playOn){ playUI(); }
  if(skipOn){ skipUI(); }
  // if(rwOn){ rwUI(-0.1); }
  // if(ffOn){ ffUI(); }
  // if(volumeOn){ volUI(); }

  function skipUI(){
      $("#video_player").append('<button type="button" class="btn_skip" id="btn_prev" disabled> < < </button>');
      $("#video_player").append('<button type="button" class="btn_skip" id="btn_next" > > > </button>');

      prevBtn = document.getElementById("btn_prev");
      nextBtn = document.getElementById("btn_next");

      // iterate through video playlist backwards
      prevBtn.addEventListener("click", function(){
        if(vindex == 0){
          prevBtn.disabled = true;
        }
        else if (vindex == 1) {
          hideVjs("video"+vindex);
          vindex--;
          showVjs("video"+vindex);
          prevBtn.disabled = true;
        }
        else{
          hideVjs("video"+vindex);
          vindex--;
          showVjs("video"+vindex);
          nextBtn.disabled = false;
        }
      })
      // iterate through video playlist forwards
      nextBtn.addEventListener("click", function(){
        if(vindex == vidElements.length-1){
          nextBtn.disabled = true;
        }
        else if(vindex == vidElements.length-2){
          hideVjs("video"+vindex);
          vindex++;
          showVjs("video"+vindex);
          // theVideo.pause();
          prevBtn.disabled = false;
          nextBtn.disabled = true;
        }
        else{
          hideVjs("video"+vindex);
          vindex++;
          showVjs("video"+vindex);
          prevBtn.disabled = false;
        }

      })
    }

  function addVjs(vidElement, options){
    // theVideo = document.getElementById(vidElement);
    var thePlayer = videojs(vidElement, options, function(){
      console.log("initializing video js player for "+vidElement);
    });
    if(rwOn){
       var rwBtn = thePlayer.controlBar.addChild('button',{
        text: "REWIND",

       });
       rwBtn.addClass("btn_rw");
    }
  }

  function showVjs(vidElement){
    theVideo = document.getElementById(vidElement+"_html5_api");
    theVideo.style.display="block";
    theVjs = document.getElementById(vidElement);
    theVjs.style.display="block";
    $("#"+vidElement).addClass("video-js vjs-default-skin");
    console.log("showing video js player for "+vidElement);
  }

  function hideVjs(vidElement){
    var oldVideo = document.getElementById(vidElement);
    // reset to beginning
    videojs(oldVideo).pause();
    videojs(oldVideo).currentTime(0);
    oldVideo.style.display="none";
    $("#"+vidElement).removeClass("video-js vjs-default-skin");
    console.log("player hidden for "+vidElement);
  }

} // end player function

// send the appropriate videos to the video loader
var preLoader = function(){
  var vidList = [];
  var vidType ;

  // check browser compatibilty and initiate fall-backs
  if (Modernizr.video.h264){ vidList = myVidsH264; vidType = "video/mp4";}
  else if (Modernizr.video.webm){  vidList = myVidsWebm; vidType = "video/webm";}
  else if(Modernizr.video.ogg){ vidList = myVidsOgg; vidType = "video/ogg";}
  else { console.log("no html5 video support :( consider upgrading to a modern browser"); }

  // randomize the videos, if necessary
  if (randomOrder){ shuffle(vidList); }

  // check file sizes for progress bar
  checkSizes(vidList);

  // start loading the videos
  for (var i = 0; i < vidList.length; i++) {
    // add form elements to track play count
    $('form[name="myForm"]').append('<input type="hidden" name="'+vidList[i]+'" value="0" />');
    // add video element for each video
    $("#videos").append('<video id="video'+i+'"  > Your browser does not support the video tag. </video>');
    // load the videos into their elements
    console.log("video " +i+ " filename: " + (vidList[i]));
    loadVid(vidList[i], vidType, i);

  }

  // progress bar
  checkLoad();
}

$(document).ready(preLoader());

// load the videos asynchronously while the user waits
// based on http://stackoverflow.com/questions/18251632/another-force-chrome-to-fully-buffer-mp4-video
function loadVid(vidFile, vidType, i){
	var elementID = "video"+i;
  var xhr = new XMLHttpRequest();
  xhr.open('GET', vidFile, true);
  xhr.responseType = 'blob';
  xhr.onload = function(e) {
    if (this.status == 200) {
      var myBlob = this.response;
      var vid = (window.webkitURL ? webkitURL : URL).createObjectURL(myBlob);
      // myBlob is now the blob that the object URL pointed to.
      var video = document.getElementById(elementID);
      video.style.display="none";
      console.log("Loading video "+vidFile+" into element video"+i);
      video.src = vid;
      video.type=vidType ;
      video.controls=true;
      // update hidden form with play count
      $(video).on('play', function(){
        var v = parseInt($('input[name="'+vidFile+'"]').val());
        $('input[name="'+vidFile+'"]').val(++v);
      })
      vidElements.push(video);
    }
  }
  xhr.send();

}

<commit_msg>savepoint before dumping things on the floor<commit_after>//====================================================//
//==== Playlist: enter the file names here: ==========//

var allVideos = {
  "videos":
     {"title": "video 1",
      "h264": "videos/01-16s_h264.mp4",
      "webm": "videos/01-16s_h264.webm",
      "ogv": "videos/01-16s_h264.ogv",
      "doubleplay": "true",
     }
  ]
};

var myVidsH264=[
   "videos/01-16s_h264.mp4",
   "videos/02-18s_h264.mp4",
   "videos/03-13s_h264.mp4",
   "videos/04-13s_h264.mp4",
   "videos/05-22s_h264.mp4",
   "videos/magenta_test.mp4",
   "videos/magenta_test2.mp4",
]

// var myVidsH264=[
//    "videos/01-16s_h264.mov",
//    "videos/02-18s_h264.mov",
//    "videos/03-13s_h264.mov",
//    "videos/04-13s_h264.mov",
//    "videos/05-22s_h264.mov",
// ]

var myVidsWebm=[
   "videos/01-16s_h264.webm",
   "videos/02-18s_h264.webm",
   "videos/03-13s_h264.webm",
   "videos/04-13s_h264.webm",
   "videos/05-22s_h264.webm",
]

var myVidsOgg=[
   "videos/01-16s_h264.ogv",
   "videos/02-18s_h264.ogv",
   "videos/03-13s_h264.ogv",
   "videos/04-13s_h264.ogv",
   "videos/05-22s_h264.ogv",
]

var randomOrder = true; // done - set to false if playlist order is non-random

///TBD
var autoplayOn = false;  /// what is behavior of autoplay? this doesnt seem like a UI feature
var doubleplay = false; // play each video twice

//=== UI control toggles - set to false to remove from UI
var playOn = true;
var ffOn = true; // uhh
var rwOn = true; // wat
var skipOn = true;
var volumeOn =  true;
var muteOn = true;
var durationOn = true;
var seekOn = true;
var fullscOn = true;

//====================================================//
//===================================================//

// html5 video elements
var vidElements = [];

console.log("modernizr video h264 support? " + Modernizr.video.h264);
console.log("modernizr video webm support? " + Modernizr.video.webm);
console.log("modernizr video ogg support? " + Modernizr.video.ogg);

// this function will randomly shuffle the list of videos upon page load
// from http://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array
function shuffle(array) {
  var currentIndex = array.length, temporaryValue, randomIndex ;

  // While there remain elements to shuffle...
  while (0 !== currentIndex) {

    // Pick a remaining element...
    randomIndex = Math.floor(Math.random() * currentIndex);
    currentIndex -= 1;

    // And swap it with the current element.
    temporaryValue = array[currentIndex];
    array[currentIndex] = array[randomIndex];
    array[randomIndex] = temporaryValue;
  }

  return array;
}

// check the file sizes n the server
function checkSizes(files){
  var fileBytes;
  // from: http://stackoverflow.com/questions/17416274/ajax-get-size-of-file-before-downloading
  for (var i = 0; i < files.length; i++){
    get_filesize(files[i], function(filesize){
      console.log("file of size: "+filesize);
      fileBytes += filesize;
    });

  }
  console.log("total fileBytes: "+fileBytes);

  function get_filesize(url, callback) {
    var xhr = new XMLHttpRequest();
    xhr.open("HEAD", url, true); // Notice "HEAD" instead of "GET",
                                 //  to get only the header
    xhr.onreadystatechange = function() {
        if (this.readyState == this.DONE) {
            callback(parseInt(xhr.getResponseHeader("Content-Length")));
        }
    };
    xhr.send();

    // return fileBytes;
  }

}

// check the video loading progress and update the progress bar
function checkLoad(){
  var percentLoaded = 0;
  var timeoutID = setInterval(function(){
    console.log("videlements.length: "+vidElements.length);
    console.log("myVidsH264.length: "+myVidsH264.length);
    if(percentLoaded < 100){
      percentLoaded = (vidElements.length+1) / (myVidsH264.length+1) * 100;
      $('progress').val(percentLoaded);
    }
    else{
      console.log("percentLoaded: "+percentLoaded);
      clearTimeout(timeoutID);
      $('progress').val(percentLoaded);
      document.getElementById("loading").style.display="none";
      player();
    }
  }, 400);}

// initialize video player and its UI
var player = function(){
  // add videojs player to each video
  for (var i = 0; i < vidElements.length; i++){
    var options = {
      "width": $("#video"+i).prop('videoWidth'),
      "height": $("#video"+i).prop('videoHeight'),
      controlBar: {
        playToggle: (playOn ? true : false),
        fullscreenToggle: (fullscOn ? true : false),
        currentTimeDisplay: (durationOn ? true : false),
        durationDisplay: (durationOn ? true : false),
        remainingTimeDisplay: (durationOn ? true : false),
        timeDivider: (durationOn ? true : false),
        progressControl: {
          seekBar: (seekOn ? true : false),
        },
        volumeControl: (volumeOn ? true : false),
        muteToggle: (muteOn ? true : false),
      },
    };
    // add options for controls based on toggled options or whatever

    addVjs("video"+i, options);

  }

  // video elements
  var vindex = 0;
  var videoPlayer, theVideo;

  // buttons - so they can access each other
  var prevBtn, nextBtn, playBtn;

  // player state
  var playing, speed, ffing, rwing, rwinterval;

  videoPlayer = document.getElementById("video_player");
  theVideo = document.getElementById("video"+vindex);

  videoPlayer.style.display="table";
  theVideo.style.display="block";
  showVjs("video"+vindex);

  // UI controls
  // if(playOn){ playUI(); }
  if(skipOn){ skipUI(); }
  // if(rwOn){ rwUI(-0.1); }
  // if(ffOn){ ffUI(); }
  // if(volumeOn){ volUI(); }

  function skipUI(){
      $("#video_player").append('<button type="button" class="btn_skip" id="btn_prev" disabled> < < </button>');
      $("#video_player").append('<button type="button" class="btn_skip" id="btn_next" > > > </button>');

      prevBtn = document.getElementById("btn_prev");
      nextBtn = document.getElementById("btn_next");

      // iterate through video playlist backwards
      prevBtn.addEventListener("click", function(){
        if(vindex == 0){
          prevBtn.disabled = true;
        }
        else if (vindex == 1) {
          hideVjs("video"+vindex);
          vindex--;
          showVjs("video"+vindex);
          prevBtn.disabled = true;
        }
        else{
          hideVjs("video"+vindex);
          vindex--;
          showVjs("video"+vindex);
          nextBtn.disabled = false;
        }
      })
      // iterate through video playlist forwards
      nextBtn.addEventListener("click", function(){
        if(vindex == vidElements.length-1){
          nextBtn.disabled = true;
        }
        else if(vindex == vidElements.length-2){
          hideVjs("video"+vindex);
          vindex++;
          showVjs("video"+vindex);
          // theVideo.pause();
          prevBtn.disabled = false;
          nextBtn.disabled = true;
        }
        else{
          hideVjs("video"+vindex);
          vindex++;
          showVjs("video"+vindex);
          prevBtn.disabled = false;
        }

      })
    }

  function addVjs(vidElement, options){
    // theVideo = document.getElementById(vidElement);
    var thePlayer = videojs(vidElement, options, function(){
      console.log("initializing video js player for "+vidElement);
      this.addClass("vjs-big-play-centered");
    });
    if(rwOn){
      ///// TO BE DONE FULLY!!
       var rwBtn = thePlayer.controlBar.addChild('button',{
        text: "REWIND",

       });
       rwBtn.addClass("btn_rw");
    }
  }

  function showVjs(vidElement){
    theVideo = document.getElementById(vidElement+"_html5_api");
    theVideo.style.display="block";
    theVjs = document.getElementById(vidElement);
    theVjs.style.display="block";
    $("#"+vidElement).addClass("video-js vjs-default-skin");
    console.log("showing video js player for "+vidElement);
  }

  function hideVjs(vidElement){
    var oldVideo = document.getElementById(vidElement);
    // reset to beginning
    videojs(oldVideo).pause();
    videojs(oldVideo).currentTime(0);
    oldVideo.style.display="none";
    $("#"+vidElement).removeClass("video-js vjs-default-skin");
    console.log("player hidden for "+vidElement);
  }

} // end player function

// send the appropriate videos to the video loader
var preLoader = function(){
  var vidList = [];
  var vidType ;

  // check browser compatibilty and initiate fall-backs
  if (Modernizr.video.h264){ vidList = myVidsH264; vidType = "video/mp4";}
  else if (Modernizr.video.webm){  vidList = myVidsWebm; vidType = "video/webm";}
  else if(Modernizr.video.ogg){ vidList = myVidsOgg; vidType = "video/ogg";}
  else { console.log("no html5 video support :( consider upgrading to a modern browser"); }

  // randomize the videos, if necessary
  if (randomOrder){ shuffle(vidList); }

  // check file sizes for progress bar
  checkSizes(vidList);

  // start loading the videos
  for (var i = 0; i < vidList.length; i++) {
    // add form elements to track play count
    $('form[name="myForm"]').append('<input type="hidden" name="'+vidList[i]+'" value="0" />');
    // add video element for each video
    $("#videos").append('<video id="video'+i+'"  > Your browser does not support the video tag. </video>');
    // load the videos into their elements
    console.log("video " +i+ " filename: " + (vidList[i]));
    loadVid(vidList[i], vidType, i);

  }

  // progress bar
  checkLoad();
}

$(document).ready(preLoader());

// load the videos asynchronously while the user waits
// based on http://stackoverflow.com/questions/18251632/another-force-chrome-to-fully-buffer-mp4-video
function loadVid(vidFile, vidType, i){
	var elementID = "video"+i;
  var xhr = new XMLHttpRequest();
  xhr.open('GET', vidFile, true);
  xhr.responseType = 'blob';
  xhr.onload = function(e) {
    if (this.status == 200) {
      var myBlob = this.response;
      var vid = (window.webkitURL ? webkitURL : URL).createObjectURL(myBlob);
      // myBlob is now the blob that the object URL pointed to.
      var video = document.getElementById(elementID);
      video.style.display="none";
      console.log("Loading video "+vidFile+" into element video"+i);
      video.src = vid;
      video.type=vidType ;
      video.controls=true;
      // update hidden form with play count
      $(video).on('play', function(){
        var v = parseInt($('input[name="'+vidFile+'"]').val());
        $('input[name="'+vidFile+'"]').val(++v);
      })
      vidElements.push(video);
    }
  }
  xhr.send();

}

<|endoftext|>