/* global $,d3 */

//## Sortable barchart ##
$.fn.barChart = function(data, inSettings) {
	var plot = {};
	var extraDefaults = {
		yLabels: '',
		yTickFormat: '',
		minY: 0,
		dimension: {w: 850, h: 300},
		padding: {t: 20, r: 20, b: 85, l: 50},
		bgColor: '',
		barWidth: 11,
		xLabelsHide: false,
		extendedTooltip: false
	};
	var settings = addDefaultPlotSettings(inSettings, extraDefaults);
	plot.settings = settings;

	// Inner dimension
	var width = plot.width = settings.dimension.w - settings.padding.l - settings.padding.r; // inner width
	var height = plot.height = settings.dimension.h - settings.padding.t - settings.padding.b; // inner height

	var xScale = plot.x = d3.scaleBand()
		.range([0, width])
		.domain(data.map(function(d) { return d.label; }));
	var xAxis = plot.xAxis = d3.axisBottom(xScale)
		.tickSizeOuter(0);

	if (settings.xLabelsHide) {
		xAxis
			.tickFormat('')
			.tickSize(0);
	}

	var yScale;
	var yMin, yMax, yAxis;
	if (settings.yLabels) {
		yMin = plot.yMin = 0;
		yMax = plot.yMax = d3.max(d3.keys(settings.yLabels));
		yScale = plot.y = d3.scaleLinear()
			.range([height, 0])
			.domain([yMin, yMax]);
		yAxis = d3.axisLeft(yScale)
			.tickFormat(function(d) { return settings.yLabels[d]; })
			.tickValues(d3.keys(settings.yLabels));
	}
	else {
		yMin = (plot.settings.minY >= 0) ? 0 : plot.yMin = Math.min(d3.min(data, function(d) { return parseFloat(d.value); }), settings.minY);
		yMax = plot.yMax = Math.max(d3.max(data, function(d) { return parseFloat(d.value); }), settings.minY);
		yScale = plot.y = d3.scaleLinear()
			.range([height, 0])
			.domain([yMin, yMax])
			.nice(5);
		yAxis = d3.axisLeft(yScale)
			.ticks(5);
		if (settings.yTickSize != undefined) {
			yAxis.tickSize(settings.yTickSize);
		}
		if (settings.yTickFormat) {
			yAxis.tickFormat(d3.format(settings.yTickFormat));
		}
	}

	var vis = plot.vis = d3.select($(this).get(0))
		.append("svg")
		.attr("class", "barchart")
		.attr("width", width + settings.padding.l + settings.padding.r)
		.attr("height", height + settings.padding.t + settings.padding.b)
		.append("g")
		.attr("transform", "translate(" + settings.padding.l + "," + settings.padding.t + ")")
		.attr("width", width)
		.attr("height", height);

	$(this).on('click', '[url]', function() {
		document.location.href = $(this).attr('url');
	});

	if (settings.bgColor) {
		vis.append("rect")
			.attr("class", "chart_field")
			.attr("width", width)
			.attr("height", height+4)
			.attr("transform", "translate(0,-4)")
			.style("fill", settings.bgColor);
	}

	var bar_g = vis.selectAll(".bar_g")
		.data(data)
		.enter()
		.append("g")
		.attr("class", function(d) { return d.class? "bar_g " + d.class : "bar_g"; })
		.attr("transform", function(d) { return 'translate(' + xScale(d.label) + ', 0)'; })
		.attr("title", function(d) { return d.tooltip; });

	var link = bar_g.append("a")
		.each(function(d) { if (d.url) d3.select(this).attr("xlink:href", d.url); });

	if (settings.extendedTooltip) {
		var bar_transp = link.append("rect")
			.attr("class","bartrans")
			.attr("y", 0)
			.attr("x", function(d) { return xScale.bandwidth() / 2 - settings.barWidth / 2; })
			.attr("width", settings.barWidth)
			.attr("height", height)
			.style("fill", "transparent");
	}

	var bar_rect = link.append("rect")
		.attr("class", "bar")
		.attr("y", function(d) {
			return (d.na == 1)?0:yScale(Math.max(0, d.value));
		})
		.attr("x", function(d) { return xScale.bandwidth() / 2 - settings.barWidth / 2 ; })
		.attr("width", settings.barWidth)
		.attr("height", function(d) {
			return (d.na == 1)?height:Math.abs(yScale(d.value) - yScale(0));
		})
		.style("fill", function(d){
			if (d.na == 1) {
				return "#FAFAFA";
			} else if (typeof d.color_group != 'undefined') {
				return colorbrewer[d.color_group[0]][d.color_group[1]][d.color_group[2]];
			} else {
				return d.color;
			}
		});

	var bar_text = bar_g.filter(function(d) { return d.na == 1; })
		.append("text")
		.attr("class", "na_text")
		.style("text-anchor", "middle")
		.attr("transform", "rotate(-90)")
		.attr("y", settings.barWidth / 2 + 6)
		.attr("x", -height / 2)
		.text("N/A");

	if (settings.valueLabels) {
		link.append("text")
			.attr("class", "valueLabel")
			.style("text-anchor", "middle")
			.attr("x", function(d, i) { return xScale.bandwidth() / 2; })
			.attr("y", function(d) { return (d.na == 1) ? 0 : yScale(d.value) - 2; })
			.text(function(d) { return (d.na == 1) ? '' : d.value; });
	}

	vis.append("g")
		.attr("class", "x axis")
		.attr("transform", "translate(0," + height + ")")
		.call(xAxis)
		.selectAll(".tick text")
		.attr("class", "barchartlabel")
		.attr("y", 0)
		.attr("x", -9)
		.attr("transform", "rotate(-55)")
		.style("text-anchor", "end")
		.each(function(d, i) {
			if (data[i].tooltip) {
				d3.select(this).attr('title', data[i].tooltip);
			}
			if (data[i].url) {
				d3.select(this).attr('url', data[i].url).style('cursor', 'pointer');
			}
		});

	if (plot.settings.minY < 0) {
		// zero line
		vis.append("line")
			.attr("class", "barchartlabel")
			.attr("x1", 0)
			.attr("x2", width)
			.attr("y1", yScale(0) + 0.5)
			.attr("y2", yScale(0) + 0.5)
			.attr("stroke", "#000")
			.attr("stroke-width", "1");
	}

	// x axis label
	vis.selectAll(".x.axis")
		.append("text")
		.attr("y", 20)
		.attr("x", width / 2)
		.text(settings.xAxisLabel);

	// y axis label
	vis.append("g")
		.attr("class", "y axis")
		.call(yAxis)
		.append("text")
		.attr("y", -8)
		.style("text-anchor", "middle")
		.text(settings.yAxisLabel);

	vis.selectAll(".y .tick text")
		.call(wrap, settings.padding.l);

	// Legend
	if (settings.legend !== '') {
		var legend = vis.selectAll(".legend")
			.data(d3.map(data, function(d) { return d.legend; }).values())
			.enter().append("g")
			.attr("class", "legend")
			.attr("transform", function(d, i) { return "translate(0," + i * (settings.barWidth + 2) + ")"; });

		legend.append("rect")
			.attr("x", settings.legend.x)
			.attr("y", settings.legend.y)
			.attr("width", settings.barWidth)
			.attr("height", settings.barWidth)
			.style("fill", function(d, i) { return d.color; });

		legend.append("text")
			.attr("x", settings.legend.x + settings.barWidth + 2)
			.attr("y", settings.legend.y + settings.barWidth / 2)
			.attr("dy", ".35em")
			.style("text-anchor", settings.legend.txtAnchor)
			.text(function(d) { return d.legend; });
	}

	$(this).on('sort', function(e, order) {
		var x0 = xScale.domain(
			data.sort(
				order === 'pos' ? function(a, b) { return a.pos - b.pos; }
					: order === 'value' ? function(a, b) { return a.na ? 1000 : b.na ? -1000 : b.value - a.value; }
						: order === 'real_value' ? function(a, b) { return a.na ? 1000 : b.na ? -1000 : b.real_value - a.real_value; }
							: order === 'label' ? function(a, b) { return a.label.localeCompare(b.label); }
								: order === 'origin' ? function(a, b) { return a.origin.localeCompare(b.origin); }
									: order === 'category' ? function(a, b) { return a.category.localeCompare(b.category); }
										: function(a, b) { return 0; }
			)
				.map(function(d) { return d.label; })
		).copy();

		vis.selectAll(".bar_g").sort(function(a, b) { return x0(a.value) - x0(b.value); });
		vis.selectAll(".x.axis.tick").sort(function(a, b) { return x0(a.value) - x0(b.value); });

		var transition = vis.transition().duration(100);
		transition.selectAll(".bar_g")
			.attr("transform", function(d) { return 'translate(' + x0(d.label) + ', 0)'; });
		transition.select(".x.axis")
			.call(xAxis)
			.selectAll(".tick text")
			.style("text-anchor", "end")
			.attr("y", 0)
			.attr("x", -9);
	});

	function wrap(text, width) {
		text.each(function() {
			var text = d3.select(this),
				words = text.text().split(/\s+/).reverse(),
				word,
				line = [],
				lineNumber = 0,
				lineHeight = 1.1, // ems
				y = text.attr("y"),
				x = text.attr("x"),
				dy = parseFloat(text.attr("dy")),
				tspan = text.text(null).append("tspan").attr("x", x).attr("y", y).attr("dy", dy + "em");
			while (word = words.pop()) {
				line.push(word);
				tspan.text(line.join(" "));
				if (tspan.node().getComputedTextLength() > width) {
					line.pop();
					tspan.text(line.join(" "));
					line = [word];
					tspan = text.append("tspan").attr("x", x).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
				}
			}
		});
	}

	return plot;
};

