<commit_before>var enableArcade = function(sprite) {
	sprite.enableBody = true;
	sprite.physicsBodyType = Phaser.Physics.ARCADE;
	sprite.collideWorldBounds = true;
	return sprite;
}

Ghost = function(game, layer, x, y, id) {
	this.id = id;
	this.game = game;
	this.layer = layer;
	this.x = x;
	this.y = y;

	this.controls = {
		up : this.game.input.keyboard.addKey(Phaser.Keyboard.W),
		left : this.game.input.keyboard.addKey(Phaser.Keyboard.A),
		right : this.game.input.keyboard.addKey(Phaser.Keyboard.D),
		down : this.game.input.keyboard.addKey(Phaser.Keyboard.S)
	};

	this.sprite = layer.create(x,y,'ghost');
	this.sprite = enableArcade(this.sprite);
	this.sprite.body.gravity.y = .1;
	this.body = this.sprite.body;
}

Ghost.prototype.update = function() {
	if(this.sprite.body.x <= this.game.camera.x){
        this.sprite.body.x = this.game.camera.x;
    }
    if (this.sprite.body.x >= this.game.camera.x + 1280 - this.sprite.body.width) {
        this.sprite.body.x = this.game.camera.x + 1280 - this.sprite.body.width;
    }
    if (this.sprite.body.y <= 0) {
        this.sprite.body.y = this.game.camera.y;
    }
    if (this.sprite.body.y >= 800 - this.sprite.body.height) {
        this.sprite.body.y = 800 - this.sprite.body.height;
    }

    if(this.controls.left.isDown){
        this.sprite.x -= ghostSpeed;
        ghostFacing = 'left';
        this.sprite.scale.x=1;
    }
    if(this.controls.right.isDown){
        this.sprite.x += ghostSpeed;
        ghostFacing = 'right';
        this.sprite.scale.x=-1;
    }
    if(this.controls.up.isDown){
        this.sprite.y -= ghostSpeed;
        ghostFacing = 'up';
    }
    if(this.controls.down.isDown){
        this.sprite.y +=ghostSpeed;
        ghostFacing = 'down';
    }

    for(i = 0; i < 5;i++)
    {
	     if(Math.abs((enemies[i].body.center.x - this.sprite.body.center.x)/(enemies[i].body.center.y - this.sprite.body.center.y)) <= .02)
	     {
	        if(this.game.input.keyboard.isDown(Phaser.Keyboard.L)){
	            possess(enemies[i]);
	        }
	    }
	}

    //mouse movement and decision logic
    var ghostDist = (ghost.x - mouse.body.x);
    //var dist = girl.body.x - mouse.body.x;
    //mouse.body.acceleration.x = dist;
    if(mouse.scale.x > 0 && mouse.body.velocity.x > 0) mouse.scale.x *=-1;
    if(mouse.scale.x < 0 && mouse.body.velocity.x < 0) mouse.scale.x *=-1;
    if(Math.abs(ghostDist) < 100) {
        mouse.body.acceleration.x = 0;
        mouse.body.velocity.x=0;
        mouse.animations.play('ghost',4,true);
    }

    if(Math.abs(this.sprite.body.acceleration.x) < 0.01) {
        this.sprite.body.velocity.x *= .90;
    }
}

Girl = function(game, layer, x, y, id) {
	this.init = function() {
		this.id = id;
		this.game = game;
		//this.girl = this.game.add.sprite(x,y,'girl');

		this.girl = layer.create(x,y,'girl');//this.game.add.sprite(x,y,'girl');//113,105
		var girl = this.girl;
		console.log(this.girl);
		enableArcade(girl);
	    //this.game.physics.enable(girl, Phaser.Physics.ARCADE);
	    //jam.girl = new Girl(this.game, jam.sprites,150,150);
		var body = this.girl.body;
		body.setSize(94,133,43,0);
		body.bounce.y = 0.1;

        girl.animations.add('girlmove', [6,7,8,9,10,11],0,true);
        girl.animations.add('girlidle', [0,1,2] ,4,true);
        girl.animations.add('girlsmack',[3,4,5] ,8,true);
        girl.anchor.setTo(.5,1);
        girl.animations.play('girlidle');

		this.controls = {
			jump : this.game.input.keyboard.addKey(Phaser.Keyboard.W),
			left : this.game.input.keyboard.addKey(Phaser.Keyboard.A),
			right : this.game.input.keyboard.addKey(Phaser.Keyboard.D),
			attack : this.game.input.keyboard.addKey(Phaser.Keyboard.S)
		};
	    body.checkCollision.up = true;
	    body.checkCollision.down = true;
	    body.checkCollision.left= true;
	    body.checkCollision.right = true;
	    this.body = this.girl.body;
	    this.touching = this.girl.body.touching;
	    this.baseScale = 1.0;
	}
}

Girl.prototype.update = function() {
	var controls = this.controls;
	var velocity = this.body.velocity;
	var acceleration = this.body.acceleration;
	var girl = this.girl;
	var game = this.game;
	if(!girl.alive) return;

	var floored = (girl.body.onFloor() || girl.body.touching.down);

	//Girl movement decision tree
    fGround.body.velocity.x = 0;
    treeline.body.velocity.x = 0;

    if(controls.left.isDown){
        girl.body.velocity.x = -girlSpeed;
        girl.scale.x=-1;
        girl.animations.play('girlmove',8,true);
        if(this.game.camera.x > 0 && this.game.camera.x < 2560){
            fGround.body.velocity.x =+ girlSpeed*1.3;
            treeline.body.velocity.x =- girlSpeed*0.3;
        }
    }
    else if(controls.right.isDown){
        girl.body.velocity.x = girlSpeed;
        girl.scale.x=1;
        girl.animations.play('girlmove',8,true);
        if(this.game.camera.x > 0 && this.game.camera.x < 2560){
            fGround.body.velocity.x =- girlSpeed*1.3;
            treeline.body.velocity.x =+ girlSpeed*0.3;
        }
    }
     else if(controls.attack.isDown)
     {
         girl.body.velocity.y = +girlSpeed;
         girl.animations.play('girlsmack',12,false);
     }
    else
    {
        girl.animations.play('girlidle', 8,true);
    }
    if(!floored)
    {
        girl.body.velocity.x *=.8
    }
    if (controls.jump.isDown && this.game.time.now > jumpTimer)
    {
        //console.log("Jumping");
        girl.body.velocity.y = -300;
        jumpTimer = this.game.time.now + 450;
    }
    if(Math.abs(girl.body.velocity.y)<0.1 && Math.abs(girl.body.acceleration.x) < 0.01) {
        girl.body.velocity.x *= .90;
    }
    if (girl.body.x >= 1280){

    }

	acceleration.set(0,0);
	if(controls.jump.isDown) {
	}
	if(controls.left.isDown) {
		acceleration.x = -800;
	}
	if(controls.right.isDown) {
		acceleration.x = 800;
	}

	velocity.x *= 0.9;

	this.girl.scale.x = this.baseScale * ((velocity.x>0)?1:-1);
	// if(Math.abs(velocity.x) > 0.1) {
	// 	//this.girl.animations.play('walk');
	// 	//this.walk.speed = Math.max(velocity.x*0.05,4);
	// } else {
	// 	this.girl.animations.play('idle');
	// }

    this.game.network.server.userToServer({
    	x:this.body.x,
    	y:this.body.y,
    	state:"idle"
    });
}

Girl.prototype.remove = function() {
	console.log("Removing");
	this.alive = false;
	this.girl.destroy();
}<commit_msg>night push<commit_after>var enableArcade = function(sprite) {
	sprite.enableBody = true;
	sprite.physicsBodyType = Phaser.Physics.ARCADE;
	sprite.collideWorldBounds = true;
	return sprite;
}

Ghost = function(game, layer, x, y, id) {
	this.id = id;
	this.game = game;
	this.layer = layer;
	this.x = x;
	this.y = y;

	this.controls = {
		up : this.game.input.keyboard.addKey(Phaser.Keyboard.W),
		left : this.game.input.keyboard.addKey(Phaser.Keyboard.A),
		right : this.game.input.keyboard.addKey(Phaser.Keyboard.D),
		down : this.game.input.keyboard.addKey(Phaser.Keyboard.S)
	};

	this.sprite = layer.create(x,y,'ghost');
	this.sprite = enableArcade(this.sprite);
	this.sprite.anchor.setTo(0.5,0.5);
	this.sprite.body.gravity.y = .01;
	this.sprite.drag.set(0.1,0.1);
	this.body = this.sprite.body;
}

Ghost.prototype.update = function() {
	if(this.sprite.body.x <= this.game.camera.x){
        this.sprite.body.x = this.game.camera.x;
    }
    if (this.sprite.body.x >= this.game.camera.x + 1280 - this.sprite.body.width) {
        this.sprite.body.x = this.game.camera.x + 1280 - this.sprite.body.width;
    }
    if (this.sprite.body.y <= 0) {
        this.sprite.body.y = this.game.camera.y;
    }
    if (this.sprite.body.y >= 800 - this.sprite.body.height) {
        this.sprite.body.y = 800 - this.sprite.body.height;
    }

    this.sprite.body.acceleration.set(0,-400);
    if(this.controls.left.isDown){
        this.sprite.x -= ghostSpeed;
        ghostFacing = 'left';
        this.sprite.scale.x=-1;
    }
    if(this.controls.right.isDown){
        this.sprite.x += ghostSpeed;
        ghostFacing = 'right';
        this.sprite.scale.x=1;
    }
    if(this.controls.up.isDown){
        this.sprite.y -= ghostSpeed;
        this.sprite.body.velocity.y -= 10;
        this.sprite.body.acceleration.y -= 10;
        ghostFacing = 'up';
    }
    if(this.controls.down.isDown){
        this.sprite.y +=ghostSpeed;
        ghostFacing = 'down';
    }

    for(i = 0; i < 5;i++)
    {
	     if(Math.abs((enemies[i].body.center.x - this.sprite.body.center.x)/(enemies[i].body.center.y - this.sprite.body.center.y)) <= .02)
	     {
	        if(this.game.input.keyboard.isDown(Phaser.Keyboard.L)){
	            possess(enemies[i]);
	        }
	    }
	}

    //mouse movement and decision logic
    var ghostDist = (ghost.x - mouse.body.x);
    //var dist = girl.body.x - mouse.body.x;
    //mouse.body.acceleration.x = dist;
    if(mouse.scale.x > 0 && mouse.body.velocity.x > 0) mouse.scale.x *=-1;
    if(mouse.scale.x < 0 && mouse.body.velocity.x < 0) mouse.scale.x *=-1;
    if(Math.abs(ghostDist) < 100) {
        mouse.body.acceleration.x = 0;
        mouse.body.velocity.x=0;
        mouse.animations.play('ghost',4,true);
    }

    if(Math.abs(this.sprite.body.acceleration.x) < 0.01) {
        this.sprite.body.velocity.x *= .90;
    }
}

