<commit_before>
function shopSheet(len,wid,thk){
    this.length = len;
    this.width = wid;
    this.thickness = thk;

    this.makeCsg = function() {
        var cube = new CSG.cube({corner1: [0,0,0], corner2: [len,wid,thk]});
        return cube;
    }
}

//supported rail
//linear slide direction along the x axis, bottom of the support sits on z=0
function shopSbrxx(len,diam){
    //define dimensions based on underlying real world spec sheet
    switch (diam) {
        case 16:
            var dimB = 40;
            var dimT = 5;
            var dimh = 25;
            var dimh1 = 10;
        case 20:
            var dimB = 45;
            var dimT = 5;
            var dimh = 25;
            var dimh1 = 10;
        default: //assume default = 20mm diam
            var dimB = 45;
            var dimT = 5;
            var dimh = 27;
            var dimh1 = 10;
    }

    this.specUrl = "http://cnc4you.co.uk/resources/SBRxxUU.pdf";
    this.width = dimB;
    this.railZPos = dimh; //height of the centre of the rail
    this.length = len;

    this.makeCsg = function(){
        var cyl = new CSG.cylinder({start:[0,0,dimh],end:[len ,0,dimh],radius:diam /2,resolution:16});
        var cube0 = new CSG.cube({corner1:[0,dimB/2,0],corner2:[len,-dimB/2,dimT]});
        var cube1 = new CSG.cube({corner1:[0,dimh1/2,0],corner2:[len,-dimh1/2,dimh]});
        return cyl.union([cube0,cube1]);
    }
}

//linear bearing
//oriented so that open side faced down, and slide direction is along X axis
function shopSbrxxuu(diam) {
        //define dimensions based on underlying real world spec sheet
    switch (diam) {
        case 16:
            var dimW = 45;
            var dimB = 40;
            var dimG = 33;
            var dimh2 = 20;
            var dimh1 = 10;
            var dimM = 45;
            var dimI = 12;
        case 20:
            var dimW = 48;
            var dimB = 45;
            var dimG = 39;
            var dimh2 = 23;
            var dimh1 = 10;
            var dimM = 50;
            var dimI = 12;
        default: //assume default = 20mm diam
            var dimW = 48;
            var dimB = 45;
            var dimG = 39;
            var dimh2 = 23;
            var dimh1 = 10;
            var dimM = 50;
            var dimI = 12;
    }

    this.specUrl = "http://cnc4you.co.uk/resources/SBRxxUU.pdf";
    this.width = dimW;
    this.length = dimM;
    this.railZPos = dimG - dimh2; //height of the centre of the rail
    this.height = dimG;

    this.makeCsg = function() {
        var cube0 = new CSG.cube({corner1:[0,-dimB/2,0],corner2:[dimM,dimB/2,dimG]});
        var cube1 = new CSG.cube({corner1:[0,-dimW/2,dimG - dimI],corner2:[dimM,dimW/2,dimG]});
        var cylCutout = new CSG.cylinder({start: [0,0,this.railZPos], end: [dimM,0,this.railZPos], radius: diam/2, resolution:16});
        var cubeCutOut = new CSG.cube({corner1:[0,-dimh1/2,0],corner2:[dimM,dimh1/2,this.railZPos]});
        return cube0.union(cube1).subtract([cylCutout,cubeCutOut]);
    }
}

// angle aluminium- width goes along y axis, height along z axis
function shopAluAngle(width,height,thick,len) {
    console.log ("w: " + width + ", h: " + height + ", t: " + thick + ", l: " + len)
    //properties
    this.inWidth = width - thick;
    this.inHeight = height - thick;

    this.makeCsg = function() {
        var cube0 = new CSG.cube({corner1: [0,0,0], corner2: [len,width,thick]});
        var cube1 = new CSG.cube({corner1: [0,0,0], corner2: [len,thick,height]});
        return cube0.union(cube1);
    }
}

//ball screw fixed support
//input is ballscrew diameter (NOT support inner diam)
function shopBkxx(diam){
    //define dimensions according to diam
    switch (diam) {
        case 15:
            var dimd1 = 12;
            var dimB = 60;
            var dimH1 = 32.5;
            var dimH = 43;
            var dimh = 25;
            var dimB1 = 34;
            var dimL = 25;
            var dimL1 = 5;
        case 20:
            var dimd1 = 15;
            var dimB = 70;
            var dimH1 = 38;
            var dimH = 48;
            var dimh = 28;
            var dimB1 = 40;
            var dimL = 27;
            var dimL1 = 6
        default:
           console.log ("Fixed bearing for " + diam + "mm diameter ballscrew not yet set up!!");
    }

    //define properties
    this.specUrl = "http://cnc4you.co.uk/resources/BK%20xx%20Fixed%20Bearing.pdf";
    this.width = dimB;
    this.bscrewZPos = dimh;
    this.thick = dimL;

    //return csg
    this.makeCsg = function() {
        var cube0 = new CSG.cube({corner1: [0,-dimB/2,0], corner2:[dimL,dimB/2,dimH1]});
        var cube1 = new CSG.cube({corner1: [-dimL1,-dimB1/2,0], corner2:[dimL,dimB1/2,dimH]});
        var cylCutout = new CSG.cylinder({start: [-dimL1,0,dimh], end: [dimL,0,dimh], radius: dimd1/2, resolution:16});
        return cube0.union(cube1).subtract(cylCutout);
    }
}

//ball screw floating support
//input is ballscrew diameter (NOT support inner diam)
function shopBfxx(diam){
    //define dimensions according to diam
    switch (diam) {
        case 15:
            var dimd1 = 10;
            var dimB = 60;
            var dimB1 = 34;
            var dimH = 43;
            var dimH1 = 32.5;
            var dimh = 25;
            var dimL = 20;
        case 20:
            var dimd1 = 15;
            var dimB = 70;
            var dimB1 = 40;
            var dimH = 48;
            var dimH1 = 38;
            var dimh = 28;
            var dimL = 20;
        default:
           console.log ("Floating bearing for " + diam + "mm diameter ballscrew not yet set up!!");
    }

    //define properties
    this.specUrl = "http://cnc4you.co.uk/resources/BF%20xx%20Floating%20Bearing.pdf";
    this.width = dimB;
    this.bscrewZPos = dimh;
    this.thick = dimL;

    //return csg
    this.makeCsg = function() {
        var cube0 = new CSG.cube({corner1: [0,-dimB/2,0], corner2:[dimL,dimB/2,dimH1]});
        var cube1 = new CSG.cube({corner1: [0,-dimB1/2,0], corner2:[dimL,dimB1/2,dimH]});
        var cylCutout = new CSG.cylinder({start: [0,0,dimh], end: [dimL,0,dimh], radius: dimd1/2, resolution:16});
        return cube0.union(cube1).subtract(cylCutout);
    }
}

//ballscrew, left end machined for BKxx fixed bearing, right end machined for BFxx floating bearing
//oriented along the x axis, starting at (0,0,0)
function shopRmxx05(diam, len) {
    //dimensions
    switch(diam) {
        case 16:
            var dimB = 10;
            var dimD = 12;
            var dimF = 15;
            var dimE = 36;
            var dimD2 = 10; //floating end
            var dimE2 = 11; //floating end
        case 20:
            var dimB = 12;
            var dimD = 15;
            var dimF = 20;
            var dimE = 40;
            var dimD2 = 15; //floating end
            var dimE2 = 13; //floating end
        default:
            console.log (diam + "mm diameter ballscrew not yet set up!!");
    }

    //define properties
    this.specUrl = "http://cnc4you.co.uk/resources/BK12+Machining.PDF";
    this.diam = diam;
    this.length = len;
    this.threadStart = dimF + dimE;
    this.threadEnd = len - dimE2;

    //return csg
    this.makeCsg = function() {
        var cyl0 = new CSG.cylinder({start: [0,0,0], end: [dimF,0,0], radius: dimB/2, resolution:16});
        var cyl1 = new CSG.cylinder({start: [dimF,0,0], end: [this.threadStart,0,0], radius: dimD/2, resolution:16});
        var cyl2 = new CSG.cylinder({start: [this.threadStart,0,0], end: [this.threadEnd,0,0], radius: diam/2, resolution:16});
        var cyl3 = new CSG.cylinder({start: [this.threadEnd,0,0], end: [len,0,0], radius: dimD2/2, resolution:16});
        return cyl0.union([cyl1, cyl2, cyl3]);
    }
}

/*
function CNC4YOU() {

    this.shopCube = function(len,wid,thk){
        this.length = len;
        this.width = wid;
        this.thickness = thk;

        this.makeCsg = function() {
            var cube = new CSG.cube({corner1: [0,0,0], corner2: [len,wid,thk]});
            var cube1 = cube.rotateY(180);
            var cubes = cube.union(cube1);
            return cubes;
        }
    }

}
*/
<commit_msg>add property to x ballscrew<commit_after>
function shopSheet(len,wid,thk){
    this.length = len;
    this.width = wid;
    this.thickness = thk;

    this.makeCsg = function() {
        var cube = new CSG.cube({corner1: [0,0,0], corner2: [len,wid,thk]});
        return cube;
    }
}

//supported rail
//linear slide direction along the x axis, bottom of the support sits on z=0
function shopSbrxx(len,diam){
    //define dimensions based on underlying real world spec sheet
    switch (diam) {
        case 16:
            var dimB = 40;
            var dimT = 5;
            var dimh = 25;
            var dimh1 = 10;
        case 20:
            var dimB = 45;
            var dimT = 5;
            var dimh = 25;
            var dimh1 = 10;
        default: //assume default = 20mm diam
            var dimB = 45;
            var dimT = 5;
            var dimh = 27;
            var dimh1 = 10;
    }

    this.specUrl = "http://cnc4you.co.uk/resources/SBRxxUU.pdf";
    this.width = dimB;
    this.railZPos = dimh; //height of the centre of the rail
    this.length = len;

    this.makeCsg = function(){
        var cyl = new CSG.cylinder({start:[0,0,dimh],end:[len ,0,dimh],radius:diam /2,resolution:16});
        var cube0 = new CSG.cube({corner1:[0,dimB/2,0],corner2:[len,-dimB/2,dimT]});
        var cube1 = new CSG.cube({corner1:[0,dimh1/2,0],corner2:[len,-dimh1/2,dimh]});
        return cyl.union([cube0,cube1]);
    }
}

//linear bearing
//oriented so that open side faced down, and slide direction is along X axis
function shopSbrxxuu(diam) {
        //define dimensions based on underlying real world spec sheet
    switch (diam) {
        case 16:
            var dimW = 45;
            var dimB = 40;
            var dimG = 33;
            var dimh2 = 20;
            var dimh1 = 10;
            var dimM = 45;
            var dimI = 12;
        case 20:
            var dimW = 48;
            var dimB = 45;
            var dimG = 39;
            var dimh2 = 23;
            var dimh1 = 10;
            var dimM = 50;
            var dimI = 12;
        default: //assume default = 20mm diam
            var dimW = 48;
            var dimB = 45;
            var dimG = 39;
            var dimh2 = 23;
            var dimh1 = 10;
            var dimM = 50;
            var dimI = 12;
    }

    this.specUrl = "http://cnc4you.co.uk/resources/SBRxxUU.pdf";
    this.width = dimW;
    this.length = dimM;
    this.railZPos = dimG - dimh2; //height of the centre of the rail
    this.height = dimG;

    this.makeCsg = function() {
        var cube0 = new CSG.cube({corner1:[0,-dimB/2,0],corner2:[dimM,dimB/2,dimG]});
        var cube1 = new CSG.cube({corner1:[0,-dimW/2,dimG - dimI],corner2:[dimM,dimW/2,dimG]});
        var cylCutout = new CSG.cylinder({start: [0,0,this.railZPos], end: [dimM,0,this.railZPos], radius: diam/2, resolution:16});
        var cubeCutOut = new CSG.cube({corner1:[0,-dimh1/2,0],corner2:[dimM,dimh1/2,this.railZPos]});
        return cube0.union(cube1).subtract([cylCutout,cubeCutOut]);
    }
}

// angle aluminium- width goes along y axis, height along z axis
function shopAluAngle(width,height,thick,len) {
    console.log ("w: " + width + ", h: " + height + ", t: " + thick + ", l: " + len)
    //properties
    this.inWidth = width - thick;
    this.inHeight = height - thick;

    this.makeCsg = function() {
        var cube0 = new CSG.cube({corner1: [0,0,0], corner2: [len,width,thick]});
        var cube1 = new CSG.cube({corner1: [0,0,0], corner2: [len,thick,height]});
        return cube0.union(cube1);
    }
}