//## Horizontal sortable barchart ##
$.fn.horizontalBarChart= function(data, inSettings) {
	var plot = {};
	var extraDefaults = {
		yLabels: '',
		yTickFormat: '',
		minX: 0,
		dimension: {w: 500, h: 300},
		padding: {t: 20, r: 20, b: 20, l: 150},
		bgColor: '',
		barHeight: 15,
		xAxis: true,
		yLabelsHide: false,
		yLabelsChars: 0,
		extendedTooltip: false
	};
	var settings = addDefaultPlotSettings(inSettings, extraDefaults);
	plot.settings = settings;
	
	// Inner dimension
	var width = plot.width = settings.dimension.w - settings.padding.l - settings.padding.r; // inner width
  var height = plot.height = settings.dimension.h - settings.padding.t - settings.padding.b; // inner height
	
	var yScale = plot.y = d3.scaleBand()
		.range([0, height])
		.domain(data.map(function(d) { return d.label; }));
	var yAxis = d3.axisLeft(yScale)
		.tickSizeOuter(0);

	if (settings.yLabelsHide) {
		yAxis
		.tickFormat('')
		.tickSize(0);
	}
	var xScale;
	var xMin, xMax, xAxis;
	if (settings.xLabels) {
		xMin = plot.xMin = 0;
		xMax = plot.xMax = d3.max(d3.keys(settings.xLabels));
		xScale = plot.x = d3.scaleLinear()
			.range([0, width])
			.domain([xMin, xMax]);
		xAxis = d3.axisTop(xScale)
			.tickFormat(function(d) { return settings.xLabels[d]; })
			.tickValues(d3.keys(settings.xLabels));
	}
	else {
		xMin = plot.xMin = 0;
		xMax = plot.xMax = Math.max(d3.max(data, function(d) { return parseFloat(d.value); }), settings.minX);
		xScale = plot.x = d3.scaleLinear()
			.range([0, width])
			.domain([xMin, xMax])
			.nice(5);
		xAxis = d3.axisTop(xScale)
			.ticks(5);
		if (settings.xTickSize != undefined) {
			xAxis.tickSize(settings.xTickSize);
		}
		if (settings.xTickFormat) {
			xAxis.tickFormat(d3.format(settings.xTickFormat));
		}
	}
	var vis = plot.vis = d3.select($(this).get(0))
		.append("svg")
		.attr("class", "horizbarchart")
		.attr("width", width + settings.padding.l + settings.padding.r)
		.attr("height", height + settings.padding.t + settings.padding.b)
		.append("g")
		.attr("transform", "translate(" + settings.padding.l + "," + settings.padding.t + ")")
		.attr("width", width)
		.attr("height", height);
		
		$(this).on('click', '[url]', function() {
			document.location.href = $(this).attr('url');
		});
	
	if (settings.bgColor) {
		vis.append("rect")
			.attr("class", "chart_field")
			.attr("width", width)
			.attr("height", height+4)
			.attr("transform", "translate(0,-4)")
			.style("fill", settings.bgColor);
	}

  var bar_g = vis.selectAll(".bar_g")
		.data(data)
		.enter()
		.append("g")
		.attr("class", function(d) { return d.class? "bar_g "+d.class : "bar_g"; })
		.attr("transform", function(d) { return 'translate(0, '+yScale(d.label)+')'; })
		.attr("title", function(d) { return d.tooltip; });
		
	var link = bar_g.append("a")
		.each(function(d) { if (d.url) d3.select(this).attr("xlink:href", d.url); });
	
	if (settings.extendedTooltip) {
		var bar_transp = link.append("rect")
			.attr("class","bartrans")
			.attr("x", 0)
			.attr("y", function(d) { return yScale.bandwidth()/2 - settings.barHeight/2 ; })
			.attr("height", settings.barHeight)
			.attr("width", width)
			.style("fill", "transparent");
	}
	var bar_rect = link.append("rect")
		.attr("class", "bar")
		.attr("x", 0)
		.attr("y", function(d) { return yScale.bandwidth()/2 - settings.barHeight/2 ; })
		.attr("height", settings.barHeight)
		.attr("width", function(d) { return (d.na == 1)?0:(xScale(d.value));})
		.style("fill", function(d){
			if (d.na==1) {
					return "#FAFAFA";
			} else if (typeof d.color_group!='undefined') {
				return colorbrewer[d.color_group[0]][d.color_group[1]][d.color_group[2]];
			} else {
				return d.color;
			}
		});
		
	var bar_text = bar_g.filter(function(d){ return d.na == 1; })
		.append("text")
		.attr("class", "na_text")
		.style("text-anchor", "middle")
		.attr("transform", "rotate(-90)")
		.attr("x", settings.barHeight/2+6)
		.attr("y", width/2)
		.text("N/A");
	
	if (settings.valueLabels) {
		link.append("text")
			.attr("class", "valueLabel")
			.style("text-anchor", "start")
			.attr("x", function(d) { return (d.na == 1)?0:xScale(d.value)+2; })
			.attr("y", function(d, i) { return yScale.bandwidth()/2 + settings.barHeight/2-4; })
			.text(function(d) { return (d.valueLabel!=undefined)? d.valueLabel : ((d.na == 1)?'':d.value); });
	}
	
	// x axis
	if (settings.xAxis) {
		vis.append("g")
			.attr("class", "x axis")
			.call(xAxis)
			.selectAll(".tick text")
			.attr("class", "barchartlabel")
			.attr("x", 0)
			.attr("y", -9)
			.style("text-anchor", "end");
		vis.selectAll(".x.axis")
			.append("text")
			.attr("x", width/2)
			.attr("y", -20)
			.text(settings.xAxisLabel);
	}
	// y axis
	vis.append("g")
		.attr("class", "y axis")
		.call(yAxis)
		.append("text")
		.attr("y", -8)
		.style("text-anchor", "middle")
		.text(settings.yAxisLabel);
	if (settings.yLabelsChars) {
		vis.selectAll(".y.axis .tick text").each(function(d) {
			if (d3.select(this).text().length > settings.yLabelsChars) {
				d3.select(this)
					.attr('title', d3.select(this).text())
					.text(d3.select(this).text().substring(0,settings.yLabelsChars) + '...');
			}
		});
	}
	else {
		vis.selectAll(".y .tick text")
			.call(wrap, settings.padding.l);
	}
	
	// Legend
	if (settings.legend !== '') {
		var legend = vis.selectAll(".legend")
			.data(d3.map(data, function(d){return d.legend;}).values())
			.enter().append("g")
			.attr("class", "legend")
			.attr("transform", function(d, i) { return "translate(0," + i * (settings.barHeight+2) + ")"; });

		legend.append("rect")
			.attr("x", settings.legend.x)
			.attr("y", settings.legend.y)
			.attr("width", settings.barHeight)
			.attr("height", settings.barHeight)
			.style("fill", function(d, i) {return d.color; });

		legend.append("text")
			.attr("x", settings.legend.x+settings.barHeight+2)
			.attr("y", settings.legend.y+settings.barHeight/2)
			.attr("dy", ".35em")
			.style("text-anchor", settings.legend.txtAnchor)
			.text(function(d) { return d.legend; });
		
	}
	
	$(this).on('sort', function(e, order) {
		var y0 = yScale.domain(
			data.sort(
				order==='pos'? function(a, b) { return a.pos - b.pos; }
				:order==='value'? function(a, b) { return a.na? 1000 : b.na? -1000 : b.value - a.value; }
				:order==='real_value'? function(a, b) { return a.na? 1000 : b.na? -1000 : b.real_value - a.real_value; }
				:order==='label'? function(a, b) { return a.label.localeCompare(b.label); }
				:order==='origin'? function(a, b) { return a.origin.localeCompare(b.origin); }
				:order==='category'? function(a, b) { return a.category.localeCompare(b.category); }
				: function(a,b) { return 0; }
			)
			.map(function(d) { return d.label; })
		)
		.copy();
		
    vis.selectAll(".bar_g").sort(function(a, b) { return y0(a.value) - y0(b.value); });
    vis.selectAll(".y.axis.tick").sort(function(a, b) { return y0(a.value) - y0(b.value); });

    var transition = vis.transition().duration(100);
    transition.selectAll(".bar_g")
			.attr("transform", function(d) { return 'translate(0, '+y0(d.label)+')'; });
		transition.select(".y.axis")
			.call(yAxis)
			.selectAll(".tick text")
			.style("text-anchor", "end")
			.attr("x", 0)
			.attr("y", -9);
  });

	function wrap(text, width) {
		text.each(function() {
			var text = d3.select(this),
					words = text.text().split(/\s+/).reverse(),
					word,
					line = [],
					lineNumber = 0,
					lineHeight = 1.1, // ems
					y = text.attr("y"),
					x = text.attr("x"),
					dy = parseFloat(text.attr("dy")),
					tspan = text.text(null).append("tspan").attr("x", x).attr("y", y).attr("dy", dy + "em");
			while (word = words.pop()) {
				line.push(word);
				tspan.text(line.join(" "));
				if (tspan.node().getComputedTextLength() > width) {
					line.pop();
					tspan.text(line.join(" "));
					line = [word];
					tspan = text.append("tspan").attr("x", x).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
				}
			}
		});
	}
	return plot;
};

$.fn.horizBarChart = function(data, inSettings) {
	var plot = {};
	var extraDefaults = {
		yLabels: '',
		yTickFormat: '',
		minY: 0,
		dimension: {w: 400, h: 300},
		padding: {t: 20, r: 20, b: 20, l: 100},
		bgColor: '',
		barheight: 11,
		xLabelsHide: false,
		extendedTooltip: false
	};
	var settings = addDefaultPlotSettings(inSettings, extraDefaults);
	plot.settings = settings;
	
	// Inner dimension
	var width = plot.width = settings.dimension.w - settings.padding.l - settings.padding.r; // inner width
  var height = plot.height = settings.dimension.h - settings.padding.t - settings.padding.b; // inner height
	
	
	
	var defaultColorScale = ["#CCCCFF", "#0000FF"];
	if (typeof settings == "undefined") settings = {};
	if (typeof settings.colorset != "undefined") {
		if (settings.colorset == "blue") {
			settings.colorscale = defaultColorScale;
		} else if (settings.colorset == "red") {
			settings.colorscale = ["#FFCCCC", "#FF0000"];
		} else if (settings.colorset == "Cytoplasm") {
			settings.colorscale = ["#bede8a", "#94c83d"];
		} else if (settings.colorset == "Nucleus") {
			settings.colorscale = ["#87d3ff", "#017bc0"];
		} else if (settings.colorset == "Endomembrane system") {
			settings.colorscale = ["#ffaf91", "#f26531"];
		}
	}
	if (typeof settings.colorscale == 'undefined') settings.colorscale = defaultColorScale;
	if (typeof settings.dimension == 'undefined') settings.width = 600;
	if (typeof settings.barheight == 'undefined') settings.barheight = 20;
	if (typeof settings.bgcolor == 'undefined') settings.bgcolor = "#FFFFFF";
	if (typeof settings.maxvalue == 'undefined') settings.maxvalue = 40;
	if (typeof settings.xAxisLabel == 'undefined') settings.xAxisLabel = '';
	if (typeof settings.padding == 'undefined') settings.padding = {'top': 30, 'bottom': 0, 'left': 200, 'right': 40};
	if (typeof settings.xLabelTransform == 'undefined') settings.xLabelTransform = '';
	if (typeof settings.valueLabels == 'undefined') settings.valueLabels = false;
	if (typeof settings.grid == 'undefined') settings.grid = false;
	if (typeof settings.showRows == 'undefined') settings.showRows = false;
	var barHeight = settings.barheight;
	var chartHeight = settings.barheight * data.length;
	var height = chartHeight + settings.padding.top + settings.padding.bottom;
	var chartWidth = settings.width - settings.padding.left - settings.padding.right;
	var svg = d3.select(id)
			.attr("tabindex", 1)
			.append("svg")
			.attr("class", "chart cluster")
			.attr("width", settings.width)
			.attr("height", height);
	if (settings.bgColor != '') {
		svg.append("rect")
				.attr("class", "background")
				.attr("width", settings.width)
				.attr("height", height)
				.attr("fill", settings.bgcolor);
	}
	var chart = svg
			.append("g")
			.attr("width", chartWidth)
			.attr("height", chartHeight)
			.attr("transform", "translate("+settings.padding.left+","+settings.padding.top+")");

	// Bar color
	var color = d3.scaleLinear()
			.domain([0, settings.maxvalue])
			.range(settings.colorscale);

	// X axis
	var x = d3.scaleLinear()
			.range([0, chartWidth])
			.domain([0, d3.max(data, function(d){ return d.value;})]);
	var xAxis = d3.axisTop().scale(x).tickSizeOuter(0);
	if (settings.grid) xAxis.tickSizeInner(-chartHeight);

	// Y axis
	var y = d3.scaleBand()
			.range([0, chartHeight])
			.domain(data.map(function(d){return d.name;}));
	var yAxis = d3.axisLeft().scale(y).tickSizeOuter(0);

	chart.append("g")
		.attr("class", "x axis")
		.call(xAxis);

	chart.selectAll(".x.axis")
		.append("text")
		.attr("class", "xAxisLabel")
		.attr("y", -20)
		.attr("x", chartWidth/2)
		.style("text-anchor", "middle")
		.style("fill", "#000")
		.text(settings.xAxisLabel);
	
	// The bar's
	var bar = chart.selectAll("rect")
				.data(data)
				.enter().append("g")
				.attr("class", "bar")
				.attr("transform", function(d, i) { return "translate(0," + i * settings.barheight + ")"; });
	// The visual bar
	var link = bar.append("a")
		.attr("xlink:href", function(d) {return d.url;});
	link.append("rect")
		.attr("width", function(d) {return x(d.value)})
		.attr("height", settings.barheight-1)
		.attr("fill", function(d){return color(d.value)})
		.attr("y", 0)
		.attr("title", function(d){return d.value_tooltip})
		.on("mouseover", function(d, i) {
			var r = getHighlightColor($(this).css("fill"));
			$(this).attr("fill-old", $(this).css("fill"));
			d3.select(this).transition().style("fill", 'rgb('+r[1]+','+r[2]+','+r[3]+')');
    })
    .on("mouseout", function(d, i) {
			if($(this).attr("fill-old")) {
				d3.select(this).transition().style("fill", $(this).attr("fill-old"));
			}
    });
	if (settings.valueLabels) {
		bar.append("text")
			.text(function(d) {return d.value;})
			.attr("text-align", "right")
			.attr("dominant-baseline", "middle")
			.attr("y", barHeight/2)
			.attr("x", function(d) {return x(d.value)+2});
	}
	
	chart.append("g")
		.attr("class", "y axis")
		.attr("width", settings.padding.left-5)
		.call(yAxis);
	chart.selectAll(".y.axis text").attr("width", settings.padding.left-5).text(function(d) {if (d.length>31) {return d.substring(0,28)+"...";} else return d;})
		//.attr("x", 40);
	adjustClusterPlot(id);
	
	return plot;
};