Girl = function(game, layer, x, y, id) {
	this.init = function() {
		this.id = id;
		this.game = game;
		//this.girl = this.game.add.sprite(x,y,'girl');

		this.girl = layer.create(x,y,'girl');//this.game.add.sprite(x,y,'girl');//113,105
		var girl = this.girl;
		console.log(this.girl);
		enableArcade(girl);
	    //this.game.physics.enable(girl, Phaser.Physics.ARCADE);
	    //jam.girl = new Girl(this.game, jam.sprites,150,150);
		var body = this.girl.body;
		body.setSize(94,133,43,0);
		body.bounce.y = 0.1;

        girl.animations.add('girlmove', [6,7,8,9,10,11],0,true);
        girl.animations.add('girlidle', [0,1,2] ,4,true);
        girl.animations.add('girlsmack',[3,4,5] ,8,true);
        girl.anchor.setTo(.5,1);
        girl.animations.play('girlidle');

		this.controls = {
			jump : this.game.input.keyboard.addKey(Phaser.Keyboard.W),
			left : this.game.input.keyboard.addKey(Phaser.Keyboard.A),
			right : this.game.input.keyboard.addKey(Phaser.Keyboard.D),
			attack : this.game.input.keyboard.addKey(Phaser.Keyboard.S)
		};
	    body.checkCollision.up = true;
	    body.checkCollision.down = true;
	    body.checkCollision.left= true;
	    body.checkCollision.right = true;
	    this.body = this.girl.body;
	    this.touching = this.girl.body.touching;
	    this.baseScale = 1.0;
	}
}

Girl.prototype.update = function() {
	var controls = this.controls;
	var velocity = this.body.velocity;
	var acceleration = this.body.acceleration;
	var girl = this.girl;
	var game = this.game;
	if(!girl.alive) return;

	var floored = (girl.body.onFloor() || girl.body.touching.down);

	//Girl movement decision tree
    fGround.body.velocity.x = 0;
    treeline.body.velocity.x = 0;

    if(controls.left.isDown){
        girl.body.velocity.x = -girlSpeed;
        girl.scale.x=-1;
        girl.animations.play('girlmove',8,true);
        if(this.game.camera.x > 0 && this.game.camera.x < 2560){
            fGround.body.velocity.x =+ girlSpeed*1.3;
            treeline.body.velocity.x =- girlSpeed*0.3;
        }
    }
    else if(controls.right.isDown){
        girl.body.velocity.x = girlSpeed;
        girl.scale.x=1;
        girl.animations.play('girlmove',8,true);
        if(this.game.camera.x > 0 && this.game.camera.x < 2560){
            fGround.body.velocity.x =- girlSpeed*1.3;
            treeline.body.velocity.x =+ girlSpeed*0.3;
        }
    }
     else if(controls.attack.isDown)
     {
         girl.body.velocity.y = +girlSpeed;
         girl.animations.play('girlsmack',12,false);
     }
    else
    {
        girl.animations.play('girlidle', 8,true);
    }
    if(!floored)
    {
        girl.body.velocity.x *=.8
    }
    if (controls.jump.isDown && this.game.time.now > jumpTimer)
    {
        //console.log("Jumping");
        girl.body.velocity.y = -300;
        jumpTimer = this.game.time.now + 450;
    }
    if(Math.abs(girl.body.velocity.y)<0.1 && Math.abs(girl.body.acceleration.x) < 0.01) {
        girl.body.velocity.x *= .90;
    }
    if (girl.body.x >= 1280){

    }

	acceleration.set(0,0);
	if(controls.jump.isDown) {
	}
	if(controls.left.isDown) {
		acceleration.x = -800;
	}
	if(controls.right.isDown) {
		acceleration.x = 800;
	}

	velocity.x *= 0.9;

	this.girl.scale.x = this.baseScale * ((velocity.x>0)?1:-1);
	// if(Math.abs(velocity.x) > 0.1) {
	// 	//this.girl.animations.play('walk');
	// 	//this.walk.speed = Math.max(velocity.x*0.05,4);
	// } else {
	// 	this.girl.animations.play('idle');
	// }

    this.game.network.server.userToServer({
    	x:this.body.x,
    	y:this.body.y,
    	state:"idle"
    });
}

Girl.prototype.remove = function() {
	console.log("Removing");
	this.alive = false;
	this.girl.destroy();
}<|endoftext|>


<commit_before>(function () {

  'use strict';
  // Store a local reference to jQuery and Underscore.
  var $ = window.jQuery;
  var _ = window._;

  var Chartabull = window.Chartabull = {};

  var Base = Chartabull.Base = {
    defaults: {
      "textColor": '#777;',           // Overlay text color
      "textPadding": 20,              // Offset from corner for text
      "secondaryFont": "normal 24px 'helvetica neue', arial, verdana, sans-serif",
      "primaryFont": "bold 50px 'helvetica neue', arial, verdana, sans-serif",
      "backgroundColor":  "#ffffff",  // Background of Chart
      "secondaryColor":   "#dedede",  // Certain charts use a secondary color
      "chartColor": "#cccccc"         // Primary color of a chart element
    },
    draw: function() {},
    getContext: function($el) { return $el[0].getContext("2d"); },
    // Drop some text on the graph
    buildText: function($el, chartValue, chartText, options){
      var height  = $el.height();

      var context = Base.getContext($el);

      context.fillStyle = options.textColor;
      context.font = options.primaryFont;
      context.fillText(chartValue, options.textPadding, height-50);
      context.font = options.secondaryFont;
      context.fillText(chartText, options.textPadding, height-options.textPadding);

      return this;
    }
  };

  Chartabull.Line = herit(Base, {
   draw: function(el, data, options, text){
      var height = el.height();
      var width = el.width();
      var dataSize = data.length;
      var max = _.max(data);
      var segmentWidth = width/(dataSize-1);
      var segmentHeight = height/max;
      var drawContext = Base.getContext(el);

      options = _.defaults(options || {}, Base.defaults);

      // Fill the background
      drawContext.fillStyle = options.backgroundColor;
      drawContext.fillRect(0,0, width, height);

      // Begin our path at the bottom left of the chart area
      drawContext.beginPath();
      drawContext.moveTo(0, height);

      // Draw the lines
      $.each(data, function(val){
        var current_x = val * segmentWidth;
        var current_y = (height) - (data[val] * segmentHeight);
        drawContext.lineTo(current_x, current_y);
      });

      // Close out the graph (connect to bottom right)
      drawContext.lineTo(width, height);
      drawContext.closePath();

      // Apply optional styles
      drawContext.fillStyle = options.chartColor;
      drawContext.fill();

      if (text) Base.buildText(el, data[data.length-1], text, options);

      return this;
    }
  });

  Chartabull.Donut = herit(Base, {
    draw: function(el, data, options, text){
      var height = el.height();
      var width = el.width();
      var drawContext = Base.getContext(el);
      var x = width/2;
      var y = height/2;
      var r = _.min([width,height])/2;
      var percentage = Math.round((data[0]/data[1] * 100)*10)/10;

      options = _.defaults(options || {}, Base.defaults);

      // Fill the background
      drawContext.fillStyle = options.secondaryColor;
      drawContext.arc(x, y, r, 0, (Math.PI * 2), false);
      drawContext.fill();

      // Draw the arc
      drawContext.beginPath();
      drawContext.arc(x, y, r, 0, (Math.PI * 2) * (data[0]/data[1]), false);
      drawContext.lineTo(x,y);
      drawContext.closePath();
      drawContext.fillStyle = options.chartColor;
      drawContext.fill();

      // Cut out the internal arc by dropping a filled circle on top
      // that is the background color
      drawContext.beginPath();
      drawContext.arc(x,y,r/2,0, (Math.PI * 2), false);
      drawContext.closePath();
      drawContext.fillStyle = options.backgroundColor;
      drawContext.fill();

      if (text) Base.buildText(el, percentage + '%', text, options);

      return this;
    }
  });

  Chartabull.Bar = herit(Base, {
    draw: function(el, data, options, text){
      var height = el.height();
      var width = el.width();
      var dataSize = data.length;
      var max = _.max(data);
      var segmentWidth = width/dataSize;
      var segmentHeight = height/max;
      var drawContext = Base.getContext(el);
      var gdrawContext = Base.getContext(el);
      var current_x = 0;
      var total =  _.reduce(data, function(a, b){ return a + b; }, 0);

      options = _.defaults(options || {}, Base.defaults);

      // Fill the background
      gdrawContext.fillStyle = options.backgroundColor;
      gdrawContext.fillRect(0,0, width, height);

      // Begin our path at the bottom left of the chart area
      drawContext.beginPath();
      drawContext.moveTo(0, height);

      // Draw the bars
      drawContext.fillStyle = options.chartColor;
      $.each(data, function(val){
        var barYPosition = height - (data[val] * segmentHeight);
        drawContext.fillRect(current_x, barYPosition, segmentWidth, height);
        current_x += segmentWidth + 2;
      });

      drawContext.closePath();

      // Apply optional styles
      drawContext.fillStyle = options.chartColor;
      drawContext.fill();

      if (text) Base.buildText(el, total, text, options);

      return this;
    }
  });
})();
<commit_msg>fix js lint errors<commit_after>(function () {
  'use strict';

  // Store a local reference to jQuery and Underscore.
  var $ = window.jQuery;
  var _ = window._;

  var Chartabull = window.Chartabull = {};

  var Base = Chartabull.Base = {
    defaults: {
      "textColor": '#777;',           // Overlay text color
      "textPadding": 20,              // Offset from corner for text
      "secondaryFont": "normal 24px 'helvetica neue', arial, verdana, sans-serif",
      "primaryFont": "bold 50px 'helvetica neue', arial, verdana, sans-serif",
      "backgroundColor":  "#ffffff",  // Background of Chart
      "secondaryColor":   "#dedede",  // Certain charts use a secondary color
      "chartColor": "#cccccc"         // Primary color of a chart element
    },
    draw: function () {},
    getContext: function ($el) { return $el[0].getContext("2d"); },
    // Drop some text on the graph
    buildText: function ($el, chartValue, chartText, options) {
      var height  = $el.height();

      var context = Base.getContext($el);

      context.fillStyle = options.textColor;
      context.font = options.primaryFont;
      context.fillText(chartValue, options.textPadding, height - 50);
      context.font = options.secondaryFont;
      context.fillText(chartText, options.textPadding, height - options.textPadding);

      return this;
    }
  };

  Chartabull.Line = herit(Base, {
    draw: function (el, data, options, text) {
      var height = el.height();
      var width = el.width();
      var dataSize = data.length;
      var max = _.max(data);
      var segmentWidth = width / (dataSize - 1);
      var segmentHeight = height / max;
      var drawContext = Base.getContext(el);

      options = _.defaults(options || {}, Base.defaults);

      // Fill the background
      drawContext.fillStyle = options.backgroundColor;
      drawContext.fillRect(0, 0, width, height);

      // Begin our path at the bottom left of the chart area
      drawContext.beginPath();
      drawContext.moveTo(0, height);

      // Draw the lines
      $.each(data, function (val) {
        var current_x = val * segmentWidth;
        var current_y = height - data[val] * segmentHeight;
        drawContext.lineTo(current_x, current_y);
      });

      // Close out the graph (connect to bottom right)
      drawContext.lineTo(width, height);
      drawContext.closePath();

      // Apply optional styles
      drawContext.fillStyle = options.chartColor;
      drawContext.fill();

      if (text) Base.buildText(el, data[data.length - 1], text, options);

      return this;
    }
  });

  Chartabull.Donut = herit(Base, {
    draw: function (el, data, options, text) {
      var height = el.height();
      var width = el.width();
      var drawContext = Base.getContext(el);
      var x = width / 2;
      var y = height / 2;
      var r = _.min([width,height])/2;
      var percentage = Math.round((data[0]/data[1] * 100)*10)/10;

      options = _.defaults(options || {}, Base.defaults);

      // Fill the background
      drawContext.fillStyle = options.secondaryColor;
      drawContext.arc(x, y, r, 0, (Math.PI * 2), false);
      drawContext.fill();

      // Draw the arc
      drawContext.beginPath();
      drawContext.arc(x, y, r, 0, (Math.PI * 2) * (data[0]/data[1]), false);
      drawContext.lineTo(x,y);
      drawContext.closePath();
      drawContext.fillStyle = options.chartColor;
      drawContext.fill();

      // Cut out the internal arc by dropping a filled circle on top
      // that is the background color
      drawContext.beginPath();
      drawContext.arc(x, y, r/2, 0, (Math.PI * 2), false);
      drawContext.closePath();
      drawContext.fillStyle = options.backgroundColor;
      drawContext.fill();

      if (text) Base.buildText(el, percentage + '%', text, options);

      return this;
    }
  });

  Chartabull.Bar = herit(Base, {
    draw: function (el, data, options, text) {
      var height = el.height();
      var width = el.width();
      var dataSize = data.length;
      var max = _.max(data);
      var segmentWidth = width / dataSize;
      var segmentHeight = height / max;
      var drawContext = Base.getContext(el);
      var gdrawContext = Base.getContext(el);
      var current_x = 0;
      var total =  _.reduce(data, function (a, b) { return a + b; }, 0);

      options = _.defaults(options || {}, Base.defaults);

      // Fill the background
      gdrawContext.fillStyle = options.backgroundColor;
      gdrawContext.fillRect(0, 0, width, height);

      // Begin our path at the bottom left of the chart area
      drawContext.beginPath();
      drawContext.moveTo(0, height);

      // Draw the bars
      drawContext.fillStyle = options.chartColor;
      $.each(data, function (val) {
        var barYPosition = height - data[val] * segmentHeight;
        drawContext.fillRect(current_x, barYPosition, segmentWidth, height);
        current_x += segmentWidth + 2;
      });

      drawContext.closePath();

      // Apply optional styles
      drawContext.fillStyle = options.chartColor;
      drawContext.fill();

      if (text) Base.buildText(el, total, text, options);

      return this;
    }
  });
})();
<|endoftext|>


