From 554de444b87aab5f93cb1593a8095612cf9479a7 Mon Sep 17 00:00:00 2001 From: yujian <yujian@163.com> Date: 星期二, 09 六月 2020 17:34:30 +0800 Subject: [PATCH] 订单 --- fanli/src/main/webapp/admin/new/js/third-party/highcharts/modules/map.src.js | 2004 +++++++++++++++++++++++++++++----------------------------- 1 files changed, 1,002 insertions(+), 1,002 deletions(-) diff --git a/fanli/src/main/webapp/admin/new/js/third-party/highcharts/modules/map.src.js b/fanli/src/main/webapp/admin/new/js/third-party/highcharts/modules/map.src.js index 6af8e21..671ed6c 100644 --- a/fanli/src/main/webapp/admin/new/js/third-party/highcharts/modules/map.src.js +++ b/fanli/src/main/webapp/admin/new/js/third-party/highcharts/modules/map.src.js @@ -1,1002 +1,1002 @@ -/** - * @license Map plugin v0.1 for Highcharts - * - * (c) 2011-2013 Torstein H酶nsi - * - * License: www.highcharts.com/license - */ - -/* - * See www.highcharts.com/studies/world-map.htm for use case. - * - * To do: - * - Optimize long variable names and alias adapter methods and Highcharts namespace variables - * - Zoom and pan GUI - */ -(function (Highcharts) { - var UNDEFINED, - Axis = Highcharts.Axis, - Chart = Highcharts.Chart, - Point = Highcharts.Point, - Pointer = Highcharts.Pointer, - each = Highcharts.each, - extend = Highcharts.extend, - merge = Highcharts.merge, - pick = Highcharts.pick, - numberFormat = Highcharts.numberFormat, - defaultOptions = Highcharts.getOptions(), - seriesTypes = Highcharts.seriesTypes, - plotOptions = defaultOptions.plotOptions, - wrap = Highcharts.wrap, - Color = Highcharts.Color, - noop = function () {}; - - - - /* - * Return an intermediate color between two colors, according to pos where 0 - * is the from color and 1 is the to color - */ - function tweenColors(from, to, pos) { - var i = 4, - rgba = []; - - while (i--) { - rgba[i] = Math.round( - to.rgba[i] + (from.rgba[i] - to.rgba[i]) * (1 - pos) - ); - } - return 'rgba(' + rgba.join(',') + ')'; - } - - // Set the default map navigation options - defaultOptions.mapNavigation = { - buttonOptions: { - align: 'right', - verticalAlign: 'bottom', - x: 0, - width: 18, - height: 18, - style: { - fontSize: '15px', - fontWeight: 'bold', - textAlign: 'center' - } - }, - buttons: { - zoomIn: { - onclick: function () { - this.mapZoom(0.5); - }, - text: '+', - y: -32 - }, - zoomOut: { - onclick: function () { - this.mapZoom(2); - }, - text: '-', - y: 0 - } - } - // enableButtons: false, - // enableTouchZoom: false, - // zoomOnDoubleClick: false, - // zoomOnMouseWheel: false - - }; - - /** - * Utility for reading SVG paths directly. - */ - Highcharts.splitPath = function (path) { - var i; - - // Move letters apart - path = path.replace(/([A-Za-z])/g, ' $1 '); - // Trim - path = path.replace(/^\s*/, "").replace(/\s*$/, ""); - - // Split on spaces and commas - path = path.split(/[ ,]+/); - - // Parse numbers - for (i = 0; i < path.length; i++) { - if (!/[a-zA-Z]/.test(path[i])) { - path[i] = parseFloat(path[i]); - } - } - return path; - }; - - // A placeholder for map definitions - Highcharts.maps = {}; - - /** - * Override to use the extreme coordinates from the SVG shape, not the - * data values - */ - wrap(Axis.prototype, 'getSeriesExtremes', function (proceed) { - var isXAxis = this.isXAxis, - dataMin, - dataMax, - xData = []; - - // Remove the xData array and cache it locally so that the proceed method doesn't use it - each(this.series, function (series, i) { - if (series.useMapGeometry) { - xData[i] = series.xData; - series.xData = []; - } - }); - - // Call base to reach normal cartesian series (like mappoint) - proceed.call(this); - - // Run extremes logic for map and mapline - dataMin = pick(this.dataMin, Number.MAX_VALUE); - dataMax = pick(this.dataMax, Number.MIN_VALUE); - each(this.series, function (series, i) { - if (series.useMapGeometry) { - dataMin = Math.min(dataMin, series[isXAxis ? 'minX' : 'minY']); - dataMax = Math.max(dataMax, series[isXAxis ? 'maxX' : 'maxY']); - series.xData = xData[i]; // Reset xData array - } - }); - - this.dataMin = dataMin; - this.dataMax = dataMax; - }); - - /** - * Override axis translation to make sure the aspect ratio is always kept - */ - wrap(Axis.prototype, 'setAxisTranslation', function (proceed) { - var chart = this.chart, - mapRatio, - plotRatio = chart.plotWidth / chart.plotHeight, - isXAxis = this.isXAxis, - adjustedAxisLength, - xAxis = chart.xAxis[0], - padAxis; - - // Run the parent method - proceed.call(this); - - // On Y axis, handle both - if (chart.options.chart.type === 'map' && !isXAxis && xAxis.transA !== UNDEFINED) { - - // Use the same translation for both axes - this.transA = xAxis.transA = Math.min(this.transA, xAxis.transA); - - mapRatio = (xAxis.max - xAxis.min) / (this.max - this.min); - - // What axis to pad to put the map in the middle - padAxis = mapRatio > plotRatio ? this : xAxis; - - // Pad it - adjustedAxisLength = (padAxis.max - padAxis.min) * padAxis.transA; - padAxis.minPixelPadding = (padAxis.len - adjustedAxisLength) / 2; - } - }); - - - //--- Start zooming and panning features - - wrap(Chart.prototype, 'render', function (proceed) { - var chart = this, - mapNavigation = chart.options.mapNavigation; - - proceed.call(chart); - - // Render the plus and minus buttons - chart.renderMapNavigation(); - - // Add the double click event - if (mapNavigation.zoomOnDoubleClick) { - Highcharts.addEvent(chart.container, 'dblclick', function (e) { - chart.pointer.onContainerDblClick(e); - }); - } - - // Add the mousewheel event - if (mapNavigation.zoomOnMouseWheel) { - Highcharts.addEvent(chart.container, document.onmousewheel === undefined ? 'DOMMouseScroll' : 'mousewheel', function (e) { - chart.pointer.onContainerMouseWheel(e); - }); - } - }); - - // Extend the Pointer - extend(Pointer.prototype, { - - /** - * The event handler for the doubleclick event - */ - onContainerDblClick: function (e) { - var chart = this.chart; - - e = this.normalize(e); - - if (chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop)) { - chart.mapZoom( - 0.5, - chart.xAxis[0].toValue(e.chartX), - chart.yAxis[0].toValue(e.chartY) - ); - } - }, - - /** - * The event handler for the mouse scroll event - */ - onContainerMouseWheel: function (e) { - var chart = this.chart, - delta; - - e = this.normalize(e); - - // Firefox uses e.detail, WebKit and IE uses wheelDelta - delta = e.detail || -(e.wheelDelta / 120); - if (chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop)) { - chart.mapZoom( - delta > 0 ? 2 : 0.5, - chart.xAxis[0].toValue(e.chartX), - chart.yAxis[0].toValue(e.chartY) - ); - } - } - }); - // Implement the pinchType option - wrap(Pointer.prototype, 'init', function (proceed, chart, options) { - - proceed.call(this, chart, options); - - // Pinch status - if (options.mapNavigation.enableTouchZoom) { - this.pinchX = this.pinchHor = - this.pinchY = this.pinchVert = true; - } - }); - - // Add events to the Chart object itself - extend(Chart.prototype, { - renderMapNavigation: function () { - var chart = this, - options = this.options.mapNavigation, - buttons = options.buttons, - n, - button, - buttonOptions, - outerHandler = function () { - this.handler.call(chart); - }; - - if (options.enableButtons) { - for (n in buttons) { - if (buttons.hasOwnProperty(n)) { - buttonOptions = merge(options.buttonOptions, buttons[n]); - - button = chart.renderer.button(buttonOptions.text, 0, 0, outerHandler) - .attr({ - width: buttonOptions.width, - height: buttonOptions.height - }) - .css(buttonOptions.style) - .add(); - button.handler = buttonOptions.onclick; - button.align(extend(buttonOptions, { width: button.width, height: button.height }), null, 'spacingBox'); - } - } - } - }, - - /** - * Fit an inner box to an outer. If the inner box overflows left or right, align it to the sides of the - * outer. If it overflows both sides, fit it within the outer. This is a pattern that occurs more places - * in Highcharts, perhaps it should be elevated to a common utility function. - */ - fitToBox: function (inner, outer) { - each([['x', 'width'], ['y', 'height']], function (dim) { - var pos = dim[0], - size = dim[1]; - if (inner[pos] + inner[size] > outer[pos] + outer[size]) { // right overflow - if (inner[size] > outer[size]) { // the general size is greater, fit fully to outer - inner[size] = outer[size]; - inner[pos] = outer[pos]; - } else { // align right - inner[pos] = outer[pos] + outer[size] - inner[size]; - } - } - if (inner[size] > outer[size]) { - inner[size] = outer[size]; - } - if (inner[pos] < outer[pos]) { - inner[pos] = outer[pos]; - } - - }); - - return inner; - }, - - /** - * Zoom the map in or out by a certain amount. Less than 1 zooms in, greater than 1 zooms out. - */ - mapZoom: function (howMuch, centerXArg, centerYArg) { - - if (this.isMapZooming) { - return; - } - - var chart = this, - xAxis = chart.xAxis[0], - xRange = xAxis.max - xAxis.min, - centerX = pick(centerXArg, xAxis.min + xRange / 2), - newXRange = xRange * howMuch, - yAxis = chart.yAxis[0], - yRange = yAxis.max - yAxis.min, - centerY = pick(centerYArg, yAxis.min + yRange / 2), - newYRange = yRange * howMuch, - newXMin = centerX - newXRange / 2, - newYMin = centerY - newYRange / 2, - animation = pick(chart.options.chart.animation, true), - delay, - newExt = chart.fitToBox({ - x: newXMin, - y: newYMin, - width: newXRange, - height: newYRange - }, { - x: xAxis.dataMin, - y: yAxis.dataMin, - width: xAxis.dataMax - xAxis.dataMin, - height: yAxis.dataMax - yAxis.dataMin - }); - - xAxis.setExtremes(newExt.x, newExt.x + newExt.width, false); - yAxis.setExtremes(newExt.y, newExt.y + newExt.height, false); - - // Prevent zooming until this one is finished animating - delay = animation ? animation.duration || 500 : 0; - if (delay) { - chart.isMapZooming = true; - setTimeout(function () { - chart.isMapZooming = false; - }, delay); - } - - chart.redraw(); - } - }); - - /** - * Extend the default options with map options - */ - plotOptions.map = merge(plotOptions.scatter, { - animation: false, // makes the complex shapes slow - nullColor: '#F8F8F8', - borderColor: 'silver', - borderWidth: 1, - marker: null, - stickyTracking: false, - dataLabels: { - verticalAlign: 'middle' - }, - turboThreshold: 0, - tooltip: { - followPointer: true, - pointFormat: '{point.name}: {point.y}<br/>' - }, - states: { - normal: { - animation: true - } - } - }); - - var MapAreaPoint = Highcharts.extendClass(Point, { - /** - * Extend the Point object to split paths - */ - applyOptions: function (options, x) { - - var point = Point.prototype.applyOptions.call(this, options, x); - - if (point.path && typeof point.path === 'string') { - point.path = point.options.path = Highcharts.splitPath(point.path); - } - - return point; - }, - /** - * Stop the fade-out - */ - onMouseOver: function () { - clearTimeout(this.colorInterval); - Point.prototype.onMouseOver.call(this); - }, - /** - * Custom animation for tweening out the colors. Animation reduces blinking when hovering - * over islands and coast lines. We run a custom implementation of animation becuase we - * need to be able to run this independently from other animations like zoom redraw. Also, - * adding color animation to the adapters would introduce almost the same amount of code. - */ - onMouseOut: function () { - var point = this, - start = +new Date(), - normalColor = Color(point.options.color), - hoverColor = Color(point.pointAttr.hover.fill), - animation = point.series.options.states.normal.animation, - duration = animation && (animation.duration || 500); - - if (duration && normalColor.rgba.length === 4 && hoverColor.rgba.length === 4) { - delete point.pointAttr[''].fill; // avoid resetting it in Point.setState - - clearTimeout(point.colorInterval); - point.colorInterval = setInterval(function () { - var pos = (new Date() - start) / duration, - graphic = point.graphic; - if (pos > 1) { - pos = 1; - } - if (graphic) { - graphic.attr('fill', tweenColors(hoverColor, normalColor, pos)); - } - if (pos >= 1) { - clearTimeout(point.colorInterval); - } - }, 13); - } - Point.prototype.onMouseOut.call(point); - } - }); - - /** - * Add the series type - */ - seriesTypes.map = Highcharts.extendClass(seriesTypes.scatter, { - type: 'map', - pointAttrToOptions: { // mapping between SVG attributes and the corresponding options - stroke: 'borderColor', - 'stroke-width': 'borderWidth', - fill: 'color' - }, - colorKey: 'y', - pointClass: MapAreaPoint, - trackerGroups: ['group', 'markerGroup', 'dataLabelsGroup'], - getSymbol: noop, - supportsDrilldown: true, - getExtremesFromAll: true, - useMapGeometry: true, // get axis extremes from paths, not values - init: function (chart) { - var series = this, - valueDecimals = chart.options.legend.valueDecimals, - legendItems = [], - name, - from, - to, - fromLabel, - toLabel, - colorRange, - valueRanges, - gradientColor, - grad, - tmpLabel, - horizontal = chart.options.legend.layout === 'horizontal'; - - - Highcharts.Series.prototype.init.apply(this, arguments); - colorRange = series.options.colorRange; - valueRanges = series.options.valueRanges; - - if (valueRanges) { - each(valueRanges, function (range) { - from = range.from; - to = range.to; - - // Assemble the default name. This can be overridden by legend.options.labelFormatter - name = ''; - if (from === UNDEFINED) { - name = '< '; - } else if (to === UNDEFINED) { - name = '> '; - } - if (from !== UNDEFINED) { - name += numberFormat(from, valueDecimals); - } - if (from !== UNDEFINED && to !== UNDEFINED) { - name += ' - '; - } - if (to !== UNDEFINED) { - name += numberFormat(to, valueDecimals); - } - - // Add a mock object to the legend items - legendItems.push(Highcharts.extend({ - chart: series.chart, - name: name, - options: {}, - drawLegendSymbol: seriesTypes.area.prototype.drawLegendSymbol, - visible: true, - setState: function () {}, - setVisible: function () {} - }, range)); - }); - series.legendItems = legendItems; - - } else if (colorRange) { - - from = colorRange.from; - to = colorRange.to; - fromLabel = colorRange.fromLabel; - toLabel = colorRange.toLabel; - - // Flips linearGradient variables and label text. - grad = horizontal ? [0, 0, 1, 0] : [0, 1, 0, 0]; - if (!horizontal) { - tmpLabel = fromLabel; - fromLabel = toLabel; - toLabel = tmpLabel; - } - - // Creates color gradient. - gradientColor = { - linearGradient: { x1: grad[0], y1: grad[1], x2: grad[2], y2: grad[3] }, - stops: - [ - [0, from], - [1, to] - ] - }; - - // Add a mock object to the legend items. - legendItems = [{ - chart: series.chart, - options: {}, - fromLabel: fromLabel, - toLabel: toLabel, - color: gradientColor, - drawLegendSymbol: this.drawLegendSymbolGradient, - visible: true, - setState: function () {}, - setVisible: function () {} - }]; - - series.legendItems = legendItems; - } - }, - - /** - * If neither valueRanges nor colorRanges are defined, use basic area symbol. - */ - drawLegendSymbol: seriesTypes.area.prototype.drawLegendSymbol, - - /** - * Gets the series' symbol in the legend and extended legend with more information. - * - * @param {Object} legend The legend object - * @param {Object} item The series (this) or point - */ - drawLegendSymbolGradient: function (legend, item) { - var spacing = legend.options.symbolPadding, - padding = pick(legend.options.padding, 8), - positionY, - positionX, - gradientSize = this.chart.renderer.fontMetrics(legend.options.itemStyle.fontSize).h, - horizontal = legend.options.layout === 'horizontal', - box1, - box2, - box3, - rectangleLength = pick(legend.options.rectangleLength, 200); - - // Set local variables based on option. - if (horizontal) { - positionY = -(spacing / 2); - positionX = 0; - } else { - positionY = -rectangleLength + legend.baseline - (spacing / 2); - positionX = padding + gradientSize; - } - - // Creates the from text. - item.fromText = this.chart.renderer.text( - item.fromLabel, // Text. - positionX, // Lower left x. - positionY // Lower left y. - ).attr({ - zIndex: 2 - }).add(item.legendGroup); - box1 = item.fromText.getBBox(); - - // Creates legend symbol. - // Ternary changes variables based on option. - item.legendSymbol = this.chart.renderer.rect( - horizontal ? box1.x + box1.width + spacing : box1.x - gradientSize - spacing, // Upper left x. - box1.y, // Upper left y. - horizontal ? rectangleLength : gradientSize, // Width. - horizontal ? gradientSize : rectangleLength, // Height. - 2 // Corner radius. - ).attr({ - zIndex: 1 - }).add(item.legendGroup); - box2 = item.legendSymbol.getBBox(); - - // Creates the to text. - // Vertical coordinate changed based on option. - item.toText = this.chart.renderer.text( - item.toLabel, - box2.x + box2.width + spacing, - horizontal ? positionY : box2.y + box2.height - spacing - ).attr({ - zIndex: 2 - }).add(item.legendGroup); - box3 = item.toText.getBBox(); - - // Changes legend box settings based on option. - if (horizontal) { - legend.offsetWidth = box1.width + box2.width + box3.width + (spacing * 2) + padding; - legend.itemY = gradientSize + padding; - } else { - legend.offsetWidth = Math.max(box1.width, box3.width) + (spacing) + box2.width + padding; - legend.itemY = box2.height + padding; - legend.itemX = spacing; - } - }, - - /** - * Get the bounding box of all paths in the map combined. - */ - getBox: function (paths) { - var maxX = Number.MIN_VALUE, - minX = Number.MAX_VALUE, - maxY = Number.MIN_VALUE, - minY = Number.MAX_VALUE; - - - // Find the bounding box - each(paths || this.options.data, function (point) { - var path = point.path, - i = path.length, - even = false, // while loop reads from the end - pointMaxX = Number.MIN_VALUE, - pointMinX = Number.MAX_VALUE, - pointMaxY = Number.MIN_VALUE, - pointMinY = Number.MAX_VALUE; - - while (i--) { - if (typeof path[i] === 'number' && !isNaN(path[i])) { - if (even) { // even = x - pointMaxX = Math.max(pointMaxX, path[i]); - pointMinX = Math.min(pointMinX, path[i]); - } else { // odd = Y - pointMaxY = Math.max(pointMaxY, path[i]); - pointMinY = Math.min(pointMinY, path[i]); - } - even = !even; - } - } - // Cache point bounding box for use to position data labels - point._maxX = pointMaxX; - point._minX = pointMinX; - point._maxY = pointMaxY; - point._minY = pointMinY; - - maxX = Math.max(maxX, pointMaxX); - minX = Math.min(minX, pointMinX); - maxY = Math.max(maxY, pointMaxY); - minY = Math.min(minY, pointMinY); - }); - this.minY = minY; - this.maxY = maxY; - this.minX = minX; - this.maxX = maxX; - - }, - - - - /** - * Translate the path so that it automatically fits into the plot area box - * @param {Object} path - */ - translatePath: function (path) { - - var series = this, - even = false, // while loop reads from the end - xAxis = series.xAxis, - yAxis = series.yAxis, - i; - - // Preserve the original - path = [].concat(path); - - // Do the translation - i = path.length; - while (i--) { - if (typeof path[i] === 'number') { - if (even) { // even = x - path[i] = Math.round(xAxis.translate(path[i])); - } else { // odd = Y - path[i] = Math.round(yAxis.len - yAxis.translate(path[i])); - } - even = !even; - } - } - return path; - }, - - setData: function () { - Highcharts.Series.prototype.setData.apply(this, arguments); - this.getBox(); - }, - - /** - * Add the path option for data points. Find the max value for color calculation. - */ - translate: function () { - var series = this, - dataMin = Number.MAX_VALUE, - dataMax = Number.MIN_VALUE; - - series.generatePoints(); - - each(series.data, function (point) { - - point.shapeType = 'path'; - point.shapeArgs = { - d: series.translatePath(point.path) - }; - - // TODO: do point colors in drawPoints instead of point.init - if (typeof point.y === 'number') { - if (point.y > dataMax) { - dataMax = point.y; - } else if (point.y < dataMin) { - dataMin = point.y; - } - } - }); - - series.translateColors(dataMin, dataMax); - }, - - /** - * In choropleth maps, the color is a result of the value, so this needs translation too - */ - translateColors: function (dataMin, dataMax) { - - var seriesOptions = this.options, - valueRanges = seriesOptions.valueRanges, - colorRange = seriesOptions.colorRange, - colorKey = this.colorKey, - from, - to; - - if (colorRange) { - from = Color(colorRange.from); - to = Color(colorRange.to); - } - - each(this.data, function (point) { - var value = point[colorKey], - range, - color, - i, - pos; - - if (valueRanges) { - i = valueRanges.length; - while (i--) { - range = valueRanges[i]; - from = range.from; - to = range.to; - if ((from === UNDEFINED || value >= from) && (to === UNDEFINED || value <= to)) { - color = range.color; - break; - } - - } - } else if (colorRange && value !== undefined) { - - pos = 1 - ((dataMax - value) / (dataMax - dataMin)); - color = value === null ? seriesOptions.nullColor : tweenColors(from, to, pos); - } - - if (color) { - point.color = null; // reset from previous drilldowns, use of the same data options - point.options.color = color; - } - }); - }, - - drawGraph: noop, - - /** - * We need the points' bounding boxes in order to draw the data labels, so - * we skip it now and call if from drawPoints instead. - */ - drawDataLabels: noop, - - /** - * Use the drawPoints method of column, that is able to handle simple shapeArgs. - * Extend it by assigning the tooltip position. - */ - drawPoints: function () { - var series = this, - xAxis = series.xAxis, - yAxis = series.yAxis, - colorKey = series.colorKey; - - // Make points pass test in drawing - each(series.data, function (point) { - point.plotY = 1; // pass null test in column.drawPoints - if (point[colorKey] === null) { - point[colorKey] = 0; - point.isNull = true; - } - }); - - // Draw them - seriesTypes.column.prototype.drawPoints.apply(series); - - each(series.data, function (point) { - - var dataLabels = point.dataLabels, - minX = xAxis.toPixels(point._minX, true), - maxX = xAxis.toPixels(point._maxX, true), - minY = yAxis.toPixels(point._minY, true), - maxY = yAxis.toPixels(point._maxY, true); - - point.plotX = Math.round(minX + (maxX - minX) * pick(dataLabels && dataLabels.anchorX, 0.5)); - point.plotY = Math.round(minY + (maxY - minY) * pick(dataLabels && dataLabels.anchorY, 0.5)); - - - // Reset escaped null points - if (point.isNull) { - point[colorKey] = null; - } - }); - - // Now draw the data labels - Highcharts.Series.prototype.drawDataLabels.call(series); - - }, - - /** - * Animate in the new series from the clicked point in the old series. - * Depends on the drilldown.js module - */ - animateDrilldown: function (init) { - var toBox = this.chart.plotBox, - level = this.chart.drilldownLevels[this.chart.drilldownLevels.length - 1], - fromBox = level.bBox, - animationOptions = this.chart.options.drilldown.animation, - scale; - - if (!init) { - - scale = Math.min(fromBox.width / toBox.width, fromBox.height / toBox.height); - level.shapeArgs = { - scaleX: scale, - scaleY: scale, - translateX: fromBox.x, - translateY: fromBox.y - }; - - // TODO: Animate this.group instead - each(this.points, function (point) { - - point.graphic - .attr(level.shapeArgs) - .animate({ - scaleX: 1, - scaleY: 1, - translateX: 0, - translateY: 0 - }, animationOptions); - - }); - - delete this.animate; - } - - }, - - /** - * When drilling up, pull out the individual point graphics from the lower series - * and animate them into the origin point in the upper series. - */ - animateDrillupFrom: function (level) { - seriesTypes.column.prototype.animateDrillupFrom.call(this, level); - }, - - - /** - * When drilling up, keep the upper series invisible until the lower series has - * moved into place - */ - animateDrillupTo: function (init) { - seriesTypes.column.prototype.animateDrillupTo.call(this, init); - } - }); - - - // The mapline series type - plotOptions.mapline = merge(plotOptions.map, { - lineWidth: 1, - backgroundColor: 'none' - }); - seriesTypes.mapline = Highcharts.extendClass(seriesTypes.map, { - type: 'mapline', - pointAttrToOptions: { // mapping between SVG attributes and the corresponding options - stroke: 'color', - 'stroke-width': 'lineWidth', - fill: 'backgroundColor' - }, - drawLegendSymbol: seriesTypes.line.prototype.drawLegendSymbol - }); - - // The mappoint series type - plotOptions.mappoint = merge(plotOptions.scatter, { - dataLabels: { - enabled: true, - format: '{point.name}', - color: 'black', - style: { - textShadow: '0 0 5px white' - } - } - }); - seriesTypes.mappoint = Highcharts.extendClass(seriesTypes.scatter, { - type: 'mappoint' - }); - - - - /** - * A wrapper for Chart with all the default values for a Map - */ - Highcharts.Map = function (options, callback) { - - var hiddenAxis = { - endOnTick: false, - gridLineWidth: 0, - labels: { - enabled: false - }, - lineWidth: 0, - minPadding: 0, - maxPadding: 0, - startOnTick: false, - tickWidth: 0, - title: null - }, - seriesOptions; - - // Don't merge the data - seriesOptions = options.series; - options.series = null; - - options = merge({ - chart: { - type: 'map', - panning: 'xy' - }, - xAxis: hiddenAxis, - yAxis: merge(hiddenAxis, { reversed: true }) - }, - options, // user's options - - { // forced options - chart: { - inverted: false - } - }); - - options.series = seriesOptions; - - - return new Highcharts.Chart(options, callback); - }; -}(Highcharts)); +/** + * @license Map plugin v0.1 for Highcharts + * + * (c) 2011-2013 Torstein H酶nsi + * + * License: www.highcharts.com/license + */ + +/* + * See www.highcharts.com/studies/world-map.htm for use case. + * + * To do: + * - Optimize long variable names and alias adapter methods and Highcharts namespace variables + * - Zoom and pan GUI + */ +(function (Highcharts) { + var UNDEFINED, + Axis = Highcharts.Axis, + Chart = Highcharts.Chart, + Point = Highcharts.Point, + Pointer = Highcharts.Pointer, + each = Highcharts.each, + extend = Highcharts.extend, + merge = Highcharts.merge, + pick = Highcharts.pick, + numberFormat = Highcharts.numberFormat, + defaultOptions = Highcharts.getOptions(), + seriesTypes = Highcharts.seriesTypes, + plotOptions = defaultOptions.plotOptions, + wrap = Highcharts.wrap, + Color = Highcharts.Color, + noop = function () {}; + + + + /* + * Return an intermediate color between two colors, according to pos where 0 + * is the from color and 1 is the to color + */ + function tweenColors(from, to, pos) { + var i = 4, + rgba = []; + + while (i--) { + rgba[i] = Math.round( + to.rgba[i] + (from.rgba[i] - to.rgba[i]) * (1 - pos) + ); + } + return 'rgba(' + rgba.join(',') + ')'; + } + + // Set the default map navigation options + defaultOptions.mapNavigation = { + buttonOptions: { + align: 'right', + verticalAlign: 'bottom', + x: 0, + width: 18, + height: 18, + style: { + fontSize: '15px', + fontWeight: 'bold', + textAlign: 'center' + } + }, + buttons: { + zoomIn: { + onclick: function () { + this.mapZoom(0.5); + }, + text: '+', + y: -32 + }, + zoomOut: { + onclick: function () { + this.mapZoom(2); + }, + text: '-', + y: 0 + } + } + // enableButtons: false, + // enableTouchZoom: false, + // zoomOnDoubleClick: false, + // zoomOnMouseWheel: false + + }; + + /** + * Utility for reading SVG paths directly. + */ + Highcharts.splitPath = function (path) { + var i; + + // Move letters apart + path = path.replace(/([A-Za-z])/g, ' $1 '); + // Trim + path = path.replace(/^\s*/, "").replace(/\s*$/, ""); + + // Split on spaces and commas + path = path.split(/[ ,]+/); + + // Parse numbers + for (i = 0; i < path.length; i++) { + if (!/[a-zA-Z]/.test(path[i])) { + path[i] = parseFloat(path[i]); + } + } + return path; + }; + + // A placeholder for map definitions + Highcharts.maps = {}; + + /** + * Override to use the extreme coordinates from the SVG shape, not the + * data values + */ + wrap(Axis.prototype, 'getSeriesExtremes', function (proceed) { + var isXAxis = this.isXAxis, + dataMin, + dataMax, + xData = []; + + // Remove the xData array and cache it locally so that the proceed method doesn't use it + each(this.series, function (series, i) { + if (series.useMapGeometry) { + xData[i] = series.xData; + series.xData = []; + } + }); + + // Call base to reach normal cartesian series (like mappoint) + proceed.call(this); + + // Run extremes logic for map and mapline + dataMin = pick(this.dataMin, Number.MAX_VALUE); + dataMax = pick(this.dataMax, Number.MIN_VALUE); + each(this.series, function (series, i) { + if (series.useMapGeometry) { + dataMin = Math.min(dataMin, series[isXAxis ? 'minX' : 'minY']); + dataMax = Math.max(dataMax, series[isXAxis ? 'maxX' : 'maxY']); + series.xData = xData[i]; // Reset xData array + } + }); + + this.dataMin = dataMin; + this.dataMax = dataMax; + }); + + /** + * Override axis translation to make sure the aspect ratio is always kept + */ + wrap(Axis.prototype, 'setAxisTranslation', function (proceed) { + var chart = this.chart, + mapRatio, + plotRatio = chart.plotWidth / chart.plotHeight, + isXAxis = this.isXAxis, + adjustedAxisLength, + xAxis = chart.xAxis[0], + padAxis; + + // Run the parent method + proceed.call(this); + + // On Y axis, handle both + if (chart.options.chart.type === 'map' && !isXAxis && xAxis.transA !== UNDEFINED) { + + // Use the same translation for both axes + this.transA = xAxis.transA = Math.min(this.transA, xAxis.transA); + + mapRatio = (xAxis.max - xAxis.min) / (this.max - this.min); + + // What axis to pad to put the map in the middle + padAxis = mapRatio > plotRatio ? this : xAxis; + + // Pad it + adjustedAxisLength = (padAxis.max - padAxis.min) * padAxis.transA; + padAxis.minPixelPadding = (padAxis.len - adjustedAxisLength) / 2; + } + }); + + + //--- Start zooming and panning features + + wrap(Chart.prototype, 'render', function (proceed) { + var chart = this, + mapNavigation = chart.options.mapNavigation; + + proceed.call(chart); + + // Render the plus and minus buttons + chart.renderMapNavigation(); + + // Add the double click event + if (mapNavigation.zoomOnDoubleClick) { + Highcharts.addEvent(chart.container, 'dblclick', function (e) { + chart.pointer.onContainerDblClick(e); + }); + } + + // Add the mousewheel event + if (mapNavigation.zoomOnMouseWheel) { + Highcharts.addEvent(chart.container, document.onmousewheel === undefined ? 'DOMMouseScroll' : 'mousewheel', function (e) { + chart.pointer.onContainerMouseWheel(e); + }); + } + }); + + // Extend the Pointer + extend(Pointer.prototype, { + + /** + * The event handler for the doubleclick event + */ + onContainerDblClick: function (e) { + var chart = this.chart; + + e = this.normalize(e); + + if (chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop)) { + chart.mapZoom( + 0.5, + chart.xAxis[0].toValue(e.chartX), + chart.yAxis[0].toValue(e.chartY) + ); + } + }, + + /** + * The event handler for the mouse scroll event + */ + onContainerMouseWheel: function (e) { + var chart = this.chart, + delta; + + e = this.normalize(e); + + // Firefox uses e.detail, WebKit and IE uses wheelDelta + delta = e.detail || -(e.wheelDelta / 120); + if (chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop)) { + chart.mapZoom( + delta > 0 ? 2 : 0.5, + chart.xAxis[0].toValue(e.chartX), + chart.yAxis[0].toValue(e.chartY) + ); + } + } + }); + // Implement the pinchType option + wrap(Pointer.prototype, 'init', function (proceed, chart, options) { + + proceed.call(this, chart, options); + + // Pinch status + if (options.mapNavigation.enableTouchZoom) { + this.pinchX = this.pinchHor = + this.pinchY = this.pinchVert = true; + } + }); + + // Add events to the Chart object itself + extend(Chart.prototype, { + renderMapNavigation: function () { + var chart = this, + options = this.options.mapNavigation, + buttons = options.buttons, + n, + button, + buttonOptions, + outerHandler = function () { + this.handler.call(chart); + }; + + if (options.enableButtons) { + for (n in buttons) { + if (buttons.hasOwnProperty(n)) { + buttonOptions = merge(options.buttonOptions, buttons[n]); + + button = chart.renderer.button(buttonOptions.text, 0, 0, outerHandler) + .attr({ + width: buttonOptions.width, + height: buttonOptions.height + }) + .css(buttonOptions.style) + .add(); + button.handler = buttonOptions.onclick; + button.align(extend(buttonOptions, { width: button.width, height: button.height }), null, 'spacingBox'); + } + } + } + }, + + /** + * Fit an inner box to an outer. If the inner box overflows left or right, align it to the sides of the + * outer. If it overflows both sides, fit it within the outer. This is a pattern that occurs more places + * in Highcharts, perhaps it should be elevated to a common utility function. + */ + fitToBox: function (inner, outer) { + each([['x', 'width'], ['y', 'height']], function (dim) { + var pos = dim[0], + size = dim[1]; + if (inner[pos] + inner[size] > outer[pos] + outer[size]) { // right overflow + if (inner[size] > outer[size]) { // the general size is greater, fit fully to outer + inner[size] = outer[size]; + inner[pos] = outer[pos]; + } else { // align right + inner[pos] = outer[pos] + outer[size] - inner[size]; + } + } + if (inner[size] > outer[size]) { + inner[size] = outer[size]; + } + if (inner[pos] < outer[pos]) { + inner[pos] = outer[pos]; + } + + }); + + return inner; + }, + + /** + * Zoom the map in or out by a certain amount. Less than 1 zooms in, greater than 1 zooms out. + */ + mapZoom: function (howMuch, centerXArg, centerYArg) { + + if (this.isMapZooming) { + return; + } + + var chart = this, + xAxis = chart.xAxis[0], + xRange = xAxis.max - xAxis.min, + centerX = pick(centerXArg, xAxis.min + xRange / 2), + newXRange = xRange * howMuch, + yAxis = chart.yAxis[0], + yRange = yAxis.max - yAxis.min, + centerY = pick(centerYArg, yAxis.min + yRange / 2), + newYRange = yRange * howMuch, + newXMin = centerX - newXRange / 2, + newYMin = centerY - newYRange / 2, + animation = pick(chart.options.chart.animation, true), + delay, + newExt = chart.fitToBox({ + x: newXMin, + y: newYMin, + width: newXRange, + height: newYRange + }, { + x: xAxis.dataMin, + y: yAxis.dataMin, + width: xAxis.dataMax - xAxis.dataMin, + height: yAxis.dataMax - yAxis.dataMin + }); + + xAxis.setExtremes(newExt.x, newExt.x + newExt.width, false); + yAxis.setExtremes(newExt.y, newExt.y + newExt.height, false); + + // Prevent zooming until this one is finished animating + delay = animation ? animation.duration || 500 : 0; + if (delay) { + chart.isMapZooming = true; + setTimeout(function () { + chart.isMapZooming = false; + }, delay); + } + + chart.redraw(); + } + }); + + /** + * Extend the default options with map options + */ + plotOptions.map = merge(plotOptions.scatter, { + animation: false, // makes the complex shapes slow + nullColor: '#F8F8F8', + borderColor: 'silver', + borderWidth: 1, + marker: null, + stickyTracking: false, + dataLabels: { + verticalAlign: 'middle' + }, + turboThreshold: 0, + tooltip: { + followPointer: true, + pointFormat: '{point.name}: {point.y}<br/>' + }, + states: { + normal: { + animation: true + } + } + }); + + var MapAreaPoint = Highcharts.extendClass(Point, { + /** + * Extend the Point object to split paths + */ + applyOptions: function (options, x) { + + var point = Point.prototype.applyOptions.call(this, options, x); + + if (point.path && typeof point.path === 'string') { + point.path = point.options.path = Highcharts.splitPath(point.path); + } + + return point; + }, + /** + * Stop the fade-out + */ + onMouseOver: function () { + clearTimeout(this.colorInterval); + Point.prototype.onMouseOver.call(this); + }, + /** + * Custom animation for tweening out the colors. Animation reduces blinking when hovering + * over islands and coast lines. We run a custom implementation of animation becuase we + * need to be able to run this independently from other animations like zoom redraw. Also, + * adding color animation to the adapters would introduce almost the same amount of code. + */ + onMouseOut: function () { + var point = this, + start = +new Date(), + normalColor = Color(point.options.color), + hoverColor = Color(point.pointAttr.hover.fill), + animation = point.series.options.states.normal.animation, + duration = animation && (animation.duration || 500); + + if (duration && normalColor.rgba.length === 4 && hoverColor.rgba.length === 4) { + delete point.pointAttr[''].fill; // avoid resetting it in Point.setState + + clearTimeout(point.colorInterval); + point.colorInterval = setInterval(function () { + var pos = (new Date() - start) / duration, + graphic = point.graphic; + if (pos > 1) { + pos = 1; + } + if (graphic) { + graphic.attr('fill', tweenColors(hoverColor, normalColor, pos)); + } + if (pos >= 1) { + clearTimeout(point.colorInterval); + } + }, 13); + } + Point.prototype.onMouseOut.call(point); + } + }); + + /** + * Add the series type + */ + seriesTypes.map = Highcharts.extendClass(seriesTypes.scatter, { + type: 'map', + pointAttrToOptions: { // mapping between SVG attributes and the corresponding options + stroke: 'borderColor', + 'stroke-width': 'borderWidth', + fill: 'color' + }, + colorKey: 'y', + pointClass: MapAreaPoint, + trackerGroups: ['group', 'markerGroup', 'dataLabelsGroup'], + getSymbol: noop, + supportsDrilldown: true, + getExtremesFromAll: true, + useMapGeometry: true, // get axis extremes from paths, not values + init: function (chart) { + var series = this, + valueDecimals = chart.options.legend.valueDecimals, + legendItems = [], + name, + from, + to, + fromLabel, + toLabel, + colorRange, + valueRanges, + gradientColor, + grad, + tmpLabel, + horizontal = chart.options.legend.layout === 'horizontal'; + + + Highcharts.Series.prototype.init.apply(this, arguments); + colorRange = series.options.colorRange; + valueRanges = series.options.valueRanges; + + if (valueRanges) { + each(valueRanges, function (range) { + from = range.from; + to = range.to; + + // Assemble the default name. This can be overridden by legend.options.labelFormatter + name = ''; + if (from === UNDEFINED) { + name = '< '; + } else if (to === UNDEFINED) { + name = '> '; + } + if (from !== UNDEFINED) { + name += numberFormat(from, valueDecimals); + } + if (from !== UNDEFINED && to !== UNDEFINED) { + name += ' - '; + } + if (to !== UNDEFINED) { + name += numberFormat(to, valueDecimals); + } + + // Add a mock object to the legend items + legendItems.push(Highcharts.extend({ + chart: series.chart, + name: name, + options: {}, + drawLegendSymbol: seriesTypes.area.prototype.drawLegendSymbol, + visible: true, + setState: function () {}, + setVisible: function () {} + }, range)); + }); + series.legendItems = legendItems; + + } else if (colorRange) { + + from = colorRange.from; + to = colorRange.to; + fromLabel = colorRange.fromLabel; + toLabel = colorRange.toLabel; + + // Flips linearGradient variables and label text. + grad = horizontal ? [0, 0, 1, 0] : [0, 1, 0, 0]; + if (!horizontal) { + tmpLabel = fromLabel; + fromLabel = toLabel; + toLabel = tmpLabel; + } + + // Creates color gradient. + gradientColor = { + linearGradient: { x1: grad[0], y1: grad[1], x2: grad[2], y2: grad[3] }, + stops: + [ + [0, from], + [1, to] + ] + }; + + // Add a mock object to the legend items. + legendItems = [{ + chart: series.chart, + options: {}, + fromLabel: fromLabel, + toLabel: toLabel, + color: gradientColor, + drawLegendSymbol: this.drawLegendSymbolGradient, + visible: true, + setState: function () {}, + setVisible: function () {} + }]; + + series.legendItems = legendItems; + } + }, + + /** + * If neither valueRanges nor colorRanges are defined, use basic area symbol. + */ + drawLegendSymbol: seriesTypes.area.prototype.drawLegendSymbol, + + /** + * Gets the series' symbol in the legend and extended legend with more information. + * + * @param {Object} legend The legend object + * @param {Object} item The series (this) or point + */ + drawLegendSymbolGradient: function (legend, item) { + var spacing = legend.options.symbolPadding, + padding = pick(legend.options.padding, 8), + positionY, + positionX, + gradientSize = this.chart.renderer.fontMetrics(legend.options.itemStyle.fontSize).h, + horizontal = legend.options.layout === 'horizontal', + box1, + box2, + box3, + rectangleLength = pick(legend.options.rectangleLength, 200); + + // Set local variables based on option. + if (horizontal) { + positionY = -(spacing / 2); + positionX = 0; + } else { + positionY = -rectangleLength + legend.baseline - (spacing / 2); + positionX = padding + gradientSize; + } + + // Creates the from text. + item.fromText = this.chart.renderer.text( + item.fromLabel, // Text. + positionX, // Lower left x. + positionY // Lower left y. + ).attr({ + zIndex: 2 + }).add(item.legendGroup); + box1 = item.fromText.getBBox(); + + // Creates legend symbol. + // Ternary changes variables based on option. + item.legendSymbol = this.chart.renderer.rect( + horizontal ? box1.x + box1.width + spacing : box1.x - gradientSize - spacing, // Upper left x. + box1.y, // Upper left y. + horizontal ? rectangleLength : gradientSize, // Width. + horizontal ? gradientSize : rectangleLength, // Height. + 2 // Corner radius. + ).attr({ + zIndex: 1 + }).add(item.legendGroup); + box2 = item.legendSymbol.getBBox(); + + // Creates the to text. + // Vertical coordinate changed based on option. + item.toText = this.chart.renderer.text( + item.toLabel, + box2.x + box2.width + spacing, + horizontal ? positionY : box2.y + box2.height - spacing + ).attr({ + zIndex: 2 + }).add(item.legendGroup); + box3 = item.toText.getBBox(); + + // Changes legend box settings based on option. + if (horizontal) { + legend.offsetWidth = box1.width + box2.width + box3.width + (spacing * 2) + padding; + legend.itemY = gradientSize + padding; + } else { + legend.offsetWidth = Math.max(box1.width, box3.width) + (spacing) + box2.width + padding; + legend.itemY = box2.height + padding; + legend.itemX = spacing; + } + }, + + /** + * Get the bounding box of all paths in the map combined. + */ + getBox: function (paths) { + var maxX = Number.MIN_VALUE, + minX = Number.MAX_VALUE, + maxY = Number.MIN_VALUE, + minY = Number.MAX_VALUE; + + + // Find the bounding box + each(paths || this.options.data, function (point) { + var path = point.path, + i = path.length, + even = false, // while loop reads from the end + pointMaxX = Number.MIN_VALUE, + pointMinX = Number.MAX_VALUE, + pointMaxY = Number.MIN_VALUE, + pointMinY = Number.MAX_VALUE; + + while (i--) { + if (typeof path[i] === 'number' && !isNaN(path[i])) { + if (even) { // even = x + pointMaxX = Math.max(pointMaxX, path[i]); + pointMinX = Math.min(pointMinX, path[i]); + } else { // odd = Y + pointMaxY = Math.max(pointMaxY, path[i]); + pointMinY = Math.min(pointMinY, path[i]); + } + even = !even; + } + } + // Cache point bounding box for use to position data labels + point._maxX = pointMaxX; + point._minX = pointMinX; + point._maxY = pointMaxY; + point._minY = pointMinY; + + maxX = Math.max(maxX, pointMaxX); + minX = Math.min(minX, pointMinX); + maxY = Math.max(maxY, pointMaxY); + minY = Math.min(minY, pointMinY); + }); + this.minY = minY; + this.maxY = maxY; + this.minX = minX; + this.maxX = maxX; + + }, + + + + /** + * Translate the path so that it automatically fits into the plot area box + * @param {Object} path + */ + translatePath: function (path) { + + var series = this, + even = false, // while loop reads from the end + xAxis = series.xAxis, + yAxis = series.yAxis, + i; + + // Preserve the original + path = [].concat(path); + + // Do the translation + i = path.length; + while (i--) { + if (typeof path[i] === 'number') { + if (even) { // even = x + path[i] = Math.round(xAxis.translate(path[i])); + } else { // odd = Y + path[i] = Math.round(yAxis.len - yAxis.translate(path[i])); + } + even = !even; + } + } + return path; + }, + + setData: function () { + Highcharts.Series.prototype.setData.apply(this, arguments); + this.getBox(); + }, + + /** + * Add the path option for data points. Find the max value for color calculation. + */ + translate: function () { + var series = this, + dataMin = Number.MAX_VALUE, + dataMax = Number.MIN_VALUE; + + series.generatePoints(); + + each(series.data, function (point) { + + point.shapeType = 'path'; + point.shapeArgs = { + d: series.translatePath(point.path) + }; + + // TODO: do point colors in drawPoints instead of point.init + if (typeof point.y === 'number') { + if (point.y > dataMax) { + dataMax = point.y; + } else if (point.y < dataMin) { + dataMin = point.y; + } + } + }); + + series.translateColors(dataMin, dataMax); + }, + + /** + * In choropleth maps, the color is a result of the value, so this needs translation too + */ + translateColors: function (dataMin, dataMax) { + + var seriesOptions = this.options, + valueRanges = seriesOptions.valueRanges, + colorRange = seriesOptions.colorRange, + colorKey = this.colorKey, + from, + to; + + if (colorRange) { + from = Color(colorRange.from); + to = Color(colorRange.to); + } + + each(this.data, function (point) { + var value = point[colorKey], + range, + color, + i, + pos; + + if (valueRanges) { + i = valueRanges.length; + while (i--) { + range = valueRanges[i]; + from = range.from; + to = range.to; + if ((from === UNDEFINED || value >= from) && (to === UNDEFINED || value <= to)) { + color = range.color; + break; + } + + } + } else if (colorRange && value !== undefined) { + + pos = 1 - ((dataMax - value) / (dataMax - dataMin)); + color = value === null ? seriesOptions.nullColor : tweenColors(from, to, pos); + } + + if (color) { + point.color = null; // reset from previous drilldowns, use of the same data options + point.options.color = color; + } + }); + }, + + drawGraph: noop, + + /** + * We need the points' bounding boxes in order to draw the data labels, so + * we skip it now and call if from drawPoints instead. + */ + drawDataLabels: noop, + + /** + * Use the drawPoints method of column, that is able to handle simple shapeArgs. + * Extend it by assigning the tooltip position. + */ + drawPoints: function () { + var series = this, + xAxis = series.xAxis, + yAxis = series.yAxis, + colorKey = series.colorKey; + + // Make points pass test in drawing + each(series.data, function (point) { + point.plotY = 1; // pass null test in column.drawPoints + if (point[colorKey] === null) { + point[colorKey] = 0; + point.isNull = true; + } + }); + + // Draw them + seriesTypes.column.prototype.drawPoints.apply(series); + + each(series.data, function (point) { + + var dataLabels = point.dataLabels, + minX = xAxis.toPixels(point._minX, true), + maxX = xAxis.toPixels(point._maxX, true), + minY = yAxis.toPixels(point._minY, true), + maxY = yAxis.toPixels(point._maxY, true); + + point.plotX = Math.round(minX + (maxX - minX) * pick(dataLabels && dataLabels.anchorX, 0.5)); + point.plotY = Math.round(minY + (maxY - minY) * pick(dataLabels && dataLabels.anchorY, 0.5)); + + + // Reset escaped null points + if (point.isNull) { + point[colorKey] = null; + } + }); + + // Now draw the data labels + Highcharts.Series.prototype.drawDataLabels.call(series); + + }, + + /** + * Animate in the new series from the clicked point in the old series. + * Depends on the drilldown.js module + */ + animateDrilldown: function (init) { + var toBox = this.chart.plotBox, + level = this.chart.drilldownLevels[this.chart.drilldownLevels.length - 1], + fromBox = level.bBox, + animationOptions = this.chart.options.drilldown.animation, + scale; + + if (!init) { + + scale = Math.min(fromBox.width / toBox.width, fromBox.height / toBox.height); + level.shapeArgs = { + scaleX: scale, + scaleY: scale, + translateX: fromBox.x, + translateY: fromBox.y + }; + + // TODO: Animate this.group instead + each(this.points, function (point) { + + point.graphic + .attr(level.shapeArgs) + .animate({ + scaleX: 1, + scaleY: 1, + translateX: 0, + translateY: 0 + }, animationOptions); + + }); + + delete this.animate; + } + + }, + + /** + * When drilling up, pull out the individual point graphics from the lower series + * and animate them into the origin point in the upper series. + */ + animateDrillupFrom: function (level) { + seriesTypes.column.prototype.animateDrillupFrom.call(this, level); + }, + + + /** + * When drilling up, keep the upper series invisible until the lower series has + * moved into place + */ + animateDrillupTo: function (init) { + seriesTypes.column.prototype.animateDrillupTo.call(this, init); + } + }); + + + // The mapline series type + plotOptions.mapline = merge(plotOptions.map, { + lineWidth: 1, + backgroundColor: 'none' + }); + seriesTypes.mapline = Highcharts.extendClass(seriesTypes.map, { + type: 'mapline', + pointAttrToOptions: { // mapping between SVG attributes and the corresponding options + stroke: 'color', + 'stroke-width': 'lineWidth', + fill: 'backgroundColor' + }, + drawLegendSymbol: seriesTypes.line.prototype.drawLegendSymbol + }); + + // The mappoint series type + plotOptions.mappoint = merge(plotOptions.scatter, { + dataLabels: { + enabled: true, + format: '{point.name}', + color: 'black', + style: { + textShadow: '0 0 5px white' + } + } + }); + seriesTypes.mappoint = Highcharts.extendClass(seriesTypes.scatter, { + type: 'mappoint' + }); + + + + /** + * A wrapper for Chart with all the default values for a Map + */ + Highcharts.Map = function (options, callback) { + + var hiddenAxis = { + endOnTick: false, + gridLineWidth: 0, + labels: { + enabled: false + }, + lineWidth: 0, + minPadding: 0, + maxPadding: 0, + startOnTick: false, + tickWidth: 0, + title: null + }, + seriesOptions; + + // Don't merge the data + seriesOptions = options.series; + options.series = null; + + options = merge({ + chart: { + type: 'map', + panning: 'xy' + }, + xAxis: hiddenAxis, + yAxis: merge(hiddenAxis, { reversed: true }) + }, + options, // user's options + + { // forced options + chart: { + inverted: false + } + }); + + options.series = seriesOptions; + + + return new Highcharts.Chart(options, callback); + }; +}(Highcharts)); -- Gitblit v1.8.0