//## Grouped barchart ##
$.fn.groupedBarChart = function(data, inSettings) {
	var extraDefaults = {
		padding: {t: 10, r: 10, b: 20, l: 30}
	};
	var settings = addDefaultPlotSettings(inSettings, extraDefaults);
  if (typeof inSettings.ColorGroup !== 'undefined') {
  		if (inSettings.ColorGroup === "Cytoplasm") {
			settings.color[0] = ["#94c83d"];
			settings.color[1] = ["#cdccca"];
		} else if (inSettings.ColorGroup === "Nucleus") {
			settings.color[0] = ["#017bc0"];
			settings.color[1] = ["#cdccca"];
		} else if (inSettings.ColorGroup === "Endomembrane system") {
			settings.color[0] = ["#f26531"];
			settings.color[1] = ["#cdccca"];
		}
	}
  
	// Inner dimension
	var w = settings.dimension.w - settings.padding.l - settings.padding.r; // inner width
  var h = settings.dimension.h - settings.padding.t - settings.padding.b; // inner height
	
	// Max y-value
	var max;
	if (typeof settings.max == 'undefined') {
		max = d3.max(data, function(dt) {
			return d3.max(dt, function(d) {
				return parseFloat(d.value);
			});
		});
	}
	else max = settings.max;
	
  // Groups scale, x axis
  var x0Scale = d3.scaleBand()
		.domain(d3.range(data[0].length))
		.range([settings.padding.l, w])
		.padding(0.2);
  // Series scale, x axis
  var x1Scale = d3.scaleBand()
		.domain(d3.range(data.length))
		.range([0, x0Scale.bandwidth()]);

  // Values scale, y axis
  var yScale = d3.scaleLinear()
		.domain([0, max])
		.range([h, 0]);
	
	var xAxis = d3.axisBottom(x0Scale)
		.tickFormat(function(d, i) { return data[0][i]['label']; });

	var yAxis = d3.axisLeft(yScale)
		.tickFormat(d3.format(settings.yTickFormat));

			
  // Visualisation selection
  var vis = d3.select($(this).get(0))
		.append("svg:svg")
		.attr("width", settings.dimension.w)
		.attr("height", settings.dimension.h)
		.attr("class", "barchart");

  // Series selection
  var series = vis.selectAll("g.series")
		.data(data)
    .enter().append("svg:g")
		.attr("class", "series") // Not strictly necessary, but helpful when inspecting the DOM
		.attr("fill", function (d, i) { return d[0]['color']? d[0]['color'] : settings.color[i]; })
		.attr("transform", function (d, i) { return "translate(" + x1Scale(i) + ")"; });

  // Groups selection
  var groups = series.selectAll("rect").data(Object); // The second dimension in the two-dimensional data array
	
	groups.enter().append("a")
		.each(function(d) { if (d.value_url) d3.select(this).attr("xlink:href", d.value_url); })
		.append("svg:rect")
		.attr("x", 0)
		.attr("y", function (d) { return yScale(d.value)+settings.padding.t ; })
		.attr("width", x1Scale.bandwidth())
		.attr("height", function (d) { return h-yScale(d.value); })
		.attr("class", "bar")
		.attr("transform", function (d, i) { return "translate(" + x0Scale(i) + ")"; })
		.attr("title", function(d) { return d.value_tooltip; });
	
	// Significance bars
	if (typeof settings.significance != 'undefined') {
		var sigBars = groups.enter().append("svg:g")
			.attr("class", "significance-bar")
			.attr("transform", function (d, i) { return "translate(" + x0Scale(i) + ", "+settings.padding.t+")"; });
		sigBars.each(function(d) {
			if (d.pval<settings.significance) {
				d3.select(this).append("text")
					.attr("x", x1Scale.bandwidth()*data.length/2)
					.attr("color", 'black')
					.text("*");
				d3.select(this).append("svg:rect")
					.attr("fill", 'black')
					.attr("width", x1Scale.bandwidth()*data.length)
					.attr("height", 1);
			}
		});
	}
	
	// X axis
	vis.append("g")
		.attr("class", "x axis")
		.attr("transform", "translate(0," + (h+settings.padding.t) + ")")
		.call(xAxis)
		.selectAll(".tick text")
		.attr("transform", settings.xLabelTransform)
		.attr("text-anchor", settings.xLabelTransform.indexOf('rotate')!==-1? "end":"middle")
		.attr("title", function(d, i) { if (data[0][i]['label_tooltip']) return data[0][i]['label_tooltip']; })
		.each(function (d, i) {
			if (data[0][i]['label_url']) {
				var node = d3.select(this);
				var txt = node.text();
				node.text('');
				node.append('a').attr("xlink:href", data[0][i]['label_url']).text(txt);
			}
		});
			
	// X axis label
	vis.selectAll(".x.axis")
		.append("text")
		.attr("y", -6)
		.attr("x", w)
		.text(settings.xAxisLabel);
	
	// Y axis
	vis.append("g")
		.attr("class", "y axis")
		.attr('transform', 'translate(' + [settings.padding.l, settings.padding.t] + ')')
		.call(yAxis)
		.append("text")
		.attr("transform", "rotate(-90)")
		.attr("y", 6)
		.attr("dy", ".71em")
		.style("text-anchor", "end")
		.text(settings.yAxisLabel);
	
	// Legend
	if (settings.legend !== '') {
		
		var legend = vis.selectAll(".legend")
			.data(data)
			.enter().append("g")
			.attr("class", "legend")
			.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });

		legend.append("rect")
			.attr("x", settings.legend.x)
			.attr("y", settings.legend.y)
			.attr("width", 18)
			.attr("height", 18)
			.style("fill", function(d, i) {return d[0]['color']? d[0]['color'] : settings.color[i]; });

		legend.append("text")
			.attr("x", settings.legend.x+settings.legend.txtX)
			.attr("y", settings.legend.y+9)
			.attr("dy", ".35em")
			.style("text-anchor", settings.legend.txtAnchor)
			.text(function(d) { return d[0].name; });
	}
	return {
		vis: vis,
		settings: settings,
		h: settings.dimension.h,
		w: settings.dimension.w,
		x0: x0Scale,
		x0Min: x0Scale.domain()[0],
		x0Max: x0Scale.domain()[1],
		x1: x1Scale,
		x1Min: x1Scale.domain()[0],
		x1Max: x1Scale.domain()[1],
		y: yScale,
		yMin: yScale.domain()[0],
		yMax: yScale.domain()[1],
		color: settings.color
	};
};
//## Stacked barchart ##
$.fn.stackedChart = function(data, inSettings) {
	var extraDefaults = {
		normalize: true,
		overlapStack: false,
	};
	var settings = addDefaultPlotSettings(inSettings, extraDefaults);

	
	// Inner dimension
	var w = settings.dimension.w - settings.padding.l - settings.padding.r; // inner width
  var h = settings.dimension.h - settings.padding.t - settings.padding.b; // inner height
	
  // Groups scale, x axis
  var xScale = d3.scaleBand()
		.range([settings.padding.l, w])
		.padding(0.2);
	
  // Values scale, y axis
  var yScale = d3.scaleLinear()
		.range([h, 0]);
	
	var color = d3.scaleOrdinal()
    .range(settings.color);
	
	var xAxis = d3.axisBottom(xScale)
		.tickSize(0)
		.tickFormat(function(d, i) { return (data[i][0]['name_label'] === undefined) ? data[i][0]['name'] : data[i][0]['name_label']; });

	var yAxis = d3.axisLeft(yScale)
		.tickSize(0)
		.tickFormat(d3.format(settings.yTickFormat));
	if (settings.yNumTicks) {
		yAxis.ticks(settings.yNumTicks);
	}

	if (settings.grid) yAxis.tickSizeInner(-w+settings.padding.l);
	if (settings.normalize) yAxis.tickFormat(d3.format(".0%"));
	
	color.domain(data[0].map(function(d, i) { return d.label; }));
	
	// Calc y-positions
	var max = 0;
	var i, d;
	for (var g=0; g<data.length; g++) {
		var y0 = 0;
		for (i=0; i<data[g].length; i++) {
			d = data[g][i];
			d.y0 = y0;
			if (settings.overlapStack) d.y1 = parseFloat(d.value);
			else d.y1 = y0 += +parseFloat(d.value);
			if (max < d.y1) max = d.y1;
		}
		if (settings.normalize) {
			for (i=0; i<data[g].length; i++) {
				d = data[g][i];
				d.y0 /= y0;
				d.y1 /= y0;
			}
		}
	}
	
	xScale.domain(data.map(function(d) { return (d[0].name_label === undefined) ? d[0].name : d[0].name_label; }));
	if (!settings.normalize) {
		var yMax = (settings.minY)? Math.max(settings.minY, max) : max;
		yScale.domain([0, yMax]);
	}
	
  // Visualisation selection
  var vis = d3.select($(this).get(0))
		.append("svg")
		.attr("width", settings.dimension.w)
		.attr("height", settings.dimension.h)
		.attr("class", "chart stackedchart")
		.append("g");
	
	// Y axis
	var yTxt = vis.append("g")
		.attr("class", "y axis")
		.attr('transform', 'translate(' + [settings.padding.l, settings.padding.t] + ')')
		.call(yAxis)
		.append("text")
		.text(settings.yAxisLabel);
		if (settings.yAxisLabelAbove) {
			yTxt
				.attr("y", -8)
				.style("text-anchor", "middle");
		}
		else {
			yTxt
				.attr("transform", "rotate(-90)")
				.attr("y", 6)
				.attr("dy", ".71em")
				.style("text-anchor", "end");
		}
	
	// X axis
	vis.append("g")
		.attr("class", "x axis")
		.attr("transform", "translate(0," + (h+settings.padding.t) + ")")
		.call(xAxis)
		.selectAll(".tick text")
		.attr("transform", settings.xLabelTransform)
		.attr("text-anchor", settings.xLabelTransform.indexOf('rotate')!==-1? "end":"middle")
		.attr("title", function(d, i) { return data[i][0]['name_tooltip']; })
		.each(function(d, i) {
			if (data[i][0]['name_url']) {
				var node = d3.select(this);
				var txt = node.text();
				node.text('');
				node.append('a').attr("xlink:href", data[i][0]['name_url']).text(txt);
			}
		});

	// X axis label
	vis.selectAll(".x.axis")
		.append("text")
		.attr("y", -6)
		.attr("x", settings.dimension.w)
		.style("text-anchor", "end")
		.text(settings.xAxisLabel);
	
	var group = vis.selectAll(".group")
		.data(data)
    .enter().append("g")
		.attr("class", "group")
		.attr("transform", function(d) { return "translate(" + xScale((d[0].name_label === undefined) ? d[0].name : d[0].name_label) + ",0)"; });

  group.selectAll("rect")
		.data(Object)
    .enter().append("a")
		.each(function(d) { if (d.value_url) d3.select(this).attr("xlink:href", d.value_url); })
		.append("rect")
		.attr("width", xScale.bandwidth())
		.attr("y", function(d) { return yScale(d.y1)+settings.padding.t; })
		.attr("height", function(d) { return yScale(d.y0) - yScale(d.y1); })
		.style("fill", function(d) { return d.color? d.color : color(d.label); })
		.attr("title", function(d) { return d.value_tooltip; })
		.on("mouseover", function(d, i) {
			var r = getHighlightColor($(this).css("fill"));
			if (typeof $(this).attr("fill-old") === "undefined") {
				$(this).attr("fill-old", $(this).css("fill"));
			}
			d3.select(this).transition().style("fill", 'rgb('+r[1]+','+r[2]+','+r[3]+')');
			
    })
    .on("mouseout", function(d, i) {
			if ($(this).attr("fill-old")) {
				d3.select(this).transition().style("fill", $(this).attr("fill-old"));
			}
    });
		
	if (settings.valueLabels) {
		group.selectAll("text")
			.data(Object)
			.enter().append("text")
			.attr("class", "valueLabel")
			.style("text-anchor", "middle")
			.attr("x", function(d, i) { return xScale.bandwidth()/2; })
			.attr("y", function(d) { return yScale(d.y1)+settings.padding.t+12; })
			.text(function(d) { return d.value; });
	}

	// Legend
	if (settings.legend !== '') {
		var legend = vis.selectAll(".legend")
			.data(data[0].reverse())
			.enter().append("g")
			.attr("class", "legend")
			.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });

		legend.append("rect")
			.attr("x", settings.legend.x)
			.attr("y", settings.legend.y)
			.attr("width", 18)
			.attr("height", 18)
			.style("fill", function(d, i) {return d.color? d.color : color(d.label); });

		legend.append("text")
			.attr("x", settings.legend.x+settings.legend.txtX)
			.attr("y", settings.legend.y+9)
			.attr("dy", ".35em")
			.style("text-anchor", settings.legend.txtAnchor)
			.text(function(d) { return d.label; });
	}

	$(this).on('sort', function(e, order_func) {
		var x0 = xScale.domain(
			data.sort(order_func)
				.map(function(d) {
					return (d[0].name_label === undefined) ? d[0].name : d[0].name_label;
				})
		)
			.copy();

		var comp = function(a, b) {
			return x0(a[0].name) - x0(b[0].name);
		};
		vis.selectAll(".group").sort(comp);
		vis.selectAll(".x.axis.tick").sort(comp);

		var transition = vis.transition().duration(100);
		transition.selectAll(".group")
			.attr("transform", function(d) { return 'translate('+x0((d[0].name_label === undefined) ? d[0].name : d[0].name_label)+', 0)'; });
		transition.select(".x.axis")
			.call(xAxis);
			// .selectAll(".tick text")
	});

	return {
		vis: vis,
		settings: settings,
		h: h,
		w: w,
		x: xScale,
		xMin: xScale.domain()[0],
		xMax: xScale.domain()[1],
		y: yScale,
		yMin: yScale.domain()[0],
		yMax: yScale.domain()[1],
		color: settings.color
	};
};

