| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289 | /** * @license  * Highcharts funnel module, Beta * * (c) 2010-2012 Torstein Hønsi * * License: www.highcharts.com/license *//*global Highcharts */(function (Highcharts) {	'use strict';// create shortcutsvar defaultOptions = Highcharts.getOptions(),	defaultPlotOptions = defaultOptions.plotOptions,	seriesTypes = Highcharts.seriesTypes,	merge = Highcharts.merge,	noop = function () {},	each = Highcharts.each;// set default optionsdefaultPlotOptions.funnel = merge(defaultPlotOptions.pie, {	center: ['50%', '50%'],	width: '90%',	neckWidth: '30%',	height: '100%',	neckHeight: '25%',	dataLabels: {		//position: 'right',		connectorWidth: 1,		connectorColor: '#606060'	},	size: true, // to avoid adapting to data label size in Pie.drawDataLabels	states: {		select: {			color: '#C0C0C0',			borderColor: '#000000',			shadow: false		}	}	});seriesTypes.funnel = Highcharts.extendClass(seriesTypes.pie, {		type: 'funnel',	animate: noop,	/**	 * Overrides the pie translate method	 */	translate: function () {				var 			// Get positions - either an integer or a percentage string must be given			getLength = function (length, relativeTo) {				return (/%$/).test(length) ?					relativeTo * parseInt(length, 10) / 100 :					parseInt(length, 10);			},						sum = 0,			series = this,			chart = series.chart,			plotWidth = chart.plotWidth,			plotHeight = chart.plotHeight,			cumulative = 0, // start at top			options = series.options,			center = options.center,			centerX = getLength(center[0], plotWidth),			centerY = getLength(center[0], plotHeight),			width = getLength(options.width, plotWidth),			tempWidth,			getWidthAt,			height = getLength(options.height, plotHeight),			neckWidth = getLength(options.neckWidth, plotWidth),			neckHeight = getLength(options.neckHeight, plotHeight),			neckY = height - neckHeight,			data = series.data,			path,			fraction,			half = options.dataLabels.position === 'left' ? 1 : 0,			x1, 			y1, 			x2, 			x3, 			y3, 			x4, 			y5;		// Return the width at a specific y coordinate		series.getWidthAt = getWidthAt = function (y) {			return y > height - neckHeight || height === neckHeight ?				neckWidth :				neckWidth + (width - neckWidth) * ((height - neckHeight - y) / (height - neckHeight));		};		series.getX = function (y, half) {			return centerX + (half ? -1 : 1) * ((getWidthAt(y) / 2) + options.dataLabels.distance);		};		// Expose		series.center = [centerX, centerY, height];		series.centerX = centerX;		/*		 * Individual point coordinate naming:		 *		 * x1,y1 _________________ x2,y1		 *  \                         /		 *   \                       /		 *    \                     /		 *     \                   /		 *      \                 /		 *     x3,y3 _________ x4,y3		 *		 * Additional for the base of the neck:		 *		 *       |               |		 *       |               |		 *       |               |		 *     x3,y5 _________ x4,y5		 */		// get the total sum		each(data, function (point) {			sum += point.y;		});		each(data, function (point) {			// set start and end positions			y5 = null;			fraction = sum ? point.y / sum : 0;			y1 = centerY - height / 2 + cumulative * height;			y3 = y1 + fraction * height;			//tempWidth = neckWidth + (width - neckWidth) * ((height - neckHeight - y1) / (height - neckHeight));			tempWidth = getWidthAt(y1);			x1 = centerX - tempWidth / 2;			x2 = x1 + tempWidth;			tempWidth = getWidthAt(y3);			x3 = centerX - tempWidth / 2;			x4 = x3 + tempWidth;			// the entire point is within the neck			if (y1 > neckY) {				x1 = x3 = centerX - neckWidth / 2;				x2 = x4 = centerX + neckWidth / 2;						// the base of the neck			} else if (y3 > neckY) {				y5 = y3;				tempWidth = getWidthAt(neckY);				x3 = centerX - tempWidth / 2;				x4 = x3 + tempWidth;				y3 = neckY;			}			// save the path			path = [				'M',				x1, y1,				'L',				x2, y1,				x4, y3			];			if (y5) {				path.push(x4, y5, x3, y5);			}			path.push(x3, y3, 'Z');			// prepare for using shared dr			point.shapeType = 'path';			point.shapeArgs = { d: path };			// for tooltips and data labels			point.percentage = fraction * 100;			point.plotX = centerX;			point.plotY = (y1 + (y5 || y3)) / 2;			// Placement of tooltips and data labels			point.tooltipPos = [				centerX,				point.plotY			];			// Slice is a noop on funnel points			point.slice = noop;						// Mimicking pie data label placement logic			point.half = half;			cumulative += fraction;		});		series.setTooltipPoints();	},	/**	 * Draw a single point (wedge)	 * @param {Object} point The point object	 * @param {Object} color The color of the point	 * @param {Number} brightness The brightness relative to the color	 */	drawPoints: function () {		var series = this,			options = series.options,			chart = series.chart,			renderer = chart.renderer;		each(series.data, function (point) {						var graphic = point.graphic,				shapeArgs = point.shapeArgs;			if (!graphic) { // Create the shapes				point.graphic = renderer.path(shapeArgs).					attr({						fill: point.color,						stroke: options.borderColor,						'stroke-width': options.borderWidth					}).					add(series.group);								} else { // Update the shapes				graphic.animate(shapeArgs);			}		});	},	/**	 * Funnel items don't have angles (#2289)	 */	sortByAngle: noop,		/**	 * Extend the pie data label method	 */	drawDataLabels: function () {		var data = this.data,			labelDistance = this.options.dataLabels.distance,			leftSide,			sign,			point,			i = data.length,			x,			y;				// In the original pie label anticollision logic, the slots are distributed		// from one labelDistance above to one labelDistance below the pie. In funnels		// we don't want this.		this.center[2] -= 2 * labelDistance;				// Set the label position array for each point.		while (i--) {			point = data[i];			leftSide = point.half;			sign = leftSide ? 1 : -1;			y = point.plotY;			x = this.getX(y, leftSide);							// set the anchor point for data labels			point.labelPos = [				0, // first break of connector				y, // a/a				x + (labelDistance - 5) * sign, // second break, right outside point shape				y, // a/a				x + labelDistance * sign, // landing point for connector				y, // a/a				leftSide ? 'right' : 'left', // alignment				0 // center angle			];		}				seriesTypes.pie.prototype.drawDataLabels.call(this);	}});}(Highcharts));
 |