<commit_before>/* jshint unused:true, browser: true, devel: true */
/* global exports, define */

/**
 * @Author Calvin (Min) Zhang
 * @Email ye111111ow at gmail.com
 */

(function (root, factory) {
    if (typeof exports === 'object') {
        factory(exports);
    } else if (typeof define === 'function' && define.amd) {
        define(['exports'], factory);
    } else {
        factory(root);
    }
}(this, function (exports) {

    var DEFAULT_STAGE = {
        id: "",
        title: "",
        type: "node",
        description: "",
        onStageCallback: null
    };

    /**
     * Constructor of CheckpointJs.
     *
     * @param target the DOM container of CheckpointJs. Div is preferred.
     */
    function CheckpointJs(target, options) {
        var self = this;

        this.checkpointJs = target;
        this.stages = [];
        this.currentIndex = 0;
        this.inited = false;

        /* Set default options */
        this._options = {
            namespace: "checkpoint",
            direction: "right",
            theme: "default",
            debug: false
        };

        /* Override default options */
        if (typeof options === "object") {
            for (var attr in options) {
                this._options[attr] = options[attr];
            }
        }

        // Issue #1
        window.addEventListener('resize', function(){
            _resize.call(self);
        });
    }

    /**
     * Constructor of Stage.
     * Stage is the basic component of CheckpointJs. Each Stage refers a checkpoint of CheckpointJs.
     * There are three (four) type of Stage:
     *   1. start
     *   2. end
     *   3. node
     *   (4. millstone)
     *
     * @param arg the argument that used for constructing a Stage.
     */
    function Stage(arg) {
        /* Set default */
        for (var attr in DEFAULT_STAGE) {
            this[attr] = arg[attr];
        }

        /* 1. Use an Object to construct Stage */
        if (typeof arg === "object"){
            for (var attr in arg) {
                this[attr] = arg[attr];
            }
        }
        /* 2. Use a String to construct Stage */
        else if (typeof arg === "string") {
            this.title = arg;
        }
        /* 3. Wrong Constructor */
        else {
            return null;
        }
        return this;
    }

    /**
     * Initialize the CheckpointJs.
     * This function firstly cleans everything within the CheckpointJs container.
     * Then add the DOM of CheckpointJs to that container.
     *
     * @param at
     */
    function _init(at) {
        this.destroy();
        if (typeof this.checkpointJs == "object") {
            this.inited = true;
            if (typeof at != "number") {
                at = 0;
            }

            for ( var i = 0; i< this.stages.length; i++ ) {
                if ( i === 0 ) {
                    this.stages[i].type = "start";
                } else if ( i === this.stages.length - 1 ) {
                    this.stages[i].type = "end";
                }
                var stage = this.stages[i];
                var stageDom = _createDomFromStage.call(this, stage, i, at);
                this.checkpointJs.appendChild(stageDom);
            }
            this.currentIndex = at;
            _resize.call(this);
            _markStages.call(this, this.currentIndex);
            return this;
        }
    }

    /**
     * Move to the next Stage.
     */
    function _next() {
        if ( this.currentIndex < this.stages.length - 1 ) {
            this.currentIndex++;
            _markStages.call(this, this.currentIndex);
        } else {
            console.log( "[CheckpointJS] No next stage." );
        }
        return this;
    }

    /**
     * Move back to the previous Stage.
     */
    function _prev() {
        if ( this.currentIndex > 0 ) {
            this.currentIndex--;
            _markStages.call(this, this.currentIndex);
        } else {
            console.log( "[CheckpointJS] No previous stage." );
        }
        return this;
    }

    /**
     * Move to the latest Stage. Mark all the Stage as done.
     */
    function _complete() {
        this.currentIndex = this.stages.length - 1;
        _markStages.call(this, this.currentIndex);
        return this;
    }

    /**
     * Move to a specific Stage
     *
     * @param index the index of Stage, started from 0.
     */
    function _atStage(index) {
        if (index >= 0 && index <= this.stages.length - 1) {
            this.currentIndex = index;
            _markStages.call(this, this.currentIndex);
        } else {
            console.log( "[CheckpointJS] No such stage." );
        }
        return this;
    }

    /**
     * Clear all changes. Restore the status of CheckpointJs.
     */
    function _reset() {
        this.currentIndex = 0;
        _markStages.call(this, this.currentIndex);
        return this;
    }

    /**
     * Mark all Stages before the index one as done. Mark all Stages after the index one as todo.
     * The callee must make sure the  index is correct.
     *
     * @private
     */
    function _markStages(index) {
        var namespace = this._options.namespace,
            stageDom = null,
            i = 0;

        for (i = 0;i < index;i++) {
            stageDom = this.checkpointJs.querySelector(  '[data-index="' + i + '"]' );
            stageDom.setAttribute( "class", namespace + "-block " + namespace + "-done" );
        }
        for (i = index + 1;i < this.stages.length;i++) {
            stageDom = this.checkpointJs.querySelector(  '[data-index="' + i + '"]' );
            stageDom.setAttribute( "class", namespace + "-block " + namespace + "-default" );
        }
        stageDom = this.checkpointJs.querySelector(  '[data-index="' + index + '"]' );
        stageDom.setAttribute( "class", namespace + "-block " + namespace + "-current" );

        /* OnStage Callback */
        if (typeof this.stages[index].onStageCallback === "function") {
            this.stages[index].onStageCallback(this.stages[index]);
        }
    }

    /**
     * Create the DOM for a specific Stage
     *
     * @param stage
     * @param index the index of this Stage among all Stages
     *
     * @private
     */
    function _createDomFromStage( stage, index, at ) {
        var blockTitleDom = document.createElement( "div" ),
            namespace = this._options.namespace;

        blockTitleDom.setAttribute( "data-role", namespace + "-block-title" );
        if ( index % 2 === 0 ) {
            blockTitleDom.setAttribute( "class", namespace + "-block-title" );
        } else {
            blockTitleDom.setAttribute( "class", namespace + "-block-title " + namespace + "-alt" );
        }
        blockTitleDom.appendChild( document.createTextNode( stage.title ) );

        var blockIndicatorDom = document.createElement( "div" );
        blockIndicatorDom.setAttribute( "data-role", namespace + "-block-indicator" );
        blockIndicatorDom.setAttribute( "class", namespace + "-block-indicator" );

        var blockBodyDom = document.createElement( "div" );
        blockBodyDom.setAttribute( "class", namespace + "-block-body" );
        blockBodyDom.appendChild( blockTitleDom );
        blockBodyDom.appendChild( blockIndicatorDom );

        var blockConnectorDom = document.createElement( "div" );
        blockConnectorDom.setAttribute( "data-role", namespace + "-block-connector" );
        blockConnectorDom.setAttribute( "class", namespace + "-block-connector" );

        var stageDom = document.createElement( "div" );
        if (typeof stage.id === "string") {
            stageDom.setAttribute( "id", stage.id );
        }
        stageDom.setAttribute( "class", namespace + "-block" );
        stageDom.setAttribute( "data-index", index );
        stageDom.setAttribute( "data-role", namespace + "-block" );
        stageDom.style.width = 100 / this.stages.length + "%";

        stageDom.appendChild(blockBodyDom);

        if ( stage.type != "end" ) {
            stageDom.appendChild(blockConnectorDom);
        }

        return stageDom;
    }

    /**
     * Resize checkpoint dynamically based on its width
     */
    function _resize() {
        var namespace = this._options.namespace,
            blocks = this.checkpointJs.querySelectorAll('[data-role="' + namespace + '-block"]'),
            containerWidth = this.checkpointJs.offsetWidth,
            stageCount = this.stages.length,
            blockWidth = containerWidth / stageCount;

        for ( var i = 0; i < blocks.length - 1; i++ ) {
            var connector = blocks[i].querySelector('[data-role="' + namespace + '-block-connector"]');

            connector.style["margin-left"] = blockWidth / 2 + 8 + "px";
            connector.style.width = blockWidth - 16 + "px";
        }

    }

    /**
     * Set Stages of the CheckpointJs.
     * Should be called before CheckpointJs#init()
     *
     * @param arguments an array of Stage constructor.
     */
    function _setStages(stages) {
        if (Object.prototype.toString.call( stages ) === '[object Array]') {
            for(var index in stages) {
                var stage = stages[index];
                if (typeof stage === "string" || typeof stage === "object") {
                    this.stages.push(new Stage(stage));
                }
                else {
                    console.log( "[CheckpointJS] Wrong stage parameters." );
                }
            }
        }
        return this;
    }

    /**
     * Replace a current Stage with the new one.
     *
     * @param index the index of the Stage
     * @param arg the argument that used for constructing the Stage.
     */
    function _setStage(index, arg) {
        if (typeof stage === "object") {
            if (index >= 0 && index <= this.stages.length - 1) {
                var stage = new Stage(arg);
                if ( stage !== null ) {
                    this.stages[index] = stage;
                } else {
                    console.log( "[CheckpointJS] Wrong stage parameters." );
                }
            } else {
                console.log( "[CheckpointJS] No such stage." );
            }
        }
        return this;
    }

    /**
     * Add a Stage to the Stage array (after index).
     *
     * @param index the new Stage will be added before this index
     * @param stage
     */
    function _insertStage(index, stage) {
        if (index < 0 || index > this.stages.length - 1) {
            console.log( "[CheckpointJS] Wrong index." );
            return this;
        }
        var newStage = new Stage(stage);
        if (newStage) {
            this.stages.splice(index, 0, newStage);
        } else {
            return this;
        }
        if (index <= this.currentIndex) {
            this.currentIndex++;
        }
        if (this.inited) {
            _init.call(this, this.currentIndex);
        }

        return this;
    }

    /**
     * Append a Stage to the end of Stages.
     *
     * @param stage
     */
    function _appendStage(stage) {
        var newStage = new Stage(stage);
        if (newStage) {
            this.stages.splice(this.stages.length, 0, newStage);
        }

        return this;
    }

    /**
     * Remove a specific Stage.
     * @param index the index of that Stage
     */
    function _removeStage(index) {
        if (index >= 0 && index <= this.stages.length - 1) {
            this.stages.splice(index, 1);
            this.destroy();
            this.init();
        } else {
            console.log( "[CheckpointJS] No such stage." );
        }
        return this;
    }

    /**
     * Destroy all dom elements inside CheckpointJs container.
     */
    function _destroy() {
        if (this.checkpointJs !== null) {
            var container = this.checkpointJs;
            while (container.firstChild) {
                container.removeChild(container.firstChild);
            }
        }
        this.inited = false;
        return this;
    }

    var checkpointJs = function (targetSelector, options) {
        var target = document.querySelector(targetSelector);
        if (target === null) {
            console.log( "[CheckpointJS] Wrong selector." );
            return null;
        } else {
            var checkpoint = new CheckpointJs(target, options);
            return checkpoint;
        }
    };

    /**
     * Register public functions.
     */
    CheckpointJs.prototype = {
        clone: function () {
            return new CheckpointJs(this);
        },
        setStages: _setStages,
        setStage: _setStage,
        addStage: _insertStage,
        appendStage: _appendStage,
        atStage: _atStage,
        removeStage: _removeStage,
        next: _next,
        prev: _prev,
        complete: _complete,
        init: _init,
        reset: _reset,
        destroy: _destroy
    };

    /* Export public functions */
    exports.checkpointJs = checkpointJs;

    /* Return public functions */
    return checkpointJs;
}));<commit_msg>Fixed insertStage<commit_after>/* jshint unused:true, browser: true, devel: true */
/* global exports, define */