//## LineBandplot ##
$.fn.lineBandPlot = function(data, inSettings) {
	var settings = addDefaultPlotSettings(inSettings);
	
	// Inner dimension
	var w = settings.dimension.w - settings.padding.l - settings.padding.r; // inner width
  var h = settings.dimension.h - settings.padding.t - settings.padding.b; // inner height
	
  // Groups scale, x axis
  var xScale = d3.scaleBand()
		.range([0, w])
		.padding(0)
		.domain(data[0].map(function(d) { return d.label; }));
	
  // Values scale, y axis
  var yScale = d3.scaleLinear()
		.range([h, 0]);
	
	var color = d3.scaleOrdinal()
    .range(settings.color);
	
	
	var xAxis = d3.axisBottom(xScale);
	if (settings.xTickFormat) xAxis.tickFormat(d3.format(settings.xTickFormat));
	if (settings.xTickValues) xAxis.tickValues(settings.xTickValues);
	
	var yAxis = d3.axisLeft(yScale)
		//.tickSize(6)
		.tickFormat(d3.format(settings.yTickFormat));
	if (settings.grid) yAxis.innerTickSize(-w+settings.padding.l);
	if (settings.yTicks) yAxis.ticks(settings.yTicks);
	
	color.domain(data[0].map(function(d, i) { return d.name; }));
	
	// Calc y-positions
	var max = 0;
	var legends = [];
	var i;
	for (var g=0; g<data.length; g++) {
		for (i=0; i<data[g].length; i++) {
			var d = data[g][i];
			if (max < parseFloat(d.value)) max = parseFloat(d.value);
			legends[d.name] = d;
		}
	}
	var lgnd = [];
	for (i in legends) {
		lgnd.push(legends[i]);
	}
	
	yScale.domain([0, max]);
	
  // Visualisation selection
  var vis = d3.select($(this).get(0))
		.append("svg")
		.attr("width", settings.dimension.w)
		.attr("height", settings.dimension.h)
		.attr("class", "chart scatterplot")
		.append("g")
		.attr("transform", "translate(" + settings.padding.l + "," + settings.padding.t + ")");
	
	var group = vis.selectAll(".group")
		.data(data)
    .enter().append("g")
		.attr("class", "group");

	group.append("path")
      .datum(Object)
      .attr("fill", "none")
      .style("stroke", function(d, i) { return d[0].color? d[0].color : color(d[0].name); })
      .attr("stroke-width", 1.5)
      .attr("d", d3.line()
        .x(function(d, i) { return (i+0.5)*xScale.bandwidth() })
        .y(function(d) { return yScale(d.value) })
			)
			.each(function(d) {
				d3.select(this).attr("class", 'yScaleDependent '+d[0].class);
				d3.select(this).attr("yMax", function() {
					var max = 0;
					for (i=0; i<d.length; i++) {
						var dv = d[i];
						if (max < parseFloat(dv.value)) max = parseFloat(dv.value);
					}
					return max;
				});
			})
	
	var markerSize = settings.markerSize? settings.markerSize : 4;
  if (settings.showMarkers) {
		group.selectAll("rect")
			.data(Object)
			.enter().append("a")
			.each(function(d) { if (d.value_url) d3.select(this).attr("xlink:href", d.value_url); })
			.append("rect")
			.attr("y", function(d) { return yScale(d.value)-markerSize/2; })
			.attr("x", function(d, i) { return (i+0.5)*xScale.bandwidth()-markerSize/2; })
			.attr("width", markerSize)
			.attr("height", markerSize)
			.style("fill", 'transparent')
			.style("stroke", function(d) { return d.color? d.color : color(d.name); })
			.attr("title", function(d) { return d.value_tooltip; })
			.on("mouseover", function(d, i) {
				d3.select(this).transition().style("fill", $(this).css("stroke"));
			})
			.on("mouseout", function(d, i) {
				d3.select(this).transition().style("fill", 'transparent');
			});
	}
		
	// Y axis
	vis.append("g")
		.attr("class", "y axis")
		.call(yAxis)
		.append("text")
		.attr("transform", "rotate(-90)")
		.attr("y", 6)
		.attr("dy", ".71em")
		.style("text-anchor", "end")
		.text(settings.yAxisLabel);
	
	// X axis
	vis.append("g")
		.attr("class", "x axis")
		.attr("transform", "translate(0," + h + ")")
		.call(xAxis)
		.selectAll(".tick text")
		.attr("transform", settings.xLabelTransform)
		.style("text-anchor", "end")
		.style("cursor", function(d, i) { return data[0][i]['label_url']? "pointer" : "auto"; })
		.on('click', function(d, i) { if (data[0][i]['label_url']) window.location.href = data[i][0]['label_url']; } )
		.attr("title", function(d, i) { return data[0][i]['label_tooltip']; });
			
	// X axis label
	vis.selectAll(".x.axis")
		.append("text")
		.attr("y", -6)
		.attr("x", settings.dimension.w)
		.style("text-anchor", "end")
		.text(settings.xAxisLabel);
		
	if (settings.valueLabels) {
		group.selectAll("text")
			.data(Object)
			.enter().append("text")
			.attr("class", "valueLabel")
			.style("text-anchor", "middle")
			.attr("x", function(d, i) { return (i+1)*xScale.bandwidth(); })
			.attr("y", function(d) { return yScale(d.value)-markerSize*1; })
			.text(function(d) { return d.value; });
	}

	// Legend
	if (settings.legend !== '') {
		var legend = vis.selectAll(".legend")
			.data(lgnd.reverse())
			.enter().append("g")
			.attr("class", "legend")
			.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });

		legend.append("rect")
			.attr("x", settings.legend.x)
			.attr("y", settings.legend.y)
			.attr("width", 18)
			.attr("height", 18)
			.style("fill", function(d, i) {return d.color? d.color : color(d.name); });

		legend.append("text")
			.attr("x", settings.legend.x+settings.legend.txtX)
			.attr("y", settings.legend.y+9)
			.attr("dy", ".35em")
			.style("text-anchor", settings.legend.txtAnchor)
			.text(function(d) { return d.name; });
	}

	var plot = {
		vis: vis,
		settings: settings,
		h: settings.dimension.h,
		w: settings.dimension.w,
		x: xScale,
		xAxis: xAxis,
		xMin: xScale.domain()[0],
		xMax: xScale.domain()[1],
		y: yScale,
		yAxis: yAxis,
		yMin: yScale.domain()[0],
		yMax: yScale.domain()[1],
		color: settings.color
	};
	
	
	$(this).on('rescale', function(e, scale) {
		var plot = $(this).data('plot');
		if (scale === 'ylin') {
			yScale = plot.y = d3.scaleLinear()
				.domain([plot.yMin, plot.yMax])
				.range([h, 0]);
			yAxis = d3.axisLeft(yScale)
				.tickFormat(d3.format(".2s"));
			vis.selectAll('.y.axis .label').text(function() { return d3.select(this).text().replace('log ', ''); });
		}
		else if (scale === 'ycurmax') {
			var yCurMax = 0;
			$(this).find('path:visible').each(function() {
				if (yCurMax < parseFloat($(this).attr('yMax'))) yCurMax = parseFloat($(this).attr('yMax'));
			});
			yScale = plot.y = d3.scaleLinear()
				.domain([plot.yMin, yCurMax])
				.range([h, 0]);
			yAxis = d3.axisLeft(yScale)
				.tickFormat(d3.format(".2s"));
			vis.selectAll('.y.axis .label').text(function() { return d3.select(this).text().replace('log ', ''); });
		}
		else return;
		
		var t = d3.transition().duration(settings.transitionTime);
		vis.selectAll(".yScaleDependent")
			.transition(t)
			.attr('transform', function() {
				var node = d3.select(this);
				node.attr("d", d3.line()
					.x(function(d, i) { return (i+0.5)*xScale.bandwidth() })
					.y(function(d) { return yScale(d.value) })
				);
			});
		vis.selectAll(".axis.x")
			.transition(t)
			.call(xAxis);
		vis.selectAll(".axis.y")
			.transition(t)
			.call(yAxis);
			
		$(this).data('plot', plot);
  });
	$(this).data('plot', plot);
	return plot;
};

