<commit_before>NCI.socialGraph = function(socialGraphID, params){
	if (!params)
	    params = {}
	var me = $(socialGraphID);
	var force;
	var selectedDots = {};
	var socialGraphSelector = socialGraphID + " .socialGraph"
	var color = d3.scale.category10();
	var notNetworkColor = params.notNetworkColor || "#000000";
	var isClustered = params.isClustered || false ;
	var isDevided = params.isDevided || false ;
	var isFiltered = false;
	var graphWidth = params.width || me.width();
	var graphHeight =  params.height || $('#nciDetails').height() - 150;
	var radius = params.radius || function() { return 4};
	var charge = params.charge || function() { return -20};
	var linkDistance = params.linkDistance || function() { return 30};
	var graphBuilder = params.graphBuilder || undefined;
	var numOfPoints = params.numOfPoints || 0;
	var isExpandable = params.isExpandable || false;
	var tooltip;

	me.find('.byactivities').on('click', function(event){
		isDevided = this.checked;
		me.show();
	});
	me.find('.pretty').on('click', function(event){
		isClustered = this.checked;
		me.show();
	});
	me.find('.internal').on('click', function(event){
		isFiltered = this.checked;
		me.show();
	});

	me.show = function(needDraw){
		if (tooltip === undefined)
			tooltip = d3.select(socialGraphSelector).append("div").attr("class", "endpoint-tooltip");
		if (NCI.Communities.length == 0){
			return;
		};
		if (needDraw) {
		    d3.select("#activities_graph").remove();
			if (numOfPoints > NCI.max_vertices) {
				d3.select(socialGraphSelector).append('text')
				.attr("id","activities_graph")
				.html('Too many endpoints to draw')
				.attr('class', 'centrate');
			} else {
			    me.draw();
			}
		//otherwise just change appearance
		} else {
		    me.setupNodes();
		    force.start();
		}
	};

	//colorify and set radius
	me.setupNodes = function(){
		me.node.style("fill", function(d) {
		    if ( isFiltered && NCI.isExternal(d.name)){
			    return notNetworkColor;
		    };
			//if this dot is selected on Activities graph, draw it in red
			if (d.external)
			    return "red"
		    return isDevided ? color(d.group) : color(0);
		});
		me.node.attr("r", function(d) {
			return radius(d);
		});
		force.linkStrength(isClustered ? 1 : 0);
	};

	me.draw = function(){
		force = d3.layout.force()
		    .charge(charge())
			.linkDistance(linkDistance())
			.size([graphWidth,  graphHeight])
			.linkStrength(isClustered ? 1 : 0);

	    me.activitiesGraphSvg = d3.select(socialGraphSelector).append("svg")
		    .attr("id","activities_graph")
			.attr("width", graphWidth)
			.attr("height", graphHeight);

		var setupLinks = function(){
			force.links(graphBuilder.graph.links);
			me.activitiesGraphSvg.selectAll("line").remove();
			var linksData = me.activitiesGraphSvg.selectAll(".link").data(graphBuilder.graph.links);
			me.link = linksData.enter().append("line")
		    .attr("class", "activities_link");
			linksData.exit().remove();
		};

		var setupNodes = function(){
			force.nodes(graphBuilder.graph.nodes);
			me.activitiesGraphSvg.selectAll("circle").remove();
			var nodesData =  me.activitiesGraphSvg.selectAll(".node").data(graphBuilder.graph.nodes);
            me.node = nodesData.enter().append("circle").call(force.drag);
            nodesData.exit().remove();
			me.setupNodes(isFiltered, isDevided, isClustered);
			me.node.on('mouseover', function(d){
				var info = d.fullname;
				if (d.external){
					info += "
doesn't belong to activity";
				};
				info += "
" + d.connections + " connections";
				tooltip.html(info).style("top", d.py + me.position().top).
				style("left", d.px + me.position().left).style("display", "inline");
			}).on('mouseout', function(){
				tooltip.style("display", "none");
			});
			if (isExpandable) {
				me.node.on('click', function(d){
					var label = d.name.split(":")[0];
					var group = selectedDots[label]
					if (group !== undefined){
						delete selectedDots[selectedDots[label]]
						delete selectedDots[label]
						graphBuilder.removeCommunity(group);
						setupLinks();
						setupNodes();
						force.start();
						return;
					}
					for (var i=0; i < Object.keys(selectedDots).length + 1; i++){
						if (selectedDots[i + 1] === undefined){
							selectedDots[i + 1] = true;
							selectedDots[label] =  i + 1;
							break
						}
					}
					$.each(NCI.Communities, function(index, community){
						if (community.Label == label){
							graphBuilder.addCommunity(community, label, selectedDots[label] );
							setupLinks();
							setupNodes();
							force.start();
							return false;
						}
					});
	 		    });
			};
		};

		setupLinks();
		setupNodes();
	    force.on("tick", function() {
	        me.link.attr("x1", function(d) { return d.source.x; })
	            .attr("y1", function(d) { return d.source.y; })
	            .attr("x2", function(d) { return d.target.x; })
	            .attr("y2", function(d) { return d.target.y; });

	        me.node.attr("cx", function(d) { return d.x; })
	            .attr("cy", function(d) { return d.y; });
		});
		force.start();
	};

	return me;
};

NCI.graphBuilder = function(communities){
	var thisBuilder = this;
	var endpointsHash = {};
	var groupCount = 0;
    thisBuilder.graph = { "nodes":[], "links": []};
	NCI.maxActivitySize = 0;

	//add community
	thisBuilder.addCommunity = function(community, mainEndpoint, group){
		var communityEndpoints = {};
		var startIndex = Object.keys(endpointsHash).length
		var addConnection = function(endPoint, endpoints){
			var ip = endPoint.split(":")[0];
			if (!endpointsHash[ip]){
			    if (!communityEndpoints[ip]){
					var size = endPoint.split(":")[1];
				    var index = Object.keys(communityEndpoints).length + startIndex
				    communityEndpoints[ip] = {
						fullname: endPoint,
					    index: index,
					    external: endpoints.indexOf(endPoint) == -1,
					    connections: 0,
						size: size};
			    };
			    communityEndpoints[ip].connections++;
			    return communityEndpoints[ip].index;
		    } else {
				endpointsHash[ip].external = false;
		    	return endpointsHash[ip].index;
		    }
		};

		$.each(community.Interactions, function(index, interacton){
			thisBuilder.graph.links.push({
				source: addConnection(interacton[0], community.Endpoints),
				target: addConnection(interacton[1], community.Endpoints),
				value: 1});
		});

		$.each(Object.keys(communityEndpoints), function(index, key){
			if (mainEndpoint == key)
			   return
			var endpoint = communityEndpoints[key];
			if (NCI.maxActivitySize < parseInt(endpoint.size))
			    NCI.maxActivitySize = parseInt(endpoint.size);
			thisBuilder.graph.nodes[endpoint.index] = {
				name: key,
				fullname: endpoint.fullname,
				group: group,
				connections: endpoint.connections,
				external: endpoint.external,
				size: endpoint.size
			};
			endpointsHash[key] = communityEndpoints[key];
		});
	};

	//remove community
	thisBuilder.removeCommunity = function(group){
	    thisBuilder.graph.nodes = $.grep(thisBuilder.graph.nodes, function(node, index){
			var remove = node.group == group
			if (remove)
			  delete endpointsHash[node.name];
			return !remove;
		});

	    thisBuilder.graph.links = $.grep(thisBuilder.graph.links, function(link, index){
			var remove = endpointsHash[link.target.name] === undefined || endpointsHash[link.source.name] === undefined;
			return !remove;
		});
		groupCount--;
	};

	$.each(communities, function(index, community){
		thisBuilder.addCommunity(community, undefined, groupCount);
		groupCount++;
	});
};<commit_msg>Make expandable nodes rect from<commit_after>NCI.socialGraph = function(socialGraphID, params){
	if (!params)
	    params = {}
	var me = $(socialGraphID);
	var force;
	var selectedDots = {};
	var socialGraphSelector = socialGraphID + " .socialGraph"
	var color = d3.scale.category10();
	var notNetworkColor = params.notNetworkColor || "#000000";
	var isClustered = params.isClustered || false ;
	var isDevided = params.isDevided || false ;
	var isFiltered = false;
	var graphWidth = params.width || me.width();
	var graphHeight =  params.height || $('#nciDetails').height() - 150;
	var radius = params.radius || function() { return 4};
	var charge = params.charge || function() { return -20};
	var linkDistance = params.linkDistance || function() { return 30};
	var graphBuilder = params.graphBuilder || undefined;
	var numOfPoints = params.numOfPoints || 0;
	var isExpandable = params.isExpandable || false;
	var tooltip;

	me.find('.byactivities').on('click', function(event){
		isDevided = this.checked;
		me.show();
	});
	me.find('.pretty').on('click', function(event){
		isClustered = this.checked;
		me.show();
	});
	me.find('.internal').on('click', function(event){
		isFiltered = this.checked;
		me.show();
	});

	me.show = function(needDraw){
		if (tooltip === undefined)
			tooltip = d3.select(socialGraphSelector).append("div").attr("class", "endpoint-tooltip");
		if (NCI.Communities.length == 0){
			return;
		};
		if (needDraw) {
		    d3.select("#activities_graph").remove();
			if (numOfPoints > NCI.max_vertices) {
				d3.select(socialGraphSelector).append('text')
				.attr("id","activities_graph")
				.html('Too many endpoints to draw')
				.attr('class', 'centrate');
			} else {
			    me.draw();
			}
		//otherwise just change appearance
		} else {
		    me.setupNodes();
		    force.start();
		}
	};

	//colorify and set radius
	me.setupNodes = function(){
		me.node.style("fill", function(d) {
		    if ( isFiltered && NCI.isExternal(d.name)){
			    return notNetworkColor;
		    };
			//if this dot is selected on Activities graph, draw it in red
			if (d.external)
			    return "red"
		    return isDevided ? color(d.group) : color(0);
		});
		me.node.attr("r", function(d) {
			return radius(d);
		});
		me.node.attr("height", function(d) {
			return radius(d)*2;
		});
		me.node.attr("width", function(d) {
			return radius(d)*2;
		});
		force.linkStrength(isClustered ? 1 : 0);
	};

	me.draw = function(){
		force = d3.layout.force()
		    .charge(charge())
			.linkDistance(linkDistance())
			.size([graphWidth,  graphHeight])
			.linkStrength(isClustered ? 1 : 0);

	    me.activitiesGraphSvg = d3.select(socialGraphSelector).append("svg")
		    .attr("id","activities_graph")
			.attr("width", graphWidth)
			.attr("height", graphHeight);

		var setupLinks = function(){
			force.links(graphBuilder.graph.links);
			me.activitiesGraphSvg.selectAll("line").remove();
			var linksData = me.activitiesGraphSvg.selectAll(".link").data(graphBuilder.graph.links);
			me.link = linksData.enter().append("line")
		    .attr("class", "activities_link");
			linksData.exit().remove();
		};

		var setupNodes = function(){
			force.nodes(graphBuilder.graph.nodes);
			me.activitiesGraphSvg.selectAll("rect").remove();
			me.activitiesGraphSvg.selectAll("circle").remove();
			var nodesData =  me.activitiesGraphSvg.selectAll(".node").data(graphBuilder.graph.nodes);
			me.node = nodesData.enter().select(function(d) {
				var type = (isExpandable && d.group == 0) ? "rect" : "circle"
				return this.appendChild(document.createElementNS(d3.ns.prefix.svg, type));
			}).call(force.drag);
            nodesData.exit().remove();
			me.setupNodes(isFiltered, isDevided, isClustered);
			me.node.on('mouseover', function(d){
				var info = d.fullname;
				if (d.external){
					info += "
doesn't belong to activity";
				};
				info += "
" + d.connections + " connections";
				tooltip.html(info).style("top", d.py + me.position().top).
				style("left", d.px + me.position().left).style("display", "inline");
			}).on('mouseout', function(){
				tooltip.style("display", "none");
			});
			if (isExpandable) {
				me.node.on('click', function(d){
					var label = d.name.split(":")[0];
					var group = selectedDots[label]
					if (group !== undefined){
						delete selectedDots[selectedDots[label]]
						delete selectedDots[label]
						graphBuilder.removeCommunity(group);
						setupLinks();
						setupNodes();
						force.start();
						return;
					}
					for (var i=0; i < Object.keys(selectedDots).length + 1; i++){
						if (selectedDots[i + 1] === undefined){
							selectedDots[i + 1] = true;
							selectedDots[label] =  i + 1;
							break
						}
					}
					$.each(NCI.Communities, function(index, community){
						if (community.Label == label){
							graphBuilder.addCommunity(community, label, selectedDots[label] );
							setupLinks();
							setupNodes();
							force.start();
							return false;
						}
					});
	 		    });
			};
		};

		setupLinks();
		setupNodes();
	    force.on("tick", function() {
	        me.link.attr("x1", function(d) { return d.source.x; })
	            .attr("y1", function(d) { return d.source.y; })
	            .attr("x2", function(d) { return d.target.x; })
	            .attr("y2", function(d) { return d.target.y; });

			me.node.attr("x", function(d) { return d.x - radius(d); })
			    .attr("y", function(d) { return d.y - radius(d); });
			me.node.attr("cx", function(d) { return d.x; })
			    .attr("cy", function(d) { return d.y; });
		});
		force.start();
	};

	return me;
};