/**
 * @Author Calvin (Min) Zhang
 * @Email ye111111ow at gmail.com
 */

(function (root, factory) {
    if (typeof exports === 'object') {
        factory(exports);
    } else if (typeof define === 'function' && define.amd) {
        define(['exports'], factory);
    } else {
        factory(root);
    }
}(this, function (exports) {

    var DEFAULT_STAGE = {
        id: "",
        title: "",
        type: "node",
        description: "",
        onStageCallback: null
    };

    /**
     * Constructor of CheckpointJs.
     *
     * @param target the DOM container of CheckpointJs. Div is preferred.
     */
    function CheckpointJs(target, options) {
        var self = this;

        this.checkpointJs = target;
        this.stages = [];
        this.currentIndex = 0;
        this.inited = false;

        /* Set default options */
        this._options = {
            namespace: "checkpoint",
            direction: "right",
            theme: "default",
            debug: false
        };

        /* Override default options */
        if (typeof options === "object") {
            for (var attr in options) {
                this._options[attr] = options[attr];
            }
        }

        // Issue #1
        window.addEventListener('resize', function(){
            _resize.call(self);
        });
    }

    /**
     * Constructor of Stage.
     * Stage is the basic component of CheckpointJs. Each Stage refers a checkpoint of CheckpointJs.
     * There are three (four) type of Stage:
     *   1. start
     *   2. end
     *   3. node
     *   (4. millstone)
     *
     * @param arg the argument that used for constructing a Stage.
     */
    function Stage(arg) {
        /* Set default */
        for (var attr in DEFAULT_STAGE) {
            this[attr] = arg[attr];
        }

        /* 1. Use an Object to construct Stage */
        if (typeof arg === "object"){
            for (var attr in arg) {
                this[attr] = arg[attr];
            }
        }
        /* 2. Use a String to construct Stage */
        else if (typeof arg === "string") {
            this.title = arg;
        }
        /* 3. Wrong Constructor */
        else {
            return null;
        }
        return this;
    }

    /**
     * Initialize the CheckpointJs.
     * This function firstly cleans everything within the CheckpointJs container.
     * Then add the DOM of CheckpointJs to that container.
     *
     * @param at
     */
    function _init(at) {
        this.destroy();
        if (typeof this.checkpointJs == "object") {
            this.inited = true;
            if (typeof at != "number") {
                at = 0;
            }

            for ( var i = 0; i< this.stages.length; i++ ) {
                if ( i === 0 ) {
                    this.stages[i].type = "start";
                } else if ( i === this.stages.length - 1 ) {
                    this.stages[i].type = "end";
                }
                var stage = this.stages[i];
                var stageDom = _createDomFromStage.call(this, stage, i, at);
                this.checkpointJs.appendChild(stageDom);
            }
            this.currentIndex = at;
            _resize.call(this);
            _markStages.call(this, this.currentIndex);
            return this;
        }
    }

    /**
     * Move to the next Stage.
     */
    function _next() {
        if ( this.currentIndex < this.stages.length - 1 ) {
            this.currentIndex++;
            _markStages.call(this, this.currentIndex);
        } else {
            console.log( "[CheckpointJS] No next stage." );
        }
        return this;
    }

    /**
     * Move back to the previous Stage.
     */
    function _prev() {
        if ( this.currentIndex > 0 ) {
            this.currentIndex--;
            _markStages.call(this, this.currentIndex);
        } else {
            console.log( "[CheckpointJS] No previous stage." );
        }
        return this;
    }

    /**
     * Move to the latest Stage. Mark all the Stage as done.
     */
    function _complete() {
        this.currentIndex = this.stages.length - 1;
        _markStages.call(this, this.currentIndex);
        return this;
    }

    /**
     * Move to a specific Stage
     *
     * @param index the index of Stage, started from 0.
     */
    function _atStage(index) {
        if (index >= 0 && index <= this.stages.length - 1) {
            this.currentIndex = index;
            _markStages.call(this, this.currentIndex);
        } else {
            console.log( "[CheckpointJS] No such stage." );
        }
        return this;
    }

    /**
     * Clear all changes. Restore the status of CheckpointJs.
     */
    function _reset() {
        this.currentIndex = 0;
        _markStages.call(this, this.currentIndex);
        return this;
    }

    /**
     * Mark all Stages before the index one as done. Mark all Stages after the index one as todo.
     * The callee must make sure the  index is correct.
     *
     * @private
     */
    function _markStages(index) {
        var namespace = this._options.namespace,
            stageDom = null,
            i = 0;

        for (i = 0;i < index;i++) {
            stageDom = this.checkpointJs.querySelector(  '[data-index="' + i + '"]' );
            stageDom.setAttribute( "class", namespace + "-block " + namespace + "-done" );
        }
        for (i = index + 1;i < this.stages.length;i++) {
            stageDom = this.checkpointJs.querySelector(  '[data-index="' + i + '"]' );
            stageDom.setAttribute( "class", namespace + "-block " + namespace + "-default" );
        }
        stageDom = this.checkpointJs.querySelector(  '[data-index="' + index + '"]' );
        stageDom.setAttribute( "class", namespace + "-block " + namespace + "-current" );

        /* OnStage Callback */
        if (typeof this.stages[index].onStageCallback === "function") {
            this.stages[index].onStageCallback(this.stages[index]);
        }
    }

    /**
     * Create the DOM for a specific Stage
     *
     * @param stage
     * @param index the index of this Stage among all Stages
     *
     * @private
     */
    function _createDomFromStage( stage, index, at ) {
        var blockTitleDom = document.createElement( "div" ),
            namespace = this._options.namespace;

        blockTitleDom.setAttribute( "data-role", namespace + "-block-title" );
        if ( index % 2 === 0 ) {
            blockTitleDom.setAttribute( "class", namespace + "-block-title" );
        } else {
            blockTitleDom.setAttribute( "class", namespace + "-block-title " + namespace + "-alt" );
        }
        blockTitleDom.appendChild( document.createTextNode( stage.title ) );

        var blockIndicatorDom = document.createElement( "div" );
        blockIndicatorDom.setAttribute( "data-role", namespace + "-block-indicator" );
        blockIndicatorDom.setAttribute( "class", namespace + "-block-indicator" );

        var blockBodyDom = document.createElement( "div" );
        blockBodyDom.setAttribute( "class", namespace + "-block-body" );
        blockBodyDom.appendChild( blockTitleDom );
        blockBodyDom.appendChild( blockIndicatorDom );

        var blockConnectorDom = document.createElement( "div" );
        blockConnectorDom.setAttribute( "data-role", namespace + "-block-connector" );
        blockConnectorDom.setAttribute( "class", namespace + "-block-connector" );

        var stageDom = document.createElement( "div" );
        if (typeof stage.id === "string") {
            stageDom.setAttribute( "id", stage.id );
        }
        stageDom.setAttribute( "class", namespace + "-block" );
        stageDom.setAttribute( "data-index", index );
        stageDom.setAttribute( "data-role", namespace + "-block" );
        stageDom.style.width = 100 / this.stages.length + "%";

        stageDom.appendChild(blockBodyDom);

        if ( stage.type != "end" ) {
            stageDom.appendChild(blockConnectorDom);
        }

        return stageDom;
    }

    /**
     * Resize checkpoint dynamically based on its width
     */
    function _resize() {
        var namespace = this._options.namespace,
            blocks = this.checkpointJs.querySelectorAll('[data-role="' + namespace + '-block"]'),
            containerWidth = this.checkpointJs.offsetWidth,
            stageCount = this.stages.length,
            blockWidth = containerWidth / stageCount;

        for ( var i = 0; i < blocks.length - 1; i++ ) {
            var connector = blocks[i].querySelector('[data-role="' + namespace + '-block-connector"]');

            connector.style["margin-left"] = blockWidth / 2 + 8 + "px";
            connector.style.width = blockWidth - 16 + "px";
        }

    }

    /**
     * Set Stages of the CheckpointJs.
     * Should be called before CheckpointJs#init()
     *
     * @param arguments an array of Stage constructor.
     */
    function _setStages(stages) {
        if (Object.prototype.toString.call( stages ) === '[object Array]') {
            for(var index in stages) {
                var stage = stages[index];
                if (typeof stage === "string" || typeof stage === "object") {
                    this.stages.push(new Stage(stage));
                }
                else {
                    console.log( "[CheckpointJS] Wrong stage parameters." );
                }
            }
        }
        return this;
    }

    /**
     * Replace a current Stage with the new one.
     *
     * @param index the index of the Stage
     * @param arg the argument that used for constructing the Stage.
     */
    function _setStage(index, arg) {
        if (typeof stage === "object") {
            if (index >= 0 && index <= this.stages.length - 1) {
                var stage = new Stage(arg);
                if ( stage !== null ) {
                    this.stages[index] = stage;
                } else {
                    console.log( "[CheckpointJS] Wrong stage parameters." );
                }
            } else {
                console.log( "[CheckpointJS] No such stage." );
            }
        }
        return this;
    }

    /**
     * Add a Stage to the Stage array (after index).
     *
     * @param index the new Stage will be added before this index
     * @param stage
     */
    function _insertStage(index, stage) {
        if (index < 0 || index > this.stages.length) {
            console.log( "[CheckpointJS] Wrong index." );
            return this;
        }
        var newStage = new Stage(stage);
        if (newStage) {
            this.stages.splice(index, 0, newStage);
        } else {
            return this;
        }
        if (index <= this.currentIndex) {
            this.currentIndex++;
        }
        if (this.inited) {
            _init.call(this, this.currentIndex);
        }

        return this;
    }

    /**
     * Append a Stage to the end of Stages.
     *
     * @param stage
     */
    function _appendStage(stage) {
        var newStage = new Stage(stage);
        if (newStage) {
            this.stages.splice(this.stages.length, 0, newStage);
        }

        return this;
    }

    /**
     * Remove a specific Stage.
     * @param index the index of that Stage
     */
    function _removeStage(index) {
        if (index >= 0 && index <= this.stages.length - 1) {
            this.stages.splice(index, 1);
            this.destroy();
            this.init();
        } else {
            console.log( "[CheckpointJS] No such stage." );
        }
        return this;
    }

    /**
     * Destroy all dom elements inside CheckpointJs container.
     */
    function _destroy() {
        if (this.checkpointJs !== null) {
            var container = this.checkpointJs;
            while (container.firstChild) {
                container.removeChild(container.firstChild);
            }
        }
        this.inited = false;
        return this;
    }

    var checkpointJs = function (targetSelector, options) {
        var target = document.querySelector(targetSelector);
        if (target === null) {
            console.log( "[CheckpointJS] Wrong selector." );
            return null;
        } else {
            var checkpoint = new CheckpointJs(target, options);
            return checkpoint;
        }
    };

    /**
     * Register public functions.
     */
    CheckpointJs.prototype = {
        clone: function () {
            return new CheckpointJs(this);
        },
        setStages: _setStages,
        setStage: _setStage,
        insertStage: _insertStage,
        appendStage: _appendStage,
        atStage: _atStage,
        removeStage: _removeStage,
        next: _next,
        prev: _prev,
        complete: _complete,
        init: _init,
        reset: _reset,
        destroy: _destroy
    };

    /* Export public functions */
    exports.checkpointJs = checkpointJs;

    /* Return public functions */
    return checkpointJs;
}));<|endoftext|>