//## ScatterBandplot ##
$.fn.scatterBandPlot = function(data, inSettings) {
	var settings = addDefaultPlotSettings(inSettings);
	
	// Inner dimension
	var w = settings.dimension.w - settings.padding.l - settings.padding.r; // inner width
  var h = settings.dimension.h - settings.padding.t - settings.padding.b; // inner height
	
  // Groups scale, x axis
  var xScale = d3.scaleBand()
		.range([settings.padding.l, w])
		.padding(0.2);
	
  // Values scale, y axis
  var yScale = d3.scaleLinear()
		.range([h, 0]);
	
	var color = d3.scaleOrdinal()
    .range(settings.color);
	
	var xAxis = d3.axisBottom(xScale)
		//.tickSize(0)
		.tickFormat(function(d, i) { return data[i][0]['name']; });

	var yAxis = d3.axisLeft(yScale)
		//.tickSize(6)
		.tickFormat(d3.format(settings.yTickFormat));
	if (settings.grid) yAxis.innerTickSize(-w+settings.padding.l);
	
	color.domain(data[0].map(function(d, i) { return d.label; }));
	
	// Calc y-positions
	var max = 0;
	var legends = [];
	var i;
	for (var g=0; g<data.length; g++) {
		for (i=0; i<data[g].length; i++) {
			var d = data[g][i];
			if (max < parseFloat(d.value)) max = parseFloat(d.value);
			legends[d.label] = d;
		}
	}
	var lgnd = [];
	for (i in legends) {
		lgnd.push(legends[i]);
	}
	
	xScale.domain(data.map(function(d) { return d[0].name; }));
	yScale.domain([0, max]);
	
  // Visualisation selection
  var vis = d3.select($(this).get(0))
		.append("svg")
		.attr("width", settings.dimension.w)
		.attr("height", settings.dimension.h)
		.attr("class", "chart scatterplot")
		.append("g");
	
	var group = vis.selectAll(".group")
		.data(data)
    .enter().append("g")
		.attr("class", "group")
		.attr("transform", function(d) { return "translate(" + xScale(d[0].name) + ",0)"; });

  group.selectAll("rect")
		.data(Object)
    .enter().append("a")
		.each(function(d) { if (d.value_url) d3.select(this).attr("xlink:href", d.value_url); })
		.append("rect")
		.attr("y", function(d) { return yScale(d.value)+settings.padding.t-3; })
		.attr("x", 4)
		.attr("width", 6)
		.attr("height", 6)
		.style("fill", 'transparent')
		.style("stroke", function(d) { return d.color? d.color : color(d.label); })
		.attr("title", function(d) { return d.value_tooltip; })
		.on("mouseover", function(d, i) {
			d3.select(this).transition().style("fill", $(this).css("stroke"));
    })
    .on("mouseout", function(d, i) {
			d3.select(this).transition().style("fill", 'transparent');
    });
		
	// Y axis
	vis.append("g")
		.attr("class", "y axis")
		.attr('transform', 'translate(' + [settings.padding.l, settings.padding.t] + ')')
		.call(yAxis)
		.append("text")
		.attr("transform", "rotate(-90)")
		.attr("y", 6)
		.attr("dy", ".71em")
		.style("text-anchor", "end")
		.text(settings.yAxisLabel);
	
	// X axis
	vis.append("g")
		.attr("class", "x axis")
		.attr("transform", "translate(0," + (h+settings.padding.t) + ")")
		.call(xAxis)
		.selectAll(".tick text")
		.attr("transform", settings.xLabelTransform)
		.style("text-anchor", "end")
		.style("cursor", function(d, i) { return data[i][0]['name_url']? "pointer" : "auto"; })
		.on('click', function(d, i) { if (data[i][0]['name_url']) window.location.href = data[i][0]['name_url']; } )
		.attr("title", function(d, i) { return data[i][0]['name_tooltip']; });
			
	// X axis label
	vis.selectAll(".x.axis")
		.append("text")
		.attr("y", -6)
		.attr("x", settings.dimension.w)
		.style("text-anchor", "end")
		.text(settings.xAxisLabel);
		
	if (settings.valueLabels) {
		group.selectAll("text")
			.data(Object)
			.enter().append("text")
			.attr("class", "valueLabel")
			.style("text-anchor", "middle")
			.attr("x", function(d, i) { return xScale.bandwidth()/2; })
			.attr("y", function(d) { return yScale(d.value)+settings.padding.t+12; })
			.text(function(d) { return d.value; });
	}

	// Legend
	if (settings.legend !== '') {
		var legend = vis.selectAll(".legend")
			.data(lgnd.reverse())
			.enter().append("g")
			.attr("class", "legend")
			.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });

		legend.append("rect")
			.attr("x", settings.legend.x)
			.attr("y", settings.legend.y)
			.attr("width", 18)
			.attr("height", 18)
			.style("fill", function(d, i) {return d.color? d.color : settings.color[i]; });

		legend.append("text")
			.attr("x", settings.legend.x+settings.legend.txtX)
			.attr("y", settings.legend.y+9)
			.attr("dy", ".35em")
			.style("text-anchor", settings.legend.txtAnchor)
			.text(function(d) { return d.label; });
	}

	return {
		vis: vis,
		settings: settings,
		h: settings.dimension.h,
		w: settings.dimension.w,
		x: xScale,
		xMin: xScale.domain()[0],
		xMax: xScale.domain()[1],
		y: yScale,
		yMin: yScale.domain()[0],
		yMax: yScale.domain()[1],
		color: settings.color
	};
};

//## ScatterPlot ##
$.fn.scatterPlot = function(data, inSettings) {
	var plot = {};
	var extraDefaults = {
		xMin: false,
		xMax: false,
		yMin: false,
		yMax: false,
		xTickFormat: ".2s",
		xNumMaxTicks: 10,
		xNumMinTicks: 4,
		yNumMaxTicks: 10,
		yNumMinTicks: 4,
		transitionTime: 1000,
		origoAxis: false,
		objectSize: 15,
	};
	var settings = addDefaultPlotSettings(inSettings, extraDefaults);
	plot.settings = settings;
	
	// Inner dimension
	var w = plot.w = settings.dimension.w - settings.padding.l - settings.padding.r; // inner width
  var h = plot.h = settings.dimension.h - settings.padding.t - settings.padding.b; // inner height
	
	var xMin = Infinity;
	var xMax = -Infinity;
	var yMin = Infinity;
	var yMax = -Infinity;
	for (var i in data) {
		if (!data.hasOwnProperty(i)) {
			continue;
		}
		for (var j in data[i].data) {
			if (!data[i].data.hasOwnProperty(j)) {
				continue;
			}
			xMin = settings.xMin===false? Math.min(xMin, data[i].data[j].x) : settings.xMin;
			xMax = settings.xMax===false? Math.max(xMax, data[i].data[j].x) : settings.xMax;
			yMin = settings.yMin===false? Math.min(yMin, data[i].data[j].y) : settings.yMin;
			yMax = settings.yMax===false? Math.max(yMax, data[i].data[j].y) : settings.yMax;
		}
	}
	//plot.data = data; 
	plot.xMin = xMin; plot.xMax = xMax; plot.yMin = yMin; plot.yMax = yMax;
	
	var color = plot.color = d3.scaleOrdinal()
    .range(settings.color)
		.domain(d3.map(data, function(d, i) { return i; }));
	
  // X axis
  var xScale = plot.x = d3.scaleLinear()
		.domain([xMin, xMax])
		.range([0, w]);
	
  // Y axis
  var yScale = plot.y = d3.scaleLinear()
		.domain([yMin, yMax])
		.range([h, 0]);

	var xAxis = d3.axisBottom(xScale)
		.tickFormat(d3.format(settings.xTickFormat))
		.ticks(Math.min(Math.max(Math.ceil(xMax), settings.xNumMinTicks), settings.xNumMaxTicks));

	var yAxis = d3.axisLeft(yScale)
		.tickFormat(d3.format(settings.yTickFormat))
		.ticks(Math.min(Math.max(Math.ceil(yMax), settings.yNumMinTicks), settings.yNumMaxTicks));
	if (settings.grid) yAxis.tickSizeInner(-w);
	
  // Visualisation selection
  var vis = plot.vis = d3.select($(this).get(0))
		.append("svg")
		.attr("width", settings.dimension.w)
		.attr("height", settings.dimension.h)
		.attr("class", "chart scatterplot")
		.append("g")
		.attr("transform", "translate(" + settings.padding.l + "," + settings.padding.t + ")");
	
	// Plot area background
	vis.append("rect")
		.attr("class", "plotBG")
		.attr("width", xScale(xMax))
		.attr("height", yScale(0));
	
	var group = vis.selectAll(".group")
		.data(data)
    .enter().append("g")
		.attr("class", function(d, i) { return "group group"+i; })
		.attr("name", function(d, i) { return "group"+i; })
		.style("fill", function(d, i) { return d.color? d.color : color(i); })
		.each(function(d) {
			if (d.data) {
				d3.select(this).selectAll('.dot')
					.data(d.data)
					.enter()
					.append("a")
					.attr("class", function(d) { return d.class?"dot "+d.class:"dot"; })
					.attr('transform', function(d) { return 'translate(' + [xScale(d.x), yScale(d.y)] + ')'; })
					.each(function(d) {
						if (d.url) d3.select(this).attr("xlink:href", d.url);
						if (d.tooltip) d3.select(this).attr("title", d.tooltip);
						if (settings.valueLabels) d3.select(this).append('text').text(d.x+','+d.y);
						if (settings.objectLabels) d3.select(this).append('text').text(d.label).style("text-anchor", "middle").attr('y', -5);
						if (d.color) d3.select(this).style("fill", d.color);
					})
					.append("path")
					.attr('d', d3.symbol().size(d.size||settings.objectSize).type(getD3Symbol(d.symbol)));
			}
		});
		
	// Add larger tooltip area behind dot
	group.selectAll("a")
		.append("circle")
		.attr("r", 4)
		.style("fill", 'transparent');

	
	// Y axis
	if (settings.yAxisLabel) {
		vis.append("g")
			.attr("class", "y axis")
			.call(yAxis)
			.append("text")
			.attr("transform", "rotate(-90)")
			.attr("y", 10)
			.attr("x", -2)
			.attr("class", "label")
			.style("text-anchor", "end")
			.text(settings.yAxisLabel);
		if (settings.origoAxis) {
			vis.selectAll('g.y.axis')
				.attr("transform", "translate(" + xScale(0) + ", 0)");
		}
	}
	
	// X axis
	if (settings.xAxisLabel) {
		vis.append("g")
			.attr("class", "x axis")
			.attr("transform", "translate(0," + h + ")")
			.call(xAxis)
			.append("text")
			.attr("y",-2)
			.attr("x", w-2)
			.attr("class", "label")
			.style("text-anchor", "end")
			.text(settings.xAxisLabel);
		if (settings.origoAxis) {
			vis.selectAll('g.x.axis')
				.attr("transform", "translate(0," + yScale(0) + ")");
		}
	}
	
	// Legend
	if (settings.legend !== '') {
		var legend = vis.selectAll(".legend")
			.data(data)
			.enter().append("g")
			.attr("class", function(d, i) { return "legend legend"+i; })
			.attr("name", function(d, i) { return "legend"+i; })
			.attr("transform", function(d, i) { return "translate("+settings.legend.x+"," + (settings.legend.y + i * (settings.legend.size+2)) + ")"; });

		legend.append("rect")
			.attr("width", settings.legend.size)
			.attr("height", settings.legend.size)
			.style("fill", function(d, i) { return d.color? d.color : color(i); });

		legend.append("text")
			.attr("x", settings.legend.size+2)
			.attr("y", settings.legend.size/2)
			.attr("dy", ".35em")
			.text(function(d) { return d.name; });
	}
	
	$(this).on('rescale', function(e, scale) {
		var plot = $(this).data('plot');
		if (scale === 'xlog') {
			xScale = plot.x = d3.scaleLog()
				.domain([Math.max(0.0001, xMin), xMax])
				.range([0, w]);
			xAxis = d3.axisBottom(xScale)
				.tickValues([1e-3,1e-2,1e-1,1e0,1e1,1e2,1e3].filter(function(d) {
					return (d < xMax);
				}));
			vis.selectAll('.x.axis .label').text(function() { return 'log '+d3.select(this).text(); });
		}
		else if (scale === 'xlin') {
			xScale = plot.x = d3.scaleLinear()
				.domain([xMin, xMax])
				.range([0, w]);
			xAxis = d3.axisBottom(xScale)
				.tickFormat(d3.format(".2s"));
			vis.selectAll('.x.axis .label').text(function() { return d3.select(this).text().replace('log ', ''); });
		}
		else if (scale === 'ylog') {
			yScale = plot.y = d3.scaleLog()
				.domain([Math.max(0.0001, yMin), yMax])
				.range([h, 0]);
			yAxis = d3.axisLeft(yScale)
				.tickValues([1e-3,1e-2,1e-1,1e0,1e1,1e2,1e3].filter(function(d) {
					return (d < yMax);
				}));
			vis.selectAll('.y.axis .label').text(function() { return 'log '+d3.select(this).text(); });
		}
		else if (scale === 'ylin') {
			yScale = plot.y = d3.scaleLinear()
				.domain([yMin, yMax])
				.range([h, 0]);
			yAxis = d3.axisLeft(yScale)
				.tickFormat(d3.format(".2s"));
			vis.selectAll('.y.axis .label').text(function() { return d3.select(this).text().replace('log ', ''); });
		}
		else return;
		
		var t = d3.transition().duration(settings.transitionTime);
		group.each(function(d) {
			d3.select(this).selectAll('.dot')
				.data(d.data)
				.transition(t)
				.attr('transform', function(d) { var xp = xScale(d.x); var yp = yScale(d.y); return (xp==-Infinity || yp==-Infinity)? 'translate(0,'+h+')' : 'translate(' + [xp, yp] + ')'; });
		});
		vis.selectAll(".xScaleDependent")
			.transition(t)
			.attr('transform', function() {
				var node = d3.select(this);
				if (node.classed('yScaleDependent')) return 'translate('+xScale(node.attr('xVal'))+', '+yScale(node.attr('yVal'))+')';
				else return 'translate('+xScale(node.attr('xVal'))+')';
			});
		vis.selectAll(".axis.x")
			.transition(t)
			.call(xAxis);
		vis.selectAll(".axis.y")
			.transition(t)
			.call(yAxis);
			
		$(this).data('plot', plot);
  });
	$(this).data('plot', plot);
	return plot;
};

