| 对比新文件 |
| | |
| | | (function (Highcharts, HighchartsAdapter) { |
| | | |
| | | var UNDEFINED, |
| | | ALIGN_FACTOR, |
| | | ALLOWED_SHAPES, |
| | | Chart = Highcharts.Chart, |
| | | extend = Highcharts.extend, |
| | | each = Highcharts.each; |
| | | |
| | | ALLOWED_SHAPES = ["path", "rect", "circle"]; |
| | | |
| | | ALIGN_FACTOR = { |
| | | top: 0, |
| | | left: 0, |
| | | center: 0.5, |
| | | middle: 0.5, |
| | | bottom: 1, |
| | | right: 1 |
| | | }; |
| | | |
| | | |
| | | // Highcharts helper methods |
| | | var inArray = HighchartsAdapter.inArray, |
| | | merge = Highcharts.merge; |
| | | |
| | | function defaultOptions(shapeType) { |
| | | var shapeOptions, |
| | | options; |
| | | |
| | | options = { |
| | | xAxis: 0, |
| | | yAxis: 0, |
| | | title: { |
| | | style: {}, |
| | | text: "", |
| | | x: 0, |
| | | y: 0 |
| | | }, |
| | | shape: { |
| | | params: { |
| | | stroke: "#000000", |
| | | fill: "transparent", |
| | | strokeWidth: 2 |
| | | } |
| | | } |
| | | }; |
| | | |
| | | shapeOptions = { |
| | | circle: { |
| | | params: { |
| | | x: 0, |
| | | y: 0 |
| | | } |
| | | } |
| | | }; |
| | | |
| | | if (shapeOptions[shapeType]) { |
| | | options.shape = merge(options.shape, shapeOptions[shapeType]); |
| | | } |
| | | |
| | | return options; |
| | | } |
| | | |
| | | function isArray(obj) { |
| | | return Object.prototype.toString.call(obj) === '[object Array]'; |
| | | } |
| | | |
| | | function isNumber(n) { |
| | | return typeof n === 'number'; |
| | | } |
| | | |
| | | function defined(obj) { |
| | | return obj !== UNDEFINED && obj !== null; |
| | | } |
| | | |
| | | function translatePath(d, xAxis, yAxis, xOffset, yOffset) { |
| | | var len = d.length, |
| | | i = 0; |
| | | |
| | | while (i < len) { |
| | | if (typeof d[i] === 'number' && typeof d[i + 1] === 'number') { |
| | | d[i] = xAxis.toPixels(d[i]) - xOffset; |
| | | d[i + 1] = yAxis.toPixels(d[i + 1]) - yOffset; |
| | | i += 2; |
| | | } else { |
| | | i += 1; |
| | | } |
| | | } |
| | | |
| | | return d; |
| | | } |
| | | |
| | | |
| | | // Define annotation prototype |
| | | var Annotation = function () { |
| | | this.init.apply(this, arguments); |
| | | }; |
| | | Annotation.prototype = { |
| | | /* |
| | | * Initialize the annotation |
| | | */ |
| | | init: function (chart, options) { |
| | | var shapeType = options.shape && options.shape.type; |
| | | |
| | | this.chart = chart; |
| | | this.options = merge({}, defaultOptions(shapeType), options); |
| | | }, |
| | | |
| | | /* |
| | | * Render the annotation |
| | | */ |
| | | render: function (redraw) { |
| | | var annotation = this, |
| | | chart = this.chart, |
| | | renderer = annotation.chart.renderer, |
| | | group = annotation.group, |
| | | title = annotation.title, |
| | | shape = annotation.shape, |
| | | options = annotation.options, |
| | | titleOptions = options.title, |
| | | shapeOptions = options.shape; |
| | | |
| | | if (!group) { |
| | | group = annotation.group = renderer.g(); |
| | | } |
| | | |
| | | |
| | | if (!shape && shapeOptions && inArray(shapeOptions.type, ALLOWED_SHAPES) !== -1) { |
| | | shape = annotation.shape = renderer[options.shape.type](shapeOptions.params); |
| | | shape.add(group); |
| | | } |
| | | |
| | | if (!title && titleOptions) { |
| | | title = annotation.title = renderer.label(titleOptions); |
| | | title.add(group); |
| | | } |
| | | |
| | | group.add(chart.annotations.group); |
| | | |
| | | // link annotations to point or series |
| | | annotation.linkObjects(); |
| | | |
| | | if (redraw !== false) { |
| | | annotation.redraw(); |
| | | } |
| | | }, |
| | | |
| | | /* |
| | | * Redraw the annotation title or shape after options update |
| | | */ |
| | | redraw: function () { |
| | | var options = this.options, |
| | | chart = this.chart, |
| | | group = this.group, |
| | | title = this.title, |
| | | shape = this.shape, |
| | | linkedTo = this.linkedObject, |
| | | xAxis = chart.xAxis[options.xAxis], |
| | | yAxis = chart.yAxis[options.yAxis], |
| | | width = options.width, |
| | | height = options.height, |
| | | anchorY = ALIGN_FACTOR[options.anchorY], |
| | | anchorX = ALIGN_FACTOR[options.anchorX], |
| | | resetBBox = false, |
| | | shapeParams, |
| | | linkType, |
| | | series, |
| | | param, |
| | | bbox, |
| | | x, |
| | | y; |
| | | |
| | | if (linkedTo) { |
| | | linkType = (linkedTo instanceof Highcharts.Point) ? 'point' : |
| | | (linkedTo instanceof Highcharts.Series) ? 'series' : null; |
| | | |
| | | if (linkType === 'point') { |
| | | options.xValue = linkedTo.x; |
| | | options.yValue = linkedTo.y; |
| | | series = linkedTo.series; |
| | | } else if (linkType === 'series') { |
| | | series = linkedTo; |
| | | } |
| | | |
| | | if (group.visibility !== series.group.visibility) { |
| | | group.attr({ |
| | | visibility: series.group.visibility |
| | | }); |
| | | } |
| | | } |
| | | |
| | | |
| | | // Based on given options find annotation pixel position |
| | | x = (defined(options.xValue) ? xAxis.toPixels(options.xValue + xAxis.minPointOffset) - xAxis.minPixelPadding : options.x); |
| | | y = defined(options.yValue) ? yAxis.toPixels(options.yValue) : options.y; |
| | | |
| | | if (isNaN(x) || isNaN(y) || !isNumber(x) || !isNumber(y)) { |
| | | return; |
| | | } |
| | | |
| | | |
| | | if (title) { |
| | | title.attr(options.title); |
| | | title.css(options.title.style); |
| | | resetBBox = true; |
| | | } |
| | | |
| | | if (shape) { |
| | | shapeParams = extend({}, options.shape.params); |
| | | |
| | | if (options.units === 'values') { |
| | | for (param in shapeParams) { |
| | | if (inArray(param, ['width', 'x']) > -1) { |
| | | shapeParams[param] = xAxis.translate(shapeParams[param]); |
| | | } else if (inArray(param, ['height', 'y']) > -1) { |
| | | shapeParams[param] = yAxis.translate(shapeParams[param]); |
| | | } |
| | | } |
| | | |
| | | if (shapeParams.width) { |
| | | shapeParams.width -= xAxis.toPixels(0) - xAxis.left; |
| | | } |
| | | |
| | | if (shapeParams.x) { |
| | | shapeParams.x += xAxis.minPixelPadding; |
| | | } |
| | | |
| | | if (options.shape.type === 'path') { |
| | | translatePath(shapeParams.d, xAxis, yAxis, x, y); |
| | | } |
| | | } |
| | | |
| | | // move the center of the circle to shape x/y |
| | | if (options.shape.type === 'circle') { |
| | | shapeParams.x += shapeParams.r; |
| | | shapeParams.y += shapeParams.r; |
| | | } |
| | | |
| | | resetBBox = true; |
| | | shape.attr(shapeParams); |
| | | } |
| | | |
| | | group.bBox = null; |
| | | |
| | | // If annotation width or height is not defined in options use bounding box size |
| | | if (!isNumber(width)) { |
| | | bbox = group.getBBox(); |
| | | width = bbox.width; |
| | | } |
| | | |
| | | if (!isNumber(height)) { |
| | | // get bbox only if it wasn't set before |
| | | if (!bbox) { |
| | | bbox = group.getBBox(); |
| | | } |
| | | |
| | | height = bbox.height; |
| | | } |
| | | |
| | | // Calculate anchor point |
| | | if (!isNumber(anchorX)) { |
| | | anchorX = ALIGN_FACTOR.center; |
| | | } |
| | | |
| | | if (!isNumber(anchorY)) { |
| | | anchorY = ALIGN_FACTOR.center; |
| | | } |
| | | |
| | | // Translate group according to its dimension and anchor point |
| | | x = x - width * anchorX; |
| | | y = y - height * anchorY; |
| | | |
| | | if (chart.animation && defined(group.translateX) && defined(group.translateY)) { |
| | | group.animate({ |
| | | translateX: x, |
| | | translateY: y |
| | | }); |
| | | } else { |
| | | group.translate(x, y); |
| | | } |
| | | }, |
| | | |
| | | /* |
| | | * Destroy the annotation |
| | | */ |
| | | destroy: function () { |
| | | var annotation = this, |
| | | chart = this.chart, |
| | | allItems = chart.annotations.allItems, |
| | | index = allItems.indexOf(annotation); |
| | | |
| | | if (index > -1) { |
| | | allItems.splice(index, 1); |
| | | } |
| | | |
| | | each(['title', 'shape', 'group'], function (element) { |
| | | if (annotation[element]) { |
| | | annotation[element].destroy(); |
| | | annotation[element] = null; |
| | | } |
| | | }); |
| | | |
| | | annotation.group = annotation.title = annotation.shape = annotation.chart = annotation.options = null; |
| | | }, |
| | | |
| | | /* |
| | | * Update the annotation with a given options |
| | | */ |
| | | update: function (options, redraw) { |
| | | extend(this.options, options); |
| | | |
| | | // update link to point or series |
| | | this.linkObjects(); |
| | | |
| | | this.render(redraw); |
| | | }, |
| | | |
| | | linkObjects: function () { |
| | | var annotation = this, |
| | | chart = annotation.chart, |
| | | linkedTo = annotation.linkedObject, |
| | | linkedId = linkedTo && (linkedTo.id || linkedTo.options.id), |
| | | options = annotation.options, |
| | | id = options.linkedTo; |
| | | |
| | | if (!defined(id)) { |
| | | annotation.linkedObject = null; |
| | | } else if (!defined(linkedTo) || id !== linkedId) { |
| | | annotation.linkedObject = chart.get(id); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | |
| | | // Add annotations methods to chart prototype |
| | | extend(Chart.prototype, { |
| | | annotations: { |
| | | /* |
| | | * Unified method for adding annotations to the chart |
| | | */ |
| | | add: function (options, redraw) { |
| | | var annotations = this.allItems, |
| | | chart = this.chart, |
| | | item, |
| | | len; |
| | | |
| | | if (!isArray(options)) { |
| | | options = [options]; |
| | | } |
| | | |
| | | len = options.length; |
| | | |
| | | while (len--) { |
| | | item = new Annotation(chart, options[len]); |
| | | annotations.push(item); |
| | | item.render(redraw); |
| | | } |
| | | }, |
| | | |
| | | /** |
| | | * Redraw all annotations, method used in chart events |
| | | */ |
| | | redraw: function () { |
| | | each(this.allItems, function (annotation) { |
| | | annotation.redraw(); |
| | | }); |
| | | } |
| | | } |
| | | }); |
| | | |
| | | |
| | | // Initialize on chart load |
| | | Chart.prototype.callbacks.push(function (chart) { |
| | | var options = chart.options.annotations, |
| | | group; |
| | | |
| | | group = chart.renderer.g("annotations"); |
| | | group.attr({ |
| | | zIndex: 7 |
| | | }); |
| | | group.add(); |
| | | |
| | | // initialize empty array for annotations |
| | | chart.annotations.allItems = []; |
| | | |
| | | // link chart object to annotations |
| | | chart.annotations.chart = chart; |
| | | |
| | | // link annotations group element to the chart |
| | | chart.annotations.group = group; |
| | | |
| | | if (isArray(options) && options.length > 0) { |
| | | chart.annotations.add(chart.options.annotations); |
| | | } |
| | | |
| | | // update annotations after chart redraw |
| | | Highcharts.addEvent(chart, 'redraw', function () { |
| | | chart.annotations.redraw(); |
| | | }); |
| | | }); |
| | | }(Highcharts, HighchartsAdapter)); |