<commit_before>// module that allows you to determine if a file is natively supported by the chromecast
// also supports transcoding of a file

// Chromecast Media Player officially supports:
// Video codecs: H.264 High Profile Level 4.1, 4.2 and 5, VP8
// Audio decoding: HE-AAC, LC-AAC, CELT/Opus, MP3, Vorbis
// Image formats: BMP, GIF, JPEG, PNG, WEBP
// Containers: MP4, WebM
//
// Unofficial support discovered via testing:
// Video: h264 level 3.1, 5.1
// Containers: MKV (present as webm)

var probe = require('node-ffprobe');
var ffmpeg = require('fluent-ffmpeg');
var path = require('path');
var fs = require('fs');

//cache the responses from reading ff-probe
//based on the mtime of the file
var probe_cache = {}

//function to cache the results of probe
var probe_check_cache = function(file, callback){
	var stats = fs.statSync(file);

	var append_probe_cache = function(err, probeData){
				if(probeData){
					probe_cache[file] = {}
					probe_cache[file].stats = stats;
					probe_cache[file].probeData = probeData;
				}
				callback(err, probeData)
			}

	if(file in probe_cache){
		if(probe_cache[file].stats.mtime.valueOf() != stats.mtime.valueOf()){
			probe(file, append_probe_cache);
		} else {
			callback(0, probe_cache[file].probeData)
		}
	} else {
		probe(file, append_probe_cache);
	}
}

var get_video_encode = function(){
	//returns the default video encoding format for the chromecast
	return '-vcodec libx264 -profile:v high -level 5.0';
}

var get_audio_encode = function(){
	//returns the default audio encoding format for the chromecast
	return '-acodec aac -q:a 100';
}

var get_file_data = function(file, callback){
	//callback is: function(compatibility, data)
	//where data gives specifics about what is and isn't compatible
	//includes the output from ffprobe under ffprobe_data

	probe_check_cache(file, function(err, probeData) {
		var obj = {
			audio: 0,
			video: 0,
			container: 0,
			ffprobe_data: undefined
		}

		//error check and abort
		if(probeData == undefined){
			callback(0, obj);
			return
		}
		obj.ffprobe_data = probeData;

		//check for subtitles file
		//add it to the file info
		subtitle_extensions = ['.srt', '.ass']
		for (i in subtitle_extensions){
			var ext = subtitle_extensions[i];
			subtitle_file = path.join(path.dirname(file), path.basename(file, path.extname(file)) + ext)
			if(fs.existsSync(subtitle_file)){
				obj.subtitle_file = path.basename(subtitle_file);
				break;
			}
		}

		/*console.log('--')
		console.log(probeData)
		console.log('--')*/

		//Examine streams and recommend transcoding if needed
		track_audio = 0;
		track_video = 0;
		for(i in probeData.streams){
			stream = probeData.streams[i]
			if(stream.codec_type == 'video'){
				if(stream.codec_name == 'h264'
					&& stream.profile == 'High'
					&& (stream.level == 31 || stream.level == 41 || stream.level == 42 || stream.level == 5 || stream.level == 50 || stream.level == 51)
					){
						obj.video = 1;
						stream.chromecast_compat = 1;
						stream.video_transcode = '-vcodec copy';
				} else {
					stream.chromecast_compat = 0;
					stream.video_transcode = get_video_encode(); //get default encoding for chromecast
				}
				stream.transcode_track_id = track_video++;
			}

			if(stream.codec_type == 'audio'){
				if( (stream.codec_name == 'aac' || stream.codec_name == 'mp3' || stream.codec_name == 'vorbis' || stream.codec_name == 'opus') ){
					obj.audio = 1;
					stream.chromecast_compat = 1;
					stream.audio_transcode = '-acodec copy';
				} else {
					stream.chromecast_compat = 0;
					stream.audio_transcode = get_audio_encode(); //get default encoding for chromecast
				}
				stream.transcode_track_id = track_audio++;
			}
		}

		//generate a recommended transcode command
		var output_file = '\'' + path.basename(file, path.extname(file)) + '.mp4\''
		obj.transcode_cmd = 'ffmpeg -i \'' + path.basename(file) +'\' ' + obj.video_transcode + ' ' + obj.audio_transcode +' '+ output_file

		//ffprobe returns a list of formats that the container might be classified as
		// i.e. for mp4/mov/etc we'll get a string that looks like: 'mov,mp4,m4a,3gp,3g2,mj2'
		if(  probeData.format.format_name.split(',').indexOf('mp4') > -1 || probeData.format.format_name.split(',').indexOf('webm') > -1){
			obj.container = 1;
		}

		//return full compatibility
		compat = 0;
		if(obj.audio == 1 && obj.video==1 && obj.container == 1){
			compat = 1;
		}
		callback(compat, obj);
	});
}

var get_dir_data = function(basedir, dir, return_compat, callback){
	//reads a directory
	//calls callback( files ) with an associative array of files
	//if return_compat is true, it also returns results of get_file_info()
	var response_obj = {};
	var to_check = [];

	real_dir = path.join(basedir, dir)
	files = fs.readdirSync(real_dir);
	files.forEach(function(f){
		var file = path.join(dir, f);
		var file_loc = path.join(basedir, file);

		//fill in information about the file in this dir
		stats = fs.statSync(file_loc);
		response_obj[file] = {
				compatibility_data: undefined,
				compatible: 0,
				is_dir: 0,
				stats: stats
			};

		if(stats && stats.isFile()){
			to_check.push(file);
		} else if ( stats && stats.isDirectory()){
			response_obj[file].is_dir = 1;
		}
	});

	var append_compat = function(file){
			console.log('checking compatbility: basedir: '+basedir+' file: '+file)
			get_file_data(path.join(basedir, file), function(compat, data){
				response_obj[file].compatibility_data = data;
				response_obj[file].compatible = compat;

				if(to_check.length > 0){
					append_compat(to_check.pop());
				} else {
					callback(response_obj);
				}
			});
		};

	if(return_compat && to_check.length > 0){
			append_compat(to_check.pop());
	} else {
		callback(response_obj);
	}
}

var extract_subs = function(data, options, callback){
	//extract subtitles
	if(options.subtitletrack){
		var substream = data.ffprobe_data.streams[options.subtitletrack];
		var subexts = { //map between 'codec_name' and subtitle extension
			'ass' : 'ass',
			'subrip' : 'srt',
		}
		subext = subexts[substream.codec_name];
		if(subext){
			//extract subtitle stream and pass the file to the subtitle path
			var subfile = 'extracted_subs_' + Date.now() + '.' +subext
			var subextract = new ffmpeg({ source: pathToMovie, nolog: true, timeout: 0 })
			.toFormat(subext)
			.saveToFile(subfile, function(){
				console.log('sub extraction done');

				//call transcoder
				options.extracted_subs_file = subfile
				options.subtitle_path = subfile
				callback(options);
			})
		} else {
			callback(options);
		}
	} else {
		callback(options);
	}
}

var cleanup_subs = function(options){
	if(options.extracted_subs_file){
		//clean up the extracted file
		fs.unlinkSync(options.extracted_subs_file);
		console.log('deleting subs file '+options.extracted_subs_file);
	}
}

var transcode_stream = function(file, res, options, ffmpeg_options, callback){
	/* runs transcoding on file, streaming it out via the res object
	  options provides some functionality:
	  ffmpeg_options should be a string that will get passed directly to ffmpeg (see below)
	  callback(err, ffmpeg_exit_code, ffmpeg_output);will be called when transcode is finished,
	  	err will be True if there was an error, and error_string will contain whatever ffmpeg complained about

	  supported options:
	  		{
	  			'audiotrack' : 0 // number corresponding to the 'transcode_track_id' member from get_file_data()
	  			'videotrack' : 0 //same as audiotrack
	  			//coming soon:
				'subtitle_path' : 'something.srt' //optional, specifies file to read subs from
				'subtitletrack' : 0 //optional, stream number of subtitles track
	  		}
	*/
	res.contentType('video/mp4');

	console.log('Invoking transcode with options: ' + options);

	get_file_data(pathToMovie, function(compat, data){

		//figure out if a track has been selected from the options
		if(options.audiotrack == undefined)
			options.audiotrack = 0;

		if(options.videotrack == undefined)
			options.videotrack = 0;

		var opts = ['-strict experimental'];

		//handle subtitles
		var subtitle_arg = '';
		var subtitle_opt = '';

		//check for and extract subs (if needed)
		extract_subs(data, options, function(options){

			//build the transcoding parameters for ffmpeg
			//file based subtitles
			if(options.subtitle_path){
				//todo: need to check if this version of ffmpeg supports subs before enabling
				escaped_subs = options.subtitle_path;
				subtitle_arg = '-vf';
				subtitle_opt = 'subtitles=' + escaped_subs;
			}

			//scan the streams to find the selected ones
			for(var i in data.ffprobe_data.streams){
				var stream = data.ffprobe_data.streams[i];

				if(stream.codec_type == 'audio' && stream.transcode_track_id == options.audiotrack){
							opts.push('-map a:' + stream.transcode_track_id);
							opts.push(stream.audio_transcode);
				}
				else if(stream.codec_type == 'video' && stream.transcode_track_id == options.videotrack){
							opts.push('-map v:' + stream.transcode_track_id);

							if(options.subtitle_path){
								//need to use full video encoding with subtitles
								opts.push( get_video_encode() );
							} else {
								opts.push(stream.video_transcode);
							}
				}
			}
			//todo: error if any selected stream number is not valid

			console.log('calling transcode with options: '+opts)
			var proc = new ffmpeg({ source: pathToMovie, nolog: true, timeout: 0 })
			.toFormat('matroska')
			.addOptions( opts )
			//.withVideoCodec('copy')
			//.withAudioCodec('copy')
			// save to stream
			/*.onProgress(function(progress) {
			    console.log(progress);
			  })*/

			//add subtitles options
			if(subtitle_arg != ''){
				proc.addOption( subtitle_arg, subtitle_opt ) //have to add subtitle arg separately to deal with spaces
			}

			proc.writeToStream(res, function(retcode, ffmpeg_output){
				//console.log('transcoding finished: '+retcode); //+' error: '+error);

				err = 0;
				if(retcode == 255){
					//retcode seems like transcoding was terminated early by node, which is fine
				} else if (retcode == 1){
					//genuine error
					err = 1;
				}

				cleanup_subs(options);
				callback(err, retcode, ffmpeg_output);
			});
		}); //extract_subs
	}); //get_file_data
}