//## VennDiagram ##
$.fn.vennDiagram = function(data, inSettings) {
	var plot = {};
	var settings = addDefaultPlotSettings(inSettings);
  plot.settings = settings;
	
	// Inner dimension
	var w = settings.dimension.w - settings.padding.l - settings.padding.r; // inner width
  var h = settings.dimension.h - settings.padding.t - settings.padding.b; // inner height
	
	//var vis = plot.vis = d3.select($(this).get(0)).datum(d).call(chart);
  // Visualisation selection
  var vis = plot.vis = d3.select($(this).get(0))
		.append("svg")
		.attr("width", settings.dimension.w)
		.attr("height", settings.dimension.h)
		.attr("class", "chart venn")
		.append("g")
		.attr("transform", "translate(" + settings.padding.l + "," + settings.padding.t + ")");
	var chart = venn.VennDiagram().width(w).height(h);
	vis.datum(data).call(chart);
	
	// circle style
	vis.selectAll(".venn-circle path")
		.style("fill", function(d, i) { return d.color? d.color : settings.color[i]; })
		.style("fill-opacity", 0.5)
		.attr("title", function(d, i) { return d.tooltip? d.tooltip : ""; });
		
	// Links
	vis.selectAll("text.label")
		.attr("style", "")
		.each(function(d, i) {
			if (d.url) {
				var t = d3.select(this).selectAll('tspan').text();
				d3.select(this)
					.append('a')
					.attr("xlink:href", d.url)
					.text(t);
				d3.select(this).selectAll('tspan').remove();
			}
			if (d.tooltip) {
				d3.select(this)
					.attr('title', d.tooltip);
			}
		});
	
	//return plot;
	
	// Legend
	if (settings.legend !== '') {
		var legend = vis.selectAll(".legend")
			.data(data)
			.enter()
			.filter(function(d) { return (d.sets.length == 1); })
			.append("g")
			.attr("class", function(d, i) { return "legend legend"+i; })
			.attr("name", function(d, i) { return "legend"+i; })
			.attr("transform", function(d, i) { return "translate(0," + i * (settings.legend.size+2) + ")"; });

		legend.append("rect")
			.attr("x", settings.legend.x)
			.attr("y", settings.legend.y)
			.attr("width", settings.legend.size)
			.attr("height", settings.legend.size)
			.style("fill", function(d, i) { return d.color? d.color : settings.color[i]; })
			.style("fill-opacity", 0.5);

		legend.append("text")
			.attr("x", settings.legend.x+settings.legend.size+2)
			.attr("y", settings.legend.y+settings.legend.size/2)
			.attr("dy", ".35em")
			.text(function(d) { return d.title; });
	}
	return plot;
};

//## pieChart ##
$.fn.pieChart = function(data, inSettings) {
	var plot = {};
	var extraDefaults = {
		valueLabels: true,
		labelRadiusOffset: 10,
		labelOffset: 3,
		labelSpacing: 10,
	};
	var settings = addDefaultPlotSettings(inSettings, extraDefaults);
	plot.settings = settings;
	
	// Inner dimension
	var w = settings.dimension.w - settings.padding.l - settings.padding.r; // inner width
  var h = settings.dimension.h - settings.padding.t - settings.padding.b; // inner height
	var radius = (Math.min(h, w) / 2 );
	
	// Piechart
	var arc = d3.arc()
		.outerRadius(radius)
		.innerRadius(0);

	var pie = d3.pie()
		.sort(null)
		.value(function(d) { return d.value; });

	var vis = plot.vis = d3.select($(this).get(0))
		.append("svg")
		.attr("width", settings.dimension.w)
		.attr("height", settings.dimension.h)
		.attr("class", "chart pie")
		.append("g")
		.attr("transform", "translate(" + (radius+settings.padding.l) + "," + (radius+settings.padding.t) + ")");
	
	//make sure "value" is a number:
	data.forEach(function(d) {
		d.value = +d.value;
	});
	var piedata = pie(data);
	
	var pieg = vis.selectAll()
		.data(piedata)
		.enter()
		.append("g")
			.attr("class", "sector")
			.attr("title", function(d) { return d.data.tooltip? d.data.tooltip : ''; })
			.on("mouseover", function(d, i) {
				var p = d3.select(this).selectAll('path');
				var r = getHighlightColor(p.style('fill'));
				if (!p.attr("fill-old")) {
					p.attr("fill-old", p.style("fill"));
				}
				p.transition().style("fill", 'rgb('+r[1]+','+r[2]+','+r[3]+')');
			})
			.on("mouseout", function(d, i) {
				var p = d3.select(this).selectAll('path');
				if (p.attr("fill-old")) {
					p.transition().style("fill", p.attr("fill-old"));
				}
			});
	
	var glink = pieg
		.append("a").each(function(d, i) {
			if (d.data.url) d3.select(this).attr("xlink:href", d.data.url);
			d3.select(this)
				.append("path")
				.attr("d", arc)
				.style("fill", d.data.color? d.data.color : settings.color[i])
				.style("stroke", "black")
				.style("stroke-width", "0.5");
			if (settings.valueLabels) {
				var c = arc.centroid(d),
						h = Math.sqrt(c[0]*c[0] + c[1]*c[1]),
						modifier = Math.round((d.data.value.toString().length)*0.5),
						midAngle = Math.atan2(c[1], c[0]),
						x = Math.cos(midAngle) * (radius+settings.labelRadiusOffset);
				d3.select(this)
					.append("text")
						.text(d.data.value)
						.attr('x', x + (settings.labelOffset * ((x > 0) ? 1 : -1)))
						.attr('y', Math.sin(midAngle) * (radius+settings.labelRadiusOffset))
						.attr('text-anchor', (x > 0) ? "start" : "end")
						.attr('alignment-baseline', "middle")
						.attr('class', 'label-text');
				d3.select(this)
					.append("line")
						.attr('x1', Math.cos(midAngle) * radius)
						.attr('y1', Math.sin(midAngle) * radius)
						.attr('x2', Math.cos(midAngle) * (radius+settings.labelRadiusOffset))
						.attr('y2', Math.sin(midAngle) * (radius+settings.labelRadiusOffset))
						.attr("stroke", "black")
						.attr("stroke-width", "0.7")
						.attr('class', "label-line");
			}
		});

	var textLabels = glink.select('text');
	var labelLines = glink.select('line');
	var alpha = 0.5;
	function relaxPieLabels() {
		var again = false;
		textLabels.each(function (d, i) {
			var a = this;
			var da = d3.select(a);
			var y1 = da.attr("y");
			textLabels.each(function (d, j) {
				var b = this;
				// a & b are the same element and don't collide.
				if (a == b) return;
				var db = d3.select(b);
				// a & b are on opposite sides of the chart and don't collide
				if (da.attr("text-anchor") != db.attr("text-anchor")) return;
				// Now let's calculate the distance between these elements.
				var y2 = db.attr("y");
				var deltaY = y1 - y2;
				// Our spacing is greater than our specified spacing, so they don't collide.
				if (Math.abs(deltaY) > settings.labelSpacing) return;
				// If the labels collide, we'll push each of the two labels up and down a little bit.
				again = true;
				var sign = deltaY > 0 ? 1 : -1;
				var adjust = sign * alpha;
				da.attr("y",+y1 + adjust);
				db.attr("y",+y2 - adjust);
			});
		});
		// Adjust lines so that they follow the labels.
		if (again) {
			labelLines.attr("y2",function(d,i) {
				return d3.select(textLabels.nodes()[i]).attr("y");
			});
			setTimeout(relaxPieLabels, 20);
		}
	}
	relaxPieLabels();
	
	// Legend
	if (settings.legend !== '') {
		var legend = vis.selectAll(".legend")
			.data(data)
			.enter().append("g")
			.attr("class", "legend")
			.attr("transform", function(d, i) { return "translate(0," + i * (settings.legend.size+2) + ")"; });

		var links = legend.append("a")
			.attr("href", function(d) {return d.url ? d.url : "";})
			.attr("xlink:href", function(d) {return d.url ? d.url : "";});
		links.append("rect")
			.attr("x", settings.legend.x-radius)
			.attr("y", settings.legend.y-radius)
			.attr("width", settings.legend.size)
			.attr("height", settings.legend.size)
			.style("fill", function(d, i) {return d.color? d.color : settings.color[i]; });

		links.append("text")
			.attr("x", settings.legend.x+settings.legend.txtX-radius)
			.attr("y", settings.legend.y+(settings.legend.size/2)-radius)
			.attr("dy", ".35em")
			.style("text-anchor", settings.legend.txtAnchor)
			.text(function(d) { return d.label; });
	}

	return {
		vis: vis,
		settings: settings,
		h: settings.dimension.h,
		w: settings.dimension.w,
		color: settings.color
	};
};