NCI.graphBuilder = function(communities){
	var thisBuilder = this;
	var endpointsHash = {};
	var groupCount = 0;
    thisBuilder.graph = { "nodes":[], "links": []};
	NCI.maxActivitySize = 0;

	//add community
	thisBuilder.addCommunity = function(community, mainEndpoint, group){
		var communityEndpoints = {};
		var startIndex = Object.keys(endpointsHash).length
		var addConnection = function(endPoint, endpoints){
			var ip = endPoint.split(":")[0];
			if (!endpointsHash[ip]){
			    if (!communityEndpoints[ip]){
					var size = endPoint.split(":")[1];
				    var index = Object.keys(communityEndpoints).length + startIndex
				    communityEndpoints[ip] = {
						fullname: endPoint,
					    index: index,
					    external: endpoints.indexOf(endPoint) == -1,
					    connections: 0,
						size: size};
			    };
			    communityEndpoints[ip].connections++;
			    return communityEndpoints[ip].index;
		    } else {
				endpointsHash[ip].external = false;
		    	return endpointsHash[ip].index;
		    }
		};

		$.each(community.Interactions, function(index, interacton){
			thisBuilder.graph.links.push({
				source: addConnection(interacton[0], community.Endpoints),
				target: addConnection(interacton[1], community.Endpoints),
				value: 1});
		});

		$.each(Object.keys(communityEndpoints), function(index, key){
			if (mainEndpoint == key)
			   return
			var endpoint = communityEndpoints[key];
			if (NCI.maxActivitySize < parseInt(endpoint.size))
			    NCI.maxActivitySize = parseInt(endpoint.size);
			thisBuilder.graph.nodes[endpoint.index] = {
				name: key,
				fullname: endpoint.fullname,
				group: group,
				connections: endpoint.connections,
				external: endpoint.external,
				size: endpoint.size
			};
			endpointsHash[key] = communityEndpoints[key];
		});
	};

	//remove community
	thisBuilder.removeCommunity = function(group){
	    thisBuilder.graph.nodes = $.grep(thisBuilder.graph.nodes, function(node, index){
			var remove = node.group == group
			if (remove)
			  delete endpointsHash[node.name];
			return !remove;
		});

	    thisBuilder.graph.links = $.grep(thisBuilder.graph.links, function(link, index){
			var remove = endpointsHash[link.target.name] === undefined || endpointsHash[link.source.name] === undefined;
			return !remove;
		});
		groupCount--;
	};

	$.each(communities, function(index, community){
		thisBuilder.addCommunity(community, undefined, groupCount);
		groupCount++;
	});
};<|endoftext|>


<commit_before>// MIT License
//
// Copyright (c) 2016 Akshay Chavan
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

var SICjs = (function SICjs() {

	// Canvas Id
	var canvasId = "";
	// Canvas Object
	var canvasObj = null;
	// ctxd Object
	var ctxdObj = null;
	// Rect color
	var rectcolor = '#FF0000';

	// Position of the canvas
	var cPos = {};
	// To store rectangle
	var rect = {};
	// Initialize rect width and height to zero
	rect.w = 0;
	rect.h = 0;
	// To store rectangle anchor point
	// Used only while dragging the whole rectangle
	var anchor = {};
	// Selection marker size
	var sBlk = 4;

	// FLAGS
	// Rect already present
	var active = false;
	// Drag for rect resize in progress
	var drag = false;
	// Marker flags by positions
	var TL = false;
	var TM = false;
	var TR = false;
	var LM = false;
	var RM = false;
	var BL = false;
	var BM = false;
	var BR = false;
	var hold = false;

    // Return the constructor
    return function constructor(canvasidIn,canvasObjIn,ctxdIn,rColorIn) {

		// Set canvasId variable
		canvasId = "#"+canvasidIn;
		// Set canvasObj variable
		canvasObj = canvasObjIn;
		// Set ctxdObj variable
		ctxdObj = ctxdIn;
		// Rect color
		rectcolor = rColorIn;

		console.log(canvasId);
		console.log(canvasObj);
		console.log(ctxdObj);
		console.log(rectcolor);

		// Check the arguments
		if( typeof canvasId != 'string' ) { return; }
		if( !canvasObj ) { return; }
		if( !ctxdObj ) { return; }
		if( typeof rectcolor != 'string' ) { return; }

		// Get the position of the canvas
		cPos = canvasObj.getBoundingClientRect();

		// Listeners
		$(canvasId).mousedown(function(e) {
			mouseDown(e);
		});

		$(canvasId).mouseup(function(e) {
			mouseUp(e);
		});

		$(canvasId).mousemove(function(e) {
			mouseMove(e);
		});

		$(document).on('keyup',function(e) {
			keyUp(e);
		});

		// Public method to return rect
		// position -> top-left  and
		// size -> width and height
		// [left, top, width, height]
        this.getRect = function () {
            return rect;
        };
    };

	// Private methods
	function mouseDown(e) {
		eX = e.pageX - cPos.left;
		eY = e.pageY - cPos.top;

		if( active )
		{
			if( pointInRect(eX,eY,rect.x-sBlk,rect.y-sBlk,sBlk*2,sBlk*2) )
			{
				TL = true;
				return;
			}
			if( pointInRect(eX,eY,rect.x+rect.w-sBlk,rect.y-sBlk,sBlk*2,sBlk*2) )
			{
				TR = true;
				return;
			}
			if( pointInRect(eX,eY,rect.x-sBlk,rect.y+rect.h-sBlk,sBlk*2,sBlk*2) )
			{
				BL = true;
				return;
			}
			if( pointInRect(eX,eY,rect.x+rect.w-sBlk,rect.y+rect.h-sBlk,sBlk*2,sBlk*2) )
			{
				BR = true;
				return;
			}

			if( pointInRect(eX,eY,rect.x+rect.w/2-sBlk,rect.y-sBlk,sBlk*2,sBlk*2) )
			{
				TM = true;
				return;
			}
			if( pointInRect(eX,eY,rect.x+rect.w/2-sBlk,rect.y+rect.h-sBlk,sBlk*2,sBlk*2) )
			{
				BM = true;
				return;
			}
			if( pointInRect(eX,eY,rect.x-sBlk,rect.y+rect.h/2-sBlk,sBlk*2,sBlk*2) )
			{
				LM = true;
				return;
			}
			if( pointInRect(eX,eY,rect.x+rect.w-sBlk,rect.y+rect.h/2-sBlk,sBlk*2,sBlk*2) )
			{
				RM = true;
				return;
			}

			// This has to be below all of the other conditions
			if( pointInRect(eX,eY,rect.x,rect.y,rect.w,rect.h) )
			{
				anchor.xLeft = eX-rect.x;
				anchor.xRight = rect.w-anchor.xLeft;
				anchor.xTop = eY-rect.y;
				anchor.xBottom = rect.h-anchor.xTop;
				hold = true;
				return;
			}
		}
		else
		{
			rect.x = eX;
			rect.y = eY;
			drag = true;
			active = true;
			return;
		}
	}

	function mouseMove(e) {
		eX = e.pageX - cPos.left;
		eY = e.pageY - cPos.top;

		if( drag & active ) {
			rect.w = eX - rect.x;
			rect.h = eY - rect.y ;
			clearCanvasNDraw();
			return;
		}

		if( hold )
		{
			rect.x = eX-anchor.xLeft;
			rect.y = eY-anchor.xTop;
			straightenUpRect();
			keepRectInCanvas();
			clearCanvasNDraw();
			return;
		}

		if( TL ) {
			rect.w = (rect.x + rect.w) - eX;
			rect.h = (rect.y + rect.h) - eY;
			rect.x = eX;
			rect.y = eY;
			clearCanvasNDraw();
			return;
		}

		if( BR ) {
			rect.w = eX - rect.x;
			rect.h = eY - rect.y;
			clearCanvasNDraw();
			return;
		}

		if( TR ) {
			rect.h = (rect.y + rect.h) - eY;
			rect.y = eY;
			rect.w = eX - rect.x;
			clearCanvasNDraw();
			return;
		}

		if( BL ) {
			rect.w = (rect.x + rect.w) - eX;
			rect.x = eX;
			rect.h = eY - rect.y;
			clearCanvasNDraw();
			return;
		}

		if( TM ) {
			rect.h = (rect.y + rect.h) - eY;
			rect.y = eY;
			clearCanvasNDraw();
			return;
		}
		if( BM ) {
			rect.h = eY - rect.y;
			clearCanvasNDraw();
			return;
		}
		if( LM ) {
			rect.w = (rect.x + rect.w) - eX;
			rect.x = eX;
			clearCanvasNDraw();
			return;
		}
		if( RM ) {
			rect.w = eX - rect.x;
			clearCanvasNDraw();
			return;
		}
	}

	function mouseUp(e) {
		drag = false;
		disableResizeButtons();
		straightenUpRect();
		if( rect.w == 0 || rect.h == 0 )
			active = false;
		keepRectInCanvas();
	}

	function keyUp(e) {
		// esc or del
		if (e.keyCode == 27 || e.keyCode == 46 ) {
			active = false;
			ctx.clearRect(0, 0, canvasObj.width, canvasObj.height);
		}
	}

	function disableResizeButtons(){
		TL = TM = TR = false;
		LM = RM = false;
		BL = BM = BR = false;
		hold = false;
	}

	function pointInRect(pX,pY,rX,rY,rW,rH) {
		if( (pX >= rX) && (pY >= rY) &&
			(pX <= (rX+rW)) && (pY <= (rY+rH)) )
			return true;
		else
			return false;
	}

	function clearCanvasNDraw() {
		// Clear canvas
		ctx.clearRect(0, 0, canvasObj.width, canvasObj.height);

		// Draw
		ctx.strokeStyle = rectcolor;
		ctx.strokeRect(rect.x, rect.y, rect.w, rect.h);
		drawSelectMarkers(rect.x, rect.y, rect.w, rect.h);
	}

	function drawSelectMarkers(x,y,w,h){
		// Top-Left
		ctx.strokeRect(x-sBlk,y-sBlk,sBlk*2,sBlk*2);
		// Top-Rigth
		ctx.strokeRect(x+w-sBlk,y-sBlk,sBlk*2,sBlk*2);
		// Bottom-Left
		ctx.strokeRect(x-sBlk,y+h-sBlk,sBlk*2,sBlk*2);
		// Bottom-Right
		ctx.strokeRect(x+w-sBlk,y+h-sBlk,sBlk*2,sBlk*2);

		// Top-Mid
		ctx.strokeRect(x+w/2-sBlk,y-sBlk,sBlk*2,sBlk*2);
		// Bottom-Mid
		ctx.strokeRect(x+w/2-sBlk,y+h-sBlk,sBlk*2,sBlk*2);
		// Left-Mid
		ctx.strokeRect(x-sBlk,y+h/2-sBlk,sBlk*2,sBlk*2);
		// Right-Mid
		ctx.strokeRect(x+w-sBlk,y+h/2-sBlk,sBlk*2,sBlk*2);
	}

	function keepRectInCanvas() {
		if( rect.x < 0 )
			rect.x = 0;
		if( rect.y < 0 )
			rect.y = 0;
		if( (rect.x+rect.w) > canvasObj.width )
			rect.x = canvasObj.width - rect.w;
		if( (rect.y+rect.h) > canvasObj.height )
			rect.y = canvasObj.height - rect.h;
	}

	function straightenUpRect(){
		if( rect.w < 0 ){
			rect.x = rect.x + rect.w;
			rect.w = -rect.w;
		}
		if( rect.h < 0 ){
			rect.y = rect.y + rect.h;
			rect.h = -rect.h;
		}
	}

	//function printRect() {
	//	console.log(rect.x + ',' + rect.y + ',' + rect.w + ',' + rect.h);
	//}

}());
<commit_msg>rectified ctx to ctxdObj<commit_after>// MIT License
//
// Copyright (c) 2016 Akshay Chavan
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