module.exports = {
  get_file_data: get_file_data,
  get_dir_data: get_dir_data,
  transcode_stream : transcode_stream
}
<commit_msg>allow VP8 to pass through<commit_after>// module that allows you to determine if a file is natively supported by the chromecast
// also supports transcoding of a file

// Chromecast Media Player officially supports:
// Video codecs: H.264 High Profile Level 4.1, 4.2 and 5, VP8
// Audio decoding: HE-AAC, LC-AAC, CELT/Opus, MP3, Vorbis
// Image formats: BMP, GIF, JPEG, PNG, WEBP
// Containers: MP4, WebM
//
// Unofficial support discovered via testing:
// Video: h264 level 3.1, 5.1
// Containers: MKV (present as webm)

var probe = require('node-ffprobe');
var ffmpeg = require('fluent-ffmpeg');
var path = require('path');
var fs = require('fs');

//cache the responses from reading ff-probe
//based on the mtime of the file
var probe_cache = {}

//function to cache the results of probe
var probe_check_cache = function(file, callback){
	var stats = fs.statSync(file);

	var append_probe_cache = function(err, probeData){
				if(probeData){
					probe_cache[file] = {}
					probe_cache[file].stats = stats;
					probe_cache[file].probeData = probeData;
				}
				callback(err, probeData)
			}

	if(file in probe_cache){
		if(probe_cache[file].stats.mtime.valueOf() != stats.mtime.valueOf()){
			probe(file, append_probe_cache);
		} else {
			callback(0, probe_cache[file].probeData)
		}
	} else {
		probe(file, append_probe_cache);
	}
}

var get_video_encode = function(){
	//returns the default video encoding format for the chromecast
	return '-vcodec libx264 -profile:v high -level 5.0';
}

var get_audio_encode = function(){
	//returns the default audio encoding format for the chromecast
	return '-acodec aac -q:a 100';
}

var get_file_data = function(file, callback){
	//callback is: function(compatibility, data)
	//where data gives specifics about what is and isn't compatible
	//includes the output from ffprobe under ffprobe_data

	probe_check_cache(file, function(err, probeData) {
		var obj = {
			audio: 0,
			video: 0,
			container: 0,
			ffprobe_data: undefined
		}

		//error check and abort
		if(probeData == undefined){
			callback(0, obj);
			return
		}
		obj.ffprobe_data = probeData;

		//check for subtitles file
		//add it to the file info
		subtitle_extensions = ['.srt', '.ass']
		for (i in subtitle_extensions){
			var ext = subtitle_extensions[i];
			subtitle_file = path.join(path.dirname(file), path.basename(file, path.extname(file)) + ext)
			if(fs.existsSync(subtitle_file)){
				obj.subtitle_file = path.basename(subtitle_file);
				break;
			}
		}

		/*console.log('--')
		console.log(probeData)
		console.log('--')*/

		//Examine streams and recommend transcoding if needed
		track_audio = 0;
		track_video = 0;
		for(i in probeData.streams){
			stream = probeData.streams[i]
			if(stream.codec_type == 'video'){
				if(stream.codec_name == 'h264'
					&& stream.profile == 'High'
					&& (stream.level == 31 || stream.level == 41 || stream.level == 42 || stream.level == 5 || stream.level == 50 || stream.level == 51)
					){
						obj.video = 1;
						stream.chromecast_compat = 1;
						stream.video_transcode = '-vcodec copy';
				} else if(stream.codec_name == 'vp8'){
					obj.video = 1;
					stream.chromecast_compat = 1;
					stream.video_transcode = '-vcodec copy';
				} else {
					stream.chromecast_compat = 0;
					stream.video_transcode = get_video_encode(); //get default encoding for chromecast
				}
				stream.transcode_track_id = track_video++;
			}

			if(stream.codec_type == 'audio'){
				if( (stream.codec_name == 'aac' || stream.codec_name == 'mp3' || stream.codec_name == 'vorbis' || stream.codec_name == 'opus') ){
					obj.audio = 1;
					stream.chromecast_compat = 1;
					stream.audio_transcode = '-acodec copy';
				} else {
					stream.chromecast_compat = 0;
					stream.audio_transcode = get_audio_encode(); //get default encoding for chromecast
				}
				stream.transcode_track_id = track_audio++;
			}
		}

		//generate a recommended transcode command
		var output_file = '\'' + path.basename(file, path.extname(file)) + '.mp4\''
		obj.transcode_cmd = 'ffmpeg -i \'' + path.basename(file) +'\' ' + obj.video_transcode + ' ' + obj.audio_transcode +' '+ output_file

		//ffprobe returns a list of formats that the container might be classified as
		// i.e. for mp4/mov/etc we'll get a string that looks like: 'mov,mp4,m4a,3gp,3g2,mj2'
		if(  probeData.format.format_name.split(',').indexOf('mp4') > -1 || probeData.format.format_name.split(',').indexOf('webm') > -1){
			obj.container = 1;
		}

		//return full compatibility
		compat = 0;
		if(obj.audio == 1 && obj.video==1 && obj.container == 1){
			compat = 1;
		}
		callback(compat, obj);
	});
}

var get_dir_data = function(basedir, dir, return_compat, callback){
	//reads a directory
	//calls callback( files ) with an associative array of files
	//if return_compat is true, it also returns results of get_file_info()
	var response_obj = {};
	var to_check = [];

	real_dir = path.join(basedir, dir)
	files = fs.readdirSync(real_dir);
	files.forEach(function(f){
		var file = path.join(dir, f);
		var file_loc = path.join(basedir, file);

		//fill in information about the file in this dir
		stats = fs.statSync(file_loc);
		response_obj[file] = {
				compatibility_data: undefined,
				compatible: 0,
				is_dir: 0,
				stats: stats
			};

		if(stats && stats.isFile()){
			to_check.push(file);
		} else if ( stats && stats.isDirectory()){
			response_obj[file].is_dir = 1;
		}
	});

	var append_compat = function(file){
			console.log('checking compatbility: basedir: '+basedir+' file: '+file)
			get_file_data(path.join(basedir, file), function(compat, data){
				response_obj[file].compatibility_data = data;
				response_obj[file].compatible = compat;

				if(to_check.length > 0){
					append_compat(to_check.pop());
				} else {
					callback(response_obj);
				}
			});
		};

	if(return_compat && to_check.length > 0){
			append_compat(to_check.pop());
	} else {
		callback(response_obj);
	}
}

var extract_subs = function(data, options, callback){
	//extract subtitles
	if(options.subtitletrack){
		var substream = data.ffprobe_data.streams[options.subtitletrack];
		var subexts = { //map between 'codec_name' and subtitle extension
			'ass' : 'ass',
			'subrip' : 'srt',
		}
		subext = subexts[substream.codec_name];
		if(subext){
			//extract subtitle stream and pass the file to the subtitle path
			var subfile = 'extracted_subs_' + Date.now() + '.' +subext
			var subextract = new ffmpeg({ source: pathToMovie, nolog: true, timeout: 0 })
			.toFormat(subext)
			.saveToFile(subfile, function(){
				console.log('sub extraction done');

				//call transcoder
				options.extracted_subs_file = subfile
				options.subtitle_path = subfile
				callback(options);
			})
		} else {
			callback(options);
		}
	} else {
		callback(options);
	}
}

var cleanup_subs = function(options){
	if(options.extracted_subs_file){
		//clean up the extracted file
		fs.unlinkSync(options.extracted_subs_file);
		console.log('deleting subs file '+options.extracted_subs_file);
	}
}

var transcode_stream = function(file, res, options, ffmpeg_options, callback){
	/* runs transcoding on file, streaming it out via the res object
	  options provides some functionality:
	  ffmpeg_options should be a string that will get passed directly to ffmpeg (see below)
	  callback(err, ffmpeg_exit_code, ffmpeg_output);will be called when transcode is finished,
	  	err will be True if there was an error, and error_string will contain whatever ffmpeg complained about

	  supported options:
	  		{
	  			'audiotrack' : 0 // number corresponding to the 'transcode_track_id' member from get_file_data()
	  			'videotrack' : 0 //same as audiotrack
	  			//coming soon:
				'subtitle_path' : 'something.srt' //optional, specifies file to read subs from
				'subtitletrack' : 0 //optional, stream number of subtitles track
	  		}
	*/
	res.contentType('video/mp4');

	console.log('Invoking transcode with options: ' + options);

	get_file_data(pathToMovie, function(compat, data){

		//figure out if a track has been selected from the options
		if(options.audiotrack == undefined)
			options.audiotrack = 0;

		if(options.videotrack == undefined)
			options.videotrack = 0;

		var opts = ['-strict experimental'];

		//handle subtitles
		var subtitle_arg = '';
		var subtitle_opt = '';

		//check for and extract subs (if needed)
		extract_subs(data, options, function(options){

			//build the transcoding parameters for ffmpeg
			//file based subtitles
			if(options.subtitle_path){
				//todo: need to check if this version of ffmpeg supports subs before enabling
				escaped_subs = options.subtitle_path;
				subtitle_arg = '-vf';
				subtitle_opt = 'subtitles=' + escaped_subs;
			}

			//scan the streams to find the selected ones
			for(var i in data.ffprobe_data.streams){
				var stream = data.ffprobe_data.streams[i];

				if(stream.codec_type == 'audio' && stream.transcode_track_id == options.audiotrack){
							opts.push('-map a:' + stream.transcode_track_id);
							opts.push(stream.audio_transcode);
				}
				else if(stream.codec_type == 'video' && stream.transcode_track_id == options.videotrack){
							opts.push('-map v:' + stream.transcode_track_id);

							if(options.subtitle_path){
								//need to use full video encoding with subtitles
								opts.push( get_video_encode() );
							} else {
								opts.push(stream.video_transcode);
							}
				}
			}
			//todo: error if any selected stream number is not valid

			console.log('calling transcode with options: '+opts)
			var proc = new ffmpeg({ source: pathToMovie, nolog: true, timeout: 0 })
			.toFormat('matroska')
			.addOptions( opts )
			//.withVideoCodec('copy')
			//.withAudioCodec('copy')
			// save to stream
			/*.onProgress(function(progress) {
			    console.log(progress);
			  })*/

			//add subtitles options
			if(subtitle_arg != ''){
				proc.addOption( subtitle_arg, subtitle_opt ) //have to add subtitle arg separately to deal with spaces
			}

			proc.writeToStream(res, function(retcode, ffmpeg_output){
				//console.log('transcoding finished: '+retcode); //+' error: '+error);

				err = 0;
				if(retcode == 255){
					//retcode seems like transcoding was terminated early by node, which is fine
				} else if (retcode == 1){
					//genuine error
					err = 1;
				}

				cleanup_subs(options);
				callback(err, retcode, ffmpeg_output);
			});
		}); //extract_subs
	}); //get_file_data
}

module.exports = {
  get_file_data: get_file_data,
  get_dir_data: get_dir_data,
  transcode_stream : transcode_stream
}
<|endoftext|>


<commit_before>Session.set('initialLoad', true);

