summaryrefslogtreecommitdiff
path: root/html/includes/js/modules/drilldown.src.js
diff options
context:
space:
mode:
Diffstat (limited to 'html/includes/js/modules/drilldown.src.js')
-rw-r--r--html/includes/js/modules/drilldown.src.js606
1 files changed, 606 insertions, 0 deletions
diff --git a/html/includes/js/modules/drilldown.src.js b/html/includes/js/modules/drilldown.src.js
new file mode 100644
index 0000000..cd3522c
--- /dev/null
+++ b/html/includes/js/modules/drilldown.src.js
@@ -0,0 +1,606 @@
+/**
+ * Highcharts Drilldown plugin
+ *
+ * Author: Torstein Honsi
+ * 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,
+ format = H.format,
+ pick = H.pick,
+ wrap = H.wrap,
+ Chart = H.Chart,
+ seriesTypes = H.seriesTypes,
+ PieSeries = seriesTypes.pie,
+ ColumnSeries = seriesTypes.column,
+ fireEvent = HighchartsAdapter.fireEvent,
+ inArray = HighchartsAdapter.inArray,
+ dupes = [];
+
+ // 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: '#0d233a',
+ fontWeight: 'bold',
+ textDecoration: 'underline'
+ },
+ activeDataLabelStyle: {
+ cursor: 'pointer',
+ color: '#0d233a',
+ 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 (animation) {
+ this
+ .attr({
+ opacity: 0.1,
+ visibility: 'inherit'
+ })
+ .animate({
+ opacity: pick(this.newOpacity, 1) // newOpacity used in maps
+ }, animation || {
+ duration: 250
+ });
+ };
+
+ Chart.prototype.addSeriesAsDrilldown = function (point, ddOptions) {
+ this.addSingleSeriesAsDrilldown(point, ddOptions);
+ this.applyDrilldown();
+ };
+ Chart.prototype.addSingleSeriesAsDrilldown = function (point, ddOptions) {
+ var oldSeries = point.series,
+ xAxis = oldSeries.xAxis,
+ yAxis = oldSeries.yAxis,
+ newSeries,
+ color = point.color || oldSeries.color,
+ pointIndex,
+ levelSeries = [],
+ levelSeriesOptions = [],
+ level,
+ levelNumber;
+
+ levelNumber = oldSeries.levelNumber || 0;
+
+ ddOptions = extend({
+ color: color
+ }, ddOptions);
+ pointIndex = inArray(point, oldSeries.points);
+
+ // Record options for all current series
+ each(oldSeries.chart.series, function (series) {
+ if (series.xAxis === xAxis) {
+ levelSeries.push(series);
+ levelSeriesOptions.push(series.userOptions);
+ series.levelNumber = series.levelNumber || levelNumber; // #3182
+ }
+ });
+
+ // Add a record of properties for each drilldown level
+ level = {
+ levelNumber: levelNumber,
+ seriesOptions: oldSeries.userOptions,
+ levelSeriesOptions: levelSeriesOptions,
+ levelSeries: levelSeries,
+ shapeArgs: point.shapeArgs,
+ bBox: point.graphic.getBBox(),
+ color: color,
+ lowerSeriesOptions: 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
+ }
+ };
+
+ // Generate and push it to a lookup array
+ if (!this.drilldownLevels) {
+ this.drilldownLevels = [];
+ }
+ this.drilldownLevels.push(level);
+
+ newSeries = level.lowerSeries = this.addSeries(ddOptions, false);
+ newSeries.levelNumber = levelNumber + 1;
+ 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;
+ }
+ };
+
+ Chart.prototype.applyDrilldown = function () {
+ var drilldownLevels = this.drilldownLevels,
+ levelToRemove;
+
+ if (drilldownLevels && drilldownLevels.length > 0) { // #3352, async loading
+ levelToRemove = drilldownLevels[drilldownLevels.length - 1].levelNumber;
+ each(this.drilldownLevels, function (level) {
+ if (level.levelNumber === levelToRemove) {
+ each(level.levelSeries, function (series) {
+ if (series.levelNumber === levelToRemove) { // Not removed, not added as part of a multi-series drilldown
+ series.remove(false);
+ }
+ });
+ }
+ });
+ }
+
+ this.redraw();
+ this.showDrillUpButton();
+ };
+
+ Chart.prototype.getDrilldownBackText = function () {
+ var drilldownLevels = this.drilldownLevels,
+ lastLevel;
+ if (drilldownLevels && drilldownLevels.length > 0) { // #3352, async loading
+ lastLevel = drilldownLevels[drilldownLevels.length - 1];
+ lastLevel.series = lastLevel.seriesOptions;
+ return format(this.options.lang.drillUpText, lastLevel);
+ }
+
+ };
+
+ Chart.prototype.showDrillUpButton = function () {
+ var chart = this,
+ backText = this.getDrilldownBackText(),
+ buttonOptions = chart.options.drilldown.drillUpButton,
+ attr,
+ states;
+
+
+ if (!this.drillUpButton) {
+ attr = buttonOptions.theme;
+ states = attr && attr.states;
+
+ this.drillUpButton = this.renderer.button(
+ backText,
+ null,
+ null,
+ function () {
+ chart.drillUp();
+ },
+ attr,
+ states && states.hover,
+ states && states.select
+ )
+ .attr({
+ align: buttonOptions.position.align,
+ zIndex: 9
+ })
+ .add()
+ .align(buttonOptions.position, false, buttonOptions.relativeTo || 'plotBox');
+ } else {
+ this.drillUpButton.attr({
+ text: backText
+ })
+ .align();
+ }
+ };
+
+ Chart.prototype.drillUp = function () {
+ var chart = this,
+ drilldownLevels = chart.drilldownLevels,
+ levelNumber = drilldownLevels[drilldownLevels.length - 1].levelNumber,
+ i = drilldownLevels.length,
+ chartSeries = chart.series,
+ seriesI = chartSeries.length,
+ level,
+ oldSeries,
+ newSeries,
+ oldExtremes,
+ addSeries = function (seriesOptions) {
+ var addedSeries;
+ each(chartSeries, function (series) {
+ if (series.userOptions === seriesOptions) {
+ addedSeries = series;
+ }
+ });
+
+ addedSeries = addedSeries || chart.addSeries(seriesOptions, false);
+ if (addedSeries.type === oldSeries.type && addedSeries.animateDrillupTo) {
+ addedSeries.animate = addedSeries.animateDrillupTo;
+ }
+ if (seriesOptions === level.seriesOptions) {
+ newSeries = addedSeries;
+ }
+ };
+
+ while (i--) {
+
+ level = drilldownLevels[i];
+ if (level.levelNumber === levelNumber) {
+ drilldownLevels.pop();
+
+ // Get the lower series by reference or id
+ oldSeries = level.lowerSeries;
+ if (!oldSeries.chart) { // #2786
+ while (seriesI--) {
+ if (chartSeries[seriesI].options.id === level.lowerSeriesOptions.id) {
+ oldSeries = chartSeries[seriesI];
+ break;
+ }
+ }
+ }
+ oldSeries.xData = []; // Overcome problems with minRange (#2898)
+
+ each(level.levelSeriesOptions, addSeries);
+
+ fireEvent(chart, 'drillup', { seriesOptions: level.seriesOptions });
+
+ if (newSeries.type === oldSeries.type) {
+ newSeries.drilldownLevel = level;
+ newSeries.options.animation = chart.options.drilldown.animation;
+
+ if (oldSeries.animateDrillupFrom) {
+ oldSeries.animateDrillupFrom(level);
+ }
+ }
+ newSeries.levelNumber = levelNumber;
+
+ oldSeries.remove(false);
+
+ // Reset the zoom level of the upper series
+ if (newSeries.xAxis) {
+ oldExtremes = level.oldExtremes;
+ 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();
+ }
+
+ dupes.length = []; // #3315
+ };
+
+
+ ColumnSeries.prototype.supportsDrilldown = true;
+
+ /**
+ * When drilling up, keep the upper series invisible until the lower series has
+ * moved into place
+ */
+ 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 && level.pointIndex) ? 'show' : 'fadeIn',
+ inherit = verb === 'show' ? true : undefined;
+ point.graphic[verb](inherit);
+ if (point.dataLabel) {
+ point.dataLabel[verb](inherit);
+ }
+ if (point.connector) {
+ point.connector[verb](inherit);
+ }
+ });
+ }, Math.max(this.chart.options.drilldown.animation.duration - 50, 0));
+
+ // Reset
+ this.animate = noop;
+ }
+
+ };
+
+ ColumnSeries.prototype.animateDrilldown = function (init) {
+ var series = this,
+ drilldownLevels = this.chart.drilldownLevels,
+ animateFrom = this.chart.drilldownLevels[this.chart.drilldownLevels.length - 1].shapeArgs,
+ animationOptions = this.chart.options.drilldown.animation;
+
+ if (!init) {
+ each(drilldownLevels, function (level) {
+ if (series.userOptions === level.lowerSeriesOptions) {
+ animateFrom = level.shapeArgs;
+ }
+ });
+
+ animateFrom.x += (this.xAxis.oldPos - this.xAxis.pos);
+
+ each(this.points, function (point) {
+ if (point.graphic) {
+ point.graphic
+ .attr(animateFrom)
+ .animate(point.shapeArgs, animationOptions);
+ }
+ if (point.dataLabel) {
+ point.dataLabel.fadeIn(animationOptions);
+ }
+ });
+ this.animate = null;
+ }
+
+ };
+
+ /**
+ * 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 = function (level) {
+ var animationOptions = this.chart.options.drilldown.animation,
+ group = this.group,
+ series = this;
+
+ // Cancel mouse events on the series group (#2787)
+ each(series.trackerGroups, function (key) {
+ if (series[key]) { // we don't always have dataLabelsGroup
+ series[key].on('mouseover');
+ }
+ });
+
+
+ delete this.group;
+ each(this.points, function (point) {
+ var graphic = point.graphic,
+ startColor = H.Color(point.color).rgba,
+ endColor = H.Color(level.color).rgba,
+ complete = function () {
+ graphic.destroy();
+ if (group) {
+ group = group.destroy();
+ }
+ };
+
+ if (graphic) {
+
+ delete point.graphic;
+
+ if (animationOptions) {
+ /*jslint unparam: true*/
+ graphic.animate(level.shapeArgs, H.merge(animationOptions, {
+ step: function (val, fx) {
+ if (fx.prop === 'start' && startColor.length === 4 && endColor.length === 4) {
+ this.attr({
+ fill: tweenColors(startColor, endColor, fx.pos)
+ });
+ }
+ },
+ complete: complete
+ }));
+ /*jslint unparam: false*/
+ } else {
+ graphic.attr(level.shapeArgs);
+ complete();
+ }
+ }
+ });
+ };
+
+ if (PieSeries) {
+ extend(PieSeries.prototype, {
+ supportsDrilldown: true,
+ animateDrillupTo: ColumnSeries.prototype.animateDrillupTo,
+ animateDrillupFrom: ColumnSeries.prototype.animateDrillupFrom,
+
+ 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
+ }))[animationOptions ? 'animate' : 'attr'](point.shapeArgs, H.merge(animationOptions, {
+ step: function (val, fx) {
+ if (fx.prop === 'start' && startColor.length === 4 && endColor.length === 4) {
+ this.attr({
+ fill: tweenColors(startColor, endColor, fx.pos)
+ });
+ }
+ }
+ }));
+ /*jslint unparam: false*/
+ });
+ this.animate = null;
+ }
+ }
+ });
+ }
+
+ H.Point.prototype.doDrilldown = function (_holdRedraw) {
+ 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 && inArray(this.drilldown, dupes) === -1) {
+ seriesOptions = drilldown.series[i];
+ dupes.push(this.drilldown);
+ }
+ }
+
+ // 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) {
+ if (_holdRedraw) {
+ chart.addSingleSeriesAsDrilldown(this, seriesOptions);
+ } else {
+ 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
+ H.addEvent(point, 'click', function () {
+ point.doDrilldown();
+ });
+ /*wrap(point, 'importEvents', function (proceed) { // wrapping importEvents makes point.click event work
+ if (!this.hasImportedEvents) {
+ proceed.call(this);
+ H.addEvent(this, 'click', function () {
+ this.doDrilldown();
+ });
+ }
+ });*/
+
+ // Make axis labels clickable
+ if (tickLabel) {
+ if (!tickLabel.basicStyles) {
+ tickLabel.basicStyles = H.merge(tickLabel.styles);
+ }
+ tickLabel
+ .addClass('highcharts-drilldown-axis-label')
+ .css(chart.options.drilldown.activeAxisLabelStyle)
+ .on('click', function () {
+ each(tickLabel.ddPoints, function (point) {
+ if (point.doDrilldown) {
+ point.doDrilldown(true);
+ }
+ });
+ chart.applyDrilldown();
+ });
+ if (!tickLabel.ddPoints) {
+ tickLabel.ddPoints = [];
+ }
+ tickLabel.ddPoints.push(point);
+
+ }
+ } else if (tickLabel && tickLabel.basicStyles) {
+ tickLabel.styles = {}; // reset for full overwrite of styles
+ tickLabel.css(tickLabel.basicStyles);
+ }
+
+ 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
+ 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));