var SICjs = (function SICjs() {

	// Canvas Id
	var canvasId = "";
	// Canvas Object
	var canvasObj = null;
	// ctxd Object
	var ctxdObj = null;
	// Rect color
	var rectcolor = '#FF0000';

	// Position of the canvas
	var cPos = {};
	// To store rectangle
	var rect = {};
	// Initialize rect width and height to zero
	rect.w = 0;
	rect.h = 0;
	// To store rectangle anchor point
	// Used only while dragging the whole rectangle
	var anchor = {};
	// Selection marker size
	var sBlk = 4;

	// FLAGS
	// Rect already present
	var active = false;
	// Drag for rect resize in progress
	var drag = false;
	// Marker flags by positions
	var TL = false;
	var TM = false;
	var TR = false;
	var LM = false;
	var RM = false;
	var BL = false;
	var BM = false;
	var BR = false;
	var hold = false;

    // Return the constructor
    return function constructor(canvasidIn,canvasObjIn,ctxdIn,rColorIn) {

		// Set canvasId variable
		canvasId = "#"+canvasidIn;
		// Set canvasObj variable
		canvasObj = canvasObjIn;
		// Set ctxdObj variable
		ctxdObj = ctxdIn;
		// Rect color
		rectcolor = rColorIn;

		// Check the arguments
		if( typeof canvasId != 'string' ) { return; }
		if( !canvasObj ) { return; }
		if( !ctxdObj ) { return; }
		if( typeof rectcolor != 'string' ) { return; }

		// Get the position of the canvas
		cPos = canvasObj.getBoundingClientRect();

		// Listeners
		$(canvasId).mousedown(function(e) {
			mouseDown(e);
		});

		$(canvasId).mouseup(function(e) {
			mouseUp(e);
		});

		$(canvasId).mousemove(function(e) {
			mouseMove(e);
		});

		$(document).on('keyup',function(e) {
			keyUp(e);
		});

		// Public method to return rect
		// position -> top-left  and
		// size -> width and height
		// [left, top, width, height]
        this.getRect = function () {
            return rect;
        };
    };

	// Private methods
	function mouseDown(e) {
		eX = e.pageX - cPos.left;
		eY = e.pageY - cPos.top;

		if( active )
		{
			if( pointInRect(eX,eY,rect.x-sBlk,rect.y-sBlk,sBlk*2,sBlk*2) )
			{
				TL = true;
				return;
			}
			if( pointInRect(eX,eY,rect.x+rect.w-sBlk,rect.y-sBlk,sBlk*2,sBlk*2) )
			{
				TR = true;
				return;
			}
			if( pointInRect(eX,eY,rect.x-sBlk,rect.y+rect.h-sBlk,sBlk*2,sBlk*2) )
			{
				BL = true;
				return;
			}
			if( pointInRect(eX,eY,rect.x+rect.w-sBlk,rect.y+rect.h-sBlk,sBlk*2,sBlk*2) )
			{
				BR = true;
				return;
			}

			if( pointInRect(eX,eY,rect.x+rect.w/2-sBlk,rect.y-sBlk,sBlk*2,sBlk*2) )
			{
				TM = true;
				return;
			}
			if( pointInRect(eX,eY,rect.x+rect.w/2-sBlk,rect.y+rect.h-sBlk,sBlk*2,sBlk*2) )
			{
				BM = true;
				return;
			}
			if( pointInRect(eX,eY,rect.x-sBlk,rect.y+rect.h/2-sBlk,sBlk*2,sBlk*2) )
			{
				LM = true;
				return;
			}
			if( pointInRect(eX,eY,rect.x+rect.w-sBlk,rect.y+rect.h/2-sBlk,sBlk*2,sBlk*2) )
			{
				RM = true;
				return;
			}

			// This has to be below all of the other conditions
			if( pointInRect(eX,eY,rect.x,rect.y,rect.w,rect.h) )
			{
				anchor.xLeft = eX-rect.x;
				anchor.xRight = rect.w-anchor.xLeft;
				anchor.xTop = eY-rect.y;
				anchor.xBottom = rect.h-anchor.xTop;
				hold = true;
				return;
			}
		}
		else
		{
			rect.x = eX;
			rect.y = eY;
			drag = true;
			active = true;
			return;
		}
	}

	function mouseMove(e) {
		eX = e.pageX - cPos.left;
		eY = e.pageY - cPos.top;

		if( drag & active ) {
			rect.w = eX - rect.x;
			rect.h = eY - rect.y ;
			clearCanvasNDraw();
			return;
		}

		if( hold )
		{
			rect.x = eX-anchor.xLeft;
			rect.y = eY-anchor.xTop;
			straightenUpRect();
			keepRectInCanvas();
			clearCanvasNDraw();
			return;
		}

		if( TL ) {
			rect.w = (rect.x + rect.w) - eX;
			rect.h = (rect.y + rect.h) - eY;
			rect.x = eX;
			rect.y = eY;
			clearCanvasNDraw();
			return;
		}

		if( BR ) {
			rect.w = eX - rect.x;
			rect.h = eY - rect.y;
			clearCanvasNDraw();
			return;
		}

		if( TR ) {
			rect.h = (rect.y + rect.h) - eY;
			rect.y = eY;
			rect.w = eX - rect.x;
			clearCanvasNDraw();
			return;
		}

		if( BL ) {
			rect.w = (rect.x + rect.w) - eX;
			rect.x = eX;
			rect.h = eY - rect.y;
			clearCanvasNDraw();
			return;
		}

		if( TM ) {
			rect.h = (rect.y + rect.h) - eY;
			rect.y = eY;
			clearCanvasNDraw();
			return;
		}
		if( BM ) {
			rect.h = eY - rect.y;
			clearCanvasNDraw();
			return;
		}
		if( LM ) {
			rect.w = (rect.x + rect.w) - eX;
			rect.x = eX;
			clearCanvasNDraw();
			return;
		}
		if( RM ) {
			rect.w = eX - rect.x;
			clearCanvasNDraw();
			return;
		}
	}

	function mouseUp(e) {
		drag = false;
		disableResizeButtons();
		straightenUpRect();
		if( rect.w == 0 || rect.h == 0 )
			active = false;
		keepRectInCanvas();
	}

	function keyUp(e) {
		// esc or del
		if (e.keyCode == 27 || e.keyCode == 46 ) {
			active = false;
			ctx.clearRect(0, 0, canvasObj.width, canvasObj.height);
		}
	}

	function disableResizeButtons(){
		TL = TM = TR = false;
		LM = RM = false;
		BL = BM = BR = false;
		hold = false;
	}

	function pointInRect(pX,pY,rX,rY,rW,rH) {
		if( (pX >= rX) && (pY >= rY) &&
			(pX <= (rX+rW)) && (pY <= (rY+rH)) )
			return true;
		else
			return false;
	}

	function clearCanvasNDraw() {
		// Clear canvas
		ctxdObj.clearRect(0, 0, canvasObj.width, canvasObj.height);

		// Draw
		ctxdObj.strokeStyle = rectcolor;
		ctxdObj.strokeRect(rect.x, rect.y, rect.w, rect.h);
		drawSelectMarkers(rect.x, rect.y, rect.w, rect.h);
	}

	function drawSelectMarkers(x,y,w,h){
		// Top-Left
		ctxdObj.strokeRect(x-sBlk,y-sBlk,sBlk*2,sBlk*2);
		// Top-Rigth
		ctxdObj.strokeRect(x+w-sBlk,y-sBlk,sBlk*2,sBlk*2);
		// Bottom-Left
		ctxdObj.strokeRect(x-sBlk,y+h-sBlk,sBlk*2,sBlk*2);
		// Bottom-Right
		ctxdObj.strokeRect(x+w-sBlk,y+h-sBlk,sBlk*2,sBlk*2);

		// Top-Mid
		ctxdObj.strokeRect(x+w/2-sBlk,y-sBlk,sBlk*2,sBlk*2);
		// Bottom-Mid
		ctxdObj.strokeRect(x+w/2-sBlk,y+h-sBlk,sBlk*2,sBlk*2);
		// Left-Mid
		ctxdObj.strokeRect(x-sBlk,y+h/2-sBlk,sBlk*2,sBlk*2);
		// Right-Mid
		ctxdObj.strokeRect(x+w-sBlk,y+h/2-sBlk,sBlk*2,sBlk*2);
	}

	function keepRectInCanvas() {
		if( rect.x < 0 )
			rect.x = 0;
		if( rect.y < 0 )
			rect.y = 0;
		if( (rect.x+rect.w) > canvasObj.width )
			rect.x = canvasObj.width - rect.w;
		if( (rect.y+rect.h) > canvasObj.height )
			rect.y = canvasObj.height - rect.h;
	}

	function straightenUpRect(){
		if( rect.w < 0 ){
			rect.x = rect.x + rect.w;
			rect.w = -rect.w;
		}
		if( rect.h < 0 ){
			rect.y = rect.y + rect.h;
			rect.h = -rect.h;
		}
	}

	//function printRect() {
	//	console.log(rect.x + ',' + rect.y + ',' + rect.w + ',' + rect.h);
	//}

}());
<|endoftext|>


<commit_before>import onlyOnce from './internal/onlyOnce';
import wrapAsync from './internal/wrapAsync';
import awaitify from './internal/awaitify';

