| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447 | /** * Highcharts Drilldown plugin *  * Author: Torstein Honsi * Last revision: 2013-02-18 * License: MIT License * * Demo: http://jsfiddle.net/highcharts/Vf3yT/ *//*global HighchartsAdapter*/(function (H) {	"use strict";	var noop = function () {},		defaultOptions = H.getOptions(),		each = H.each,		extend = H.extend,		wrap = H.wrap,		Chart = H.Chart,		seriesTypes = H.seriesTypes,		PieSeries = seriesTypes.pie,		ColumnSeries = seriesTypes.column,		fireEvent = HighchartsAdapter.fireEvent;	// Utilities	function tweenColors(startColor, endColor, pos) {		var rgba = [				Math.round(startColor[0] + (endColor[0] - startColor[0]) * pos),				Math.round(startColor[1] + (endColor[1] - startColor[1]) * pos),				Math.round(startColor[2] + (endColor[2] - startColor[2]) * pos),				startColor[3] + (endColor[3] - startColor[3]) * pos			];		return 'rgba(' + rgba.join(',') + ')';	}	// Add language	extend(defaultOptions.lang, {		drillUpText: '◁ Back to {series.name}'	});	defaultOptions.drilldown = {		activeAxisLabelStyle: {			cursor: 'pointer',			color: '#039',			fontWeight: 'bold',			textDecoration: 'underline'					},		activeDataLabelStyle: {			cursor: 'pointer',			color: '#039',			fontWeight: 'bold',			textDecoration: 'underline'					},		animation: {			duration: 500		},		drillUpButton: {			position: { 				align: 'right',				x: -10,				y: 10			}			// relativeTo: 'plotBox'			// theme		}	};		/**	 * A general fadeIn method	 */	H.SVGRenderer.prototype.Element.prototype.fadeIn = function () {		this		.attr({			opacity: 0.1,			visibility: 'visible'		})		.animate({			opacity: 1		}, {			duration: 250		});	};	// Extend the Chart prototype	Chart.prototype.drilldownLevels = [];	Chart.prototype.addSeriesAsDrilldown = function (point, ddOptions) {		var oldSeries = point.series,			xAxis = oldSeries.xAxis,			yAxis = oldSeries.yAxis,			newSeries,			color = point.color || oldSeries.color,			pointIndex,			level;					ddOptions = extend({			color: color		}, ddOptions);		pointIndex = HighchartsAdapter.inArray(this, oldSeries.points);		level = {			seriesOptions: oldSeries.userOptions,			shapeArgs: point.shapeArgs,			bBox: point.graphic.getBBox(),			color: color,			newSeries: ddOptions,			pointOptions: oldSeries.options.data[pointIndex],			pointIndex: pointIndex,			oldExtremes: {				xMin: xAxis && xAxis.userMin,				xMax: xAxis && xAxis.userMax,				yMin: yAxis && yAxis.userMin,				yMax: yAxis && yAxis.userMax			}		};		this.drilldownLevels.push(level);		newSeries = this.addSeries(ddOptions, false);		if (xAxis) {			xAxis.oldPos = xAxis.pos;			xAxis.userMin = xAxis.userMax = null;			yAxis.userMin = yAxis.userMax = null;		}		// Run fancy cross-animation on supported and equal types		if (oldSeries.type === newSeries.type) {			newSeries.animate = newSeries.animateDrilldown || noop;			newSeries.options.animation = true;		}				oldSeries.remove(false);				this.redraw();		this.showDrillUpButton();	};	Chart.prototype.getDrilldownBackText = function () {		var lastLevel = this.drilldownLevels[this.drilldownLevels.length - 1];		return this.options.lang.drillUpText.replace('{series.name}', lastLevel.seriesOptions.name);	};	Chart.prototype.showDrillUpButton = function () {		var chart = this,			backText = this.getDrilldownBackText(),			buttonOptions = chart.options.drilldown.drillUpButton;					if (!this.drillUpButton) {			this.drillUpButton = this.renderer.button(				backText,				null,				null,				function () {					chart.drillUp(); 				}			)			.attr(extend({				align: buttonOptions.position.align,				zIndex: 9			}, buttonOptions.theme))			.add()			.align(buttonOptions.position, false, buttonOptions.relativeTo || 'plotBox');		} else {			this.drillUpButton.attr({				text: backText			})			.align();		}	};	Chart.prototype.drillUp = function () {		var chart = this,			level = chart.drilldownLevels.pop(),			oldSeries = chart.series[0],			oldExtremes = level.oldExtremes,			newSeries = chart.addSeries(level.seriesOptions, false);				fireEvent(chart, 'drillup', { seriesOptions: level.seriesOptions });		if (newSeries.type === oldSeries.type) {			newSeries.drilldownLevel = level;			newSeries.animate = newSeries.animateDrillupTo || noop;			newSeries.options.animation = true;			if (oldSeries.animateDrillupFrom) {				oldSeries.animateDrillupFrom(level);			}		}		oldSeries.remove(false);		// Reset the zoom level of the upper series		if (newSeries.xAxis) {			newSeries.xAxis.setExtremes(oldExtremes.xMin, oldExtremes.xMax, false);			newSeries.yAxis.setExtremes(oldExtremes.yMin, oldExtremes.yMax, false);		}		this.redraw();		if (this.drilldownLevels.length === 0) {			this.drillUpButton = this.drillUpButton.destroy();		} else {			this.drillUpButton.attr({				text: this.getDrilldownBackText()			})			.align();		}	};	PieSeries.prototype.animateDrilldown = function (init) {		var level = this.chart.drilldownLevels[this.chart.drilldownLevels.length - 1],			animationOptions = this.chart.options.drilldown.animation,			animateFrom = level.shapeArgs,			start = animateFrom.start,			angle = animateFrom.end - start,			startAngle = angle / this.points.length,			startColor = H.Color(level.color).rgba;		if (!init) {			each(this.points, function (point, i) {				var endColor = H.Color(point.color).rgba;				/*jslint unparam: true*/				point.graphic					.attr(H.merge(animateFrom, {						start: start + i * startAngle,						end: start + (i + 1) * startAngle					}))					.animate(point.shapeArgs, H.merge(animationOptions, {						step: function (val, fx) {							if (fx.prop === 'start') {								this.attr({									fill: tweenColors(startColor, endColor, fx.pos)								});							}						}					}));				/*jslint unparam: false*/			});		}	};	/**	 * When drilling up, keep the upper series invisible until the lower series has	 * moved into place	 */	PieSeries.prototype.animateDrillupTo = 			ColumnSeries.prototype.animateDrillupTo = function (init) {		if (!init) {			var newSeries = this,				level = newSeries.drilldownLevel;			each(this.points, function (point) {				point.graphic.hide();				if (point.dataLabel) {					point.dataLabel.hide();				}				if (point.connector) {					point.connector.hide();				}			});			// Do dummy animation on first point to get to complete			setTimeout(function () {				each(newSeries.points, function (point, i) {  					// Fade in other points			  					var verb = i === level.pointIndex ? 'show' : 'fadeIn';					point.graphic[verb]();					if (point.dataLabel) {						point.dataLabel[verb]();					}					if (point.connector) {						point.connector[verb]();					}				});			}, Math.max(this.chart.options.drilldown.animation.duration - 50, 0));			// Reset			this.animate = noop;		}	};		ColumnSeries.prototype.animateDrilldown = function (init) {		var animateFrom = this.chart.drilldownLevels[this.chart.drilldownLevels.length - 1].shapeArgs,			animationOptions = this.chart.options.drilldown.animation;					if (!init) {			animateFrom.x += (this.xAxis.oldPos - this.xAxis.pos);				each(this.points, function (point) {				point.graphic					.attr(animateFrom)					.animate(point.shapeArgs, animationOptions);			});		}			};	/**	 * When drilling up, pull out the individual point graphics from the lower series	 * and animate them into the origin point in the upper series.	 */	ColumnSeries.prototype.animateDrillupFrom = 		PieSeries.prototype.animateDrillupFrom =	function (level) {		var animationOptions = this.chart.options.drilldown.animation,			group = this.group;		delete this.group;		each(this.points, function (point) {			var graphic = point.graphic,				startColor = H.Color(point.color).rgba;			delete point.graphic;			/*jslint unparam: true*/			graphic.animate(level.shapeArgs, H.merge(animationOptions, {				step: function (val, fx) {					if (fx.prop === 'start') {						this.attr({							fill: tweenColors(startColor, H.Color(level.color).rgba, fx.pos)						});					}				},				complete: function () {					graphic.destroy();					if (group) {						group = group.destroy();					}				}			}));			/*jslint unparam: false*/		});	};		H.Point.prototype.doDrilldown = function () {		var series = this.series,			chart = series.chart,			drilldown = chart.options.drilldown,			i = drilldown.series.length,			seriesOptions;				while (i-- && !seriesOptions) {			if (drilldown.series[i].id === this.drilldown) {				seriesOptions = drilldown.series[i];			}		}		// Fire the event. If seriesOptions is undefined, the implementer can check for 		// seriesOptions, and call addSeriesAsDrilldown async if necessary.		fireEvent(chart, 'drilldown', { 			point: this,			seriesOptions: seriesOptions		});				if (seriesOptions) {			chart.addSeriesAsDrilldown(this, seriesOptions);		}	};		wrap(H.Point.prototype, 'init', function (proceed, series, options, x) {		var point = proceed.call(this, series, options, x),			chart = series.chart,			tick = series.xAxis && series.xAxis.ticks[x],			tickLabel = tick && tick.label;				if (point.drilldown) {						// Add the click event to the point label			H.addEvent(point, 'click', function () {				point.doDrilldown();			});						// Make axis labels clickable			if (tickLabel) {				if (!tickLabel._basicStyle) {					tickLabel._basicStyle = tickLabel.element.getAttribute('style');				}				tickLabel					.addClass('highcharts-drilldown-axis-label')					.css(chart.options.drilldown.activeAxisLabelStyle)					.on('click', function () {						if (point.doDrilldown) {							point.doDrilldown();						}					});								}		} else if (tickLabel && tickLabel._basicStyle) {			tickLabel.element.setAttribute('style', tickLabel._basicStyle);		}				return point;	});	wrap(H.Series.prototype, 'drawDataLabels', function (proceed) {		var css = this.chart.options.drilldown.activeDataLabelStyle;		proceed.call(this);		each(this.points, function (point) {			if (point.drilldown && point.dataLabel) {				point.dataLabel					.attr({						'class': 'highcharts-drilldown-data-label'					})					.css(css)					.on('click', function () {						point.doDrilldown();					});			}		});	});	// Mark the trackers with a pointer 	ColumnSeries.prototype.supportsDrilldown = true;	PieSeries.prototype.supportsDrilldown = true;	var type, 		drawTrackerWrapper = function (proceed) {			proceed.call(this);			each(this.points, function (point) {				if (point.drilldown && point.graphic) {					point.graphic						.attr({							'class': 'highcharts-drilldown-point'						})						.css({ cursor: 'pointer' });				}			});		};	for (type in seriesTypes) {		if (seriesTypes[type].prototype.supportsDrilldown) {			wrap(seriesTypes[type].prototype, 'drawTracker', drawTrackerWrapper);		}	}		}(Highcharts));
 |