//## Boxplot with dots ##
$.fn.boxPlotDotsLegend = 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',
		pointRadius:1,
		addColorbar:false
	};
	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;
	
	var yMin = 20;
	var yMax = -20;
	
	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;
		data[c][0].lod_percentage = (d_in[i].lod_percentage === undefined) ? '' : d_in[i].lod_percentage
		
		
		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);
	} else {
		chart.minVal(yMin);
	}
	if (settings.yMax !== false) {
		chart.maxVal(settings.yMax);
	} else {
		chart.maxVal(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", function(d) { return 'group_' + d[0].color })
			.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;
				if (d[0].lod_percentage) tooltip += '<br>' + Math.round(d[0].lod_percentage) +'% below LOD';
				d3.select(this)
						.attr("title", tooltip)
						.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;
			});
	
	var name = "";
	// 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));
	}
	// new
	var xPositions = {};
	for (let i = 0; i < data.length; i++) {
		const name = data[i][0].name_label;
		xPositions[name] = xScale(name) + xScale.bandwidth() / 4;
	}
	
	svg.selectAll("indPoints")
			.data(data)
			.enter()
			.append('g')
			.each(function(d,i) {
				const groupName = d[0].name_label;
				const circles = d3.select(this).selectAll("circle")
						.data(d[0].data)
						.enter()
						.append("circle")
						.attr("cx", function(d) {
							return xPositions[groupName] + Math.random() * xScale.bandwidth() / 2;
						})
						.attr("cy", function(d,i) { return y(d)})
						.attr("r", settings.pointRadius)
						.attr("fill",  d[0].color)
						.style("opacity",0.5)
						.attr("stroke", "black")
						.attr("stroke-width",.4)
			})
	// new
	
	/*
	old
	
	svg.selectAll("indPoints")
			.data(data)
			.enter()
			.each(function(d,i) {
				
				name = d[0].name_label;
				d3.select(this).selectAll("indPoints")
						.data(d[0].data)
						.enter()
						.append("circle")
						.attr("cx", function(d) {return xScale(name)+ xScale.bandwidth()/4+Math.random()*xScale.bandwidth()/2})
						.attr("cy", function(d,i) { return y(d)})
						.attr("r", settings.pointRadius)
						.attr("fill",  d[0].color)
						.style("opacity",0.5)
						.attr("stroke", "black")
						.attr("stroke-width",.4)
				
			})
	*/
	var overlay = svg.selectAll(".overlay")
			.data(data)
			.enter().append("g")
			// .attr("class", "legend")
			//.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
	
	overlay.append("rect")
			.attr("x", function(d, i) { return xScale(d[0].name_label)-xScale.bandwidth()/2})
			.attr("y", 0)
			.attr("width", 2*xScale.bandwidth())
			.attr("height", height)
			.style("fill", function(d, i) {
				if (d[0].name_label === 'Healthy') {
					return "rgb(196, 164, 132,0.15)"; // Fill for healthy status
				} else {
					return 'transparent';   // Fill for non-healthy status
				}
			})
			.attr("title", function(d,i) { return tooltips[i]})
	
	
			
	// 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);
	}
	
	// Legend
	if (settings.legend !== '') {
		var legend = svg.selectAll(".legend")
				.data(d3.map(data, function(d){return d[0].legend;}).values())
				.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", 10)
				.attr("height", 10)
				.style("fill", function(d, i) {return d[0].color; })
				.on("click", function (d) { // 'd' will be the color that was clicked
					console.log(d[0].color);
						toggleElementsByColor(d[0].color);
					});
		
		legend.append("text")
				.attr("x", settings.legend.x+15)
				.attr("y", settings.legend.y+5)
				.attr("dy", ".35em")
				.style("text-anchor", settings.legend.txtAnchor)
				.text(function(d) { return d[0].legend; });
		
	}
	
	// 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);
				}
			});
	if (settings.addColorbar) {
		svg.selectAll(".bar")
				.data(data)
				.enter().append("rect")
				.attr("class", "bar")
				.attr("x", function (d) {
					return xScale(d[0].name_label) - xScale.bandwidth() / 2;
				})
				.attr("y", height )
				.attr("width", xScale.bandwidth()*2)
				.attr("height", 5)
				.attr("fill", function (d) {
					return d[0].color;
				});
	}
	
	$(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(50);
		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,
		data: data
	};
};


$.fn.boxPlotDots = 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',
				pointRadius:1
    };
    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;
		
    var yMin = 20;
    var yMax = -20;
		
    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);
    } else {
        chart.minVal(yMin);
    }
    if (settings.yMax !== false) {
        chart.maxVal(settings.yMax);
    } else {
        chart.maxVal(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;
        });

    var name = "";
    // 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));
    }
    svg.selectAll("indPoints")
        .data(data)
        .enter()
        .each(function(d,i) {

            name = d[0].name_label;
            d3.select(this).selectAll("indPoints")
                .data(d[0].data)
                .enter()
                .append("circle")
                .attr("cx", function(d) {return xScale(name)+ xScale.bandwidth()/4+Math.random()*xScale.bandwidth()/2})
                .attr("cy", function(d,i) { return y(d)})
                .attr("r", settings.pointRadius)
                .attr("fill",  d[0].color)
                .style("opacity",0.5)
                .attr("stroke", "black")
                .attr("stroke-width",.4)

        })
    // 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,
        data: data
    };
};
// 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];
    };
}


$.fn.boxPlotDotsLog2Scale = 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',
		pointRadius: 1
	};
	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));
	
	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);
	
	// Logarithmic y-axis with base 2
	var y = d3.scaleLog()
			.base(2)
			.domain([d3.min(data, d => d3.min(d[0].data)), d3.max(data, d => d3.max(d[0].data))])
			.range([height, 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("class", function(d) { return d[0].clss; });
				if (d[0].color) {
					d3.select(this).selectAll('rect').style('fill', d[0].color);
				}
				tooltips[i] = tooltip;
			});
	
	var name = "";
	
	var yAxis;
	if (settings.yAxisFormat === 'auto') {
		yAxis = d3.axisLeft(y);
	} else {
		yAxis = d3.axisLeft(y)
				.tickFormat(d3.format(settings.yAxisFormat));
	}
	
	svg.selectAll("indPoints")
			.data(data)
			.enter()
			.each(function(d,i) {
				name = d[0].name_label;
				d3.select(this).selectAll("indPoints")
						.data(d[0].data)
						.enter()
						.append("circle")
						.attr("cx", function(d) {return xScale(name)+ xScale.bandwidth()/4+Math.random()*xScale.bandwidth()/2})
						.attr("cy", function(d,i) { return y(d)})
						.attr("r", settings.pointRadius)
						.attr("fill",  d[0].color)
						.style("opacity",0.5)
						.attr("stroke", "black")
						.attr("stroke-width",.4)
			});
	
	// 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") {
		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";
		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)
			.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,
		data: data
	};
};