/**
 * Repeatedly call `iteratee`, while `test` returns `true`. Calls `callback` when
 * stopped, or an error occurs.
 *
 * @name whilst
 * @static
 * @memberOf module:ControlFlow
 * @method
 * @category Control Flow
 * @param {AsyncFunction} test - asynchronous truth test to perform before each
 * execution of `iteratee`. Invoked with ().
 * @param {AsyncFunction} iteratee - An async function which is called each time
 * `test` passes. Invoked with (callback).
 * @param {Function} [callback] - A callback which is called after the test
 * function has failed and repeated execution of `iteratee` has stopped. `callback`
 * will be passed an error and any arguments passed to the final `iteratee`'s
 * callback. Invoked with (err, [results]);
 * @returns {Promise} a promise, if no callback is passed
 * @example
 *
 * var count = 0;
 * async.whilst(
 *     function test(cb) { cb(null, count < 5;) },
 *     function iter(callback) {
 *         count++;
 *         setTimeout(function() {
 *             callback(null, count);
 *         }, 1000);
 *     },
 *     function (err, n) {
 *         // 5 seconds have passed, n = 5
 *     }
 * );
 */
function whilst(test, iteratee, callback) {
    callback = onlyOnce(callback);
    var _fn = wrapAsync(iteratee);
    var _test = wrapAsync(test);
    var results = [];

    function next(err, ...rest) {
        if (err) return callback(err);
        results = rest;
        if (err === false) return;
        _test(check);
    }

    function check(err, truth) {
        if (err) return callback(err);
        if (err === false) return;
        if (!truth) return callback(null, ...results);
        _fn(next);
    }

    return _test(check);
}
export default awaitify(whilst, 3)
<commit_msg>fix(docs): fix code example in whilst docs (#1703)<commit_after>import onlyOnce from './internal/onlyOnce';
import wrapAsync from './internal/wrapAsync';
import awaitify from './internal/awaitify';

/**
 * Repeatedly call `iteratee`, while `test` returns `true`. Calls `callback` when
 * stopped, or an error occurs.
 *
 * @name whilst
 * @static
 * @memberOf module:ControlFlow
 * @method
 * @category Control Flow
 * @param {AsyncFunction} test - asynchronous truth test to perform before each
 * execution of `iteratee`. Invoked with ().
 * @param {AsyncFunction} iteratee - An async function which is called each time
 * `test` passes. Invoked with (callback).
 * @param {Function} [callback] - A callback which is called after the test
 * function has failed and repeated execution of `iteratee` has stopped. `callback`
 * will be passed an error and any arguments passed to the final `iteratee`'s
 * callback. Invoked with (err, [results]);
 * @returns {Promise} a promise, if no callback is passed
 * @example
 *
 * var count = 0;
 * async.whilst(
 *     function test(cb) { cb(null, count < 5); },
 *     function iter(callback) {
 *         count++;
 *         setTimeout(function() {
 *             callback(null, count);
 *         }, 1000);
 *     },
 *     function (err, n) {
 *         // 5 seconds have passed, n = 5
 *     }
 * );
 */
function whilst(test, iteratee, callback) {
    callback = onlyOnce(callback);
    var _fn = wrapAsync(iteratee);
    var _test = wrapAsync(test);
    var results = [];

    function next(err, ...rest) {
        if (err) return callback(err);
        results = rest;
        if (err === false) return;
        _test(check);
    }

    function check(err, truth) {
        if (err) return callback(err);
        if (err === false) return;
        if (!truth) return callback(null, ...results);
        _fn(next);
    }

    return _test(check);
}
export default awaitify(whilst, 3)
<|endoftext|>


<commit_before>'use strict';

const path = require('path');
const EventEmitter= require('events').EventEmitter;

const versionio = require('version-io');
const changelog = require('changelog-io');
const rendy = require('rendy');
const spawnify = require('spawnify');
const series = require('async/series');
const minor = require('minor');
const prepend = require('prepend');
const jessy = require('jessy');
const readPkg = require('read-pkg-up');
const fullstore = require('fullstore');
const currify = require('currify/legacy');

const runWisdom = require('./run-wisdom');
const tag = currify(require('./tag'));
const getEnv = require('./get-env');

const removeBowerVersion = currify(require('./remove-bower-version'));

const isUndefined = (a) => typeof a === 'undefined';

const Cmd = [
    'git add --all',
    'git commit -m "chore(package) v{{ version }}"',
    'git pull --rebase',
    'git push origin {{ branch }}',
].join('&&');

const CmdTag = [
    'git tag v{{ version }}',
    'git push origin v{{ version }}'
].join('&&');

const chlog = fullstore();
const InfoDir = fullstore();

const setInfoDir = (data) => {
    const dir = path.dirname(data.path);

    InfoDir(dir);

    return data;
};

const getPkg = (data) => data.pkg;

module.exports = (version) => {
    const emitter = new EventEmitter();

    const onError = (e) => {
        const error = Error(`Error reading package.json: ${e.message}`);
        emitter.emit('error', error);
    };

    const start = currify((version, emitter, info) => {
        if (!version)
            return emitter.emit('data',
                `publish <version>\n` +
                `package: ${ info.name } ${ info.version }`
            );

        publish(version, info, emitter);
    });

    readPkg()
        .then(setInfoDir)
        .then(getPkg)
        .then(start(version, emitter))
        .catch(onError);

    return emitter;
};

function publish(version, info, emitter) {
    let type = '--';

    if (!version.indexOf('v')) {
        version = version.slice(1);
        type += 'v' + version;
    } else if (/major|minor|patch/.test(version)) {
        type += version;
        version = minor(version, info.version);
    }

    const cmd = rendy(Cmd, {
        version,
        branch: info.branch || 'master'
    });

    const cmdTag = rendy(CmdTag, {
        version,
    });

    series([
        function beforePublish(callback) {
            const cmd = jessy('scripts.wisdom', info);

            if (!cmd)
                return callback();

            runWisdom('wisdom', '', version, info, emitter, callback);
        },

        function beforePublishType(callback) {
            const cmd = jessy('scripts.wisdom:type', info);

            if (!cmd)
                return callback();

            runWisdom('wisdom:type', type, version, info, emitter, callback);
        },

        (callback) => {
            if (!isUndefined(info.changelog) && !info.changelog) {
                emitter.emit('data', 'changelog: false\n');
                return callback();
            }

            changelog(version, (error, data) => {
                if (error) {
                    error.message += '\n';
                    callback(error);
                    return;
                }

                const name = path.join(InfoDir(), 'ChangeLog');

                prepend(name, data, callback);

                const value = rmLines(data, 2);
                chlog(markdown(value));
            });
        },

        (callback) => {
            versionio(version, callback);
        },

        removeBowerVersion(emitter),

        (callback) => {
            execute(cmd, version, emitter, callback);
        },

        function release(callback) {
            const isNoRelease = !info.release && !isUndefined(info.release);

            if (isNoRelease) {
                emitter.emit('data', 'release: false\n');
                return callback();
            }

            execute(cmdTag, version, emitter, callback);
        },

        tag(version, info, chlog, emitter),

        function npmPublish(callback) {
            if (info.private)
                return callback();

            execute('npm publish --access=public', version, emitter, callback);
        },

        function afterPublish(callback) {
            const cmd = jessy('scripts.wisdom:done', info);

            if (!cmd)
                return callback();

            runWisdom('wisdom:done', '', version, info, emitter, callback);
        },
    ], (error) => {
        if (error)
            emitter.emit('error', error);

        emitter.emit('exit');
    });

    return emitter;
}

function execute(cmd, version, emitter, callback) {
    let rejected;

    const spawn = spawnify(cmd, {
        env: getEnv(version),
        cwd: InfoDir(),
    });

    spawn.on('error', (error) => {
        rejected = ~error.message.indexOf('rejected');

        emitter.emit('error', error);

        if (rejected)
            callback(error);
    });

    spawn.on('data', (data) => {
        emitter.emit('data', data);
    });

    spawn.on('exit', () => {
        if (!rejected)
            callback();
    });
}

function rmLines(str, count) {
    return str
        .split('\n')
        .slice(count)
        .join('\n');
}

function markdown(str) {
    return str
        .replace(/\bfix\b:/g, '## fix')
        .replace(/\bfeature\b:/g, '## feature');
}

<commit_msg>fix(wisdom) spawn: stdio<commit_after>'use strict';

const path = require('path');
const EventEmitter= require('events').EventEmitter;

const versionio = require('version-io');
const changelog = require('changelog-io');
const rendy = require('rendy');
const spawnify = require('spawnify');
const series = require('async/series');
const minor = require('minor');
const prepend = require('prepend');
const jessy = require('jessy');
const readPkg = require('read-pkg-up');
const fullstore = require('fullstore');
const currify = require('currify/legacy');

const runWisdom = require('./run-wisdom');
const tag = currify(require('./tag'));
const getEnv = require('./get-env');

const removeBowerVersion = currify(require('./remove-bower-version'));

const isUndefined = (a) => typeof a === 'undefined';

const Cmd = [
    'git add --all',
    'git commit -m "chore(package) v{{ version }}"',
    'git pull --rebase',
    'git push origin {{ branch }}',
].join('&&');

const CmdTag = [
    'git tag v{{ version }}',
    'git push origin v{{ version }}'
].join('&&');

const chlog = fullstore();
const InfoDir = fullstore();

const setInfoDir = (data) => {
    const dir = path.dirname(data.path);

    InfoDir(dir);

    return data;
};

const getPkg = (data) => data.pkg;

module.exports = (version) => {
    const emitter = new EventEmitter();

    const onError = (e) => {
        const error = Error(`Error reading package.json: ${e.message}`);
        emitter.emit('error', error);
    };

    const start = currify((version, emitter, info) => {
        if (!version)
            return emitter.emit('data',
                `publish <version>\n` +
                `package: ${ info.name } ${ info.version }`
            );

        publish(version, info, emitter);
    });

    readPkg()
        .then(setInfoDir)
        .then(getPkg)
        .then(start(version, emitter))
        .catch(onError);

    return emitter;
};

function publish(version, info, emitter) {
    let type = '--';

    if (!version.indexOf('v')) {
        version = version.slice(1);
        type += 'v' + version;
    } else if (/major|minor|patch/.test(version)) {
        type += version;
        version = minor(version, info.version);
    }

    const cmd = rendy(Cmd, {
        version,
        branch: info.branch || 'master'
    });

    const cmdTag = rendy(CmdTag, {
        version,
    });

    series([
        function beforePublish(callback) {
            const cmd = jessy('scripts.wisdom', info);

            if (!cmd)
                return callback();

            runWisdom('wisdom', '', version, info, emitter, callback);
        },

        function beforePublishType(callback) {
            const cmd = jessy('scripts.wisdom:type', info);

            if (!cmd)
                return callback();

            runWisdom('wisdom:type', type, version, info, emitter, callback);
        },

        (callback) => {
            if (!isUndefined(info.changelog) && !info.changelog) {
                emitter.emit('data', 'changelog: false\n');
                return callback();
            }

            changelog(version, (error, data) => {
                if (error) {
                    error.message += '\n';
                    callback(error);
                    return;
                }

                const name = path.join(InfoDir(), 'ChangeLog');

                prepend(name, data, callback);

                const value = rmLines(data, 2);
                chlog(markdown(value));
            });
        },

        (callback) => {
            versionio(version, callback);
        },

        removeBowerVersion(emitter),

        (callback) => {
            execute(cmd, version, emitter, callback);
        },

        function release(callback) {
            const isNoRelease = !info.release && !isUndefined(info.release);

            if (isNoRelease) {
                emitter.emit('data', 'release: false\n');
                return callback();
            }

            execute(cmdTag, version, emitter, callback);
        },

        tag(version, info, chlog, emitter),

        function npmPublish(callback) {
            if (info.private)
                return callback();

            execute('npm publish --access=public', version, emitter, callback);
        },

        function afterPublish(callback) {
            const cmd = jessy('scripts.wisdom:done', info);

            if (!cmd)
                return callback();

            runWisdom('wisdom:done', '', version, info, emitter, callback);
        },
    ], (error) => {
        if (error)
            emitter.emit('error', error);

        emitter.emit('exit');
    });

    return emitter;
}

function execute(cmd, version, emitter, callback) {
    let rejected;

    const spawn = spawnify(cmd, {
        env: getEnv(version),
        cwd: InfoDir(),
        stdio: 'inherit',
    });

    spawn.on('error', (error) => {
        rejected = ~error.message.indexOf('rejected');

        emitter.emit('error', error);

        if (rejected)
            callback(error);
    });

    spawn.on('data', (data) => {
        emitter.emit('data', data);
    });

    spawn.on('exit', () => {
        if (!rejected)
            callback();
    });
}

function rmLines(str, count) {
    return str
        .split('\n')
        .slice(count)
        .join('\n');
}

function markdown(str) {
    return str
        .replace(/\bfix\b:/g, '## fix')
        .replace(/\bfeature\b:/g, '## feature');
}

<|endoftext|>


<commit_before>'use strict';

const path = require('path');
const {execSync} = require('child_process');
const {EventEmitter} = require('events');

const tryCatch = require('try-catch');
const versionio = require('version-io');
const changelog = require('changelog-io');
const rendy = require('rendy');
const series = require('async/series');
const minor = require('minor');
const prepend = require('prepend');
const jessy = require('jessy');
const readPkg = require('read-pkg-up');
const fullstore = require('fullstore');
const currify = require('currify');

const checkAccess = require('./check-access');

const runWisdom = require('./run-wisdom');
const release = currify(require('./release'));
const getEnv = require('./get-env');

const isUndefined = (a) => typeof a === 'undefined';

const Cmd = [
    'git add --all',
    'git commit -m "chore(package) v{{ version }}"',
    'git pull --rebase',
    'git push origin {{ branch }}',
].join('&&');

const CmdTag = [
    'git tag v{{ version }}',
    'git push origin v{{ version }}',
].join('&&');

const chlog = fullstore();
const InfoDir = fullstore();

const setInfoDir = (data) => {
    const dir = path.dirname(data.path);

    InfoDir(dir);

    return data;
};

const getPkg = (data) => data.package;

module.exports = (version) => {
    const emitter = new EventEmitter();

    const onError = (e) => {
        const error = Error(`Error reading package.json: ${e.message}`);
        emitter.emit('error', error);
    };

    const start = currify((version, emitter, info) => {
        if (checkAccess(info))
            return emitter.emit(
                'data',
                `looks like ${info.name} has no access property\n` +
                `packages should have "access" property set to "public" or "restricted"\n`
            );

        if (!version)
            return emitter.emit(
                'data',
                `publish <version>\n` +
                `package: ${ info.name } ${ info.version }\n`
            );

        publish(version, info, emitter);
    });

    readPkg()
        .then(setInfoDir)
        .then(getPkg)
        .then(start(version, emitter))
        .catch(onError);

    return emitter;
};

function publish(version, info, emitter) {
    let type = '--';

    if (!version.indexOf('v')) {
        version = version.slice(1);
        type += 'v' + version;
    } else if (/major|minor|patch/.test(version)) {
        type += version;
        version = minor(version, info.version);
    }

    const cmd = rendy(Cmd, {
        version,
        branch: info.branch || 'master',
    });

    const cmdTag = rendy(CmdTag, {
        version,
    });

    series([
        function beforePublish(callback) {
            const cmd = jessy('scripts.wisdom', info);

            if (!cmd)
                return callback();

            runWisdom('wisdom', '', version, info, emitter, callback);
        },

        function beforePublishType(callback) {
            const cmd = jessy('scripts.wisdom:type', info);

            if (!cmd)
                return callback();

            runWisdom('wisdom:type', type, version, info, emitter, callback);
        },

        (callback) => {
            if (!isUndefined(info.changelog) && !info.changelog) {
                emitter.emit('data', 'changelog: false\n');
                return callback();
            }

            changelog(version, (error, data) => {
                if (error) {
                    error.message += '\n';
                    callback(error);
                    return;
                }

                const name = path.join(InfoDir(), 'ChangeLog');

                prepend(name, data, callback);

                const value = rmLines(data, 2);
                chlog(markdown(value));
            });
        },

        (callback) => {
            versionio(version, callback);
        },

        (callback) => {
            execute(cmd, version, emitter, callback);
        },

        function tag(callback) {
            const isNoTag = !info.tag && !isUndefined(info.tag);

            if (isNoTag) {
                emitter.emit('data', 'tag: false\n');
                return callback();
            }

            execute(cmdTag, version, emitter, callback);
        },

        release(version, info, chlog, emitter),

        function npmPublish(callback) {
            if (info.private)
                return callback();

            execute(`npm publish`, version, emitter, callback);
        },

        function afterPublish(callback) {
            const cmd = jessy('scripts.wisdom:done', info);

            if (!cmd)
                return callback();

            runWisdom('wisdom:done', '', version, info, emitter, callback);
        },
    ], (error) => {
        if (error)
            emitter.emit('error', error);

        emitter.emit('exit');
    });

    return emitter;
}

function execute(cmd, version, emitter, callback) {
    const stdio = [0, 1, 2, 'pipe'];

    const [e] = tryCatch(execSync, cmd, {
        env: getEnv(version),
        cwd: InfoDir(),
        stdio,
    });

    callback(e);
}

function rmLines(str, count) {
    return str
        .split('\n')
        .slice(count)
        .join('\n');
}

function markdown(str) {
    return str
        .replace(/\bfix\b:/g, '## fix')
        .replace(/\bfeature\b:/g, '## feature');
}

<commit_msg>fix(wisdom) check access only for scoped packages<commit_after>'use strict';

const path = require('path');
const {execSync} = require('child_process');
const {EventEmitter} = require('events');

const tryCatch = require('try-catch');
const versionio = require('version-io');
const changelog = require('changelog-io');
const rendy = require('rendy');
const series = require('async/series');
const minor = require('minor');
const prepend = require('prepend');
const jessy = require('jessy');
const readPkg = require('read-pkg-up');
const fullstore = require('fullstore');
const currify = require('currify');

const checkAccess = require('./check-access');

const runWisdom = require('./run-wisdom');
const release = currify(require('./release'));
const getEnv = require('./get-env');

const isUndefined = (a) => typeof a === 'undefined';

const Cmd = [
    'git add --all',
    'git commit -m "chore(package) v{{ version }}"',
    'git pull --rebase',
    'git push origin {{ branch }}',
].join('&&');

const CmdTag = [
    'git tag v{{ version }}',
    'git push origin v{{ version }}',
].join('&&');

const chlog = fullstore();
const InfoDir = fullstore();

const setInfoDir = (data) => {
    const dir = path.dirname(data.path);

    InfoDir(dir);

    return data;
};

const getPkg = (data) => data.package;

module.exports = (version) => {
    const emitter = new EventEmitter();

    const onError = (e) => {
        const error = Error(`Error reading package.json: ${e.message}`);
        emitter.emit('error', error);
    };

    const start = currify((version, emitter, info) => {
        if (info.name.includes('@') && checkAccess(info))
            return emitter.emit(
                'data',
                `looks like ${info.name} has no access property\n` +
                `packages should have "access" property set to "public" or "restricted"\n`
            );

        if (!version)
            return emitter.emit(
                'data',
                `publish <version>\n` +
                `package: ${ info.name } ${ info.version }\n`
            );

        publish(version, info, emitter);
    });

    readPkg()
        .then(setInfoDir)
        .then(getPkg)
        .then(start(version, emitter))
        .catch(onError);

    return emitter;
};

function publish(version, info, emitter) {
    let type = '--';

    if (!version.indexOf('v')) {
        version = version.slice(1);
        type += 'v' + version;
    } else if (/major|minor|patch/.test(version)) {
        type += version;
        version = minor(version, info.version);
    }

    const cmd = rendy(Cmd, {
        version,
        branch: info.branch || 'master',
    });

    const cmdTag = rendy(CmdTag, {
        version,
    });

    series([
        function beforePublish(callback) {
            const cmd = jessy('scripts.wisdom', info);

            if (!cmd)
                return callback();

            runWisdom('wisdom', '', version, info, emitter, callback);
        },

        function beforePublishType(callback) {
            const cmd = jessy('scripts.wisdom:type', info);

            if (!cmd)
                return callback();

            runWisdom('wisdom:type', type, version, info, emitter, callback);
        },

        (callback) => {
            if (!isUndefined(info.changelog) && !info.changelog) {
                emitter.emit('data', 'changelog: false\n');
                return callback();
            }

            changelog(version, (error, data) => {
                if (error) {
                    error.message += '\n';
                    callback(error);
                    return;
                }

                const name = path.join(InfoDir(), 'ChangeLog');

                prepend(name, data, callback);

                const value = rmLines(data, 2);
                chlog(markdown(value));
            });
        },

        (callback) => {
            versionio(version, callback);
        },

        (callback) => {
            execute(cmd, version, emitter, callback);
        },

        function tag(callback) {
            const isNoTag = !info.tag && !isUndefined(info.tag);

            if (isNoTag) {
                emitter.emit('data', 'tag: false\n');
                return callback();
            }

            execute(cmdTag, version, emitter, callback);
        },

        release(version, info, chlog, emitter),

        function npmPublish(callback) {
            if (info.private)
                return callback();

            execute(`npm publish`, version, emitter, callback);
        },

        function afterPublish(callback) {
            const cmd = jessy('scripts.wisdom:done', info);

            if (!cmd)
                return callback();

            runWisdom('wisdom:done', '', version, info, emitter, callback);
        },
    ], (error) => {
        if (error)
            emitter.emit('error', error);

        emitter.emit('exit');
    });

    return emitter;
}

function execute(cmd, version, emitter, callback) {
    const stdio = [0, 1, 2, 'pipe'];

    const [e] = tryCatch(execSync, cmd, {
        env: getEnv(version),
        cwd: InfoDir(),
        stdio,
    });

    callback(e);
}

function rmLines(str, count) {
    return str
        .split('\n')
        .slice(count)
        .join('\n');
}

function markdown(str) {
    return str
        .replace(/\bfix\b:/g, '## fix')
        .replace(/\bfeature\b:/g, '## feature');
}

<|endoftext|>


<commit_before>var
	beanstalk   = require('./client'),
	events      = require('events'),
	util        = require('util'),
	yaml        = require('js-yaml')
	;

/*
Events emitted:

error: payload is error; execution is halted
close: no payload

warning: payload is object with error information; execution continues
info: payload is object with action info

started: no payload
stopped: no payload

job.reserved: job has been reserved; payload is job id
job.handled: payload is object with job info
job.deleted: payload is jobid
job.buried: payload is jobid
*/

var FiveBeansWorker = function(options)
{
	events.EventEmitter.call(this);

	this.id            = options.id;
	this.host          = options.host;
	this.port          = options.port;
	this.handlers      = options.handlers;
	this.ignoreDefault = options.ignoreDefault;
	this.stopped       = false;

	this.client = null;
};
util.inherits(FiveBeansWorker, events.EventEmitter);

FiveBeansWorker.prototype.start = function(tubes)
{
	var self = this;
	this.stopped = false;

	this.on('next', this.doNext.bind(this));

	function finishedStarting()
	{
		self.emit('started');
		self.emit('next');
	}

	this.client = new beanstalk(this.host, this.port);

	this.client.on('connect', function()
	{
		self.emitInfo('connected to beanstalkd at '+ self.host + ':'+self.port);
		self.watch(tubes, function()
		{
			if (tubes && tubes.length && self.ignoreDefault)
			{
				self.ignore(['default'], function()
				{
					finishedStarting();
				});
			}
			else
			{
				finishedStarting();
			}
		});
	});

	this.client.on('error', function(err)
	{
		self.emitWarning({message: 'beanstalkd connection error', error: err});
		self.emit('error', err);
	});

	this.client.on('close', function()
	{
		self.emitInfo('beanstalkd connection closed');
		self.emit('close');
	});

	this.client.connect();
};

FiveBeansWorker.prototype.watch = function(tubes, callback)
{
	var self = this;
	var tube;
	if (tubes && (tube = tubes[0]))
	{
		self.emitInfo('watching tube ' + tube);
		self.client.watch(tube, function(err)
		{
			if (err) self.emitWarning({ message: 'error watching tube', tube: tube, error: err });
			self.watch(tubes.slice(1), callback);
		});
	}
	else
		callback();
};

FiveBeansWorker.prototype.ignore = function(tubes, callback)
{
	var self = this;
	var tube;
	if (tubes && (tube = tubes[0]))
	{
		self.emitInfo('ignoring tube '+tube);
		self.client.ignore(tube, function(err)
		{
			if (err) self.emitWarning({ message: 'error ignoring tube', tube: tube, error: err });
			self.ignore(tubes.slice(1), callback);
		});
	}
	else
		callback();
};

FiveBeansWorker.prototype.stop = function()
{
	this.emitInfo('stopping...');
	this.stopped = true;
};

FiveBeansWorker.prototype.doNext = function()
{
	var self = this;
	if (self.stopped)
	{
		self.client.end();
		self.emitInfo('stopped');
		self.emit('stopped');
		return;
	}

	self.client.reserve_with_timeout(1, function(err, jobID, payload)
	{
		if (err)
		{
			if ('TIMED_OUT' !== err)
				self.emitWarning({ message: 'error reserving job', error: err });
			self.emit('next');
		}
		else
		{
			self.client.stats_job(jobID, function(err, response)
			{
				if (err)
					self.emitWarning({ message: 'unable to receive stats', error: err });

				self.stats = response;

				self.emit('job.reserved', jobID);

				var job = null;
				try { job = JSON.parse(payload); }
				catch (e) { self.emitWarning({ message: 'parsing job JSON', id: jobID, error: e }); }
				if (!job || (typeof job !== 'object'))
					self.buryAndMoveOn(jobID);
				else if (job instanceof Array)
					self.runJob(jobID, job[1]);
				else
					self.runJob(jobID, job);
			});
		}
	});
};

FiveBeansWorker.prototype.runJob = function(jobID, job)
{
	var self = this;
	var handler = this.lookupHandler(job.type);
	if (job.type === undefined)
	{
		self.emitWarning({ message: 'no job type', id: jobID, job: job });
		self.deleteAndMoveOn(jobID);
	}
	else if (!handler)
	{
		self.emitWarning({ message: 'no handler found', id: jobID, type: job.type });
		self.buryAndMoveOn(jobID);
	}
	else
	{
		self.callHandler(handler, jobID, job.payload);
	}
};

FiveBeansWorker.prototype.lookupHandler = function(type)
{
	return this.handlers[type];
};

FiveBeansWorker.prototype.callHandler = function(handler, jobID, jobdata)
{
	var self = this;
	var start = new Date().getTime();
	this.currentJob = jobID;
	this.currentHandler = handler;

	var timeout = setTimeout(function()
	{
          console.log('timeout', self.stats.ttr);
		self.emit('job.timedOut', { id: jobID, handler: handler, payload: jobdata, type: handler.type, priority: self.stats.pri });
	}, self.stats ? self.stats.ttr*1000 - 1000 : 60000);

	try
	{
		handler.work(jobdata, function(action, delay)
		{
			clearTimeout(timeout);

			var elapsed = new Date().getTime() - start;
			action = action || 'success';

			self.emit('job.handled', { id: jobID, type: handler.type, elapsed: elapsed, action: action });

			switch (action)
			{
			case 'success':
				self.deleteAndMoveOn(jobID);
				break;

			case 'release':
				self.releaseAndMoveOn(jobID, delay);
				break;

			case 'bury':
				self.buryAndMoveOn(jobID);
				break;

			default:
				self.buryAndMoveOn(jobID);
				break;
			}
		});
	}
	catch (e)
	{
		clearTimeout(timeout);

		self.emitWarning({ message: 'exception in job handler', id: jobID, handler: handler.type, error: e });
		self.buryAndMoveOn(jobID);
	}
};

FiveBeansWorker.prototype.buryAndMoveOn = function(jobID)
{
	var self = this;
	self.client.bury(jobID, beanstalk.LOWEST_PRIORITY, function(err)
	{
		if (err) self.emitWarning({ message: 'error burying', id: jobID, error: err });
		self.emit('job.buried', jobID);
		self.emit('next');
	});
};

FiveBeansWorker.prototype.releaseAndMoveOn = function(jobID, delay)
{
	var self = this;
	if (delay === undefined) delay = 30;

	self.client.release(jobID, beanstalk.LOWEST_PRIORITY, delay, function(err)
	{
		if (err) self.emitWarning({ message: 'error releasing', id: jobID, error: err });
		self.emit('job.released', jobID);
		self.emit('next');
	});
};

FiveBeansWorker.prototype.deleteAndMoveOn = function(jobID)
{
	var self = this;
	self.client.destroy(jobID, function(err)
	{
		if (err) self.emitWarning({ message: 'error deleting', id: jobID, error: err });
		self.emit('job.deleted', jobID);
		self.emit('next');
	});
};

FiveBeansWorker.prototype.emitInfo = function(message)
{
	this.emit('info',
	{
		clientid: this.id,
		message:  message,
	});
	// console.log(message);
};

FiveBeansWorker.prototype.emitWarning = function(data)
{
	data.clientid = this.id;
	this.emit('warning', data);
};

module.exports = FiveBeansWorker;
<commit_msg>Allow a job to be buried by throwing an exception in the job.reserved event<commit_after>var
	beanstalk   = require('./client'),
	events      = require('events'),
	util        = require('util'),
	yaml        = require('js-yaml')
	;

/*
Events emitted:

error: payload is error; execution is halted
close: no payload

warning: payload is object with error information; execution continues
info: payload is object with action info

started: no payload
stopped: no payload

job.reserved: job has been reserved; payload is job id
job.handled: payload is object with job info
job.deleted: payload is jobid
job.buried: payload is jobid
*/

var FiveBeansWorker = function(options)
{
	events.EventEmitter.call(this);

	this.id            = options.id;
	this.host          = options.host;
	this.port          = options.port;
	this.handlers      = options.handlers;
	this.ignoreDefault = options.ignoreDefault;
	this.stopped       = false;

	this.client = null;
};
util.inherits(FiveBeansWorker, events.EventEmitter);

FiveBeansWorker.prototype.start = function(tubes)
{
	var self = this;
	this.stopped = false;

	this.on('next', this.doNext.bind(this));

	function finishedStarting()
	{
		self.emit('started');
		self.emit('next');
	}

	this.client = new beanstalk(this.host, this.port);

	this.client.on('connect', function()
	{
		self.emitInfo('connected to beanstalkd at '+ self.host + ':'+self.port);
		self.watch(tubes, function()
		{
			if (tubes && tubes.length && self.ignoreDefault)
			{
				self.ignore(['default'], function()
				{
					finishedStarting();
				});
			}
			else
			{
				finishedStarting();
			}
		});
	});

	this.client.on('error', function(err)
	{
		self.emitWarning({message: 'beanstalkd connection error', error: err});
		self.emit('error', err);
	});

	this.client.on('close', function()
	{
		self.emitInfo('beanstalkd connection closed');
		self.emit('close');
	});

	this.client.connect();
};

FiveBeansWorker.prototype.watch = function(tubes, callback)
{
	var self = this;
	var tube;
	if (tubes && (tube = tubes[0]))
	{
		self.emitInfo('watching tube ' + tube);
		self.client.watch(tube, function(err)
		{
			if (err) self.emitWarning({ message: 'error watching tube', tube: tube, error: err });
			self.watch(tubes.slice(1), callback);
		});
	}
	else
		callback();
};

FiveBeansWorker.prototype.ignore = function(tubes, callback)
{
	var self = this;
	var tube;
	if (tubes && (tube = tubes[0]))
	{
		self.emitInfo('ignoring tube '+tube);
		self.client.ignore(tube, function(err)
		{
			if (err) self.emitWarning({ message: 'error ignoring tube', tube: tube, error: err });
			self.ignore(tubes.slice(1), callback);
		});
	}
	else
		callback();
};

FiveBeansWorker.prototype.stop = function()
{
	this.emitInfo('stopping...');
	this.stopped = true;
};

FiveBeansWorker.prototype.doNext = function()
{
	var self = this;
	if (self.stopped)
	{
		self.client.end();
		self.emitInfo('stopped');
		self.emit('stopped');
		return;
	}

	self.client.reserve_with_timeout(1, function(err, jobID, payload)
	{
		if (err)
		{
			if ('TIMED_OUT' !== err)
				self.emitWarning({ message: 'error reserving job', error: err });
			self.emit('next');
		}
		else
		{
			self.client.stats_job(jobID, function(err, response)
			{
				if (err)
					self.emitWarning({ message: 'unable to receive stats', error: err });

				self.stats = response;

				var job = null;
				try { job = JSON.parse(payload); }
				catch (e) { self.emitWarning({ message: 'parsing job JSON', id: jobID, error: e }); }

				try { self.emit('job.reserved', jobID); }
				catch (e) { return self.buryAndMoveOn(jobID); }

				if (!job || (typeof job !== 'object'))
					self.buryAndMoveOn(jobID);
				else if (job instanceof Array)
					self.runJob(jobID, job[1]);
				else
					self.runJob(jobID, job);
			});
		}
	});
};

FiveBeansWorker.prototype.runJob = function(jobID, job)
{
	var self = this;
	var handler = this.lookupHandler(job.type);
	if (job.type === undefined)
	{
		self.emitWarning({ message: 'no job type', id: jobID, job: job });
		self.deleteAndMoveOn(jobID);
	}
	else if (!handler)
	{
		self.emitWarning({ message: 'no handler found', id: jobID, type: job.type });
		self.buryAndMoveOn(jobID);
	}
	else
	{
		self.callHandler(handler, jobID, job.payload);
	}
};

FiveBeansWorker.prototype.lookupHandler = function(type)
{
	return this.handlers[type];
};

FiveBeansWorker.prototype.callHandler = function(handler, jobID, jobdata)
{
	var self = this;
	var start = new Date().getTime();
	this.currentJob = jobID;
	this.currentHandler = handler;

	var timeout = setTimeout(function()
	{
		self.emit('job.timedOut', { id: jobID, handler: handler, payload: jobdata, type: handler.type, priority: self.stats.pri });
	}, self.stats ? self.stats.ttr*1000 - 1000 : 60000);

	try
	{
		handler.work(jobdata, function(action, delay)
		{
			clearTimeout(timeout);

			var elapsed = new Date().getTime() - start;
			action = action || 'success';

			self.emit('job.handled', { id: jobID, type: handler.type, elapsed: elapsed, action: action });

			switch (action)
			{
			case 'success':
				self.deleteAndMoveOn(jobID);
				break;

			case 'release':
				self.releaseAndMoveOn(jobID, delay);
				break;

			case 'bury':
				self.buryAndMoveOn(jobID);
				break;

			default:
				self.buryAndMoveOn(jobID);
				break;
			}
		});
	}
	catch (e)
	{
		clearTimeout(timeout);

		self.emitWarning({ message: 'exception in job handler', id: jobID, handler: handler.type, error: e });
		self.buryAndMoveOn(jobID);
	}
};

FiveBeansWorker.prototype.buryAndMoveOn = function(jobID)
{
	var self = this;
	self.client.bury(jobID, beanstalk.LOWEST_PRIORITY, function(err)
	{
		if (err) self.emitWarning({ message: 'error burying', id: jobID, error: err });
		self.emit('job.buried', jobID);
		self.emit('next');
	});
};

FiveBeansWorker.prototype.releaseAndMoveOn = function(jobID, delay)
{
	var self = this;
	if (delay === undefined) delay = 30;

	self.client.release(jobID, beanstalk.LOWEST_PRIORITY, delay, function(err)
	{
		if (err) self.emitWarning({ message: 'error releasing', id: jobID, error: err });
		self.emit('job.released', jobID);
		self.emit('next');
	});
};

FiveBeansWorker.prototype.deleteAndMoveOn = function(jobID)
{
	var self = this;
	self.client.destroy(jobID, function(err)
	{
		if (err) self.emitWarning({ message: 'error deleting', id: jobID, error: err });
		self.emit('job.deleted', jobID);
		self.emit('next');
	});
};

FiveBeansWorker.prototype.emitInfo = function(message)
{
	this.emit('info',
	{
		clientid: this.id,
		message:  message,
	});
	// console.log(message);
};

FiveBeansWorker.prototype.emitWarning = function(data)
{
	data.clientid = this.id;
	this.emit('warning', data);
};

module.exports = FiveBeansWorker;
<|endoftext|>


<commit_before>
'use strict';

var fs = require('fs'),
    http = require('http'),
    https = require('https'),
    httpProxy = require('http-proxy'),
    cache = require('./cache'),
    memoryMonitor = require('./memorymonitor');

var rootDir = fs.realpathSync(__dirname + '/../');

var logType = {
    log: 1,
    accessLog: 2
};

var log = function (msg, type) {
    // Send the log data to the master
    process.send({
        type: (type === undefined) ? logType.log : type,
        from: process.pid,
        data: msg
    });
};

var debug = function (debugMode) {
    return function (msg) {
        if (debugMode !== true) {
            return;
        }
        log(msg, logType.log);
    };
};

// Ignore SIGUSR
process.on('SIGUSR1', function () {});
process.on('SIGUSR2', function () {});

function Worker(config) {
    if (!(this instanceof Worker)) {
        return new Worker(config);
    }

    debug = debug(config.server.debug);
    this.cache = cache(config, {
        logHandler: log,
        debugHandler: debug
    });
    this.runServer(config.server);
}

Worker.prototype.runServer = function (config) {
    httpProxy.setMaxSockets(config.maxSockets);

    var proxyErrorHandler = function (err, req, res) {
        if (err.code === 'ECONNREFUSED' ||
                err.code === 'ETIMEDOUT' ||
                req.error !== undefined) {
            // This backend is dead
            var backendId = req.meta.backendId;
            this.cache.markDeadBackend(req.meta.frontend, backendId);
            if (req.error) {
                err = req.error;
                // Clearing the error
                delete req.error;
            }
            log(req.headers.host + ': backend #' + backendId + ' is dead (' + JSON.stringify(err) + ') while handling request for ' + req.url);
        } else {
            log(req.headers.host + ': backend #' + req.meta.backendId + ' reported an error (' + JSON.stringify(err) + ') while handling request for ' + req.url);
        }
        req.retries = (req.retries === undefined) ? 0 : req.retries + 1;
        if (res.connection.destroyed === true) {
            // FIXME: When there is a TCP timeout, the socket of the Response
            // object is closed. Not possible to return a result after a retry.
            // BugID:5654
            log(req.headers.host + ': Response socket already closed, aborting.');
            return;
        }
        req.emit('retry');
    }.bind(this);

    var startHandler = function (req, res) {
        var remoteAddr = getRemoteAddress(req);

        // TCP timeout to 30 sec
        req.connection.setTimeout(config.tcpTimeout * 1000);
        // Make sure the listener won't be set again on retry
        if (req.connection.listeners('timeout').length < 2) {
            req.connection.once('timeout', function () {
                req.error = 'TCP timeout';
            });
        }

        // Set forwarded headers
        if (remoteAddr === null) {
            return errorMessage(res, 'Cannot read the remote address.');
        }
        if (remoteAddr.slice(0, 2) !== '::') {
            remoteAddr = '::ffff:' + remoteAddr;
        }
        // Play nicely when behind other proxies
        if (req.headers['x-forwarded-for'] === undefined) {
            req.headers['x-forwarded-for'] = remoteAddr;
        }
        if (req.headers['x-real-ip'] === undefined) {
            req.headers['x-real-ip'] = remoteAddr;
        }
        if (req.headers['x-forwarded-protocol'] === undefined) {
            req.headers['x-forwarded-protocol'] = req.connection.pair ? 'https' : 'http';
        }
        if (req.headers['x-forwarded-proto'] === undefined) {
            req.headers['x-forwarded-proto'] = req.connection.pair ? 'https' : 'http';
        }
        if (req.headers['x-forwarded-port'] === undefined) {
            // FIXME: replace by the real port instead of hardcoding it
            req.headers['x-forwarded-port'] = req.connection.pair ? '443' : '80';
        }
    };

    var getRemoteAddress = function (req) {
        if (req.connection === undefined) {
            return null;
        }
        if (req.connection.remoteAddress) {
            return req.connection.remoteAddress;
        }
        if (req.connection.socket && req.connection.socket.remoteAddress) {
            return req.connection.socket.remoteAddress;
        }
        return null;
    };

    var errorMessage = function (res, message, code) {
        if (message === undefined) {
            message = '';
        }
        code = isNaN(code) ? 400 : parseInt(code, 10);

        var staticPath = function (name) {
            return rootDir + '/static/error_' + name + '.html';
        };

        var serve = function (filePath) {
            var stream = fs.createReadStream(filePath);
            var headers = {
                'content-type': 'text/html'
            };
            if (res.debug === true) {
                headers['x-debug-error'] = message;
            }
            res.writeHead(code, headers);
            stream.on('data', function (data) {
                res.write(data);
            });
            stream.on('error', function () {
                res.end();
            });
            stream.on('end', function () {
                res.end();
            });
        };

        var errorPage = staticPath(code);
        fs.exists(errorPage, function (exists) {
            if (exists === true) {
                return serve(errorPage);
            }
            errorPage = staticPath('default');
            fs.exists(errorPage, function (exists) {
                var headers;

                if (exists === true) {
                    return serve(errorPage);
                }
                headers = {
                    'content-length': message.length,
                    'content-type': 'text/html'
                };
                if (res.debug === true) {
                    headers['x-debug-error'] = message;
                }
                res.writeHead(code, headers);
                res.write(message);
                res.end();
            });
        });
    };

    var getDomainName = function (hostname) {
        var idx = hostname.lastIndexOf('.');

        if (idx < 0) {
            return hostname;
        }
        idx = hostname.lastIndexOf('.', idx - 1);
        if (idx < 0) {
            return hostname;
        }
        return hostname.substr(idx);
    };

    var httpRequestHandler = function (req, res) {
        res.timer = {
            start: (new Date()).getTime()
        };

        // Patch the response object
        (function () {
            // Enable debug?
            res.debug = (req.headers['x-debug'] !== undefined);
            // Patch res.writeHead to return debug headers
            if (res.debug === true) {
                var resWriteHead = res.writeHead;
                res.writeHead = function () {
                    res.timer.end = (new Date()).getTime();
                    if (req.meta === undefined) {
                        return resWriteHead.apply(res, arguments);
                    }
                    res.setHeader('x-debug-backend-url', req.meta.backendUrl);
                    res.setHeader('x-debug-backend-id', req.meta.backendId);
                    res.setHeader('x-debug-vhost', req.meta.virtualHost);
                    res.setHeader('x-debug-frontend-key', req.meta.frontend);
                    res.setHeader('x-debug-time-total', (res.timer.end - res.timer.start));
                    res.setHeader('x-debug-time-backend', (res.timer.end - res.timer.startBackend));
                    return resWriteHead.apply(res, arguments);
                };
            }
            // Patch res.end to log the response stats
            var resEnd = res.end;
            res.end = function () {
                resEnd.apply(res, arguments);
                // Number of bytes written on the client socket
                var socketBytesWritten = req.connection ? req.connection._bytesDispatched : 0;
                if (req.meta === undefined ||
                        req.headers['x-real-ip'] === undefined) {
                    return; // Nothing to log
                }
                if (res.timer.end === undefined) {
                    res.timer.end = (new Date()).getTime();
                }
                // Log the request
                log({
                    remoteAddr: req.headers['x-real-ip'],
                    currentTime: res.timer.start,
                    totalTimeSpent: (res.timer.end - res.timer.start),
                    backendTimeSpent: (res.timer.end - res.timer.startBackend),
                    method: req.method,
                    url: req.url,
                    httpVersion: req.httpVersion,
                    statusCode: res.statusCode,
                    socketBytesWritten: socketBytesWritten,
                    referer: req.headers.referer,
                    userAgent: req.headers['user-agent'],
                    virtualHost: req.meta.virtualHost
                }, logType.accessLog);
            };
        }());

        // Proxy the HTTP request
        var proxyRequest = function () {
            var buffer = httpProxy.buffer(req);

            this.cache.getBackendFromHostHeader(req.headers.host, function (err, code, backend) {
                if (err) {
                    return errorMessage(res, err, code);
                }
                req.meta = {
                    backendId: backend.id,
                    frontend: backend.frontend,
                    virtualHost: backend.virtualHost,
                    backendUrl: backend.href
                };
                // Proxy the request to the backend
                res.timer.startBackend = (new Date()).getTime();
                var proxy = new httpProxy.HttpProxy({
                    target: {
                        host: backend.hostname,
                        port: backend.port
                    },
                    enable: {
                        xforward: false
                    }
                });
                proxy.on('proxyError', proxyErrorHandler);
                proxy.on('start', startHandler);
                proxy.proxyRequest(req, res, buffer);
            });
        }.bind(this);
        req.on('retry', function () {
            log('Retrying on ' + req.headers.host);
            if (req.retries > config.errorMaxRetries) {
                log(req.headers.host + ': Retried too many times, aborting.');
                return errorMessage(res, 'Reached max retries limit', 502);
            }
            proxyRequest();
        });
        proxyRequest();
    }.bind(this);

    var wsRequestHandler = function (req, socket, head) {
        var buffer = httpProxy.buffer(socket);

        this.cache.getBackendFromHostHeader(req.headers.host, function (err, code, backend) {
            var proxy;

            if (err) {
                log('proxyWebSocketRequest: ' + err);
                return;
            }
            // Proxy the WebSocket request to the backend
            proxy = new httpProxy.HttpProxy({
                target: {
                    host: backend.hostname,
                    port: backend.port
                }
            });
            proxy.proxyWebSocketRequest(req, socket, head, buffer);
        });
    }.bind(this);

    var monitor = memoryMonitor({
        logHandler: log
    });

    // Ipv4
    var ipv4HttpServer = http.createServer(httpRequestHandler);
    ipv4HttpServer.on('upgrade', wsRequestHandler);
    ipv4HttpServer.listen(config.port);

    // Ipv6
    var ipv6HttpServer = http.createServer(httpRequestHandler);
    ipv6HttpServer.on('upgrade', wsRequestHandler);
    ipv6HttpServer.listen(config.port, '::1');

    monitor.addServer(ipv4HttpServer);
    monitor.addServer(ipv6HttpServer);

    if (config.https) {
        var options = config.https;
        options.key = fs.readFileSync(options.key, 'utf8');
        options.cert = fs.readFileSync(options.cert, 'utf8');

        var ipv4HttpsServer = https.createServer(options, httpRequestHandler);
        ipv4HttpsServer.on('upgrade', wsRequestHandler);
        ipv4HttpsServer.listen(config.https.port);

        var ipv6HttpsServer = https.createServer(options, httpRequestHandler);
        ipv6HttpsServer.on('upgrade', wsRequestHandler);
        ipv6HttpsServer.listen(config.https.port, '::1');

        monitor.addServer(ipv4HttpsServer);
        monitor.addServer(ipv6HttpsServer);
    }
};

module.exports = Worker;
<commit_msg>If the worker already closed the pipe, last logs are written directly on stdout<commit_after>
'use strict';

var fs = require('fs'),
    http = require('http'),
    https = require('https'),
    httpProxy = require('http-proxy'),
    cache = require('./cache'),
    memoryMonitor = require('./memorymonitor');

var rootDir = fs.realpathSync(__dirname + '/../');

var logType = {
    log: 1,
    accessLog: 2
};

var log = function (msg, type) {
    // Send the log data to the master
    try {
        var message = {
            type: (type === undefined) ? logType.log : type,
            from: process.pid,
            data: msg
        };
        process.send(message);
    } catch (err) {
        // Cannot write on the master channel (worker is committing a suicide)
        // (from the memorymonitor), writing the log directly.
        if (message.type === 1) {
            util.log('(worker #' + message.from + ') ' + message.data);
        }
    }
};

var debug = function (debugMode) {
    return function (msg) {
        if (debugMode !== true) {
            return;
        }
        log(msg, logType.log);
    };
};

// Ignore SIGUSR
process.on('SIGUSR1', function () {});
process.on('SIGUSR2', function () {});

function Worker(config) {
    if (!(this instanceof Worker)) {
        return new Worker(config);
    }

    debug = debug(config.server.debug);
    this.cache = cache(config, {
        logHandler: log,
        debugHandler: debug
    });
    this.runServer(config.server);
}

Worker.prototype.runServer = function (config) {
    httpProxy.setMaxSockets(config.maxSockets);

    var proxyErrorHandler = function (err, req, res) {
        if (err.code === 'ECONNREFUSED' ||
                err.code === 'ETIMEDOUT' ||
                req.error !== undefined) {
            // This backend is dead
            var backendId = req.meta.backendId;
            this.cache.markDeadBackend(req.meta.frontend, backendId);
            if (req.error) {
                err = req.error;
                // Clearing the error
                delete req.error;
            }
            log(req.headers.host + ': backend #' + backendId + ' is dead (' + JSON.stringify(err) + ') while handling request for ' + req.url);
        } else {
            log(req.headers.host + ': backend #' + req.meta.backendId + ' reported an error (' + JSON.stringify(err) + ') while handling request for ' + req.url);
        }
        req.retries = (req.retries === undefined) ? 0 : req.retries + 1;
        if (res.connection.destroyed === true) {
            // FIXME: When there is a TCP timeout, the socket of the Response
            // object is closed. Not possible to return a result after a retry.
            // BugID:5654
            log(req.headers.host + ': Response socket already closed, aborting.');
            return;
        }
        req.emit('retry');
    }.bind(this);

    var startHandler = function (req, res) {
        var remoteAddr = getRemoteAddress(req);

        // TCP timeout to 30 sec
        req.connection.setTimeout(config.tcpTimeout * 1000);
        // Make sure the listener won't be set again on retry
        if (req.connection.listeners('timeout').length < 2) {
            req.connection.once('timeout', function () {
                req.error = 'TCP timeout';
            });
        }

        // Set forwarded headers
        if (remoteAddr === null) {
            return errorMessage(res, 'Cannot read the remote address.');
        }
        if (remoteAddr.slice(0, 2) !== '::') {
            remoteAddr = '::ffff:' + remoteAddr;
        }
        // Play nicely when behind other proxies
        if (req.headers['x-forwarded-for'] === undefined) {
            req.headers['x-forwarded-for'] = remoteAddr;
        }
        if (req.headers['x-real-ip'] === undefined) {
            req.headers['x-real-ip'] = remoteAddr;
        }
        if (req.headers['x-forwarded-protocol'] === undefined) {
            req.headers['x-forwarded-protocol'] = req.connection.pair ? 'https' : 'http';
        }
        if (req.headers['x-forwarded-proto'] === undefined) {
            req.headers['x-forwarded-proto'] = req.connection.pair ? 'https' : 'http';
        }
        if (req.headers['x-forwarded-port'] === undefined) {
            // FIXME: replace by the real port instead of hardcoding it
            req.headers['x-forwarded-port'] = req.connection.pair ? '443' : '80';
        }
    };

    var getRemoteAddress = function (req) {
        if (req.connection === undefined) {
            return null;
        }
        if (req.connection.remoteAddress) {
            return req.connection.remoteAddress;
        }
        if (req.connection.socket && req.connection.socket.remoteAddress) {
            return req.connection.socket.remoteAddress;
        }
        return null;
    };

    var errorMessage = function (res, message, code) {
        if (message === undefined) {
            message = '';
        }
        code = isNaN(code) ? 400 : parseInt(code, 10);

        var staticPath = function (name) {
            return rootDir + '/static/error_' + name + '.html';
        };

        var serve = function (filePath) {
            var stream = fs.createReadStream(filePath);
            var headers = {
                'content-type': 'text/html'
            };
            if (res.debug === true) {
                headers['x-debug-error'] = message;
            }
            res.writeHead(code, headers);
            stream.on('data', function (data) {
                res.write(data);
            });
            stream.on('error', function () {
                res.end();
            });
            stream.on('end', function () {
                res.end();
            });
        };

        var errorPage = staticPath(code);
        fs.exists(errorPage, function (exists) {
            if (exists === true) {
                return serve(errorPage);
            }
            errorPage = staticPath('default');
            fs.exists(errorPage, function (exists) {
                var headers;

                if (exists === true) {
                    return serve(errorPage);
                }
                headers = {
                    'content-length': message.length,
                    'content-type': 'text/html'
                };
                if (res.debug === true) {
                    headers['x-debug-error'] = message;
                }
                res.writeHead(code, headers);
                res.write(message);
                res.end();
            });
        });
    };

    var getDomainName = function (hostname) {
        var idx = hostname.lastIndexOf('.');

        if (idx < 0) {
            return hostname;
        }
        idx = hostname.lastIndexOf('.', idx - 1);
        if (idx < 0) {
            return hostname;
        }
        return hostname.substr(idx);
    };

    var httpRequestHandler = function (req, res) {
        res.timer = {
            start: (new Date()).getTime()
        };

        // Patch the response object
        (function () {
            // Enable debug?
            res.debug = (req.headers['x-debug'] !== undefined);
            // Patch res.writeHead to return debug headers
            if (res.debug === true) {
                var resWriteHead = res.writeHead;
                res.writeHead = function () {
                    res.timer.end = (new Date()).getTime();
                    if (req.meta === undefined) {
                        return resWriteHead.apply(res, arguments);
                    }
                    res.setHeader('x-debug-backend-url', req.meta.backendUrl);
                    res.setHeader('x-debug-backend-id', req.meta.backendId);
                    res.setHeader('x-debug-vhost', req.meta.virtualHost);
                    res.setHeader('x-debug-frontend-key', req.meta.frontend);
                    res.setHeader('x-debug-time-total', (res.timer.end - res.timer.start));
                    res.setHeader('x-debug-time-backend', (res.timer.end - res.timer.startBackend));
                    return resWriteHead.apply(res, arguments);
                };
            }
            // Patch res.end to log the response stats
            var resEnd = res.end;
            res.end = function () {
                resEnd.apply(res, arguments);
                // Number of bytes written on the client socket
                var socketBytesWritten = req.connection ? req.connection._bytesDispatched : 0;
                if (req.meta === undefined ||
                        req.headers['x-real-ip'] === undefined) {
                    return; // Nothing to log
                }
                if (res.timer.end === undefined) {
                    res.timer.end = (new Date()).getTime();
                }
                // Log the request
                log({
                    remoteAddr: req.headers['x-real-ip'],
                    currentTime: res.timer.start,
                    totalTimeSpent: (res.timer.end - res.timer.start),
                    backendTimeSpent: (res.timer.end - res.timer.startBackend),
                    method: req.method,
                    url: req.url,
                    httpVersion: req.httpVersion,
                    statusCode: res.statusCode,
                    socketBytesWritten: socketBytesWritten,
                    referer: req.headers.referer,
                    userAgent: req.headers['user-agent'],
                    virtualHost: req.meta.virtualHost
                }, logType.accessLog);
            };
        }());

        // Proxy the HTTP request
        var proxyRequest = function () {
            var buffer = httpProxy.buffer(req);

            this.cache.getBackendFromHostHeader(req.headers.host, function (err, code, backend) {
                if (err) {
                    return errorMessage(res, err, code);
                }
                req.meta = {
                    backendId: backend.id,
                    frontend: backend.frontend,
                    virtualHost: backend.virtualHost,
                    backendUrl: backend.href
                };
                // Proxy the request to the backend
                res.timer.startBackend = (new Date()).getTime();
                var proxy = new httpProxy.HttpProxy({
                    target: {
                        host: backend.hostname,
                        port: backend.port
                    },
                    enable: {
                        xforward: false
                    }
                });
                proxy.on('proxyError', proxyErrorHandler);
                proxy.on('start', startHandler);
                proxy.proxyRequest(req, res, buffer);
            });
        }.bind(this);
        req.on('retry', function () {
            log('Retrying on ' + req.headers.host);
            if (req.retries > config.errorMaxRetries) {
                log(req.headers.host + ': Retried too many times, aborting.');
                return errorMessage(res, 'Reached max retries limit', 502);
            }
            proxyRequest();
        });
        proxyRequest();
    }.bind(this);

    var wsRequestHandler = function (req, socket, head) {
        var buffer = httpProxy.buffer(socket);

        this.cache.getBackendFromHostHeader(req.headers.host, function (err, code, backend) {
            var proxy;

            if (err) {
                log('proxyWebSocketRequest: ' + err);
                return;
            }
            // Proxy the WebSocket request to the backend
            proxy = new httpProxy.HttpProxy({
                target: {
                    host: backend.hostname,
                    port: backend.port
                }
            });
            proxy.proxyWebSocketRequest(req, socket, head, buffer);
        });
    }.bind(this);

    var monitor = memoryMonitor({
        logHandler: log
    });

    // Ipv4
    var ipv4HttpServer = http.createServer(httpRequestHandler);
    ipv4HttpServer.on('upgrade', wsRequestHandler);
    ipv4HttpServer.listen(config.port);

    // Ipv6
    var ipv6HttpServer = http.createServer(httpRequestHandler);
    ipv6HttpServer.on('upgrade', wsRequestHandler);
    ipv6HttpServer.listen(config.port, '::1');

    monitor.addServer(ipv4HttpServer);
    monitor.addServer(ipv6HttpServer);

    if (config.https) {
        var options = config.https;
        options.key = fs.readFileSync(options.key, 'utf8');
        options.cert = fs.readFileSync(options.cert, 'utf8');

        var ipv4HttpsServer = https.createServer(options, httpRequestHandler);
        ipv4HttpsServer.on('upgrade', wsRequestHandler);
        ipv4HttpsServer.listen(config.https.port);

        var ipv6HttpsServer = https.createServer(options, httpRequestHandler);
        ipv6HttpsServer.on('upgrade', wsRequestHandler);
        ipv6HttpsServer.listen(config.https.port, '::1');

        monitor.addServer(ipv4HttpsServer);
        monitor.addServer(ipv6HttpsServer);
    }
};

module.exports = Worker;
<|endoftext|>