// HELPERS
getSetting = function(setting){
  var settings=Settings.find().fetch()[0];
  if(settings){
    return settings[setting];
  }
  return '';
}
clearSeenErrors = function(){
  Errors.update({seen:true}, {$set: {show:false}}, {multi:true});
}
// SUBSCRIPTIONS

// ** Errors **
// Local (client-only) collection

Errors = new Meteor.Collection(null);

// ** Settings **

Settings = new Meteor.Collection('settings');
Meteor.subscribe('settings', function(){

  // runs once on site load
  analyticsInit();
  Session.set('settingsLoaded',true);
});

// ** Users **

Meteor.subscribe('currentUser');
Meteor.subscribe('allUsers');

// ** Notifications **
// Only load if user is logged in

var Notifications = new Meteor.Collection('notifications');
if(Meteor.user()){
  Meteor.subscribe('notifications');
}

// ** Posts **
// We have a few subscriptions here, for the various ways we load posts
//
// The advantage is that
//   a) we can change pages a lot quicker
//     XXX: and we can animate between them (todo)
//   b) we know when an individual page is ready

Posts = new Meteor.Collection('posts');

STATUS_PENDING=1;
STATUS_APPROVED=2;
STATUS_REJECTED=3;
FIND_APPROVED={$or: [{status: {$exists : false}}, {status: STATUS_APPROVED}]};

var postListSubscription = function(find, options, per_page) {
  var handle = paginatedSubscription(per_page, 'paginatedPosts', find, options);
  handle.fetch = function() {
    return limitDocuments(Posts.find(find, options), handle.loaded());
  }
  return handle;
}

var topPostsHandle = postListSubscription(FIND_APPROVED, {sort: {score: -1}}, 10);
var newPostsHandle = postListSubscription(FIND_APPROVED, {sort: {submitted: -1}}, 10);
var bestPostsHandle = postListSubscription(FIND_APPROVED, {sort: {baseScore: -1}}, 10);
var pendingPostsHandle = postListSubscription(
  {$or: [{status: STATUS_PENDING}, {status: STATUS_REJECTED}]},
  {sort: {score: -1}},
  10
);

// digest subscriptions
DIGEST_PRELOADING = 3;
var digestHandles = {}
var dateHash = function(mDate) {
  return mDate.format('DD-MM-YYYY');
}
var currentMDateForDigest = function() {
  return moment(Session.get('currentDate')).startOf('day');
}
var currentDigestHandle = function() {
  return digestHandles[dateHash(currentMDateForDigest())];
}

// we use autorun here, because we DON'T want meteor to automatically
// unsubscribe for us
Meteor.autorun(function() {
  var daySubscription = function(mDate) {
    var find = _.extend({
        submitted: {
          $gte: mDate.startOf('day').valueOf(),
          $lt: mDate.endOf('day').valueOf()
        }
      }, FIND_APPROVED);
    var options = {sort: {score: -1}};

    // we aren't ever going to paginate this sub, but we'll use pSub
    // so we have a reactive loading() function
    // (grr... https://github.com/meteor/meteor/pull/273)
    return postListSubscription(find, options, 5);
  };

  // take it to the start of the day.
  var mDate = currentMDateForDigest();
  var firstDate = moment(mDate).subtract('days', DIGEST_PRELOADING);
  var lastDate = moment(mDate).add('days', DIGEST_PRELOADING);

  // first unsubscribe all the subscriptions that fall outside of our current range
  _.each(digestHandles, function(handle, hash) {
    var mDate = moment(hash, 'DD-MM-YYYY');
    if (mDate < firstDate || mDate > lastDate) {
      // console.log('unsubscribing digest for ' + mDate.toString())
      handle.stop();
      delete digestHandles[dateHash(mDate)];
    }
  });

  // set up a sub for each day for the DIGEST_PRELOADING days before and after
  // but we want to be smart about it --
  for (mDate = firstDate; mDate < lastDate; mDate.add('days',1 )) {
    if (! digestHandles[dateHash(mDate)] && mDate < moment().add('days', 1)) {
      // console.log('subscribing digest for ' + mDate.toString());
      digestHandles[dateHash(mDate)] = daySubscription(mDate);
    }
  }
});

// ** Categories **

Categories = new Meteor.Collection('categories');
Meteor.subscribe('categories');

// ** Comments **
// Collection depends on selectedPostId and selectedCommentId session variable

Session.set('selectedPostId', null);
Comments = new Meteor.Collection('comments');
Meteor.autosubscribe(function() {
  var query = { $or : [ { post : Session.get('selectedPostId') } , { _id : Session.get('selectedCommentId') } ] };
  Meteor.subscribe('comments', query, function() {
    Session.set('commentReady', true);
  });
});<commit_msg>switched to development branch<commit_after>Session.set('initialLoad', true);

// HELPERS
getSetting = function(setting){
  var settings=Settings.find().fetch()[0];
  if(settings){
    return settings[setting];
  }
  return '';
}
clearSeenErrors = function(){
  Errors.update({seen:true}, {$set: {show:false}}, {multi:true});
}
// SUBSCRIPTIONS

// ** Errors **
// Local (client-only) collection

Errors = new Meteor.Collection(null);

// ** Settings **

Settings = new Meteor.Collection('settings');
Meteor.subscribe('settings', function(){

  // runs once on site load
  analyticsInit();
  Session.set('settingsLoaded',true);
});

// ** Users **

Meteor.subscribe('currentUser');
Meteor.subscribe('allUsers');

// ** Notifications **
// Only load if user is logged in

var Notifications = new Meteor.Collection('notifications');
if(Meteor.user()){
  Meteor.subscribe('notifications');
}

// ** Posts **
// We have a few subscriptions here, for the various ways we load posts
//
// The advantage is that
//   a) we can change pages a lot quicker
//     XXX: and we can animate between them (todo)
//   b) we know when an individual page is ready

Posts = new Meteor.Collection('posts');

STATUS_PENDING=1;
STATUS_APPROVED=2;
STATUS_REJECTED=3;
FIND_APPROVED={$or: [{status: {$exists : false}}, {status: STATUS_APPROVED}]};

var postListSubscription = function(find, options, per_page) {
  var handle = paginatedSubscription(per_page, 'paginatedPosts', find, options);
  handle.fetch = function() {
    return limitDocuments(Posts.find(find, options), handle.loaded());
  }
  return handle;
}

var topPostsHandle = postListSubscription(FIND_APPROVED, {sort: {score: -1}}, 10);
var newPostsHandle = postListSubscription(FIND_APPROVED, {sort: {submitted: -1}}, 10);
var bestPostsHandle = postListSubscription(FIND_APPROVED, {sort: {baseScore: -1}}, 10);
var pendingPostsHandle = postListSubscription(
  {$or: [{status: STATUS_PENDING}, {status: STATUS_REJECTED}]},
  {sort: {score: -1}},
  10
);

// digest subscriptions
DIGEST_PRELOADING = 3;
var digestHandles = {}
var dateHash = function(mDate) {
  return mDate.format('DD-MM-YYYY');
}
var currentMDateForDigest = function() {
  return moment(Session.get('currentDate')).startOf('day');
}
var currentDigestHandle = function() {
  return digestHandles[dateHash(currentMDateForDigest())];
}

// we use autorun here, because we DON'T want meteor to automatically
// unsubscribe for us
Meteor.autorun(function() {
  var daySubscription = function(mDate) {
    var find = _.extend({
        submitted: {
          $gte: mDate.startOf('day').valueOf(),
          $lt: mDate.endOf('day').valueOf()
        }
      }, FIND_APPROVED);
    var options = {sort: {score: -1}};

    // we aren't ever going to paginate this sub, but we'll use pSub
    // so we have a reactive loading() function
    // (grr... https://github.com/meteor/meteor/pull/273)
    return postListSubscription(find, options, 5);
  };

  // take it to the start of the day.
  var mDate = currentMDateForDigest();
  var firstDate = moment(mDate).subtract('days', DIGEST_PRELOADING);
  var lastDate = moment(mDate).add('days', DIGEST_PRELOADING);

  // first unsubscribe all the subscriptions that fall outside of our current range
  _.each(digestHandles, function(handle, hash) {
    var mDate = moment(hash, 'DD-MM-YYYY');
    if (mDate < firstDate || mDate > lastDate) {
      // console.log('unsubscribing digest for ' + mDate.toString())
      handle.stop();
      delete digestHandles[dateHash(mDate)];
    }
  });

  // set up a sub for each day for the DIGEST_PRELOADING days before and after
  // but we want to be smart about it --
  for (mDate = firstDate; mDate < lastDate; mDate.add('days',1 )) {
    if (! digestHandles[dateHash(mDate)] && mDate < moment().add('days', 1)) {
      // console.log('subscribing digest for ' + mDate.toString());
      digestHandles[dateHash(mDate)] = daySubscription(mDate);
    }
  }
});

// ** Categories **

Categories = new Meteor.Collection('categories');
Meteor.subscribe('categories');

// ** Comments **
// Collection depends on selectedPostId and selectedCommentId session variable

Session.set('selectedPostId', null);
Comments = new Meteor.Collection('comments');
Meteor.autosubscribe(function() {
  var query = { $or : [ { post : Session.get('selectedPostId') } , { _id : Session.get('selectedCommentId') } ] };
  Meteor.subscribe('comments', query, function() {
    Session.set('commentReady', true);
  });
});
<|endoftext|>


<commit_before>#!/usr/bin/env node

/*!
 * clockdrift.js by Rich Trott, (c) 2012 Regents of University of California, MIT License
 */

var http = require('http')
var https = require('https')
var before
var tolerance
var i
var argvLength = process.argv.length

function usage (errorMessage) {
  if (errorMessage) {
    console.error('\nError: ' + errorMessage + '.\n')
  }
  console.error('Usage: clockdrift.js tolerance url1 [url2 ...]')
  console.error('    tolerance: number of seconds a timestamp can be off without being reported')
  console.error('    url1 ...: URLs to inspect')
  console.error('\nExample: clockdrift.js 15 http://tycho.usno.navy.mil/ http://www.example.com/')
}

function checkTime (host) {
  return function (res) {
    var clockTimestamp,
      diff

    if (typeof res.headers.date !== 'string') {
      console.error('No date header returned by ' + host + '.')
      process.exitCode = 1
      return
    }

    clockTimestamp = new Date(res.headers.date).getTime()
    if (isNaN(clockTimestamp)) {
      console.error('Could not convert date header from ' + host + ' to timestamp. (Malformed?)')
      process.exitCode = 1
      return
    }

    diff = Math.round((clockTimestamp - before) / 1000)

    if (Math.abs(diff) > tolerance) {
      console.log('Clock at ' + host + ' is ' + diff + 's off from local clock.')
    }
  }
}

function dispatchRequest (targetUrl) {
  var reqObj = false
  var options
  var req

  options = new URL(targetUrl)
  options.method = 'HEAD'

  switch (options.protocol) {
    case 'http:':
      reqObj = http
      break
    case 'https:':
      reqObj = https
      options.rejectUnauthorized = false
      break
  }

  if (reqObj) {
    req = reqObj.request(options, checkTime(options.host))
    req.on('error', function (e) {
      console.error('Error on ' + options.host + ': ' + e.message)
      process.exitCode = 1
    })
    req.on('socket', function (socket) {
      socket.setTimeout(1000)
      socket.on('timeout', function () {
        console.error('Socket timeout; hanging up.')
        process.exitCode = 1
        req.abort()
      })
    })
    req.end()
  } else {
    console.error('Could not determine protocol: ' + options.protocol)
    process.exitCode = 1
  }
}

