app/assets/javascripts/highcharts.js in highcharts-rails-4.0.3 vs app/assets/javascripts/highcharts.js in highcharts-rails-4.0.4
- old
+ new
@@ -1,18 +1,18 @@
// ==ClosureCompiler==
// @compilation_level SIMPLE_OPTIMIZATIONS
/**
- * @license Highcharts JS v4.0.3 (2014-07-03)
+ * @license Highcharts JS v4.0.4 (2014-09-02)
*
* (c) 2009-2014 Torstein Honsi
*
* License: www.highcharts.com/license
*/
// JSLint options:
-/*global Highcharts, document, window, navigator, setInterval, clearInterval, clearTimeout, setTimeout, location, jQuery, $, console, each, grep */
+/*global Highcharts, HighchartsAdapter, document, window, navigator, setInterval, clearInterval, clearTimeout, setTimeout, location, jQuery, $, console, each, grep */
/*jslint ass: true, sloppy: true, forin: true, plusplus: true, nomen: true, vars: true, regexp: true, newcap: true, browser: true, continue: true, white: true */
(function () {
// encapsulated variables
var UNDEFINED,
doc = document,
@@ -55,11 +55,11 @@
error,
noop = function () { return UNDEFINED; },
charts = [],
chartCount = 0,
PRODUCT = 'Highcharts',
- VERSION = '4.0.3',
+ VERSION = '4.0.4',
// some constants for frequently used strings
DIV = 'div',
ABSOLUTE = 'absolute',
RELATIVE = 'relative',
@@ -80,10 +80,11 @@
// constants for attributes
STROKE_WIDTH = 'stroke-width',
// time methods, changed based on whether or not UTC is used
+ Date, // Allow using a different Date class
makeTime,
timezoneOffset,
getMinutes,
getHours,
getDay,
@@ -361,11 +362,12 @@
* @param {Number} decimals The amount of decimals
* @param {String} decPoint The decimal point, defaults to the one given in the lang options
* @param {String} thousandsSep The thousands separator, defaults to the one given in the lang options
*/
function numberFormat(number, decimals, decPoint, thousandsSep) {
- var lang = defaultOptions.lang,
+ var externalFn = Highcharts.numberFormat,
+ lang = defaultOptions.lang,
// http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_number_format/
n = +number || 0,
c = decimals === -1 ?
(n.toString().split('.')[1] || '').length : // preserve decimals
(isNaN(decimals = mathAbs(decimals)) ? 2 : decimals),
@@ -373,12 +375,14 @@
t = thousandsSep === undefined ? lang.thousandsSep : thousandsSep,
s = n < 0 ? "-" : "",
i = String(pInt(n = mathAbs(n).toFixed(c))),
j = i.length > 3 ? i.length % 3 : 0;
- return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) +
- (c ? d + mathAbs(n - i).toFixed(c).slice(2) : "");
+ return externalFn !== numberFormat ?
+ externalFn(number, decimals, decPoint, thousandsSep) :
+ (s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) +
+ (c ? d + mathAbs(n - i).toFixed(c).slice(2) : ""));
}
/**
* Pad a string to a given length by adding 0 to the beginning
* @param {Number} number
@@ -561,11 +565,11 @@
* @param {Number} interval
* @param {Array} multiples
* @param {Number} magnitude
* @param {Object} options
*/
-function normalizeTickInterval(interval, multiples, magnitude, options) {
+function normalizeTickInterval(interval, multiples, magnitude, allowDecimals) {
var normalized, i;
// round to a tenfold of 1, 2, 2.5 or 5
magnitude = pick(magnitude, 1);
normalized = interval / magnitude;
@@ -573,11 +577,11 @@
// multiples for a linear scale
if (!multiples) {
multiples = [1, 2, 2.5, 5, 10];
// the allowDecimals option
- if (options && options.allowDecimals === false) {
+ if (allowDecimals === false) {
if (magnitude === 1) {
multiples = [1, 2, 5, 10];
} else if (magnitude <= 0.1) {
multiples = [1 / magnitude];
}
@@ -850,16 +854,11 @@
* Initialize the adapter by applying some extensions to jQuery
*/
init: function (pathAnim) {
// extend the animate function to allow SVG animations
- var Fx = $.fx,
- Step = Fx.step,
- dSetter,
- Tween = $.Tween,
- propHooks = Tween && Tween.propHooks,
- opacityHook = $.cssHooks.opacity;
+ var Fx = $.fx;
/*jslint unparam: true*//* allow unused param x in this function */
$.extend($.easing, {
easeOutQuad: function (x, t, b, c, d) {
return -c * (t /= d) * (t - 2) + b;
@@ -867,19 +866,19 @@
});
/*jslint unparam: false*/
// extend some methods to check for elem.attr, which means it is a Highcharts SVG object
$.each(['cur', '_default', 'width', 'height', 'opacity'], function (i, fn) {
- var obj = Step,
+ var obj = Fx.step,
base;
// Handle different parent objects
if (fn === 'cur') {
obj = Fx.prototype; // 'cur', the getter, relates to Fx.prototype
- } else if (fn === '_default' && Tween) { // jQuery 1.8 model
- obj = propHooks[fn];
+ } else if (fn === '_default' && $.Tween) { // jQuery 1.8 model
+ obj = $.Tween.propHooks[fn];
fn = 'set';
}
// Overwrite the method
base = obj[fn];
@@ -909,17 +908,16 @@
};
}
});
// Extend the opacity getter, needed for fading opacity with IE9 and jQuery 1.10+
- wrap(opacityHook, 'get', function (proceed, elem, computed) {
+ wrap($.cssHooks.opacity, 'get', function (proceed, elem, computed) {
return elem.attr ? (elem.opacity || 0) : proceed.call(this, elem, computed);
});
-
// Define the setter function for d (path definitions)
- dSetter = function (fx) {
+ this.addAnimSetter('d', function (fx) {
var elem = fx.elem,
ends;
// Normally start and end should be set in state == 0, but sometimes,
// for reasons unknown, this doesn't happen. Perhaps state == 0 is skipped
@@ -929,26 +927,14 @@
fx.start = ends[0];
fx.end = ends[1];
fx.started = true;
}
-
- // interpolate each value of the path
+ // Interpolate each value of the path
elem.attr('d', pathAnim.step(fx.start, fx.end, fx.pos, elem.toD));
- };
+ });
- // jQuery 1.8 style
- if (Tween) {
- propHooks.d = {
- set: dSetter
- };
- // pre 1.8
- } else {
- // animate paths
- Step.d = dSetter;
- }
-
/**
* Utility for iterating over an array. Parameters are reversed compared to jQuery.
* @param {Array} arr
* @param {Function} fn
*/
@@ -1004,10 +990,24 @@
return ret;
};
},
+ /**
+ * Add an animation setter for a specific property
+ */
+ addAnimSetter: function (prop, setter) {
+ // jQuery 1.8 style
+ if ($.Tween) {
+ $.Tween.propHooks[prop] = {
+ set: setter
+ };
+ // pre 1.8
+ } else {
+ $.fx.step[prop] = setter;
+ }
+ },
/**
* Downloads a script and executes a callback when done.
* @param {String} scriptLocation
* @param {Function} callback
@@ -1183,18 +1183,21 @@
$el.stop();
if (params.opacity !== UNDEFINED && el.attr) {
params.opacity += 'px'; // force jQuery to use same logic as width and height (#2161)
}
+ el.hasAnim = 1; // #3342
$el.animate(params, options);
},
/**
* Stop running animation
*/
stop: function (el) {
- $(el).stop();
+ if (el.hasAnim) { // #3342, memory leak on calling $(el) from destroy
+ $(el).stop();
+ }
}
});
}(win.jQuery));
@@ -1265,12 +1268,12 @@
thousandsSep: ','
},
global: {
useUTC: true,
//timezoneOffset: 0,
- canvasToolsURL: 'http://code.highcharts.com/4.0.3/modules/canvas-tools.js',
- VMLRadialGradientURL: 'http://code.highcharts.com/4.0.3/gfx/vml-radial-gradient.png'
+ canvasToolsURL: 'http://code.highcharts.com/4.0.4/modules/canvas-tools.js',
+ VMLRadialGradientURL: 'http://code.highcharts.com/4.0.4/gfx/vml-radial-gradient.png'
},
chart: {
//animation: true,
//alignTicks: false,
//reflow: true,
@@ -1590,10 +1593,11 @@
var useUTC = defaultOptions.global.useUTC,
GET = useUTC ? 'getUTC' : 'get',
SET = useUTC ? 'setUTC' : 'set';
+ Date = defaultOptions.global.Date || window.Date;
timezoneOffset = ((useUTC && defaultOptions.global.timezoneOffset) || 0) * 60000;
makeTime = useUTC ? Date.UTC : function (year, month, date, hours, minutes, seconds) {
return new Date(
year,
month,
@@ -2434,14 +2438,14 @@
*/
show: function (inherit) {
// IE9-11 doesn't handle visibilty:inherit well, so we remove the attribute instead (#2881)
if (inherit && this.element.namespaceURI === SVG_NS) {
this.element.removeAttribute('visibility');
- return this;
} else {
- return this.attr({ visibility: inherit ? 'inherit' : VISIBLE });
+ this.attr({ visibility: inherit ? 'inherit' : VISIBLE });
}
+ return this;
},
/**
* Hide the element
*/
@@ -2454,11 +2458,11 @@
elemWrapper.animate({
opacity: 0
}, {
duration: duration || 150,
complete: function () {
- elemWrapper.hide();
+ elemWrapper.attr({ y: -9999 }); // #3088, assuming we're only using this for tooltips
}
});
},
/**
@@ -2699,18 +2703,18 @@
.replace('shortdash', '3,1,')
.replace('longdash', '8,3,')
.replace(/dot/g, '1,3,')
.replace('dash', '4,3,')
.replace(/,$/, '')
- .replace('solid', 1)
.split(','); // ending comma
i = value.length;
while (i--) {
value[i] = pInt(value[i]) * this['stroke-width'];
}
- value = value.join(',');
+ value = value.join(',')
+ .replace('NaN', 'none'); // #3226
this.element.setAttribute('stroke-dasharray', value);
}
},
alignSetter: function (value) {
this.element.setAttribute('text-anchor', { left: 'start', center: 'middle', right: 'end' }[value]);
@@ -2723,11 +2727,11 @@
var titleNode = this.element.getElementsByTagName('title')[0];
if (!titleNode) {
titleNode = doc.createElementNS(SVG_NS, 'title');
this.element.appendChild(titleNode);
}
- titleNode.textContent = value;
+ titleNode.textContent = pick(value, '').replace(/<[^>]*>/g, ''); // #3276
},
textSetter: function (value) {
if (value !== this.textStr) {
// Delete bBox memo when the text changes
delete this.bBox;
@@ -3518,11 +3522,11 @@
}
}
};
imageSrc = symbol.match(imageRegex)[1];
- imageSize = symbolSizes[imageSrc];
+ imageSize = symbolSizes[imageSrc] || (options && options.width && options.height && [options.width, options.height]);
// Ireate the image synchronously, add attribs async
obj = this.image(imageSrc)
.attr({
x: x,
@@ -3532,11 +3536,10 @@
if (imageSize) {
centerImage(obj, imageSize);
} else {
// Initialize image to be 0 size so export will still function if there's no cached sizes.
- //
obj.attr({ width: 0, height: 0 });
// Create a dummy JavaScript image to get the width and height. Due to a bug in IE < 8,
// the created element must be assigned to a variable in order to load (#292).
imageElement = createElement('img', {
@@ -3936,11 +3939,11 @@
* and add it before the text in the DOM.
*/
wrapper.onAdd = function () {
text.add(wrapper);
wrapper.attr({
- text: str || '', // alignment is available now
+ text: (str || str === 0) ? str : '', // alignment is available now // #3295: 0 not rendered if given as a value
x: x,
y: y
});
if (box && defined(anchorX)) {
@@ -5593,12 +5596,11 @@
value: axis.isLog ? correctFloat(lin2log(value)) : value
});
// prepare CSS
css = width && { width: mathMax(1, mathRound(width - 2 * (labelOptions.padding || 10))) + PX };
- css = extend(css, labelOptions.style);
-
+
// first call
if (!defined(label)) {
attr = {
align: axis.labelAlign
};
@@ -5617,11 +5619,11 @@
0,
labelOptions.useHTML
)
.attr(attr)
// without position absolute, IE export sometimes is wrong
- .css(css)
+ .css(extend(css, labelOptions.style))
.add(axis.labelGroup) :
null;
// Set the tick baseline and correct for rotation (#1764)
axis.tickBaseline = chart.renderer.fontMetrics(labelOptions.style.fontSize, label).b;
@@ -5690,18 +5692,19 @@
rightSide = sides[1],
axisLeft,
axisRight,
neighbour,
neighbourEdge,
- line = this.label.line || 0,
+ line = this.label.line,
+ lineIndex = line || 0,
labelEdge = axis.labelEdge,
justifyLabel = axis.justifyLabels && (isFirst || isLast),
justifyToPlot;
// Hide it if it now overlaps the neighbour label
- if (labelEdge[line] === UNDEFINED || pxPos + leftSide > labelEdge[line]) {
- labelEdge[line] = pxPos + rightSide;
+ if (labelEdge[lineIndex] === UNDEFINED || pxPos + leftSide > labelEdge[lineIndex]) {
+ labelEdge[lineIndex] = pxPos + rightSide;
} else if (!justifyLabel) {
show = false;
}
@@ -6471,11 +6474,12 @@
//axis.tickPositions = UNDEFINED; // array containing predefined positions
// Tick intervals
//axis.tickInterval = UNDEFINED;
//axis.minorTickInterval = UNDEFINED;
- axis.tickmarkOffset = (axis.categories && options.tickmarkPlacement === 'between') ? 0.5 : 0;
+ axis.tickmarkOffset = (axis.categories && options.tickmarkPlacement === 'between' &&
+ pick(options.tickInterval, 1) === 1) ? 0.5 : 0; // #3202
// Major ticks
axis.ticks = {};
axis.labelEdge = [];
// Minor ticks
@@ -6639,12 +6643,12 @@
var axis = this,
chart = axis.chart;
axis.hasVisibleSeries = false;
- // reset dataMin and dataMax in case we're redrawing
- axis.dataMin = axis.dataMax = null;
+ // Reset properties in case we're redrawing (#3353)
+ axis.dataMin = axis.dataMax = axis.ignoreMinPadding = axis.ignoreMaxPadding = null;
if (axis.buildStacks) {
axis.buildStacks();
}
@@ -7202,11 +7206,18 @@
}
// for linear axes, get magnitude and normalize the interval
if (!isDatetimeAxis && !isLog) { // linear
if (!tickIntervalOption) {
- axis.tickInterval = normalizeTickInterval(axis.tickInterval, null, getMagnitude(axis.tickInterval), options);
+ axis.tickInterval = normalizeTickInterval(
+ axis.tickInterval,
+ null,
+ getMagnitude(axis.tickInterval),
+ // If the tick interval is between 1 and 5 and the axis max is in the order of
+ // thousands, chances are we are dealing with years. Don't allow decimals. #3363.
+ pick(options.allowDecimals, !(axis.tickInterval > 1 && axis.tickInterval < 5 && axis.max > 1000 && axis.max < 9999))
+ );
}
}
// get minorTickInterval
axis.minorTickInterval = options.minorTickInterval === 'auto' && axis.tickInterval ?
@@ -7252,15 +7263,10 @@
var roundedMin = tickPositions[0],
roundedMax = tickPositions[tickPositions.length - 1],
minPointOffset = axis.minPointOffset || 0,
singlePad;
- // Prevent all ticks from being removed (#3195)
- if (!startOnTick && !endOnTick && !categories && tickPositions.length === 2) {
- tickPositions.splice(1, 0, (roundedMax + roundedMin) / 2);
- }
-
if (startOnTick) {
axis.min = roundedMin;
} else if (axis.min - minPointOffset > roundedMin) {
tickPositions.shift();
}
@@ -7269,10 +7275,15 @@
axis.max = roundedMax;
} else if (axis.max + minPointOffset < roundedMax) {
tickPositions.pop();
}
+ // If no tick are left, set one tick in the middle (#3195)
+ if (tickPositions.length === 0 && defined(roundedMin)) {
+ tickPositions.push((roundedMax + roundedMin) / 2);
+ }
+
// When there is only one point, or all points have the same value on this axis, then min
// and max are equal and tickPositions.length is 0 or 1. In this case, add some padding
// in order to center the point, but leave it with one tick. #1337.
if (tickPositions.length === 1) {
singlePad = mathAbs(axis.max) > 10e12 ? 1 : 0.001; // The lowest possible number to avoid extra padding on columns (#2619, #2846)
@@ -8236,13 +8247,13 @@
}
minYear = minDate[getFullYear]();
var time = minDate.getTime(),
minMonth = minDate[getMonth](),
minDateDate = minDate[getDate](),
- localTimezoneOffset = useUTC ?
- timezoneOffset :
- (24 * 3600 * 1000 + minDate.getTimezoneOffset() * 60 * 1000) % (24 * 3600 * 1000); // #950
+ localTimezoneOffset = (timeUnits.day +
+ (useUTC ? timezoneOffset : minDate.getTimezoneOffset() * 60 * 1000)
+ ) % timeUnits.day; // #950, #3359
// iterate and add tick positions at appropriate values
while (time < max) {
tickPositions.push(time);
@@ -8580,22 +8591,22 @@
},
/**
* Hide the tooltip
*/
- hide: function () {
+ hide: function (delay) {
var tooltip = this,
hoverPoints;
clearTimeout(this.hideTimer); // disallow duplicate timers (#1728, #1766)
if (!this.isHidden) {
hoverPoints = this.chart.hoverPoints;
this.hideTimer = setTimeout(function () {
tooltip.label.fadeOut();
tooltip.isHidden = true;
- }, pick(this.options.hideDelay, 500));
+ }, pick(delay, this.options.hideDelay, 500));
// hide previous hoverPoints and set new
if (hoverPoints) {
each(hoverPoints, function (point) {
point.setState();
@@ -9163,11 +9174,11 @@
/**
* Reset the tracking by hiding the tooltip, the hover series state and the hover point
*
* @param allowMove {Boolean} Instead of destroying the tooltip altogether, allow moving it if possible
*/
- reset: function (allowMove) {
+ reset: function (allowMove, delay) {
var pointer = this,
chart = pointer.chart,
hoverSeries = chart.hoverSeries,
hoverPoint = chart.hoverPoint,
tooltip = chart.tooltip,
@@ -9198,11 +9209,11 @@
if (hoverSeries) {
hoverSeries.onMouseOut();
}
if (tooltip) {
- tooltip.hide();
+ tooltip.hide(delay);
}
if (pointer._onDocumentMouseMove) {
removeEvent(doc, 'mousemove', pointer._onDocumentMouseMove);
pointer._onDocumentMouseMove = null;
@@ -9714,11 +9725,11 @@
lastValidTouch = self.lastValidTouch,
hasZoom = self.hasZoom,
selectionMarker = self.selectionMarker,
transform = {},
fireClickEvent = touchesLength === 1 && ((self.inClass(e.target, PREFIX + 'tracker') &&
- chart.runTrackerClick) || chart.runChartClick),
+ chart.runTrackerClick) || self.runChartClick),
clip = {};
// On touch devices, only proceed to trigger click if a handler is defined
if ((hasZoom || followTouchMove) && !fireClickEvent) {
e.preventDefault();
@@ -9750,10 +9761,11 @@
// Store the bounds for use in the touchmove handler
bounds.min = mathMin(axis.pos, absMin - minPixelPadding);
bounds.max = mathMax(axis.pos + axis.len, absMax + minPixelPadding);
}
});
+ self.res = true; // reset on next move
// Event type is touchmove, handle panning and pinching
} else if (pinchDown.length) { // can be 0 when releasing, if touchend fires first
@@ -9772,10 +9784,13 @@
self.scaleGroups(transform, clip);
// Optionally move the tooltip on touchmove
if (!hasZoom && followTouchMove && touchesLength === 1) {
this.runPointActions(self.normalize(e));
+ } else if (self.res) {
+ self.res = false;
+ this.reset(false, 0);
}
}
},
onContainerTouchStart: function (e) {
@@ -11737,11 +11752,11 @@
zIndex: 1
})
.add();
} else {
plotBorder.animate(
- plotBorder.crisp({ x: plotLeft, y: plotTop, width: plotWidth, height: plotHeight })
+ plotBorder.crisp({ x: plotLeft, y: plotTop, width: plotWidth, height: plotHeight, strokeWidth: -plotBorderWidth }) //#3282 plotBorder should be negative
);
}
}
// reset
@@ -12742,11 +12757,11 @@
// If the point count is the same as is was, just run Point.update which is
// cheaper, allows animation, and keeps references to points.
if (updatePoints !== false && dataLength && oldDataLength === dataLength && !series.cropped && !series.hasGroupedData) {
each(data, function (point, i) {
- oldData[i].update(point, false);
+ oldData[i].update(point, false, null, false);
});
} else {
// Reset properties
@@ -12877,18 +12892,19 @@
// the message on to override methods like in data grouping.
if (isCartesian && !series.isDirty && !xAxis.isDirty && !series.yAxis.isDirty && !force) {
return false;
}
-
- // optionally filter out points outside the plot area
- if (isCartesian && series.sorted && (!cropThreshold || dataLength > cropThreshold || series.forceCrop)) {
-
+ if (xAxis) {
xExtremes = xAxis.getExtremes(); // corrected for log axis (#3053)
min = xExtremes.min;
max = xExtremes.max;
+ }
+ // optionally filter out points outside the plot area
+ if (isCartesian && series.sorted && (!cropThreshold || dataLength > cropThreshold || series.forceCrop)) {
+
// it's outside current extremes
if (processedXData[dataLength - 1] < min || processedXData[0] > max) {
processedXData = [];
processedYData = [];
@@ -12909,10 +12925,11 @@
distance = processedXData[i] - processedXData[i - 1];
if (!cropped && processedXData[i] > min && processedXData[i] < max) {
activePointCount++;
}
+
if (distance > 0 && (closestPointRange === UNDEFINED || distance < closestPointRange)) {
closestPointRange = distance;
// Unsorted data is not supported by the line tooltip, as well as data grouping and
// navigation in Stock charts (#725) and width calculation of columns (#1900)
@@ -13009,10 +13026,11 @@
points[i] = point;
} else {
// splat the y data in case of ohlc data array
points[i] = (new pointClass()).init(series, [processedXData[i]].concat(splat(processedYData[i])));
}
+ points[i].index = cursor; // For faster access in Point.update
}
// Hide cropped-away points - this only runs when the number of points is above cropThreshold, or when
// swithching view from non-grouped data to grouped data (#637)
if (data && (processedDataLength !== (dataLength = data.length) || hasGroupedData)) {
@@ -13119,10 +13137,11 @@
stackValues;
// Discard disallowed y values for log axes
if (yAxis.isLog && yValue <= 0) {
point.y = yValue = null;
+ error(10);
}
// Get the plotX translation
point.plotX = xAxis.translate(xValue, 0, 0, 0, 1, pointPlacement, this.type === 'flags'); // Math.round fixes #591
@@ -13293,16 +13312,17 @@
graphic,
options = series.options,
seriesMarkerOptions = options.marker,
seriesPointAttr = series.pointAttr[''],
pointMarkerOptions,
+ hasPointMarker,
enabled,
isInside,
markerGroup = series.markerGroup,
globallyEnabled = pick(
seriesMarkerOptions.enabled,
- series.activePointCount < (0.5 * series.xAxis.len / seriesMarkerOptions.radius)
+ !series.requireSorting || series.activePointCount < (0.5 * series.xAxis.len / seriesMarkerOptions.radius)
);
if (seriesMarkerOptions.enabled !== false || series._hasPointMarkers) {
i = points.length;
@@ -13310,10 +13330,11 @@
point = points[i];
plotX = mathFloor(point.plotX); // #1843
plotY = point.plotY;
graphic = point.graphic;
pointMarkerOptions = point.marker || {};
+ hasPointMarker = !!point.marker;
enabled = (globallyEnabled && pointMarkerOptions.enabled === UNDEFINED) || pointMarkerOptions.enabled;
isInside = chart.isInsidePlot(mathRound(plotX), plotY, chart.inverted); // #1858
// only draw the point if y is defined
if (enabled && plotY !== UNDEFINED && !isNaN(plotY) && point.y !== null) {
@@ -13337,11 +13358,12 @@
point.graphic = graphic = chart.renderer.symbol(
symbol,
plotX - radius,
plotY - radius,
2 * radius,
- 2 * radius
+ 2 * radius,
+ hasPointMarker ? pointMarkerOptions : seriesMarkerOptions
)
.attr(pointAttr)
.add(markerGroup);
}
@@ -13947,10 +13969,16 @@
if (series.drawGraph) {
series.drawGraph();
series.clipNeg();
}
+ each(series.points, function (point) {
+ if (point.redraw) {
+ point.redraw();
+ }
+ });
+
// draw the data labels (inn pies they go before the points)
if (series.drawDataLabels) {
series.drawDataLabels();
}
@@ -14474,43 +14502,44 @@
* @param {Boolean} redraw Whether to redraw the chart or wait for an explicit call
* @param {Boolean|Object} animation Whether to apply animation, and optionally animation
* configuration
*
*/
- update: function (options, redraw, animation) {
+ update: function (options, redraw, animation, runEvent) {
var point = this,
series = point.series,
graphic = point.graphic,
i,
- data = series.data,
chart = series.chart,
seriesOptions = series.options;
redraw = pick(redraw, true);
- // fire the event with a default handler of doing the update
- point.firePointEvent('update', { options: options }, function () {
+ function update() {
point.applyOptions(options);
- // update visuals
- if (isObject(options)) {
- series.getAttribs();
- if (graphic) {
- if (options && options.marker && options.marker.symbol) {
- point.graphic = graphic.destroy();
- } else {
- graphic.attr(point.pointAttr[point.state || '']);
+ // Update visuals
+ if (isObject(options) && !isArray(options)) {
+ // Defer the actual redraw until getAttribs has been called (#3260)
+ point.redraw = function () {
+ if (graphic) {
+ if (options && options.marker && options.marker.symbol) {
+ point.graphic = graphic.destroy();
+ } else {
+ graphic.attr(point.pointAttr[point.state || '']);
+ }
}
- }
- if (options && options.dataLabels && point.dataLabel) { // #2468
- point.dataLabel = point.dataLabel.destroy();
- }
+ if (options && options.dataLabels && point.dataLabel) { // #2468
+ point.dataLabel = point.dataLabel.destroy();
+ }
+ point.redraw = null;
+ };
}
// record changes in the parallel arrays
- i = inArray(point, data);
+ i = point.index;
series.updateParallelArrays(point, i);
seriesOptions.data[i] = point.options;
// redraw
@@ -14523,11 +14552,18 @@
chart.legend.destroyItem(point);
}
if (redraw) {
chart.redraw(animation);
}
- });
+ }
+
+ // Fire the event with a default handler of doing the update
+ if (runEvent === false) { // When called from setData
+ update();
+ } else {
+ point.firePointEvent('update', { options: options }, update);
+ }
},
/**
* Remove a point and optionally redraw the series and if necessary the axes
* @param {Boolean} redraw Whether to redraw the chart or wait for an explicit call
@@ -14629,11 +14665,11 @@
}
series.updateParallelArrays(point, 'splice', i, 0, 0); // insert undefined item
series.updateParallelArrays(point, i); // update it
- if (names) {
+ if (names && point.name) {
names[x] = point.name;
}
dataOptions.splice(i, 0, options);
if (isInTheMiddle) {
@@ -15393,12 +15429,14 @@
// Cache for access in polar
point.barX = barX;
point.pointWidth = pointWidth;
- // Fix the tooltip on center of grouped columns (#1216)
- point.tooltipPos = chart.inverted ? [yAxis.len - plotY, series.xAxis.len - barX - barW / 2] : [barX + barW / 2, plotY];
+ // Fix the tooltip on center of grouped columns (#1216, #424)
+ point.tooltipPos = chart.inverted ?
+ [yAxis.len - plotY, series.xAxis.len - barX - barW / 2] :
+ [barX + barW / 2, plotY + yAxis.pos - chart.plotTop];
// Round off to obtain crisp edges and avoid overlapping with neighbours (#2694)
right = mathRound(barX + barW) + xCrisp;
barX = mathRound(barX) + xCrisp;
barW = right - barX;
@@ -16066,10 +16104,11 @@
cursor = seriesOptions.cursor,
options = seriesOptions.dataLabels,
points = series.points,
pointOptions,
generalOptions,
+ hasRendered = series.hasRendered || 0,
str,
dataLabelsGroup;
if (options.enabled || series._hasPointLabels) {
@@ -16084,17 +16123,19 @@
'data-labels',
options.defer ? HIDDEN : VISIBLE,
options.zIndex || 6
);
- if (!series.hasRendered && pick(options.defer, true)) {
- dataLabelsGroup.attr({ opacity: 0 });
- addEvent(series, 'afterAnimate', function () {
- if (series.visible) { // #3023, #3024
- dataLabelsGroup.show();
- }
- dataLabelsGroup[seriesOptions.animation ? 'animate' : 'attr']({ opacity: 1 }, { duration: 200 });
- });
+ if (pick(options.defer, true)) {
+ dataLabelsGroup.attr({ opacity: +hasRendered }); // #3300
+ if (!hasRendered) {
+ addEvent(series, 'afterAnimate', function () {
+ if (series.visible) { // #3023, #3024
+ dataLabelsGroup.show();
+ }
+ dataLabelsGroup[seriesOptions.animation ? 'animate' : 'attr']({ opacity: 1 }, { duration: 200 });
+ });
+ }
}
// Make the labels for each point
generalOptions = options;
each(points, function (point) {