//ball screw fixed support
//input is ballscrew diameter (NOT support inner diam)
function shopBkxx(diam){
    //define dimensions according to diam
    switch (diam) {
        case 15:
            var dimd1 = 12;
            var dimB = 60;
            var dimH1 = 32.5;
            var dimH = 43;
            var dimh = 25;
            var dimB1 = 34;
            var dimL = 25;
            var dimL1 = 5;
        case 20:
            var dimd1 = 15;
            var dimB = 70;
            var dimH1 = 38;
            var dimH = 48;
            var dimh = 28;
            var dimB1 = 40;
            var dimL = 27;
            var dimL1 = 6
        default:
           console.log ("Fixed bearing for " + diam + "mm diameter ballscrew not yet set up!!");
    }

    //define properties
    this.specUrl = "http://cnc4you.co.uk/resources/BK%20xx%20Fixed%20Bearing.pdf";
    this.width = dimB;
    this.bscrewZPos = dimh;
    this.thick = dimL;

    //return csg
    this.makeCsg = function() {
        var cube0 = new CSG.cube({corner1: [0,-dimB/2,0], corner2:[dimL,dimB/2,dimH1]});
        var cube1 = new CSG.cube({corner1: [-dimL1,-dimB1/2,0], corner2:[dimL,dimB1/2,dimH]});
        var cylCutout = new CSG.cylinder({start: [-dimL1,0,dimh], end: [dimL,0,dimh], radius: dimd1/2, resolution:16});
        return cube0.union(cube1).subtract(cylCutout);
    }
}

//ball screw floating support
//input is ballscrew diameter (NOT support inner diam)
function shopBfxx(diam){
    //define dimensions according to diam
    switch (diam) {
        case 15:
            var dimd1 = 10;
            var dimB = 60;
            var dimB1 = 34;
            var dimH = 43;
            var dimH1 = 32.5;
            var dimh = 25;
            var dimL = 20;
        case 20:
            var dimd1 = 15;
            var dimB = 70;
            var dimB1 = 40;
            var dimH = 48;
            var dimH1 = 38;
            var dimh = 28;
            var dimL = 20;
        default:
           console.log ("Floating bearing for " + diam + "mm diameter ballscrew not yet set up!!");
    }

    //define properties
    this.specUrl = "http://cnc4you.co.uk/resources/BF%20xx%20Floating%20Bearing.pdf";
    this.width = dimB;
    this.bscrewZPos = dimh;
    this.thick = dimL;

    //return csg
    this.makeCsg = function() {
        var cube0 = new CSG.cube({corner1: [0,-dimB/2,0], corner2:[dimL,dimB/2,dimH1]});
        var cube1 = new CSG.cube({corner1: [0,-dimB1/2,0], corner2:[dimL,dimB1/2,dimH]});
        var cylCutout = new CSG.cylinder({start: [0,0,dimh], end: [dimL,0,dimh], radius: dimd1/2, resolution:16});
        return cube0.union(cube1).subtract(cylCutout);
    }
}

//ballscrew, left end machined for BKxx fixed bearing, right end machined for BFxx floating bearing
//oriented along the x axis, starting at (0,0,0)
function shopRmxx05(diam, len) {
    //dimensions
    switch(diam) {
        case 16:
            var dimB = 10;
            var dimD = 12;
            var dimF = 15;
            var dimE = 36;
            var dimD2 = 10; //floating end
            var dimE2 = 11; //floating end
        case 20:
            var dimB = 12;
            var dimD = 15;
            var dimF = 20;
            var dimE = 40;
            var dimD2 = 15; //floating end
            var dimE2 = 13; //floating end
        default:
            console.log (diam + "mm diameter ballscrew not yet set up!!");
    }

    //define properties
    this.specUrl = "http://cnc4you.co.uk/resources/BK12+Machining.PDF";
    this.diam = diam;
    this.length = len;
    this.threadStart = dimF + dimE;
    this.threadEnd = len - dimE2;
    this.threadFltNub = dimE2;

    //return csg
    this.makeCsg = function() {
        var cyl0 = new CSG.cylinder({start: [0,0,0], end: [dimF,0,0], radius: dimB/2, resolution:16});
        var cyl1 = new CSG.cylinder({start: [dimF,0,0], end: [this.threadStart,0,0], radius: dimD/2, resolution:16});
        var cyl2 = new CSG.cylinder({start: [this.threadStart,0,0], end: [this.threadEnd,0,0], radius: diam/2, resolution:16});
        var cyl3 = new CSG.cylinder({start: [this.threadEnd,0,0], end: [len,0,0], radius: dimD2/2, resolution:16});
        return cyl0.union([cyl1, cyl2, cyl3]);
    }
}

/*
function CNC4YOU() {

    this.shopCube = function(len,wid,thk){
        this.length = len;
        this.width = wid;
        this.thickness = thk;

        this.makeCsg = function() {
            var cube = new CSG.cube({corner1: [0,0,0], corner2: [len,wid,thk]});
            var cube1 = cube.rotateY(180);
            var cubes = cube.union(cube1);
            return cubes;
        }
    }

}
*/
<|endoftext|>


<commit_before>var crypto = require('crypto');

/**
 * utils to make signature, check signature
 */