if (argvLength < 4) {
  usage()
  process.exit(1)
}

tolerance = parseInt(process.argv[2], 10)
if (isNaN(tolerance)) {
  usage('tolerance must be a number')
  process.exit(1)
}

before = new Date().getTime()

for (i = 3; i < argvLength; i++) {
  dispatchRequest(process.argv[i])
}
<commit_msg>chore: change to prepare for hypothetical move to TypeScript<commit_after>#!/usr/bin/env node

/*!
 * clockdrift.js by Rich Trott, (c) 2012 Regents of University of California, MIT License
 */

var http = require('http')
var https = require('https')
var before
var tolerance
var i
var argvLength = process.argv.length

function usage (errorMessage) {
  if (errorMessage) {
    console.error('\nError: ' + errorMessage + '.\n')
  }
  console.error('Usage: clockdrift.js tolerance url1 [url2 ...]')
  console.error('    tolerance: number of seconds a timestamp can be off without being reported')
  console.error('    url1 ...: URLs to inspect')
  console.error('\nExample: clockdrift.js 15 http://tycho.usno.navy.mil/ http://www.example.com/')
}

function checkTime (host) {
  return function (res) {
    var clockTimestamp,
      diff

    if (typeof res.headers.date !== 'string') {
      console.error('No date header returned by ' + host + '.')
      process.exitCode = 1
      return
    }

    clockTimestamp = new Date(res.headers.date).getTime()
    if (isNaN(clockTimestamp)) {
      console.error('Could not convert date header from ' + host + ' to timestamp. (Malformed?)')
      process.exitCode = 1
      return
    }

    diff = Math.round((clockTimestamp - before) / 1000)

    if (Math.abs(diff) > tolerance) {
      console.log('Clock at ' + host + ' is ' + diff + 's off from local clock.')
    }
  }
}

function dispatchRequest (targetUrl) {
  var reqObj
  var options
  var req

  options = new URL(targetUrl)
  options.method = 'HEAD'

  switch (options.protocol) {
    case 'http:':
      reqObj = http
      break
    case 'https:':
      reqObj = https
      options.rejectUnauthorized = false
      break
  }

  if (reqObj) {
    req = reqObj.request(options, checkTime(options.host))
    req.on('error', function (e) {
      console.error('Error on ' + options.host + ': ' + e.message)
      process.exitCode = 1
    })
    req.on('socket', function (socket) {
      socket.setTimeout(1000)
      socket.on('timeout', function () {
        console.error('Socket timeout; hanging up.')
        process.exitCode = 1
        req.abort()
      })
    })
    req.end()
  } else {
    console.error('Could not determine protocol: ' + options.protocol)
    process.exitCode = 1
  }
}

if (argvLength < 4) {
  usage('')
  process.exit(1)
}

tolerance = parseInt(process.argv[2], 10)
if (isNaN(tolerance)) {
  usage('tolerance must be a number')
  process.exit(1)
}

before = new Date().getTime()

for (i = 3; i < argvLength; i++) {
  dispatchRequest(process.argv[i])
}
<|endoftext|>


<commit_before>(function() {
'use strict';

// Use Parse.Cloud.define to define as many cloud functions as you want.
Parse.Cloud.define('slove', function(request, response) {
  response.success('You\'ve been sloved!');
});

/**
 * Twilio (phone confirmation)
 */
var twilioTestMode = true;

var testSid = 'AC67aa36effde530903ec7d9a2b11b9498';
var testTok = '0b508f57260346c4b909d3f4e063c3b3';
var liveSid = 'AC7848a69b13ee905acf3aa3fc00a32270';
var liveTok = '30d9b2319348852525081e14ce693749';

var twilio = require('twilio')(
  twilioTestMode ? testSid : liveSid,
  twilioTestMode ? testTok : liveTok
);

// Parse Cloud Function
Parse.Cloud.define('sendPhoneCode', function(request, response) {
  var phoneNumber = request.params.phoneNumber;
  var query = new Parse.Query('_User');

  query.equalTo('phoneNumber', phoneNumber);
  query.find().then(function(results) {
    if (results.length > 0) {
      response.error('phone_number_already_used');
    }
    else {
      var verificationCode = Math.floor(Math.random() * 9999).toString();
      // Ensures that the code is exactly 4 characters long
      while (verificationCode.length < 4) {
        verificationCode = '0' + verificationCode;
      }

      // Save the verificationCode for the user who requested the validation
      var user = Parse.User.current();
      user.set('phoneVerificationCode', verificationCode);
      user.save();

      twilio.sendSms({
        From: '+15005550006',
        To: phoneNumber,
        Body: 'Hi from Slove! Your verification code is: ' + verificationCode
      },
      function(errorData, responseData) {
        if (!errorData) {
          responseData.status = 'ok';
          response.success(responseData);
        }
        else {
          response.error('failed_to_send');
        }
      });
    }
  });
});

Parse.Cloud.define('confirmPhoneCode', function(request, response) {
  var user = Parse.User.current();
  var verificationCode = user.get('phoneVerificationCode');

  if (verificationCode === request.params.phoneVerificationCode) {
    user.set('phoneNumber', request.params.phoneNumber);
    user.save();
    response.success({ status: 'ok' });
  }
  else {
    response.error('codes_dont_match');
  }
});

/**
 * Contacts
 */
Parse.Cloud.define('getRegisteredContacts', function(request, response) {
  var phoneNumbers = request.params.phoneNumbers;
  // Check if param was sent correctly
  if (phoneNumbers === undefined) {
    response.error('missing_param');
    return;
  }
  // Check if param is in the expected format
  if (!Array.isArray(phoneNumbers)) {
    response.error('param_is_not_an_array');
    return;
  }

  // Run the query with supplied param and send back formated results,
  // or send an error message if no match was found
  var registeredContacts = [];
  var userObject = {
    username: '',
    phoneNumber: '',
    pictureUrl: ''
  };
  var currentUser;
  var query = new Parse.Query('_User');
  query.containedIn('phoneNumber', phoneNumbers);
  query.each(function(user) {
    currentUser = Object.create(userObject);
    currentUser.username = user.get('username') ? user.get('username') : '';
    currentUser.pictureUrl = user.get('pictureUrl') ? user.get('pictureUrl') : '';
    // We know this one is present because it matched
    currentUser.phoneNumber = user.get('phoneNumber');
    // Save it to the list that we will return
    registeredContacts.push(currentUser);
  })
  .then(function() {
    if (registeredContacts.length > 0) {
      response.success({ status: 'ok', registeredContacts: registeredContacts });
    }
    else {
      response.error('no_user_found_for_these_numbers');
    }
  });
});

})();<commit_msg>Error code renaming, getRegisteredFriends method<commit_after>(function() {
'use strict';

// Use Parse.Cloud.define to define as many cloud functions as you want.
Parse.Cloud.define('slove', function(request, response) {
  response.success('You\'ve been sloved!');
});

/**
 * Twilio (phone confirmation)
 */
var twilioTestMode = true;

var testSid = 'AC67aa36effde530903ec7d9a2b11b9498';
var testTok = '0b508f57260346c4b909d3f4e063c3b3';
var liveSid = 'AC7848a69b13ee905acf3aa3fc00a32270';
var liveTok = '30d9b2319348852525081e14ce693749';

var twilio = require('twilio')(
  twilioTestMode ? testSid : liveSid,
  twilioTestMode ? testTok : liveTok
);

// Parse Cloud Function
Parse.Cloud.define('sendPhoneCode', function(request, response) {
  var phoneNumber = request.params.phoneNumber;
  var query = new Parse.Query('_User');

  query.equalTo('phoneNumber', phoneNumber);
  query.find().then(function(results) {
    if (results.length > 0) {
      response.error('error_phone_number_already_used');
    }
    else {
      var verificationCode = Math.floor(Math.random() * 9999).toString();
      // Ensures that the code is exactly 4 characters long
      while (verificationCode.length < 4) {
        verificationCode = '0' + verificationCode;
      }

      // Save the verificationCode for the user who requested the validation
      var user = Parse.User.current();
      user.set('phoneVerificationCode', verificationCode);
      user.save();

      twilio.sendSms({
        From: '+15005550006',
        To: phoneNumber,
        Body: 'Hi from Slove! Your verification code is: ' + verificationCode
      },
      function(errorData, responseData) {
        if (!errorData) {
          responseData.status = 'ok';
          response.success(responseData);
        }
        else {
          response.error('error_failed_to_send');
        }
      });
    }
  });
});

Parse.Cloud.define('confirmPhoneCode', function(request, response) {
  var user = Parse.User.current();
  var verificationCode = user.get('phoneVerificationCode');

  if (verificationCode === request.params.phoneVerificationCode) {
    user.set('phoneNumber', request.params.phoneNumber);
    user.save();
    response.success({ status: 'ok' });
  }
  else {
    response.error('error_codes_dont_match');
  }
});

/**
 * Contacts and Facebook friends
 */
Parse.Cloud.define('getRegisteredContacts', function(request, response) {
  var phoneNumbers = request.params.phoneNumbers;
  // Check if param was sent correctly
  if (phoneNumbers === undefined) {
    response.error('error_missing_param');
    return;
  }
  // Check if param is in the expected format
  if (!Array.isArray(phoneNumbers)) {
    response.error('error_param_is_not_an_array');
    return;
  }

  // Run the query with supplied param and send back formated results,
  // or send an error message if no match was found
  var registeredContacts = [];
  var userObject = {
    username: '',
    phoneNumber: '',
    pictureUrl: ''
  };
  var currentUser;
  var query = new Parse.Query('_User');
  query.containedIn('phoneNumber', phoneNumbers);
  query.each(function(user) {
    currentUser = Object.create(userObject);
    currentUser.username = user.get('username') ? user.get('username') : '';
    currentUser.pictureUrl = user.get('pictureUrl') ? user.get('pictureUrl') : '';
    // We know this one is present because it matched
    currentUser.phoneNumber = user.get('phoneNumber');
    // Save it to the list that we will return
    registeredContacts.push(currentUser);
  })
  .then(function() {
    if (registeredContacts.length > 0) {
      response.success({ status: 'ok', registeredContacts: registeredContacts });
    }
    else {
      response.error('error_no_user_found_for_supplied_numbers');
    }
  });
});

Parse.Cloud.define('getRegisteredFriends', function(request, response) {
  var facebookIds = request.params.facebookIds;
  // Check if param was sent correctly
  if (facebookIds === undefined) {
    response.error('error_missing_param');
    return;
  }
  // Check if param is in the expected format
  if (!Array.isArray(facebookIds)) {
    response.error('error_param_is_not_an_array');
    return;
  }

  // Run the query with supplied param and send back formated results,
  // or send an error message if no match was found
  var registeredFriends = [];
  var userObject = {
    username: '',
    facebookId: '',
    pictureUrl: ''
  };
  var currentUser;
  var query = new Parse.Query('_User');
  query.containedIn('facebookId', facebookIds);
  query.each(function(user) {
    currentUser = Object.create(userObject);
    currentUser.username = user.get('username') ? user.get('username') : '';
    currentUser.pictureUrl = user.get('pictureUrl') ? user.get('pictureUrl') : '';
    // We know this one is present because it matched
    currentUser.facebookId = user.get('facebookId');
    // Save it to the list that we will return
    registeredFriends.push(currentUser);
  })
  .then(function() {
    if (registeredFriends.length > 0) {
      response.success({ status: 'ok', registeredFriends: registeredFriends });
    }
    else {
      response.error('error_no_user_found_for_supplied_fb_ids');
    }
  });
});

})();<|endoftext|>