//## kaplanMeierPlot ##
$.fn.kaplanMeierPlot = function(d, inSettings) {
	var plot = {};
	var extraDefaults = {
		showCensor: 6
	};
	var settings = addDefaultPlotSettings(inSettings, extraDefaults);
	plot.settings = settings;
	// Inner dimension
	var w = plot.w = settings.dimension.w - settings.padding.l - settings.padding.r; // inner width
  var h = plot.h = settings.dimension.h - settings.padding.t - settings.padding.b; // inner height
	
	var data = d.data;
	var groups = d.groups;
	var xMin = 0;
	var yMin = 0;
	var yMax = 1;
	
	// Y axis
  var yScale = plot.y = d3.scaleLinear()
		.domain([yMin, yMax])
		.range([h, 0]);
	
	plot.fixData = function(data, y, showCensor) {
		var xMax = 0;
		var censorY = y.invert(h-showCensor/2);
		var last_d;
		for (var i=0; i<data.length; i++) {
			last_d = {
				t: 0,
				e: false,
				s: 1,
				n: null,
				d: 0,
				rate: null
			};
			for (var j=0; j<data[i].length; j++) {
				var d = data[i][j];
				var jp = 0;
				xMax = Math.max(xMax, d.t);
				if (j === 0) {
					last_d.n = d.n;
					// Is this Object.assign actually necessary here?
					var ld = Object.assign({}, last_d);
					data[i].splice(j, 0, ld);
					j++;
				}
				// Vertical steps
				if (last_d && last_d.t != d.t && last_d.s != d.s) {
					var nd = Object.assign({}, last_d);
					nd.t = d.t;
					nd.d = 1;
					data[i].splice(j, 0, nd);
					jp++;
				}
				// Censors
				if (showCensor && d.d == 0) {
					var cd1 = Object.assign({}, d);
					cd1.s += censorY;
					data[i].splice(j+1, 0, cd1);
					var cd2 = Object.assign({}, d);
					cd2.s -= censorY;
					data[i].splice(j+2, 0, cd2);
					var d2 = Object.assign({}, d);
					data[i].splice(j+3, 0, d2);
					jp += 3;
				}
				j += jp;
				last_d = d;
			}
		}
		return xMax;
	};
	var xMax = plot.fixData(data, yScale, settings.showCensor);
  
	// X axis
  var xScale = plot.x = d3.scaleLinear()
		.domain([xMin, xMax]) 
		.range([0, w]);
	
	var ticks = [];
	for (var i=0; i<=xMax; i++) {
		ticks.push(i);
	}
	var xAxis = plot.xAxis = d3.axisBottom(xScale)
		.tickValues(ticks)
		.tickFormat(function(days) {
			return d3.format("2.0f")(days);
		});

	var yAxis = plot.yAxis = d3.axisLeft(yScale);
		//.tickFormat(d3.format(".2s"));
	if (settings.grid) yAxis.tickSizeInner(-w);
	if (settings.noTicks) {
		yAxis.tickSize(0);
		xAxis.tickSize(0);
	}

	var line = plot.line = d3.line()
		.x(function(d) { return xScale(d.t); })
		.y(function(d) { return yScale(d.s); });
	
  // Visualisation selection
  var vis = plot.vis = d3.select($(this).get(0))
		.append("svg")
		.attr("width", settings.dimension.w)
		.attr("height", settings.dimension.h)
		.attr("class", "chart kaplanplot")
		.append("g")
		.attr("transform", "translate(" + settings.padding.l + "," + settings.padding.t + ")");
	
	// Plot area background
	vis.append("rect")
		.attr("class", "plotBG")
		.attr("width", xScale(xMax))
		.attr("height", yScale(yMin));
	
	var group = vis.selectAll(".group")
		.data(data)
    .enter()
		.append("g") 
		.attr("class", function(d, i) { return "group group"+i; })
		.attr("name", function(d, i) { return "group"+i; })
		.style("fill", "transparent");
	group.append("path")
		.attr('d', function(d) { return line(d); });
	
	// Y axis
	vis.append("g")
		.attr("class", "y axis")
		.call(yAxis)
		.append("text")
		.attr("transform", "translate(10, "+(h-2)+") rotate(-90)")
		.style("text-anchor", "start")
		.text(settings.yAxisLabel);
	
	// X axis
	vis.append("g")
		.attr("class", "x axis")
		.attr("transform", "translate(0," + h + ")")
		.call(xAxis)
		.append("text")
		.attr("y",-2)
		.attr("x", w)
		.style("text-anchor", "end")
		.text(settings.xAxisLabel);
	
	// Legend
	if (settings.legend !== '') {
		var legend = vis.selectAll(".legend")
			.data(groups)
			.enter().append("g")
			.attr("class", function(d, i) { return "legend legend"+i; })
			.attr("name", function(d, i) { return "legend"+i; })
			.attr("transform", function(d, i) { return "translate("+settings.legend.x+"," + (settings.legend.y + i * (settings.legend.size+2)) + ")"; });

		legend.append("rect")
			.attr("width", settings.legend.size)
			.attr("height", settings.legend.size);
		
		legend.append("text")
			.attr("x", settings.legend.size+2)
			.attr("y", settings.legend.size/2)
			.attr("dy", ".35em")
			.text(function(d, i) { return d.name; });
	}
	return plot;
};

//## Boxplot ##
$.fn.boxPlot = function(d_in, inSettings) {
	var extraDefaults = {
		xLabelTransform: 'translate(0,5)',
		yAxisFormat: '.2s',
		boxLabels: true,
		showOutliers: true,
		xPadding: 0.4,
		yMin: false,
		yMax: false,
		yAxisLabelPos: "insideTop",
		medianTooltip: true,
		medianUnitTooltip: true,
		samplesTooltip: true,
		outlierFormat: '.1f'
	};
	var settings = addDefaultPlotSettings(inSettings, extraDefaults);

	var width = settings.dimension.w - settings.padding.l - settings.padding.r;
	var height = settings.dimension.h - settings.padding.t - settings.padding.b;
	
	var data = [];
	var c = 0;
	for (var i in d_in) {
		if (!d_in.hasOwnProperty(i)) {
			continue;
		}
		data[c] = [];
		data[c][1] = d_in[i].data;
		data[c][0] = d_in[i];
		data[c][0].name = i;
		data[c][0].url = (d_in[i].url === undefined) ? '' : d_in[i].url;
		data[c][0].name_label = (d_in[i].name_label === undefined) ? i : d_in[i].name_label; 
		c++;
	}
	
	var chart = d3.box()
		.whiskers(iqr(1.5))
		.height(height)	
		.showLabels(settings.boxLabels)
		.showOutliers(settings.showOutliers)
		.tickFormat(d3.format(settings.outlierFormat))
		;
	if (settings.yMin !== false) {
		chart.minVal(settings.yMin);
	}
	if (settings.yMax !== false) {
		chart.maxVal(settings.yMax);
	}
	
	var svg = d3.select($(this).get(0)).append("svg")
		.attr("width", settings.dimension.w)
		.attr("height", settings.dimension.h)
		.attr("class", "boxplot")    
		.append("g")
		.attr("transform", "translate(" + settings.padding.l + "," + settings.padding.t + ")");
	
	// the x-axis
	var xScale = d3.scaleBand()
		.domain(data.map(function(d) { return d[0].name_label; }))
		.range([0, width])
		.padding(settings.xPadding); 		

	var xAxis = d3.axisBottom(xScale)
		.tickSize(0);

	// draw the boxplots
	var tooltips = [];
	svg.selectAll(".boxplot")
		.data(data)
		.enter().append("g")
		.attr("class", "group")
		.attr("transform", function(d) { return "translate(" +  xScale(d[0].name_label) + ")"; } )
		.append('a')
		.call(chart.width(xScale.bandwidth()))
		.each(function(d, i) {
			var tooltip = d[0].tooltip? d[0].tooltip : d[0].name;
			if (settings.medianTooltip) tooltip += '<br>Median: '+Math.round(d['quartileData'][1]*10)/10;
			if (settings.medianUnitTooltip) tooltip += ' '+settings.yAxisLabel;
			if (settings.samplesTooltip) tooltip += '<br>Samples: '+d[1].length;
			d3.select(this)
				.attr("title", tooltip)
				.attr("xlink:href", function(d) { return d[0].url; })
				.attr("class", function(d) { return d[0].clss; });
			if (d[0].color) {
				d3.select(this).selectAll('rect').style('fill', d[0].color);
			}
			tooltips[i] = tooltip;
		});
		
	
	// the y-axis
	var y = d3.scaleLinear()
		.domain([
			chart.minVal(),
			chart.maxVal()
		])
		.range([height, 0]);
	var yAxis;
	if (settings.yAxisFormat === 'auto') {
		yAxis = d3.axisLeft(y)
			.scale(y);
	} else {
		yAxis = d3.axisLeft(y)
			.tickFormat(d3.format(settings.yAxisFormat));
	}
	
	// Plot area background
	svg.insert("rect", ":first-child")
		.attr("class", "plotBG")
		.attr("width", xScale.step() * (data.length + xScale.paddingOuter()))
		.attr("height", y(settings.yMin!==false? settings.yMin : chart.minVal()));
	
	// draw y axis
	var yAxisDrawn = svg.append("g")
		.attr("class", "y axis")
		.call(yAxis);

	// draw y axis Label
	var textTransform = "rotate(-90)";
	var textAnchor = "end";
	var textPosX = 0;
	var textPosY = 10;
	if (settings.yAxisLabelPos === "outsideMiddle") {
		// Get the middle point of the axis from the range of the scale  
		var yAxisRange = yAxis.scale().range();
		textPosX = Math.abs(yAxisRange[0]-yAxisRange[1])/2 + Math.min(yAxisRange[0], yAxisRange[1]);

		textTransform = "translate("+yAxisRange[1]+","+yAxisRange[0]+") " + textTransform;
		textAnchor = "middle";
		// Align the label outside the tick labels based on the tick labels bounding boxes and add some extra padding.
		textPosY = -10 - Math.ceil(d3.max(yAxisDrawn.selectAll("g.tick").nodes(), function(item) {return item.getBBox().width;}));
	}
	var yAxisLabel = yAxisDrawn.append("text")
		.attr("transform", textTransform)
		.attr("y", textPosY)
		.attr("x", textPosX)
		.style("text-anchor", textAnchor);
	if (settings.yAxisLabel.indexOf("\n") !== -1) {
		var yLabels = settings.yAxisLabel.split("\n");
		var dy = -1 * (yLabels.length-1);
		for (var idx in yLabels) {
			yAxisLabel.append("tspan")
				.attr("x", textPosX)
				.attr("dy", dy+"em")
				.text(yLabels[idx]);
			dy = 1.2;
		}
	} else {
		yAxisLabel.text(settings.yAxisLabel);
	}

	// draw x axis
	svg.append("g")
		.attr("class", "x axis")
		.attr("transform", "translate(0," + height + ")")
		.call(xAxis)
		.append("text")
		.attr("x", width)
		.attr("y",  -2)
		.style("text-anchor", "end")
		.text(settings.xAxisLabel);
	svg.selectAll('.x.axis .tick text')
		.attr("transform", settings.xLabelTransform)
		.attr("text-anchor", settings.xLabelTransform.indexOf('rotate')!==-1? "end":"middle")
		.each(function (name, i) {
			var node = d3.select(this);
			if (tooltips[i]) {
				node.attr('title', tooltips[i]);
			}
			if (data[i][0].url) {
				var txt = node.text();
				node.text('');
				node.append('a').attr("xlink:href", data[i][0]['url']).text(txt);
			}
		});
	
	$(this).on('sort', function(e, order_func) {
		var x0 = xScale.domain(
			data.sort(order_func)
				.map(function(d) {return d[0].name_label;})
		)
			.copy();

		var comp = function(a, b) {
			return x0(a[0].name) - x0(b[0].name);
		};
		svg.selectAll("g.group").sort(comp);
		svg.selectAll(".x.axis.tick").sort(comp);

		var transition = svg.transition().duration(100);
		transition.selectAll("g.group")
			.attr("transform", function(d) {
				return 'translate('+x0(d[0].name_label)+', 0)';
			});
		transition.select(".x.axis")
			.call(xAxis);
			// .selectAll(".tick text")
	});
	
	return {
		vis: svg,
		settings: settings,
		h: height,
		w: width,
		x: xScale,
		xMin: xScale.domain()[0],
		xMax: xScale.domain()[1],
		y: y,
		yMin: y.domain()[0],
		yMax: y.domain()[1],
		color: settings.color,
		xAxis: xAxis,
		yAxis: yAxis
		
	};
};
// Returns a function to compute the interquartile range.
function iqr(k) {
  return function(d, i) {
    var q1 = d.quartiles[0],
        q3 = d.quartiles[2],
        iqr = (q3 - q1) * k,
        idx = -1,
        j = d.length;
    while (d[++idx] < q1 - iqr);
    while (d[--j] > q3 + iqr);
    return [idx, j];
  };
}