var encodeData = exports.encodeData = function (toEncode) {
  if( toEncode == null || toEncode == "" ) return ""
  else {
    var result= encodeURIComponent(toEncode);
    // Fix the mismatch between OAuth's  RFC3986's and Javascript's beliefs in what is right and wrong ;)
    return result.replace(/\!/g, "%21")
    .replace(/\'/g, "%27")
    .replace(/\(/g, "%28")
    .replace(/\)/g, "%29")
    .replace(/\*/g, "%2A");
  }
};

/**
 * sort query object to string, e.g. {a:1, b:2, c:3} to 'a=1&b=2&c=3'}
 * @param {Object} query
 * @param {String} concat default '&'
 * @param {Boolean} encode default true
 */
exports.sortQueryToString = function (query, concat, encode) {
  return sortQueryToArray(query, encode).join(concat === undefined ? '&' : concat);
}

/**
 * sort query object to array of string, e.g. {a:1, b:2, c:3} to ['a=1', 'b=2', 'c=3'].
 *
 * @param {Object} query query Object
 * @param {Boolean} encode encode key value with encodeURI, default true.
 */
var sortQueryToArray = exports.sortQueryToArray = function (query, encode/* =true */) {
  var result = [];
  for(var key in query) {
    if(encode === false) {
      result.push(key + '=' + query[key]);
    } else {
      result.push(encodeData(key) + '=' + encodeData(query[key]));
    }
  }

  return result.sort();
}

exports.removePrefix = function (query, prefix) {
  var result = {};
  for(var key in query) {
    if(key.indexOf(prefix) === 0) {
      result[key.substring(prefix.length)] = query[key];
    } else {
      result[key] = query[key];
    }
  }
  return result;
}

/**
 *
 */
exports.removeField = function (query /*, fieldname... */) {
  var result = {},
      fieldnames = Array.prototype.slice.call(arguments, 1);
  for(var key in query) {
    if(fieldnames.indexOf(key) < 0) {
      result[key] = query[key];
    }
  }
  return result;
}

/**
 * build sign
 * @param {String} baseStr base string
 * @param {String} secret app secret
 * @param {String} method sha1 or md5
 */
exports.buildSign = function (baseStr, secret, hashmethod){
  if( !hashmethod ) hashmethod = 'sha1';

  return crypto.createHash(hashmethod).update(baseStr+secret).digest('hex');
};
<commit_msg>fix a bug of sign<commit_after>var crypto = require('crypto');

/**
 * utils to make signature, check signature
 */

var encodeData = exports.encodeData = function (toEncode) {
  if( toEncode == null || toEncode == "" ) return ""
  else {
    var result= encodeURIComponent(toEncode);
    // Fix the mismatch between OAuth's  RFC3986's and Javascript's beliefs in what is right and wrong ;)
    return result.replace(/\!/g, "%21")
    .replace(/\'/g, "%27")
    .replace(/\(/g, "%28")
    .replace(/\)/g, "%29")
    .replace(/\*/g, "%2A");
  }
};

/**
 * sort query object to string, e.g. {a:1, b:2, c:3} to 'a=1&b=2&c=3'}
 * @param {Object} query
 * @param {String} concat default '&'
 * @param {Boolean} encode default true
 */
exports.sortQueryToString = function (query, concat, encode) {
  return sortQueryToArray(query, encode).join(concat === undefined ? '&' : concat);
}

/**
 * sort query object to array of string, e.g. {a:1, b:2, c:3} to ['a=1', 'b=2', 'c=3'].
 *
 * @param {Object} query query Object
 * @param {Boolean} encode encode key value with encodeURI, default true.
 */
var sortQueryToArray = exports.sortQueryToArray = function (query, encode/* =true */) {
  var result = [];
  var keys = Object.keys(query).sort();

  for(var i in keys) {
    var key = keys[i];
    if(encode === false) {
      result.push(key + '=' + query[key]);
    } else {
      result.push(encodeData(key) + '=' + encodeData(query[key]));
    }
  }

  return result;
}

// Sorts the encoded key value pairs by encoded name, then encoded value
var sortPairs = exports.sortPairs = function(argument_pairs) {
  // Sort by name, then value.
  argument_pairs.sort(function(a,b) {
      if ( a[0]== b[0] )  {
        return a[1] < b[1] ? -1 : 1;
      }
      else return a[0] < b[0] ? -1 : 1;
  });

  return argument_pairs;
};

exports.removePrefix = function (query, prefix) {
  var result = {};
  for(var key in query) {
    if(key.indexOf(prefix) === 0) {
      result[key.substring(prefix.length)] = query[key];
    } else {
      result[key] = query[key];
    }
  }
  return result;
}

/**
 *
 */
exports.removeField = function (query /*, fieldname... */) {
  var result = {},
      fieldnames = Array.prototype.slice.call(arguments, 1);
  for(var key in query) {
    if(fieldnames.indexOf(key) < 0) {
      result[key] = query[key];
    }
  }
  return result;
}

/**
 * build sign
 * @param {String} baseStr base string
 * @param {String} secret app secret
 * @param {String} method sha1 or md5
 */
exports.buildSign = function (baseStr, secret, hashmethod, output){
  if( !hashmethod ) hashmethod = 'sha1';

  return crypto.createHash(hashmethod).update(baseStr+secret).digest(output || 'hex');
};
<|endoftext|>


<commit_before>"use strict";

let events = require( 'events' );
let jsonGate = require( 'json-gate' );

let pem = require( './pem.js' );
let time = require( './time.js' );
let DefinitionSink = require( './definition-sink.js' );
let HormoneSink = require( './hormone-sink.js' );

// The hormone names must not start or end with '/' and must not be empty
let nameSchema = jsonGate.createSchema( {
	type: 'string',
	pattern: '^[^/]+.*$'
} );

let definitionRE = /^definition\/(.*)$/;
let hormoneRE = /^hormone\/(.*)$/;

class Sink extends events.EventEmitter {

	constructor( es, filter, certCheck ) {

		super();

		// TODO: Check es
		this._es = es;

		// Check given filter
		if( typeof filter != 'string' ) {
			throw new Error( "Filter must be a string" );
		}
		nameSchema.validate( filter );
		this._filter = filter;

		// If the cert check is given an not a function, complain about it
		if( certCheck !== undefined && typeof certCheck != 'function' ) {
			throw new Error( "Given cert check is not a function" );
		}
		this._certCheck = certCheck;

		// Create some stores ...
		this._subscriptions = {}; // Received definitions and subscribed hormones
		this._hormones = {};      // Last received hormone by name

		// Subscribe to defintion messages
		es._subscribe( 'definition/' + filter, ( topic, payload ) => {
			this._receivedDefinition( topic, payload );
		} ).catch( ( err ) => {
			this.emit( 'error', err );
		} );

	}

	destroy() {

		// Unsubscribe definition topic
		return this._es._unsubscribe( 'defintion/' + this._filter ).then( () => {

			let unsubJobs = [];

			// Unsubscribe all hormones
			for( let name in this._subscriptions ) {
				unsubJobs.push( this._removeHormoneSource( name ) );
			}

			// Wait for all unsubscriptions to be done
			return Promise.all( unsubJobs );

		});

	}

	_receivedDefinition( topic, payload ) {

		// Extract name
		let tmp = definitionRE.exec( topic );
		if( ! tmp ) {
			return this.emit( 'receiveError', new Error( "Received no definition" ) );
		}
		let name = tmp[ 1 ];

		// The payload decides what to do
		if( payload == '' ) {
			// Remove hormone source
			this._removeHormoneSource( name ).catch( ( err ) => {
				this.emit( 'receiveError', err );
			} );
		} else {
			// Add hormone source
			this._addHormoneSource( name, payload ).catch( ( err ) => {
				this.emit( 'receiveError', err );
			} );
		}

	}

	_addHormoneSource( name, payload ) {

		// Store for definition data
		let definition;

		return new Promise( ( resolve ) => {

			// Create new definition from payload
			// Makes sure the format and signature are vaild
			definition = new DefinitionSink( payload );

			// At this point the payload was readable and okay
			resolve();

		} ).then( () => {

			// Check the certificate
			let data = definition.data;
			return Promise.all( [
				pem.verifySigningChain( data.cert, this._es._ca ),
				pem.readCertificateInfo( data.cert )
			] )

		} ).then( ( results ) => {

			// Is cert signed by CA?
			if( ! results[0] ) {
				throw new Error( "Certificate has not been signed by CA" );
			}

			// Is the cert still valid?
			// First get current time. If we have an ESdummy, read time from es.
			// Otherwise get the current time from the OS
			let now = time.now();
			if( results[1].validity.start > now ) {
				throw new Error( "Certificate is valid in the future" );
			}
			if( results[1].validity.end < now ) {
				throw new Error( "Certificate has been expired" );
			}

			// Own check function
			if( typeof this._certCheck == 'function' ) {
				// The check function my return a promise, a value (-> everything is
				// ok) or throws an error
				return this._certCheck( name, results[ 1 ] );
			}

		} ).then( () => {

			// If the hormone is known, remove the old one first
			if( this._subscriptions[ name ] ) this._removeHormoneSource( name );

			// Subscribe to hormone
			return this._es._subscribe( 'hormone/' + name, ( topic, payload ) => {

				// Skip empty messages
				if( payload == '' ) return;

				// Hand over hormone to handler
				this._receivedHormone( topic, payload );

			} );

		} ).then( () => {

			// Store handle
			this._subscriptions[ name ] = definition;

			// Report that we started listening to hormones
			this.emit( 'subscribe', name, definition );

		} );

	}

	_removeHormoneSource( name ) {

		// Check if we know the hormone source that shall be removed
		if( ! this._subscriptions[ name ] ) {
			return Promise.reject( new Error( "Received unkown definition removal" ) );
		}

		// Unsubscribe hormone
		return this._es._unsubscribe( 'hormone/' + name ).then( () => {

			// Remove timeout if it is pending
			if( this._hormones[ name ].timeout ) {
				clearTimeout( this._hormones[ name ].timeout );
			}

			// Remove subscription and last hormone
			delete this._subscriptions[ name ];
			delete this._hormones[ name ];

			// Emit an event
			this.emit( 'unsubscribe', name );

		} );

	}

	_receivedHormone( topic, payload ) {
		try {

			// Extract name
			let tmp = hormoneRE.exec( topic );
			if( ! tmp ) throw new Error( "Received no hormone" );
			let name = tmp[ 1 ];

			// Check if we have a definition for the received hormone
			if( ! this._subscriptions[ name ] ) throw new Error( "Received unkown hormone" );

			// Interprete hormone
			let hormone = new HormoneSink( this._subscriptions[ name ], payload );

			// At this point the hormone is okay :) At least the signature.

			// Keep the current timestamp
			let receivedAt = time.now();

			// If this is the first hormone we recieved under this name, make sure we
			// have at least some dummy data to compare with
			let formerHormone = this._hormones[ name ] ? this._hormones[ name ] : {
				isFresh: true,
				err: 0,
				timeout: null,
				stateChangedAt: time.now()
			} ;

			// Stop expiration timeout if present
			if( formerHormone.timeout ) clearTimeout( formerHormone.timeout );

			// Further checks:
			let stateChanged = false;
			// - Freshness
			let isFresh = hormone.isFresh;
			if( isFresh && ! formerHormone.isFresh ) {
				// The expired hormone went fresh -> tell the good news!
				this.emit( 'hormoneRefresh', name, hormone );
				stateChanged = true;
			} else if( ! isFresh && formerHormone.isFresh ) {
				// The received hormone has expired!
				this.emit( 'hormoneExpired', name, hormone );
				stateChanged = true;
			}
			// - Error
			let err = hormone.error;
			if( err == 0 && formerHormone.err != err ) {
				// The hormone reported recovery
				this.emit( 'hormoneRecover', name, hormone );
				stateChanged = true;
			} else if( err > 0 && formerHormone.err != err ) {
				// Some error occured!
				this.emit( 'hormoneError', name, hormone );
				stateChanged = true;
			}

			// If the hormone is fresh, set an expire timeout
			let timeout = null;
			if( isFresh ) timeout = setTimeout( () => {
				// The hormone just expired!
				this._hormones[ name ].isFresh = false;
				this._hormones[ name ].timeout = null;
				this.emit( 'hormoneExpired', name, hormone );
			}, hormone.freshness * 1000 );

			// Store all hormone related data
			this._hormones[ name ] = {
				receivedAt: receivedAt,
				stateChangedAt: stateChanged ? receivedAt : formerHormone.stateChangedAt,
				hormone: hormone,
				isFresh: isFresh,
				err: err,
				timeout: timeout
			};

			// Tell the user we got a new hormone
			this.emit( 'hormone', name, hormone );

		} catch( err ) {
			this.emit( 'receiveError', err );
		}

	}

	_fetchHormones( cond ) {
		if( typeof cond !== 'object' ) cond = {};

		// Copy all hormones into an array
		let hormones = [];
		for( let name in this._hormones ) {
			let h = this._hormones[ name ];

			// First we assume that the hormone is part of the required set
			let add = true;

			// Check conditions. If a condition is not met, add is set false
			// - A certain freshness is required
			if( cond.isFresh !== undefined && h.isFresh !== cond.isFresh ) {
				add = false
			}
			// - The hormone error state is interesting
			if( cond.err !== undefined ) {
				if( cond.err && h.err <= 0 ) {
					add = false;
				} else if( ! cond.err && h.err > 0 ) {
					add = false;
				}
			}

			// If no show stopper occured, add the hormone to the set
			if( add ) hormones.push( {
				name: name,
				sentAt: h.hormone.timestamp,
				receivedAt: h.receivedAt,
				stateChangedAt: h.stateChangedAt,
				data: h.hormone.data,
				err: h.err,
				isFresh: h.isFresh
			} );

		}

		return hormones;

	}

	get hormones() { return this._fetchHormones( {} ); }
	get expiredHormones() { return this._fetchHormones( { isFresh: false } ); }
	get errorHormones() { return this._fetchHormones( { err: true } ); }
	get goodHormones() { return this._fetchHormones( { err: false, isFresh: true } ); }

}

module.exports = Sink;
<commit_msg>Bug fix: The validity fields report unix timestamps in milliseconds<commit_after>"use strict";

let events = require( 'events' );
let jsonGate = require( 'json-gate' );

let pem = require( './pem.js' );
let time = require( './time.js' );
let DefinitionSink = require( './definition-sink.js' );
let HormoneSink = require( './hormone-sink.js' );

// The hormone names must not start or end with '/' and must not be empty
let nameSchema = jsonGate.createSchema( {
	type: 'string',
	pattern: '^[^/]+.*$'
} );

let definitionRE = /^definition\/(.*)$/;
let hormoneRE = /^hormone\/(.*)$/;

class Sink extends events.EventEmitter {

	constructor( es, filter, certCheck ) {

		super();

		// TODO: Check es
		this._es = es;

		// Check given filter
		if( typeof filter != 'string' ) {
			throw new Error( "Filter must be a string" );
		}
		nameSchema.validate( filter );
		this._filter = filter;

		// If the cert check is given an not a function, complain about it
		if( certCheck !== undefined && typeof certCheck != 'function' ) {
			throw new Error( "Given cert check is not a function" );
		}
		this._certCheck = certCheck;

		// Create some stores ...
		this._subscriptions = {}; // Received definitions and subscribed hormones
		this._hormones = {};      // Last received hormone by name

		// Subscribe to defintion messages
		es._subscribe( 'definition/' + filter, ( topic, payload ) => {
			this._receivedDefinition( topic, payload );
		} ).catch( ( err ) => {
			this.emit( 'error', err );
		} );

	}

	destroy() {

		// Unsubscribe definition topic
		return this._es._unsubscribe( 'defintion/' + this._filter ).then( () => {

			let unsubJobs = [];

			// Unsubscribe all hormones
			for( let name in this._subscriptions ) {
				unsubJobs.push( this._removeHormoneSource( name ) );
			}

			// Wait for all unsubscriptions to be done
			return Promise.all( unsubJobs );

		});

	}

	_receivedDefinition( topic, payload ) {

		// Extract name
		let tmp = definitionRE.exec( topic );
		if( ! tmp ) {
			return this.emit( 'receiveError', new Error( "Received no definition" ) );
		}
		let name = tmp[ 1 ];

		// The payload decides what to do
		if( payload == '' ) {
			// Remove hormone source
			this._removeHormoneSource( name ).catch( ( err ) => {
				this.emit( 'receiveError', err );
			} );
		} else {
			// Add hormone source
			this._addHormoneSource( name, payload ).catch( ( err ) => {
				this.emit( 'receiveError', err );
			} );
		}

	}

	_addHormoneSource( name, payload ) {

		// Store for definition data
		let definition;

		return new Promise( ( resolve ) => {

			// Create new definition from payload
			// Makes sure the format and signature are vaild
			definition = new DefinitionSink( payload );

			// At this point the payload was readable and okay
			resolve();

		} ).then( () => {

			// Check the certificate
			let data = definition.data;
			return Promise.all( [
				pem.verifySigningChain( data.cert, this._es._ca ),
				pem.readCertificateInfo( data.cert )
			] )

		} ).then( ( results ) => {

			// Is cert signed by CA?
			if( ! results[0] ) {
				throw new Error( "Certificate has not been signed by CA" );
			}

			// Is the cert still valid?
			// First get current time. If we have an ESdummy, read time from es.
			// Otherwise get the current time from the OS
			let now = time.now();
			if( results[1].validity.start / 1000 > now ) {
				throw new Error( "Certificate is valid in the future" );
			}
			if( results[1].validity.end / 1000 < now ) {
				throw new Error( "Certificate has been expired" );
			}

			// Own check function
			if( typeof this._certCheck == 'function' ) {
				// The check function my return a promise, a value (-> everything is
				// ok) or throws an error
				return this._certCheck( name, results[ 1 ] );
			}

		} ).then( () => {

			// If the hormone is known, remove the old one first
			if( this._subscriptions[ name ] ) this._removeHormoneSource( name );

			// Subscribe to hormone
			return this._es._subscribe( 'hormone/' + name, ( topic, payload ) => {

				// Skip empty messages
				if( payload == '' ) return;

				// Hand over hormone to handler
				this._receivedHormone( topic, payload );

			} );

		} ).then( () => {

			// Store handle
			this._subscriptions[ name ] = definition;

			// Report that we started listening to hormones
			this.emit( 'subscribe', name, definition );

		} );

	}

	_removeHormoneSource( name ) {

		// Check if we know the hormone source that shall be removed
		if( ! this._subscriptions[ name ] ) {
			return Promise.reject( new Error( "Received unkown definition removal" ) );
		}

		// Unsubscribe hormone
		return this._es._unsubscribe( 'hormone/' + name ).then( () => {

			// Remove timeout if it is pending
			if( this._hormones[ name ] && this._hormones[ name ].timeout ) {
				clearTimeout( this._hormones[ name ].timeout );
			}

			// Remove subscription and last hormone
			delete this._subscriptions[ name ];
			delete this._hormones[ name ];

			// Emit an event
			this.emit( 'unsubscribe', name );

		} );

	}

	_receivedHormone( topic, payload ) {
		try {

			// Extract name
			let tmp = hormoneRE.exec( topic );
			if( ! tmp ) throw new Error( "Received no hormone" );
			let name = tmp[ 1 ];

			// Check if we have a definition for the received hormone
			if( ! this._subscriptions[ name ] ) throw new Error( "Received unkown hormone" );

			// Interprete hormone
			let hormone = new HormoneSink( this._subscriptions[ name ], payload );

			// At this point the hormone is okay :) At least the signature.

			// Keep the current timestamp
			let receivedAt = time.now();

			// If this is the first hormone we recieved under this name, make sure we
			// have at least some dummy data to compare with
			let formerHormone = this._hormones[ name ] ? this._hormones[ name ] : {
				isFresh: true,
				err: 0,
				timeout: null,
				stateChangedAt: time.now()
			} ;

			// Stop expiration timeout if present
			if( formerHormone.timeout ) clearTimeout( formerHormone.timeout );

			// Further checks:
			let stateChanged = false;
			// - Freshness
			let isFresh = hormone.isFresh;
			if( isFresh && ! formerHormone.isFresh ) {
				// The expired hormone went fresh -> tell the good news!
				this.emit( 'hormoneRefresh', name, hormone );
				stateChanged = true;
			} else if( ! isFresh && formerHormone.isFresh ) {
				// The received hormone has expired!
				this.emit( 'hormoneExpired', name, hormone );
				stateChanged = true;
			}
			// - Error
			let err = hormone.error;
			if( err == 0 && formerHormone.err != err ) {
				// The hormone reported recovery
				this.emit( 'hormoneRecover', name, hormone );
				stateChanged = true;
			} else if( err > 0 && formerHormone.err != err ) {
				// Some error occured!
				this.emit( 'hormoneError', name, hormone );
				stateChanged = true;
			}

			// If the hormone is fresh, set an expire timeout
			let timeout = null;
			if( isFresh ) timeout = setTimeout( () => {
				// The hormone just expired!
				this._hormones[ name ].isFresh = false;
				this._hormones[ name ].timeout = null;
				this.emit( 'hormoneExpired', name, hormone );
			}, hormone.freshness * 1000 );

			// Store all hormone related data
			this._hormones[ name ] = {
				receivedAt: receivedAt,
				stateChangedAt: stateChanged ? receivedAt : formerHormone.stateChangedAt,
				hormone: hormone,
				isFresh: isFresh,
				err: err,
				timeout: timeout
			};

			// Tell the user we got a new hormone
			this.emit( 'hormone', name, hormone );

		} catch( err ) {
			this.emit( 'receiveError', err );
		}

	}

	_fetchHormones( cond ) {
		if( typeof cond !== 'object' ) cond = {};

		// Copy all hormones into an array
		let hormones = [];
		for( let name in this._hormones ) {
			let h = this._hormones[ name ];

			// First we assume that the hormone is part of the required set
			let add = true;

			// Check conditions. If a condition is not met, add is set false
			// - A certain freshness is required
			if( cond.isFresh !== undefined && h.isFresh !== cond.isFresh ) {
				add = false
			}
			// - The hormone error state is interesting
			if( cond.err !== undefined ) {
				if( cond.err && h.err <= 0 ) {
					add = false;
				} else if( ! cond.err && h.err > 0 ) {
					add = false;
				}
			}

			// If no show stopper occured, add the hormone to the set
			if( add ) hormones.push( {
				name: name,
				sentAt: h.hormone.timestamp,
				receivedAt: h.receivedAt,
				stateChangedAt: h.stateChangedAt,
				data: h.hormone.data,
				err: h.err,
				isFresh: h.isFresh
			} );

		}

		return hormones;

	}

	get hormones() { return this._fetchHormones( {} ); }
	get expiredHormones() { return this._fetchHormones( { isFresh: false } ); }
	get errorHormones() { return this._fetchHormones( { err: true } ); }
	get goodHormones() { return this._fetchHormones( { err: false, isFresh: true } ); }

}

module.exports = Sink;
<|endoftext|>


<commit_before>// @flow

import moment from 'moment'
import React, { Component } from 'react'
import { Col, Grid, OverlayTrigger, Row, Tooltip } from 'react-bootstrap'

import type { FeedVersion } from '../../../types'

type Props = {
  activeVersion: FeedVersion,
  comparedVersion: FeedVersion
}

const dateFormat = 'MMM. D, YYYY'
const todayDateFormat = 'MMM. D'

const BASE_HEIGHT_EM = 1.25
const HEIGHT_EM = `${BASE_HEIGHT_EM}em`
const SPAN_MARGIN_PIXELS = 5
const DATE_LABEL_ZINDEX = 9999
const TODAY_LABEL_ZINDEX = 9998

/**
 * This component renders bars representing the date ranges for which the given feed versions
 * are valid (e.g., Jan 1 - March 27) and lets the user visualize overlaps or gaps in the validity periods
 * of the active and compared feed versions.
 */
export default class FeedVersionSpanChart extends Component<Props> {
  render () {
    const { activeVersion, comparedVersion } = this.props
    // Construct moment objects for the active/compared version start/end dates.
    const activeStartDate = moment(activeVersion.validationSummary.startDate)
    const activeEndDate = moment(activeVersion.validationSummary.endDate)
    const comparedStartDate = moment(comparedVersion.validationSummary.startDate)
    const comparedEndDate = moment(comparedVersion.validationSummary.endDate)

    const firstDate = moment.min(activeStartDate, comparedStartDate)
    const lastDate = moment.max(activeEndDate, comparedEndDate)

    const earliestEnd = moment.min(activeEndDate, comparedEndDate)
    const latestStart = moment.max(activeStartDate, comparedStartDate)

    return (
      <Grid>
        <Row>
          <Col xs={3}>

Version {activeVersion.version} (selected)

            {/* Cell placeholder to prevent formatting issues */}
            {latestStart.diff(earliestEnd, 'days') > 0 &&
Gap in service
}

Version {comparedVersion.version}

          </Col>
          <Col xs={9} style={{position: 'relative', height: '120px'}}>
            <FeedStartLabel
              firstDate={firstDate}
              lastDate={lastDate}
              top={0}
              versionStartDate={activeStartDate}
            />
            <FeedEndLabel
              firstDate={firstDate}
              lastDate={lastDate}
              top={0}
              versionStartDate={activeStartDate}
              versionEndDate={activeEndDate}
            />
            <FeedSpan
              firstDate={firstDate}
              lastDate={lastDate}
              isServiceGap={false}
              top={0}
              versionStartDate={activeStartDate}
              versionEndDate={activeEndDate}
            />

            {latestStart.diff(earliestEnd, 'days') > 0 &&
              <FeedSpan
                firstDate={firstDate}
                isServiceGap
                lastDate={lastDate}
                top={`${BASE_HEIGHT_EM}em`}
                versionStartDate={earliestEnd}
                versionEndDate={latestStart}
              />
            }

            <FeedStartLabel
              firstDate={firstDate}
              lastDate={lastDate}
              top={`${2 * BASE_HEIGHT_EM}em`}
              versionStartDate={comparedStartDate}
            />
            <FeedEndLabel
              firstDate={firstDate}
              lastDate={lastDate}
              top={`${2 * BASE_HEIGHT_EM}em`}
              versionStartDate={comparedStartDate}
              versionEndDate={comparedEndDate}
            />
            <FeedSpan
              firstDate={firstDate}
              lastDate={lastDate}
              isServiceGap={false}
              top={`${2 * BASE_HEIGHT_EM}em`}
              versionStartDate={comparedStartDate}
              versionEndDate={comparedEndDate}
            />
            <TodayMarker
              firstDate={firstDate}
              lastDate={lastDate}
              versionStartDate={activeStartDate}
              versionEndDate={activeEndDate}
            />
          </Col>
        </Row>
      </Grid>
    )
  }
}

// Width of feed span
const WIDTH_PIXELS = 400
// HACK: Set a fixed offset when positioning the 'today' tooltip,
// since we cannot easily get the width of the 'today' tooltip text.
const TODAY_OFFSET_PIXELS = 45

/** Renders the validity span of one feed using the given props. */
const FeedSpan = ({
  firstDate,
  lastDate,
  isServiceGap = false,
  top,
  versionStartDate,
  versionEndDate
}) => {
  const totalDays = lastDate.diff(firstDate, 'days')
  const versionOffset = versionStartDate.diff(firstDate, 'days')
  const daysActive = versionEndDate.diff(versionStartDate, 'days')
  const relativeLength = daysActive / totalDays
  const relativeOffset = versionOffset / totalDays

  // Date labels and validity span occupy a third of the width available.
  const baseWidth = WIDTH_PIXELS / 3

  return (
    <OverlayTrigger
      placement='bottom'
      overlay={
        <Tooltip id={`feed-span`}>
          {isServiceGap && 'No service for'}{' '}
          {daysActive} days
        </Tooltip>
      }>
      <span
        style={{
          height: HEIGHT_EM,
          left: `${relativeOffset * baseWidth + SPAN_MARGIN_PIXELS + baseWidth}px`,
          position: 'absolute',
          top,
          width: `${relativeLength * baseWidth}px`
        }}
      >
        <span
          style={{
            backgroundColor: isServiceGap ? 'red' : 'grey',
            height: '.75em',
            position: 'absolute',
            bottom: '10%',
            width: '100%'
          }}
          title={`${daysActive} days`}

        />

    </OverlayTrigger>
  )
}

/**
 * Renders a validity span START label (needs to be separate from the span itself for Z order).
 */
const FeedStartLabel = ({
  firstDate,
  lastDate,
  top,
  versionStartDate
}) => {
  const totalDays = lastDate.diff(firstDate, 'days')
  const versionOffset = versionStartDate.diff(firstDate, 'days')
  const relativeOffset = versionOffset / totalDays
  const startLabel = versionStartDate.format(dateFormat)

  // Date labels and validity span occupy a third of the width available.
  const baseWidth = WIDTH_PIXELS / 3

  return (
    <span style={{
      //display: 'inline-block',
      left: `${relativeOffset * baseWidth}px`,
      height: HEIGHT_EM,
      position: 'absolute',
      textAlign: 'right',
      top,
      width: `${baseWidth}px`,
      zIndex: DATE_LABEL_ZINDEX
    }}>
      {startLabel}

  )
}

/**
 * Renders a validity span END label (needs to be separate from the span itself for Z order).
 */
const FeedEndLabel = ({
  firstDate,
  lastDate,
  top,
  versionStartDate,
  versionEndDate
}) => {
  const totalDays = lastDate.diff(firstDate, 'days')
  const versionOffset = versionStartDate.diff(firstDate, 'days')
  const daysActive = versionEndDate.diff(versionStartDate, 'days')
  const endLabel = versionEndDate.format(dateFormat)
  const relativeLength = daysActive / totalDays
  const relativeOffset = versionOffset / totalDays

  // Date labels and validity span occupy a third of the width available.
  const baseWidth = WIDTH_PIXELS / 3

  return (
    <span style={{
      left: `${relativeOffset * baseWidth + SPAN_MARGIN_PIXELS + baseWidth + relativeLength * baseWidth + SPAN_MARGIN_PIXELS}px`,
      height: HEIGHT_EM,
      position: 'absolute',
      top,
      zIndex: DATE_LABEL_ZINDEX
    }}>
      {endLabel}

  )
}

/** Renders a marker on today's date (if one of the feeds being compared is valid). */
const TodayMarker = ({
  firstDate,
  lastDate,
  versionStartDate,
  versionEndDate
}) => {
  const totalDays = lastDate.diff(firstDate, 'days')

  const today = moment()
  const todayOffset = today.diff(firstDate, 'days')

  // Date labels and validity span occupy a third of the width available.
  const baseWidth = WIDTH_PIXELS / 3
  return (
    <div style={{
      backgroundColor: 'yellow',
      border: '1px solid #bbb',
      height: `${BASE_HEIGHT_EM * 3}em`,
      left: `${baseWidth + todayOffset / totalDays * baseWidth}px`,
      position: 'absolute',
      top: 0,
      width: '4px',
      zIndex: TODAY_LABEL_ZINDEX
    }} />
  )
}
<commit_msg>refactor(FeedVersionSpanChart): Add back tooltip for today marker. Refactor.<commit_after>// @flow

import moment from 'moment'
import React, { Component } from 'react'
import { Col, Grid, OverlayTrigger, Row, Tooltip } from 'react-bootstrap'

import type { FeedVersion } from '../../../types'

type Props = {
  activeVersion: FeedVersion,
  comparedVersion: FeedVersion
}

const DATE_FORMAT = 'MMM. D, YYYY'
const TODAY_DATE_FORMAT = 'MMM. D'

const BASE_HEIGHT_EM = 1.25
const HEIGHT_EM = `${BASE_HEIGHT_EM}em`
const SPAN_MARGIN_PIXELS = 5
// Render the 'today' marker above the spans (they have default z-index),
// but underneath the date labels.
// This is also why we need to split up labels and spans during rendering.
const DATE_LABEL_ZINDEX = 9999
const TODAY_MARKER_ZINDEX = 9998

/**
 * This component renders bars representing the date ranges for which the given feed versions
 * are valid (e.g., Jan 1 - March 27) and lets the user visualize overlaps or gaps in the validity periods
 * of the active and compared feed versions.
 */
export default class FeedVersionSpanChart extends Component<Props> {
  render () {
    const { activeVersion, comparedVersion } = this.props
    // Construct moment objects for the active/compared version start/end dates.
    const activeStartDate = moment(activeVersion.validationSummary.startDate)
    const activeEndDate = moment(activeVersion.validationSummary.endDate)
    const comparedStartDate = moment(comparedVersion.validationSummary.startDate)
    const comparedEndDate = moment(comparedVersion.validationSummary.endDate)

    const firstDate = moment.min(activeStartDate, comparedStartDate)
    const lastDate = moment.max(activeEndDate, comparedEndDate)

    const earliestEnd = moment.min(activeEndDate, comparedEndDate)
    const latestStart = moment.max(activeStartDate, comparedStartDate)

    const hasGap = latestStart.diff(earliestEnd, 'days') > 0

    // Render the today marker if it is between first and last date.
    const today = moment()
    const totalDays = lastDate.diff(firstDate, 'days')
    const todayOffset = today.diff(firstDate, 'days')
    const showToday = todayOffset >= 0 && todayOffset < totalDays

    const activeChartProps = {
      firstDate,
      lastDate,
      top: 0,
      versionEndDate: activeEndDate,
      versionStartDate: activeStartDate
    }
    const comparedChartProps = {
      firstDate,
      lastDate,
      top: `${(hasGap ? 2 : 1) * BASE_HEIGHT_EM}em`,
      versionEndDate: comparedEndDate,
      versionStartDate: comparedStartDate
    }

    return (
      <Grid>
        <Row>
          <Col xs={3}>

Version {activeVersion.version} (selected)

            {/* Cell placeholder to prevent formatting issues */}
            {hasGap &&
Gap in service
}

Version {comparedVersion.version}

          </Col>
          <Col xs={9} style={{position: 'relative', height: '120px'}}>
            <FeedStartLabel {...activeChartProps} />
            <FeedEndLabel {...activeChartProps} />
            <FeedSpan {...activeChartProps} isServiceGap={false} />

            {hasGap &&
              <FeedSpan
                firstDate={firstDate}
                isServiceGap
                lastDate={lastDate}
                top={`${BASE_HEIGHT_EM}em`}
                versionStartDate={earliestEnd}
                versionEndDate={latestStart}
              />
            }

            <FeedStartLabel {...comparedChartProps} />
            <FeedEndLabel {...comparedChartProps} />
            <FeedSpan {...comparedChartProps} isServiceGap={false} />

            {showToday && <TodayMarker
              firstDate={firstDate}
              lastDate={lastDate}
            />}
          </Col>
        </Row>
      </Grid>
    )
  }
}

// Width of feed span
const WIDTH_PIXELS = 400

/** Renders the validity span of one feed using the given props. */
const FeedSpan = ({
  firstDate,
  lastDate,
  isServiceGap = false,
  top,
  versionStartDate,
  versionEndDate
}) => {
  const totalDays = lastDate.diff(firstDate, 'days')
  const versionOffset = versionStartDate.diff(firstDate, 'days')
  const daysActive = versionEndDate.diff(versionStartDate, 'days')
  const relativeLength = daysActive / totalDays
  const relativeOffset = versionOffset / totalDays

  // Date labels and validity span occupy a third of the width available.
  const baseWidth = WIDTH_PIXELS / 3

  return (
    <OverlayTrigger
      placement='bottom'
      overlay={
        <Tooltip id={`feed-span`}>
          {isServiceGap && 'No service for'}{' '}
          {daysActive} days
        </Tooltip>
      }>
      <span
        style={{
          height: HEIGHT_EM,
          left: `${relativeOffset * baseWidth + SPAN_MARGIN_PIXELS + baseWidth}px`,
          position: 'absolute',
          top,
          width: `${relativeLength * baseWidth}px`
        }}
      >
        <span
          style={{
            backgroundColor: isServiceGap ? 'red' : 'grey',
            height: '.75em',
            position: 'absolute',
            bottom: '10%',
            width: '100%'
          }}
          title={`${daysActive} days`}

        />

    </OverlayTrigger>
  )
}

/**
 * Renders a validity span START label (needs to be separate from the span itself for Z order).
 */
const FeedStartLabel = ({
  firstDate,
  lastDate,
  top,
  versionStartDate
}) => {
  const totalDays = lastDate.diff(firstDate, 'days')
  const versionOffset = versionStartDate.diff(firstDate, 'days')
  const relativeOffset = versionOffset / totalDays
  const startLabel = versionStartDate.format(DATE_FORMAT)

  // Date labels and validity span occupy a third of the width available.
  const baseWidth = WIDTH_PIXELS / 3

  return (
    <span style={{
      // display: 'inline-block',
      left: `${relativeOffset * baseWidth}px`,
      height: HEIGHT_EM,
      position: 'absolute',
      textAlign: 'right',
      top,
      width: `${baseWidth}px`,
      zIndex: DATE_LABEL_ZINDEX
    }}>
      {startLabel}

  )
}

/**
 * Renders a validity span END label (needs to be separate from the span itself for Z order).
 */
const FeedEndLabel = ({
  firstDate,
  lastDate,
  top,
  versionStartDate,
  versionEndDate
}) => {
  const totalDays = lastDate.diff(firstDate, 'days')
  const versionOffset = versionStartDate.diff(firstDate, 'days')
  const daysActive = versionEndDate.diff(versionStartDate, 'days')
  const endLabel = versionEndDate.format(DATE_FORMAT)
  const relativeLength = daysActive / totalDays
  const relativeOffset = versionOffset / totalDays

  // Date labels and validity span occupy a third of the width available.
  const baseWidth = WIDTH_PIXELS / 3

  return (
    <span style={{
      left: `${relativeOffset * baseWidth + SPAN_MARGIN_PIXELS + baseWidth + relativeLength * baseWidth + SPAN_MARGIN_PIXELS}px`,
      height: HEIGHT_EM,
      position: 'absolute',
      top,
      zIndex: DATE_LABEL_ZINDEX
    }}>
      {endLabel}

  )
}

/** Renders a marker on today's date (if one of the feeds being compared is valid). */
const TodayMarker = ({
  firstDate,
  lastDate
}) => {
  const totalDays = lastDate.diff(firstDate, 'days')

  const today = moment()
  const todayOffset = today.diff(firstDate, 'days')

  // Date labels and validity span occupy a third of the width available.
  const baseWidth = WIDTH_PIXELS / 3

  return (
    <OverlayTrigger
      overlay={<Tooltip
        className='in'
        id={`today-tip`}
        placement={'top'}
        positionTop={-25}>
        {`Today - ${today.format(TODAY_DATE_FORMAT)}`}
      </Tooltip>}
      placement='top'>
      <div
        style={{
          backgroundColor: 'yellow',
          border: '1px solid #bbb',
          height: `${BASE_HEIGHT_EM * 3}em`,
          left: `${baseWidth + todayOffset / totalDays * baseWidth}px`,
          position: 'absolute',
          top: 0,
          width: '4px',
          zIndex: TODAY_MARKER_ZINDEX
        }}
      />
    </OverlayTrigger>
  )
}
<|endoftext|>


<commit_before>const RequestRepository = require('src/plex/requestRepository.js');
const requestRepository = new RequestRepository();

/**
 * Controller: POST a media id to be donwloaded
 * @param {Request} req http request variable
 * @param {Response} res
 * @returns {Callback}
 */

function submitRequestController(req, res) {
	// This is the id that is the param of the url
	const id = req.params.mediaId;
	const type = req.query.type;

	requestRepository.sendRequest(id, type)
	.then(() => {
		res.send({ success: true, message: 'Media item sucessfully requested!' });
	})
	.catch((error) => {
		res.status(500).send({ success: false, error: error.message });
	});
}

module.exports = submitRequestController;<commit_msg>Now also the ip address is passed to the sendRequest function in requestRepository so that the requesters ip address can be logged in the database.<commit_after>const RequestRepository = require('src/plex/requestRepository.js');
const requestRepository = new RequestRepository();

/**
 * Controller: POST a media id to be donwloaded
 * @param {Request} req http request variable
 * @param {Response} res
 * @returns {Callback}
 */

function submitRequestController(req, res) {
	// This is the id that is the param of the url
	const id = req.params.mediaId;
	const type = req.query.type;
	var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;

	requestRepository.sendRequest(id, type, ip)
	.then(() => {
		res.send({ success: true, message: 'Media item sucessfully requested!' });
	})
	.catch((error) => {
		res.status(500).send({ success: false, error: error.message });
	});
}

module.exports = submitRequestController;<|endoftext|>


<commit_before>import AstroClass from '../../../core/class';

function astronomyExecute(args = {}) {
  check(args, Match.Any);

  const {
    className,
    rawDoc,
    methodName,
    methodArgs
  } = args;

  const Class = AstroClass.get(className);
  let doc;
  if (rawDoc._id) {
    doc = Class.findOne(rawDoc._id);
    doc.set(rawDoc);
  }
  else {
    doc = new Class(rawDoc, {
      clone: false
    });
  }

  // Get a method from the class. In some cases method may not be present,
  // because it might be defined only on the server.
  const method = Class.getMeteorMethod(methodName);
  if (!method) {
    return;
  }

  return method.apply(doc, methodArgs);
};

export default astronomyExecute;<commit_msg>Fix #630 fallback for not finding a document with an ID<commit_after>import AstroClass from '../../../core/class';

function astronomyExecute(args = {}) {
  check(args, Match.Any);

  const {
    className,
    rawDoc,
    methodName,
    methodArgs
  } = args;

  const Class = AstroClass.get(className);
  let doc;
  if (rawDoc._id) {
    doc = Class.findOne(rawDoc._id);
  }
  if (doc) {
    doc.set(rawDoc);
  }
  else {
    doc = new Class(rawDoc, {
      clone: false
    });
  }

  // Get a method from the class. In some cases method may not be present,
  // because it might be defined only on the server.
  const method = Class.getMeteorMethod(methodName);
  if (!method) {
    return;
  }

  return method.apply(doc, methodArgs);
};

export default astronomyExecute;<|endoftext|>


<commit_before>/*!
 * Jack.
 *
 * Veselin Todorov <hi@vesln.com>
 * MIT License.
 */

/**
 * Define method.
 *
 * @type {Function}
 */
var define = require('./utils').define;

/**
 * Stub constructor.
 *
 * @param {Object} Object.
 * @param {String} Method name.
 */
function Stub(obj, method) {
  var self = this;
  this.target = obj;
  this.method = method;
  this.orig = obj[method];
  this.reset = function() {

    // Expect count.
    if (self.expectCount) {
      if (self.expectCount !== self.count) {
        throw new Error('Expected ' + self.target.constructor.name  + '.' + self.method + '() to have been called ' + self.expectCount + ' time(s). But it was called ' + self.count + ' time(s).');
      }
    }

    // Setting the original method back.
    self.target[method] = self.orig;
  };
  this.count = 0;
  this.retValues = [];
  this.compile();
};

Stub.prototype = {

  /**
   * Sets return value of a method.
   *
   * @return `this`.
   * @api public
   */
  andReturn: function() {
    this.retValues = Array.prototype.slice.call(arguments);
    return this;
  },

  /**
   * Compiles a function.
   *
   * @param {Function} Desired new functionality.
   * @return `this`.
   * @api private
   */
  compile: function() {
    var self = this;

    this.target[this.method] = function() {
      // Incrementing the call count.
      ++self.count;

      // Return values set?
      if (self.retValues.length) {
        if (self.retValues[self.count - 1]) return self.retValues[self.count - 1];
        return self.retValues[0];
      }

      // Nothing to do, calling the original method.
      return self.orig();
    };

    // Reset function.
    this.target[this.method].reset = this.reset;

    return this;
  },

  /**
   * Sets expectation count to 1.
   *
   * @return `this`.
   * @api public
   */
  get once() {
    this.expectCount = 1;
    return this;
  },

  /**
   * Sets expectation count to 1.
   *
   * @return `this`.
   * @api public
   */
  get twice() {
    this.expectCount = 2;
    return this;
  },

  /**
   * Sets expectation to exact number.
   *
   * @param {Number} Positive number.
   * @return `this`.
   * @api public
   */
  exactly: function(n) {
    this.expectCount = n;
    return this;
  },
};

/**
 * Helper properties.
 *
 * @type {Array}
 */
var props = ['it', 'be', 'to', 'has', 'must', 'should_be', 'called', 'times'];

props.forEach(function(prop) {
  define(Stub.prototype, prop, function() {
    return this;
  });
});

/**
 * Expose `Stub`.
 */
module.exports = Stub;<commit_msg>Misc.<commit_after>/*!
 * Jack.
 *
 * Veselin Todorov <hi@vesln.com>
 * MIT License.
 */

/**
 * Define method.
 *
 * @type {Function}
 */
var define = require('./utils').define;

/**
 * Stub constructor.
 *
 * @param {Object} Object.
 * @param {String} Method name.
 */
function Stub(obj, method) {
  var self = this;
  this.target = obj;
  this.method = method;
  this.orig = obj[method];
  this.reset = function() {

    // Expect count.
    if (self.expectCount) {
      if (self.expectCount !== self.count) {
        throw new Error('Expected ' + self.target.constructor.name  + '.' + self.method + '() to have been called ' + self.expectCount + ' time(s). But it was called ' + self.count + ' time(s).');
      }
    }

    // Setting the original method back.
    self.target[method] = self.orig;
  };
  this.count = 0;
  this.retValues = [];
  this.compile();
};

Stub.prototype = {

  /**
   * Sets return value of a method.
   *
   * @return `this`.
   * @api public
   */
  andReturn: function() {
    this.retValues = Array.prototype.slice.call(arguments);
    return this;
  },

  /**
   * Compiles a function.
   *
   * @param {Function} Desired new functionality.
   * @return `this`.
   * @api private
   */
  compile: function() {
    var self = this;

    this.target[this.method] = function() {
      // Incrementing the call count.
      ++self.count;

      // Return values set?
      if (self.retValues.length) {
        if (self.retValues[self.count - 1]) return self.retValues[self.count - 1];
        return self.retValues[0];
      }

      // Nothing to do, calling the original method.
      return self.orig();
    };

    // Reset function.
    this.target[this.method].reset = this.reset;

    return this;
  },

  /**
   * Sets expectation count to 1.
   *
   * @return `this`.
   * @api public
   */
  get once() {
    this.expectCount = 1;
    return this;
  },

  /**
   * Sets expectation count to 1.
   *
   * @return `this`.
   * @api public
   */
  get twice() {
    this.expectCount = 2;
    return this;
  },

  /**
   * Sets expectation count to at least once.
   *
   * @return `this`.
   * @api public
   */
  get times() {
    this.expectCount = 1;
    return this;
  },

  /**
   * Sets expectation to exact number.
   *
   * @param {Number} Positive number.
   * @return `this`.
   * @api public
   */
  exactly: function(n) {
    this.expectCount = n;
    return this;
  },
};

/**
 * Helper properties.
 *
 * @type {Array}
 */
var props = [
  'it', 'be', 'to',
  'has', 'must', 'should_be',
  'called', 'times', 'of',
  'any', 'and'
];

props.forEach(function(prop) {
  define(Stub.prototype, prop, function() {
    return this;
  });
});

/**
 * Expose `Stub`.
 */
module.exports = Stub;<|endoftext|>


<commit_before>'use strict';

/**
 * A helper for delaying the execution of a function.
 * @private
 * @summary (Any... -> Any) -> Void
 */
var delayed = typeof setImmediate !== 'undefined'?  setImmediate
            : typeof process !== 'undefined'?       process.nextTick
            : /* otherwise */                       setTimeout

/**
 * @module lib/task
 */
module.exports = Task;

// -- Implementation ---------------------------------------------------

/**
 * The `Task[α, β]` structure represents values that depend on time. This
 * allows one to model time-based effects explicitly, such that one can have
 * full knowledge of when they're dealing with delayed computations, latency,
 * or anything that can not be computed immediately.
 *
 * A common use for this structure is to replace the usual Continuation-Passing
 * Style form of programming, in order to be able to compose and sequence
 * time-dependent effects using the generic and powerful monadic operations.
 *
 * @class
 * @summary
 * ((α → Void), (β → Void) → Void), (Void → Void) → Task[α, β]
 *
 * Task[α, β] <: Chain[β]
 *               , Monad[β]
 *               , Functor[β]
 *               , Applicative[β]
 *               , Semigroup[β]
 *               , Monoid[β]
 *               , Show
 */
function Task(computation, cleanup) {
  this.fork = computation;

  this.cleanup = cleanup || function() {};
}

/**
 * Constructs a new `Task[α, β]` containing the single value `β`.
 *
 * `β` can be any value, including `null`, `undefined`, or another
 * `Task[α, β]` structure.
 *
 * @summary β → Task[α, β]
 */
Task.prototype.of = function _of(b) {
  return new Task(function(_, resolve) {
    return resolve(b);
  });
};

Task.of = Task.prototype.of;

/**
 * Constructs a new `Task[α, β]` containing the single value `α`.
 *
 * `α` can be any value, including `null`, `undefined`, or another
 * `Task[α, β]` structure.
 *
 * @summary α → Task[α, β]
 */
Task.prototype.rejected = function _rejected(a) {
  return new Task(function(reject) {
    return reject(a);
  });
};

Task.rejected = Task.prototype.rejected;

// -- Functor ----------------------------------------------------------

/**
 * Transforms the successful value of the `Task[α, β]` using a regular unary
 * function.
 *
 * @summary @Task[α, β] => (β → γ) → Task[α, γ]
 */
Task.prototype.map = function _map(f) {
  var fork = this.fork;
  var cleanup = this.cleanup;

  return new Task(function(reject, resolve) {
    return fork(function(a) {
      return reject(a);
    }, function(b) {
      return resolve(f(b));
    });
  }, cleanup);
};

// -- Chain ------------------------------------------------------------

/**
 * Transforms the succesful value of the `Task[α, β]` using a function to a
 * monad.
 *
 * @summary @Task[α, β] => (β → Task[α, γ]) → Task[α, γ]
 */
Task.prototype.chain = function _chain(f) {
  var fork = this.fork;
  var cleanup = this.cleanup;

  return new Task(function(reject, resolve) {
    return fork(function(a) {
      return reject(a);
    }, function(b) {
      return f(b).fork(reject, resolve);
    });
  }, cleanup);
};

// -- Apply ------------------------------------------------------------

/**
 * Applys the successful value of the `Task[α, (β → γ)]` to the successful
 * value of the `Task[α, β]`
 *
 * @summary @Task[α, (β → γ)] => Task[α, β] → Task[α, γ]
 */

Task.prototype.ap = function _ap(that) {
  var forkThis = this.fork;
  var forkThat = that.fork;
  var cleanupThis = this.cleanup;
  var cleanupThat = that.cleanup;

  function cleanupBoth(state) {
    cleanupThis(state[0]);
    cleanupThat(state[1]);
  }

  return new Task(function(reject, resolve) {
    var func, funcLoaded = false;
    var val, valLoaded = false;
    var allState;

    var thisState = forkThis(reject, guard(function(x) {
      funcLoaded = true;
      func = x;
    }));

    var thatState = forkThat(reject, guard(function(x) {
      valLoaded = true;
      val = x;
    }));

    function guard(setter) {
      return function(x) {
        setter(x);
        if (funcLoaded && valLoaded) {
          delayed(function(){ cleanupBoth(allState) });
          return resolve(func(val));
        } else {
          return x;
        }
      }
    }

    return allState = [thisState, thatState];
  }, cleanupBoth);
};

// -- Semigroup ------------------------------------------------------------

/**
 * Selects the earlier of the two tasks `Task[α, β]`
 *
 * @summary @Task[α, β] => Task[α, β] → Task[α, β]
 */

Task.prototype.concat = function _concat(that) {
  var forkThis = this.fork;
  var forkThat = that.fork;
  var cleanupThis = this.cleanup;
  var cleanupThat = that.cleanup;

  function cleanupBoth(state) {
    cleanupThis(state[0]);
    cleanupThat(state[1]);
  }

  return new Task(function(reject, resolve) {
    var done = false;
    var allState;
    var thisState = forkThis(guard(reject), guard(resolve));
    var thatState = forkThat(guard(reject), guard(resolve));

    return allState = [thisState, thatState];

    function guard(f) {
      return function(x) {
        if (!done) {
          done = true;
          delayed(function(){ cleanupBoth(allState) })
          return f(x);
        }
      };
    }
  }, cleanupBoth);

};

// -- Monoid ------------------------------------------------------------

/**
 * Returns a Task that will never resolve
 *
 * @summary Void → Task[α, _]
 */
Task.empty = function _empty() {
  return new Task(function() {});
};

Task.prototype.empty = Task.empty;

// -- Show -------------------------------------------------------------

/**
 * Returns a textual representation of the `Task[α, β]`
 *
 * @summary @Task[α, β] => Void → String
 */
Task.prototype.toString = function _toString() {
  return 'Task';
};

// -- Extracting and recovering ----------------------------------------

/**
 * Transforms a failure value into a new `Task[α, β]`. Does nothing if the
 * structure already contains a successful value.
 *
 * @summary @Task[α, β] => (α → Task[γ, β]) → Task[γ, β]
 */
Task.prototype.orElse = function _orElse(f) {
  var fork = this.fork;
  var cleanup = this.cleanup;

  return new Task(function(reject, resolve) {
    return fork(function(a) {
      return f(a).fork(reject, resolve);
    }, function(b) {
      return resolve(b);
    });
  }, cleanup);
};

// -- Folds and extended transformations -------------------------------

/**
 * Catamorphism. Takes two functions, applies the leftmost one to the failure
 * value, and the rightmost one to the successful value, depending on which one
 * is present.
 *
 * @summary @Task[α, β] => (α → γ), (β → γ) → Task[δ, γ]
 */
Task.prototype.fold = function _fold(f, g) {
  var fork = this.fork;
  var cleanup = this.cleanup;

  return new Task(function(reject, resolve) {
    return fork(function(a) {
      return resolve(f(a));
    }, function(b) {
      return resolve(g(b));
    });
  }, cleanup);
};

/**
 * Catamorphism.
 *
 * @summary @Task[α, β] => { Rejected: α → γ, Resolved: β → γ } → Task[δ, γ]
 */
Task.prototype.cata = function _cata(pattern) {
  return this.fold(pattern.Rejected, pattern.Resolved);
};

/**
 * Swaps the disjunction values.
 *
 * @summary @Task[α, β] => Void → Task[β, α]
 */
Task.prototype.swap = function _swap() {
  var fork = this.fork;
  var cleanup = this.cleanup;

  return new Task(function(reject, resolve) {
    return fork(function(a) {
      return resolve(a);
    }, function(b) {
      return reject(b);
    });
  }, cleanup);
};

/**
 * Maps both sides of the disjunction.
 *
 * @summary @Task[α, β] => (α → γ), (β → δ) → Task[γ, δ]
 */
Task.prototype.bimap = function _bimap(f, g) {
  var fork = this.fork;
  var cleanup = this.cleanup;

  return new Task(function(reject, resolve) {
    return fork(function(a) {
      return reject(f(a));
    }, function(b) {
      return resolve(g(b));
    });
  }, cleanup);
};

/**
 * Maps the left side of the disjunction (failure).
 *
 * @summary @Task[α, β] => (α → γ) → Task[γ, β]
 */
Task.prototype.rejectedMap = function _rejectedMap(f) {
  var fork = this.fork;
  var cleanup = this.cleanup;

  return new Task(function(reject, resolve) {
    return fork(function(a) {
      return reject(f(a));
    }, function(b) {
      return resolve(b);
    });
  }, cleanup);
};
<commit_msg>handle case when Task is rejected in `ap`<commit_after>'use strict';

/**
 * A helper for delaying the execution of a function.
 * @private
 * @summary (Any... -> Any) -> Void
 */
var delayed = typeof setImmediate !== 'undefined'?  setImmediate
            : typeof process !== 'undefined'?       process.nextTick
            : /* otherwise */                       setTimeout

/**
 * @module lib/task
 */
module.exports = Task;

// -- Implementation ---------------------------------------------------

/**
 * The `Task[α, β]` structure represents values that depend on time. This
 * allows one to model time-based effects explicitly, such that one can have
 * full knowledge of when they're dealing with delayed computations, latency,
 * or anything that can not be computed immediately.
 *
 * A common use for this structure is to replace the usual Continuation-Passing
 * Style form of programming, in order to be able to compose and sequence
 * time-dependent effects using the generic and powerful monadic operations.
 *
 * @class
 * @summary
 * ((α → Void), (β → Void) → Void), (Void → Void) → Task[α, β]
 *
 * Task[α, β] <: Chain[β]
 *               , Monad[β]
 *               , Functor[β]
 *               , Applicative[β]
 *               , Semigroup[β]
 *               , Monoid[β]
 *               , Show
 */
function Task(computation, cleanup) {
  this.fork = computation;

  this.cleanup = cleanup || function() {};
}

/**
 * Constructs a new `Task[α, β]` containing the single value `β`.
 *
 * `β` can be any value, including `null`, `undefined`, or another
 * `Task[α, β]` structure.
 *
 * @summary β → Task[α, β]
 */
Task.prototype.of = function _of(b) {
  return new Task(function(_, resolve) {
    return resolve(b);
  });
};

Task.of = Task.prototype.of;

/**
 * Constructs a new `Task[α, β]` containing the single value `α`.
 *
 * `α` can be any value, including `null`, `undefined`, or another
 * `Task[α, β]` structure.
 *
 * @summary α → Task[α, β]
 */
Task.prototype.rejected = function _rejected(a) {
  return new Task(function(reject) {
    return reject(a);
  });
};

Task.rejected = Task.prototype.rejected;

// -- Functor ----------------------------------------------------------

/**
 * Transforms the successful value of the `Task[α, β]` using a regular unary
 * function.
 *
 * @summary @Task[α, β] => (β → γ) → Task[α, γ]
 */
Task.prototype.map = function _map(f) {
  var fork = this.fork;
  var cleanup = this.cleanup;

  return new Task(function(reject, resolve) {
    return fork(function(a) {
      return reject(a);
    }, function(b) {
      return resolve(f(b));
    });
  }, cleanup);
};

// -- Chain ------------------------------------------------------------

/**
 * Transforms the succesful value of the `Task[α, β]` using a function to a
 * monad.
 *
 * @summary @Task[α, β] => (β → Task[α, γ]) → Task[α, γ]
 */
Task.prototype.chain = function _chain(f) {
  var fork = this.fork;
  var cleanup = this.cleanup;

  return new Task(function(reject, resolve) {
    return fork(function(a) {
      return reject(a);
    }, function(b) {
      return f(b).fork(reject, resolve);
    });
  }, cleanup);
};

// -- Apply ------------------------------------------------------------

/**
 * Applys the successful value of the `Task[α, (β → γ)]` to the successful
 * value of the `Task[α, β]`
 *
 * @summary @Task[α, (β → γ)] => Task[α, β] → Task[α, γ]
 */

Task.prototype.ap = function _ap(that) {
  var forkThis = this.fork;
  var forkThat = that.fork;
  var cleanupThis = this.cleanup;
  var cleanupThat = that.cleanup;

  function cleanupBoth(state) {
    cleanupThis(state[0]);
    cleanupThat(state[1]);
  }

  return new Task(function(reject, resolve) {
    var func, funcLoaded = false;
    var val, valLoaded = false;
    var rejected = false;
    var allState;

    var thisState = forkThis(guardReject, guardResolve(function(x) {
      funcLoaded = true;
      func = x;
    }));

    var thatState = forkThat(guardReject, guardResolve(function(x) {
      valLoaded = true;
      val = x;
    }));

    function guardResolve(setter) {
      return function(x) {
        if (rejected) {
          return;
        }

        setter(x);
        if (funcLoaded && valLoaded) {
          delayed(function(){ cleanupBoth(allState) });
          return resolve(func(val));
        } else {
          return x;
        }
      }
    }

    function guardReject(x) {
      if (!rejected) {
        rejected = true;
        return reject(x);
      }
    }

    return allState = [thisState, thatState];
  }, cleanupBoth);
};

// -- Semigroup ------------------------------------------------------------

/**
 * Selects the earlier of the two tasks `Task[α, β]`
 *
 * @summary @Task[α, β] => Task[α, β] → Task[α, β]
 */

Task.prototype.concat = function _concat(that) {
  var forkThis = this.fork;
  var forkThat = that.fork;
  var cleanupThis = this.cleanup;
  var cleanupThat = that.cleanup;

  function cleanupBoth(state) {
    cleanupThis(state[0]);
    cleanupThat(state[1]);
  }

  return new Task(function(reject, resolve) {
    var done = false;
    var allState;
    var thisState = forkThis(guard(reject), guard(resolve));
    var thatState = forkThat(guard(reject), guard(resolve));

    return allState = [thisState, thatState];

    function guard(f) {
      return function(x) {
        if (!done) {
          done = true;
          delayed(function(){ cleanupBoth(allState) })
          return f(x);
        }
      };
    }
  }, cleanupBoth);

};

// -- Monoid ------------------------------------------------------------

/**
 * Returns a Task that will never resolve
 *
 * @summary Void → Task[α, _]
 */
Task.empty = function _empty() {
  return new Task(function() {});
};

Task.prototype.empty = Task.empty;

// -- Show -------------------------------------------------------------

/**
 * Returns a textual representation of the `Task[α, β]`
 *
 * @summary @Task[α, β] => Void → String
 */
Task.prototype.toString = function _toString() {
  return 'Task';
};

// -- Extracting and recovering ----------------------------------------

/**
 * Transforms a failure value into a new `Task[α, β]`. Does nothing if the
 * structure already contains a successful value.
 *
 * @summary @Task[α, β] => (α → Task[γ, β]) → Task[γ, β]
 */
Task.prototype.orElse = function _orElse(f) {
  var fork = this.fork;
  var cleanup = this.cleanup;

  return new Task(function(reject, resolve) {
    return fork(function(a) {
      return f(a).fork(reject, resolve);
    }, function(b) {
      return resolve(b);
    });
  }, cleanup);
};

// -- Folds and extended transformations -------------------------------

/**
 * Catamorphism. Takes two functions, applies the leftmost one to the failure
 * value, and the rightmost one to the successful value, depending on which one
 * is present.
 *
 * @summary @Task[α, β] => (α → γ), (β → γ) → Task[δ, γ]
 */
Task.prototype.fold = function _fold(f, g) {
  var fork = this.fork;
  var cleanup = this.cleanup;

  return new Task(function(reject, resolve) {
    return fork(function(a) {
      return resolve(f(a));
    }, function(b) {
      return resolve(g(b));
    });
  }, cleanup);
};

/**
 * Catamorphism.
 *
 * @summary @Task[α, β] => { Rejected: α → γ, Resolved: β → γ } → Task[δ, γ]
 */
Task.prototype.cata = function _cata(pattern) {
  return this.fold(pattern.Rejected, pattern.Resolved);
};

/**
 * Swaps the disjunction values.
 *
 * @summary @Task[α, β] => Void → Task[β, α]
 */
Task.prototype.swap = function _swap() {
  var fork = this.fork;
  var cleanup = this.cleanup;

  return new Task(function(reject, resolve) {
    return fork(function(a) {
      return resolve(a);
    }, function(b) {
      return reject(b);
    });
  }, cleanup);
};

/**
 * Maps both sides of the disjunction.
 *
 * @summary @Task[α, β] => (α → γ), (β → δ) → Task[γ, δ]
 */
Task.prototype.bimap = function _bimap(f, g) {
  var fork = this.fork;
  var cleanup = this.cleanup;

  return new Task(function(reject, resolve) {
    return fork(function(a) {
      return reject(f(a));
    }, function(b) {
      return resolve(g(b));
    });
  }, cleanup);
};

/**
 * Maps the left side of the disjunction (failure).
 *
 * @summary @Task[α, β] => (α → γ) → Task[γ, β]
 */
Task.prototype.rejectedMap = function _rejectedMap(f) {
  var fork = this.fork;
  var cleanup = this.cleanup;

  return new Task(function(reject, resolve) {
    return fork(function(a) {
      return reject(f(a));
    }, function(b) {
      return resolve(b);
    });
  }, cleanup);
};
<|endoftext|>


<commit_before>var {pluginState} = require('./pluginState');
exports.init = function () {
  var pageMod = require('sdk/page-mod');
  try {
    pageMod.PageMod({
      include: '*',
      exclude: pluginState.pageModDatawakeDepotIncludeFilter, // this prevents Depot urls from being trailed and paneled
      contentScriptFile: [
        './vendor/jszip/jszip.min.js',
        './vendor/jquery/jquery-2.1.4.min.js',
        './vendor/jquery/jquery.highlight.js',
        //'./vendor/highlightRegex/highlightRegex.min.js',
        './injectedPageScripts/datawake-analysis.js',
        './injectedPageScripts/scraper.js'],
      attachTo: ['existing', 'top', 'frame'],
      onAttach: pluginState.onContentScriptAttach
    });
  }
  catch (err) {
    console.log(err);
  }
  try {
    pageMod.PageMod({
      include: pluginState.pageModDatawakeDepotIncludeFilter,
      //exclude: '*',
      contentScriptFile: './injectedPageScripts/datawake-depot.js',
      attachTo: ['existing', 'top', 'frame'],
      onAttach: pluginState.onDatawakeDepotContentScriptAttach
    });
  }
  catch (err) {
    console.log(err);
  }
};
<commit_msg>remove iframe from scraping<commit_after>var {pluginState} = require('./pluginState');
exports.init = function () {
  var pageMod = require('sdk/page-mod');
  try {
    pageMod.PageMod({
      include: '*',
      exclude: pluginState.pageModDatawakeDepotIncludeFilter, // this prevents Depot urls from being trailed and paneled
      contentScriptFile: [
        './vendor/jszip/jszip.min.js',
        './vendor/jquery/jquery-2.1.4.min.js',
        './vendor/jquery/jquery.highlight.js',
        //'./vendor/highlightRegex/highlightRegex.min.js',
        './injectedPageScripts/datawake-analysis.js',
        './injectedPageScripts/scraper.js'],
      attachTo: ['existing', 'top'],
      onAttach: pluginState.onContentScriptAttach
    });
  }
  catch (err) {
    console.log(err);
  }
  try {
    pageMod.PageMod({
      include: pluginState.pageModDatawakeDepotIncludeFilter,
      //exclude: '*',
      contentScriptFile: './injectedPageScripts/datawake-depot.js',
      attachTo: ['existing', 'top'],
      onAttach: pluginState.onDatawakeDepotContentScriptAttach
    });
  }
  catch (err) {
    console.log(err);
  }
};
<|endoftext|>


<commit_before>var Promise = require("bluebird");
var mongoose;
var Schema;
var TaskMdl;
var INITIAL = 0;
var PENDING= 1;
var DONE = 2;
var defaultSchema;

var TaskProvider = function (_mongoose, _TaskMdl) {
  mongoose = _mongoose;
  Schema = _mongoose.Schema;
  mongoose.Promise = Promise;
  TaskMdl = _TaskMdl;
  defaultSchema = new Schema({}, {strict: false});

  return Task;
};

var Task = function(){
  var task = this;
  var index = 0;
  var isDead = false;

  var steps = [];

  task.getTaskCollection = function(){
    return TaskMdl;
  };

  task.update = function(model, condition, data){
    if(!data){
      if(!validDoc(model)) throw new Error("Invalid doc");

      data = condition;
      condition = {_id: model._id};
      model = model.constructor;
    }
    if(!validModel(model)) throw new Error("Invalid model");
    if(!isObject(condition)) throw new Error("Invalid Condition");
    if(!isObject(data)) throw new Error("Invalid data");

    var updateObj = {
      _id: mongoose.Types.ObjectId()
      , index: index
      , type: "update"
      , state: INITIAL
      , name: getModel(model)
      , condition: condition
      , data: data
    };

    steps.push(updateObj);
    index++;
  };

  task.save = function(model, doc){
    if(!doc){
      if(!validDoc(model)) throw new Error("Invalid doc");

      doc = model;
      model = model.constructor;
    }
    if(!validModel(model)) throw new Error("Invalid Model");
    if(!isObject(doc)) throw new Error("Invalid doc");

    var saveObj = {
      _id: mongoose.Types.ObjectId()
      , index: index
      , type: "save"
      , state: INITIAL
      , name: getModel(model)
      , data: JSON.parse(JSON.stringify(doc))
    };

    steps.push(saveObj);
    index++;
  };

  task.remove = function(model, condition){
    if(!condition){
      if(!validDoc(model)) throw new Error("Invalid doc");

      condition = {_id: model._id};
      model = model.constructor;
    }
    if(!validModel(model)) throw new Error("Invalid Model");
    if(!isObject(condition)) throw new Error("Invalid Condition");

    var removeObj = {
      _id: mongoose.Types.ObjectId()
      , index: index
      , type: "remove"
      , state: INITIAL
      , name: getModel(model)
      , condition: condition
    };

    steps.push(removeObj);
    index++;
  };

  task.run = function(){
    if(isDead) throw new Error("A task, much like condoms, cannot be reused. Create a new task");

    isDead = true;

    return new Promise(function(resolve, reject){
      var chain = Promise.resolve();
      var execute;
      var dbTask = new TaskMdl({steps: steps});

      dbTask
        .save()
        .then(function(_task){
          _task.steps.forEach(function(step){
            execute = getResolveFunc(step);

            chain = chain.then(function(){
              return execute(step, _task)
            });
          });

          chain
            .then(function(){
              dbTask.remove()
                .then(resolve)
                .catch(reject)
            })
            .catch(reject);
        })
        .catch(reject);
    });
  };
};

var getResolveFunc = function(step){
  switch(step.type){
    case "update": return performUpdate;
    case "save": return performSave;
    case "remove": return performRemove;
  }
};

var performUpdate = function(update, task) {
  return new Promise(function (resolve, reject) {
    var Collection = getCollection(update.name);

    updateState(task, update.index, PENDING)
      .then(function(_task){
        Collection.update(update.condition, update.data)
          .then(function(mongoRes){
            updateState(_task, update.index, DONE)
              .then(function(){
                resolve(mongoRes);
              })
              .catch(reject);
          })
          .catch(reject);
      })
      .catch(reject);
  });
};

var performSave = function(save, task){
  return new Promise(function(resolve, reject){
    var Collection = getCollection(save.name);
    var doc = new Collection(save.data);

    updateState(task, save.index, PENDING)
      .then(function(_task){
        doc.save()
          .then(function(doc){
            updateState(_task, save.index, DONE)
              .then(function(){
                resolve(doc);
              })
              .catch(reject);
          })
          .catch(reject);
      });
  });
};

var performRemove = function(remove, task){
  return new Promise(function(resolve, reject){
    var Collection = getCollection(remove.name);

    updateState(task, remove.index, PENDING)
      .then(function(_task){
        Collection.remove(remove.condition)
          .then(function(){
            updateState(_task, remove.index, DONE)
              .then(function(){
                resolve("removed");
              })
              .catch(reject);
          })
          .catch(reject);
      })
      .catch(reject);
  });
};

var getCollection = function(name){
  return mongoose.model(null, defaultSchema, name);
};

Task.prototype.getCollection = getCollection;

Task.prototype.dropCollection = function(collection){
  return new Promise(function(resolve, reject){
    mongoose.connection.db.dropCollection(collection, function(err) {
      if(err) reject(err);
      else resolve();
    });
  });
};

var updateState = function(task, index, state){
  task.steps[index].state = state;
  return task.save();
};

var validModel = function(model){
  if(typeof model === "string") return true;

  try {
    return typeof model === "function" && model.modelName;
  } catch (err){
    return false;
  }
};

var validDoc = function(doc){
  try {
    return doc._id && doc.constructor && doc.constructor.modelName
  } catch (err){
    return false;
  }
};

var isObject = function(obj){
  return obj && typeof obj === "object";
};

var getModel = function(model){
  return typeof model === "string" ? model : model.modelName;
};

module.exports = TaskProvider;<commit_msg>cleaned up the perform functions. Added catch to performRemove<commit_after>var Promise = require("bluebird");
var mongoose;
var Schema;
var TaskMdl;
var INITIAL = 0;
var PENDING= 1;
var DONE = 2;
var defaultSchema;

var TaskProvider = function (_mongoose, _TaskMdl) {
  mongoose = _mongoose;
  Schema = _mongoose.Schema;
  mongoose.Promise = Promise;
  TaskMdl = _TaskMdl;
  defaultSchema = new Schema({}, {strict: false});

  return Task;
};

var Task = function(){
  var task = this;
  var index = 0;
  var isDead = false;

  var steps = [];

  task.getTaskCollection = function(){
    return TaskMdl;
  };

  task.update = function(model, condition, data){
    if(!data){
      if(!validDoc(model)) throw new Error("Invalid doc");

      data = condition;
      condition = {_id: model._id};
      model = model.constructor;
    }
    if(!validModel(model)) throw new Error("Invalid model");
    if(!isObject(condition)) throw new Error("Invalid Condition");
    if(!isObject(data)) throw new Error("Invalid data");

    var updateObj = {
      _id: mongoose.Types.ObjectId()
      , index: index
      , type: "update"
      , state: INITIAL
      , name: getModel(model)
      , condition: condition
      , data: data
    };

    steps.push(updateObj);
    index++;
  };

  task.save = function(model, doc){
    if(!doc){
      if(!validDoc(model)) throw new Error("Invalid doc");

      doc = model;
      model = model.constructor;
    }
    if(!validModel(model)) throw new Error("Invalid Model");
    if(!isObject(doc)) throw new Error("Invalid doc");

    var saveObj = {
      _id: mongoose.Types.ObjectId()
      , index: index
      , type: "save"
      , state: INITIAL
      , name: getModel(model)
      , data: JSON.parse(JSON.stringify(doc))
    };

    steps.push(saveObj);
    index++;
  };

  task.remove = function(model, condition){
    if(!condition){
      if(!validDoc(model)) throw new Error("Invalid doc");

      condition = {_id: model._id};
      model = model.constructor;
    }
    if(!validModel(model)) throw new Error("Invalid Model");
    if(!isObject(condition)) throw new Error("Invalid Condition");

    var removeObj = {
      _id: mongoose.Types.ObjectId()
      , index: index
      , type: "remove"
      , state: INITIAL
      , name: getModel(model)
      , condition: condition
    };

    steps.push(removeObj);
    index++;
  };

  task.run = function(){
    if(isDead) throw new Error("A task, much like condoms, cannot be reused. Create a new task");

    isDead = true;

    return new Promise(function(resolve, reject){
      var chain = Promise.resolve();
      var execute;
      var dbTask = new TaskMdl({steps: steps});

      dbTask
        .save()
        .then(function(_task){
          _task.steps.forEach(function(step){
            execute = getResolveFunc(step);

            chain = chain.then(function(){
              return execute(step, _task)
            });
          });

          chain
            .then(function(){
              dbTask.remove()
                .then(resolve)
                .catch(reject)
            })
            .catch(reject);
        })
        .catch(reject);
    });
  };
};

var getResolveFunc = function(step){
  switch(step.type){
    case "update": return performUpdate;
    case "save": return performSave;
    case "remove": return performRemove;
  }
};

var performUpdate = function(update, task) {
  return new Promise(function (resolve, reject) {
    var Collection = getCollection(update.name);

    updateState(task, update.index, PENDING)
      .then(function(_task){
        Collection.update(update.condition, update.data)
          .then(function(){
            updateState(_task, update.index, DONE)
              .then(resolve)
              .catch(reject);
          })
          .catch(reject);
      })
      .catch(reject);
  });
};

var performSave = function(save, task){
  return new Promise(function(resolve, reject){
    var Collection = getCollection(save.name);
    var doc = new Collection(save.data);

    updateState(task, save.index, PENDING)
      .then(function(_task){
        doc.save()
          .then(function(){
            updateState(_task, save.index, DONE)
              .then(resolve)
              .catch(reject);
          })
          .catch(reject);
      })
      .catch(reject);
  });
};

var performRemove = function(remove, task){
  return new Promise(function(resolve, reject){
    var Collection = getCollection(remove.name);

    updateState(task, remove.index, PENDING)
      .then(function(_task){
        Collection.remove(remove.condition)
          .then(function(){
            updateState(_task, remove.index, DONE)
              .then(resolve)
              .catch(reject);
          })
          .catch(reject);
      })
      .catch(reject);
  });
};

var getCollection = function(name){
  return mongoose.model(null, defaultSchema, name);
};

// These be for testing purposes. Or not. I do what I want.
Task.prototype.getCollection = getCollection;
Task.prototype.dropCollection = function(collection){
  return new Promise(function(resolve, reject){
    mongoose.connection.db.dropCollection(collection, function(err) {
      if(err) reject(err);
      else resolve();
    });
  });
};

var updateState = function(task, index, state){
  task.steps[index].state = state;
  return task.save();
};

var validModel = function(model){
  if(typeof model === "string") return true;

  try {
    return typeof model === "function" && model.modelName;
  } catch (err){
    return false;
  }
};

var validDoc = function(doc){
  try {
    return doc._id && doc.constructor && doc.constructor.modelName
  } catch (err){
    return false;
  }
};

var isObject = function(obj){
  return obj && typeof obj === "object";
};

var getModel = function(model){
  return typeof model === "string" ? model : model.modelName;
};

module.exports = TaskProvider;<|endoftext|>


<commit_before>'use strict';

var getComponent = require('../utilities/utilities').getComponent;

function Dispatcher(domNode) {
    this.domNode = domNode;
}

Dispatcher.willEventsBubble = (function() {
    var willBubble = false;
    var doc = window.document;
    if (doc) {
        var parent = doc.createElement('div');
        var child = parent.cloneNode();
        parent.appendChild(child);
        parent.addEventListener('e', function() {
            willBubble = true;
        });

        child.dispatchEvent(new CustomEvent('e', { bubbles: true }));
    }
    return willBubble;
})();

Dispatcher.prototype.emit = function(key, message) {
    var element = this.domNode;
    var event = new CustomEvent(key, {
        detail: message,
        bubbles: true
    });
    if (Dispatcher.willEventsBubble) {
        element.dispatchEvent(event);
    }
    else {
        while (element.parentNode) {
            element.dispatchEvent(event);
            element = element.parentNode;
        }
    }
};

Dispatcher.prototype.trigger = function(key, message) {
    var component = getComponent(this.domNode);
    component.sendMessage(key, message);
}

Dispatcher.prototype.broadcast = function(key, message) {
    var element =  this.domNode;
    var event = new CustomEvent(key, {
        detail: message,
        trickles: true
    });
    var broadcastToChildNodes = function(node) {
        for (var i = 0; i < node.childNodes.length; i++) {
            var child = node.childNodes[i];
            child.dispatchEvent(event);
            broadcastToChildNodes(child);
        }
    };

    broadcastToChildNodes(element);
};

Dispatcher.prototype.on = function(key, cb) {
    this.domNode.addEventListener(key, cb);
};

module.exports = Dispatcher;
<commit_msg>Fix linting error in dispatcher<commit_after>'use strict';

var getComponent = require('../utilities/utilities').getComponent;

function Dispatcher(domNode) {
    this.domNode = domNode;
}

Dispatcher.willEventsBubble = (function() {
    var willBubble = false;
    var doc = window.document;
    if (doc) {
        var parent = doc.createElement('div');
        var child = parent.cloneNode();
        parent.appendChild(child);
        parent.addEventListener('e', function() {
            willBubble = true;
        });

        child.dispatchEvent(new CustomEvent('e', { bubbles: true }));
    }
    return willBubble;
})();

Dispatcher.prototype.emit = function(key, message) {
    var element = this.domNode;
    var event = new CustomEvent(key, {
        detail: message,
        bubbles: true
    });
    if (Dispatcher.willEventsBubble) {
        element.dispatchEvent(event);
    }
    else {
        while (element.parentNode) {
            element.dispatchEvent(event);
            element = element.parentNode;
        }
    }
};

Dispatcher.prototype.trigger = function(key, message) {
    var component = getComponent(this.domNode);
    component.sendMessage(key, message);
};

Dispatcher.prototype.broadcast = function(key, message) {
    var element =  this.domNode;
    var event = new CustomEvent(key, {
        detail: message,
        trickles: true
    });
    var broadcastToChildNodes = function(node) {
        for (var i = 0; i < node.childNodes.length; i++) {
            var child = node.childNodes[i];
            child.dispatchEvent(event);
            broadcastToChildNodes(child);
        }
    };

    broadcastToChildNodes(element);
};

Dispatcher.prototype.on = function(key, cb) {
    this.domNode.addEventListener(key, cb);
};

module.exports = Dispatcher;
<|endoftext|>