//## Double sided bar chart with three columns: left bar, label, right bar ##
$.fn.doubleBarChart = function(d, inSettings) {
	var extraDefaults = {
		leftColLabel: '',
		middleColLabel: '',
		rightColLabel: '',
		barHeight: 10,
		dimension: {w: 630, h: 500, col_w: 200, middle_col_w: 150},
		xMax: false,
		replaceInUrlStr: false,
		paddingInner: 0.65,
		valueLabelPos: 'outside', // [inside|outside]
		valueLabelsPadding: 10,
		logScale: false,
		wrapLongTexts: false,
	};
	var settings = addDefaultPlotSettings(inSettings, extraDefaults);

	// Inner dimension
	var w = settings.dimension.w - settings.padding.l - settings.padding.r; // inner width
  var h = settings.dimension.h - settings.padding.t - settings.padding.b; // inner height
	var colWidth = settings.dimension.col_w;
	var rightOffset = settings.padding.l + colWidth + settings.dimension.middle_col_w;
	var horiz_translate_left = (settings.dimension.w - colWidth - rightOffset - settings.padding.l)/2;
	
	var xMax = 0;
	var c = 0;
	var data = [];
	var legend_data = [];
	for (var i in d) {
		if (!d.hasOwnProperty(i)) {
			continue;
		}
		for (var i2 in d[i]) {
			if (!d[i].hasOwnProperty(i2)) {
				continue;
			}
			xMax = settings.xMax===false? Math.max(xMax, d[i][i2].value) : settings.xMax;
			var c3 = d[i][i2].legend;
			legend_data[c3] = [];
			legend_data[c3]['legend'] = d[i][i2].legend;
			legend_data[c3]['color'] = d[i][i2].color;
		}
		data[c] = d[i];
		data[c].name = i;
		c++;
	}
	legend_data = Object.values(legend_data);

	var xScale = settings.logScale? d3.scaleLog().base(2).domain([1, xMax]) : d3.scaleLinear().domain([0, xMax]);
	xScale.range([0, colWidth]);
	
	var yScale = d3.scaleBand()
		.rangeRound([0, h])
		.paddingInner(settings.paddingInner)
		.domain(data.map(function (d) { return d.name; }));

	var yPosByIndex = function (d, t, elem) {
		var y = 0;
		if (d['left2']) {
			y = yScale(d.name) - yScale.bandwidth()/2;
		}
		else {
			y = yScale(d.name);
		}

		if (elem == 'rect') {
			if (t.indexOf('2') != -1) y += yScale.bandwidth();
		}
		else {
			if (t.indexOf('2') != -1) y += yScale.bandwidth() * 1.5;
			else y += yScale.bandwidth() / 2;
		}
		return y;
	};
	var xPosByType = function (d, t, elem) {
		var x = 0;
		var xV = xScale(d[t].value);
		if (Math.abs(xV) == Infinity) xV = 0;
		if (t.indexOf('left')!=-1) {
			x = colWidth - xV;
			if (elem == 'text') {
				if (settings.valueLabelPos == 'inside') x = colWidth + settings.valueLabelsPadding;
				else x -= settings.valueLabelsPadding;
			}
		}
		else {
			x = rightOffset;
			if (elem == 'text') {
				if (settings.valueLabelPos == 'inside') x = rightOffset - settings.valueLabelsPadding;
				else x += xV + settings.valueLabelsPadding;
			}
		}
		return x;
	};

	var valueLabelPos = function(t) {
		if (settings.valueLabelPos=='inside') {
			return t.indexOf('left')!=-1? "start" : "end";
		}
		else {
			return t.indexOf('left')!=-1? "end" : "start";
		}
	};

  var vis = d3.select($(this).get(0))
		.append("svg")
		.attr("width", settings.dimension.w)
		.attr("height", settings.dimension.h)
		.attr("class", "chart barchart double")
		.append("g")
		.attr("transform", "translate(" + horiz_translate_left + "," + settings.padding.t + ")");

	['left', 'left2', 'right', 'right2'].forEach((t) => {
		vis.selectAll("rect."+t)
			.data(data)
			.enter().append("a")
			.each(function (d, i) {
				if (d[t]) {
					d3.select(this)
						.attr("xlink:href", d[t].url)
						.append("rect")
						.attr("x", xPosByType(d, t, 'rect'))
						.attr("y", yPosByIndex(d, t, 'rect'))
						.attr("class", "bar")
						.attr("width", Math.abs(xScale(d[t].value))==Infinity? 0 : xScale(d[t].value))
						.attr("height", yScale.bandwidth())
						.attr("name", "group"+i)
						.style("fill", d[t].color)
						.attr("title", d[t].tooltip);
				}
			});
			if (settings.valueLabels) {
				vis.append("g")
					.selectAll("text.lables_"+t)
					.data(data)
					.enter().append("a")
					.each(function (d, i) {
						if (d[t]) {
							d3.select(this)
								.attr("xlink:href", d[t].url)
								.append("text")
								.attr("class", "valueLabel")
								.style("text-anchor", valueLabelPos(t))
								.attr("x", xPosByType(d, t, 'text'))
								.attr("y", yPosByIndex(d, t, 'text'))
								.attr("dy", ".35em")
								.attr("title", d[t].tooltip)
								.text(d[t].value);
						}
					});
			}
	});


	var txt = vis.selectAll("text.name")
		.data(data)
		.enter().append("g")
			.attr("transform", function (d) {	return "translate(" + (settings.dimension.middle_col_w/2 + colWidth + settings.padding.l/2) + "," + (yScale(d.name) + yScale.bandwidth()/1.1) + ")"; })
		.append("a")
			.attr("xlink:href", function(d) {
				if (settings.replaceInUrlStr) {
					return d['left'].url.replace(eval('/'+settings.replaceInUrlStr+'/g'), '');
				} else if (d['left'].name_url) {
					return d['left'].name_url;
				} else {
					return '';
				}
			})
			.attr("title", function(d) { return d['left'].name_tooltip ?? ''; });

	let rectW = settings.valueLabelPos=='inside'?30:10;
	txt.append("rect")
			.attr('x', -settings.dimension.middle_col_w/2 + rectW)
			.attr('y', -yScale.bandwidth()-4)
			.attr("width", settings.dimension.middle_col_w - 2*rectW)
		.attr("height", 20)
		.attr("rx", 4)
		.style("fill", function(d) { return d['left'].name_color ?? 'transparent'; });
		//.style("stroke", function(d) { return d3.rgb("#e6653e").darker(); });
	var txtTxt = txt.append("text")
		//.attr("dy", "1.55em")
		.attr("text-anchor", "middle")
		.attr('class', 'name')
		.text(function(d){ return d.name; });
	if (settings.wrapLongTexts) txtTxt.call(D3wrap, settings.dimension.middle_col_w);

	var headers = vis.append("g");
	
	headers.append("text")
		.attr("x",colWidth/2 - (settings.valueLabels ? settings.valueLabelsPadding+20 : 0))
		.attr("y", -10)
		.attr("text-anchor", "middle")
		.attr("class","title")
		.text(settings.leftColLabel);
	headers.append("text")
		.attr("x",settings.dimension.middle_col_w/2 + colWidth + settings.padding.l/2)
		.attr("y", -10)
		.attr("text-anchor", "middle")
		.attr("class","title")
		.text(settings.middleColLabel);
	headers.append("text")
		.attr("x",rightOffset+colWidth/2 + (settings.valueLabels ? settings.valueLabelsPadding+20 : 0))
		.attr("y", -10)
		.attr("text-anchor", "middle")
		.attr("class","title")
		.text(settings.rightColLabel);

	// Legend
	if (settings.legend !== '') {
		var legend = vis.selectAll(".legend")
			.data(legend_data)
			.enter().append("g")
			.attr("class", "legend")
			.attr("name", function(d, i) { return "legend"+d.legend; })
			.attr("transform", function(d, i) { return "translate(0," + i * (settings.legend.size+3) + ")"; });

		legend.append("rect")
			.attr("x", settings.legend.x)
			.attr("y", settings.legend.y)
			.attr("width", settings.legend.size)
			.attr("height", settings.legend.size)
			.style("fill", function(d, i) { return d.color; });

		legend.append("text")
			.attr("x", settings.legend.x+settings.legend.size+2)
			.attr("y", settings.legend.y+settings.legend.size/2)
			.attr("dy", ".35em")
			.text(function(d, i) { return d.legend; });
	}
	return {
		vis: vis,
		settings: settings,
		h: settings.dimension.h,
		w: settings.dimension.w,
		x: xScale,
		xMin: xScale.domain()[0],
		xMax: xScale.domain()[1],
		y: yScale,
		yMin: yScale.domain()[0],
		yMax: yScale.domain()[1],
		color: settings.color
	};
};

function D3wrap(text, width) {
	text.each(function () {
		var text = d3.select(this),
				words = text.text().split(/\s+/).reverse(),
				word,
				line = [],
				lineNumber = 0,
				lineHeight = 1.1, // ems
				x = text.attr("x"),
				y = text.attr("y"),
				dy = 0, //parseFloat(text.attr("dy")),
				tspan = text.text(null)
						.append("tspan")
						.attr("x", x)
						.attr("y", y)
						.attr("dy", dy + "em");
		while (word = words.pop()) {
			line.push(word);
			tspan.text(line.join(" "));
			if (tspan.node().getComputedTextLength() > width) {
				line.pop();
				tspan.text(line.join(" "));
				line = [word];
				tspan = text.append("tspan")
						.attr("x", x)
						.attr("y", y)
						.attr("dy", /*++lineNumber **/ lineHeight + dy + "em")
						.text(word);
			}
		}
	});
}

function getD3Symbol(s) {
	if (s==='cross') return d3.symbolCross;
	else if (s==='diamond') return d3.symbolDiamond;
	else if (s==='square') return d3.symbolSquare;
	else if (s==='star') return d3.symbolStar;
	else if (s==='triangle') return d3.symbolTriangle;
	else if (s==='wye') return d3.symbolWye;
	else return d3.symbolCircle;
}
function getHighlightColor(color) {
	var r = color.match(/(\d+),\s*(\d+),\s*(\d+)/i);
	for (var i = 1; i < 4; i++) {
		r[i] = Math.round(r[i] * 1.2);
	}
	return r;
}

function addDefaultPlotSettings(settings, extraDefaults) {
	if (typeof settings === 'undefined') {
		settings = {};
	}
	if (typeof extraDefaults === 'undefined') {
		extraDefaults = {};
	}
	var defaults = {
		xAxisLabel: '',
		yAxisLabel: '',
		legend: '',
		dimension: {w: 750, h: 400},
		padding: {t: 10, r: 40, b: 30, l: 40},
		color: colorbrewer['Set1'][9],
		xLabelTransform: '',
		valueLabels: false,
		grid: false,
		yTickFormat: ".2s"
	};
	var i;
	for (i in extraDefaults) {
		if (!extraDefaults.hasOwnProperty(i)) {
			continue;
		}
		if (typeof settings[i] === 'undefined') {
			settings[i] = extraDefaults[i];
		}
	}
	for (i in defaults) {
		if (!defaults.hasOwnProperty(i)) {
			continue;
		}
		if (typeof settings[i] === 'undefined') {
			settings[i] = defaults[i];
		}
	}
	return settings;
}