app/assets/javascripts/highcharts.js in highcharts-rails-4.1.5 vs app/assets/javascripts/highcharts.js in highcharts-rails-4.1.6
- old
+ new
@@ -1,10 +1,10 @@
// ==ClosureCompiler==
// @compilation_level SIMPLE_OPTIMIZATIONS
/**
- * @license Highcharts JS v4.1.5 (2015-04-13)
+ * @license Highcharts JS v4.1.6 (2015-06-12)
*
* (c) 2009-2014 Torstein Honsi
*
* License: www.highcharts.com/license
*/
@@ -54,11 +54,11 @@
timeUnits,
noop = function () { return UNDEFINED; },
charts = [],
chartCount = 0,
PRODUCT = 'Highcharts',
- VERSION = '4.1.5',
+ VERSION = '4.1.6',
// some constants for frequently used strings
DIV = 'div',
ABSOLUTE = 'absolute',
RELATIVE = 'relative',
@@ -367,10 +367,17 @@
// Create an array of the remaining length +1 and join it with 0's
return new Array((length || 2) + 1 - String(number).length).join(0) + number;
}
/**
+ * Return a length based on either the integer value, or a percentage of a base.
+ */
+function relativeLength (value, base) {
+ return (/%$/).test(value) ? base * parseFloat(value) / 100 : parseFloat(value);
+}
+
+/**
* Wrap a method with extended functionality, preserving the original function
* @param {Object} obj The context object that the method belongs to
* @param {String} method The name of the method to extend
* @param {Function} func A wrapper function callback. This function is called with the same arguments
* as the original function, except that the original function is unshifted and passed as the first
@@ -1259,12 +1266,12 @@
thousandsSep: ' '
},
global: {
useUTC: true,
//timezoneOffset: 0,
- canvasToolsURL: 'http://code.highcharts.com/4.1.5/modules/canvas-tools.js',
- VMLRadialGradientURL: 'http://code.highcharts.com/4.1.5/gfx/vml-radial-gradient.png'
+ canvasToolsURL: 'http://code.highcharts.com/4.1.6/modules/canvas-tools.js',
+ VMLRadialGradientURL: 'http://code.highcharts.com/4.1.6/gfx/vml-radial-gradient.png'
},
chart: {
//animation: true,
//alignTicks: false,
//reflow: true,
@@ -1942,33 +1949,36 @@
*/
applyTextShadow: function (textShadow) {
var elem = this.element,
tspans,
hasContrast = textShadow.indexOf('contrast') !== -1,
+ styles = {},
// IE10 and IE11 report textShadow in elem.style even though it doesn't work. Check
// this again with new IE release. In exports, the rendering is passed to PhantomJS.
supports = this.renderer.forExport || (elem.style.textShadow !== UNDEFINED && !isIE);
// When the text shadow is set to contrast, use dark stroke for light text and vice versa
if (hasContrast) {
- textShadow = textShadow.replace(/contrast/g, this.renderer.getContrast(elem.style.fill));
+ styles.textShadow = textShadow = textShadow.replace(/contrast/g, this.renderer.getContrast(elem.style.fill));
}
+ // Safari with retina displays as well as PhantomJS bug (#3974). Firefox does not tolerate this,
+ // it removes the text shadows.
+ if (isWebKit) {
+ styles.textRendering = 'geometricPrecision';
+ }
+
/* Selective side-by-side testing in supported browser (http://jsfiddle.net/highcharts/73L1ptrh/)
if (elem.textContent.indexOf('2.') === 0) {
elem.style['text-shadow'] = 'none';
supports = false;
}
// */
// No reason to polyfill, we've got native support
if (supports) {
- if (hasContrast) { // Apply the altered style
- css(elem, {
- textShadow: textShadow
- });
- }
+ css(elem, styles); // Apply altered textShadow or textRendering workaround
} else {
this.fakeTS = true; // Fake text shadow
// In order to get the right y position of the clones,
@@ -3267,11 +3277,11 @@
} else {
if (tooLong) {
wasTooLong = true;
}
wordStr = span.substring(0, wordStr.length + (tooLong ? -1 : 1) * mathCeil(cursor));
- words = [wordStr + '\u2026'];
+ words = [wordStr + (width > 3 ? '\u2026' : '')];
tspan.removeChild(tspan.firstChild);
}
// Looping down, this is the first word sequence that is not too long,
// so we can move on to build the next line.
@@ -3363,11 +3373,11 @@
/**
* Returns white for dark colors and black for bright colors
*/
getContrast: function (color) {
color = Color(color).rgba;
- return color[0] + color[1] + color[2] > 384 ? '#000' : '#FFF';
+ return color[0] + color[1] + color[2] > 384 ? '#000000' : '#FFFFFF';
},
/**
* Create a button with preset states
* @param {String} text
@@ -3887,15 +3897,12 @@
halfDistance = 6,
r = mathMin((options && options.r) || 0, w, h),
safeDistance = r + halfDistance,
anchorX = options && options.anchorX,
anchorY = options && options.anchorY,
- path,
- normalizer = mathRound(options.strokeWidth || 0) % 2 / 2; // mathRound because strokeWidth can sometimes have roundoff errors;
+ path;
- x += normalizer;
- y += normalizer;
path = [
'M', x + r, y,
'L', x + w - r, y, // top side
'C', x + w, y, x + w, y, x + w, y + r, // top-right corner
'L', x + w, y + h - r, // right side
@@ -4119,12 +4126,12 @@
if (needsBox) {
// create the border box if it is not already present
if (!box) {
- boxX = mathRound(-alignFactor * padding);
- boxY = baseline ? -baselineOffset : 0;
+ boxX = mathRound(-alignFactor * padding) + crispAdjust;
+ boxY = (baseline ? -baselineOffset : 0) + crispAdjust;
wrapper.box = box = shape ?
renderer.symbol(shape, boxX, boxY, wrapper.width, wrapper.height, deferredAttr) :
renderer.rect(boxX, boxY, wrapper.width, wrapper.height, 0, deferredAttr[STROKE_WIDTH]);
box.attr('fill', NONE).add(wrapper);
@@ -4160,15 +4167,11 @@
// update if anything changed
if (x !== text.x || y !== text.y) {
text.attr('x', x);
if (y !== UNDEFINED) {
- // As a workaround for #3649, use translation instead of y attribute. #3649
- // is a rendering bug in WebKit for Retina (Mac, iOS, PhantomJS) that
- // results in duplicated text when an y attribute is used in combination
- // with a CSS text-style.
- text.attr(text.element.nodeName === 'SPAN' ? 'y' : 'translateY', y);
+ text.attr('y', y);
}
}
// record current values
text.x = x;
@@ -4261,11 +4264,11 @@
}
boxAttr(key, value);
};
wrapper.anchorXSetter = function (value, key) {
anchorX = value;
- boxAttr(key, value + crispAdjust - wrapperX);
+ boxAttr(key, mathRound(value) - crispAdjust - wrapperX);
};
wrapper.anchorYSetter = function (value, key) {
anchorY = value;
boxAttr(key, value - wrapperY);
};
@@ -5894,37 +5897,39 @@
label = this.label,
rotation = this.rotation,
factor = { left: 0, center: 0.5, right: 1 }[axis.labelAlign],
labelWidth = label.getBBox().width,
slotWidth = axis.slotWidth,
+ xCorrection = factor,
+ goRight = 1,
leftPos,
rightPos,
textWidth;
// Check if the label overshoots the chart spacing box. If it does, move it.
// If it now overshoots the slotWidth, add ellipsis.
if (!rotation) {
leftPos = pxPos - factor * labelWidth;
- rightPos = pxPos + factor * labelWidth;
+ rightPos = pxPos + (1 - factor) * labelWidth;
if (leftPos < leftBound) {
- slotWidth -= leftBound - leftPos;
- xy.x = leftBound;
- label.attr({ align: 'left' });
+ slotWidth = xy.x + slotWidth * (1 - factor) - leftBound;
} else if (rightPos > rightBound) {
- slotWidth -= rightPos - rightBound;
- xy.x = rightBound;
- label.attr({ align: 'right' });
+ slotWidth = rightBound - xy.x + slotWidth * factor;
+ goRight = -1;
}
+ slotWidth = mathMin(axis.slotWidth, slotWidth); // #4177
+ if (slotWidth < axis.slotWidth && axis.labelAlign === 'center') {
+ xy.x += goRight * (axis.slotWidth - slotWidth - xCorrection * (axis.slotWidth - mathMin(labelWidth, slotWidth)));
+ }
// If the label width exceeds the available space, set a text width to be
// picked up below. Also, if a width has been set before, we need to set a new
// one because the reported labelWidth will be limited by the box (#3938).
if (labelWidth > slotWidth || (axis.autoRotation && label.styles.width)) {
textWidth = slotWidth;
}
-
// Add ellipsis to prevent rotated labels to be clipped against the edge of the chart
} else if (rotation < 0 && pxPos - factor * labelWidth < leftBound) {
textWidth = mathRound(pxPos / mathCos(rotation * deg2rad) - leftBound);
} else if (rotation > 0 && pxPos + factor * labelWidth > rightBound) {
@@ -6907,11 +6912,11 @@
/**
* Translate from axis value to pixel position on the chart, or back
*
*/
translate: function (val, backwards, cvsCoord, old, handleLog, pointPlacement) {
- var axis = this,
+ var axis = this.linkedParent || this, // #1417
sign = 1,
cvsOffset = 0,
localA = old ? axis.oldTransA : axis.transA,
localMin = old ? axis.oldMin : axis.min,
returnValue,
@@ -7369,11 +7374,11 @@
// get tickInterval
if (axis.min === axis.max || axis.min === undefined || axis.max === undefined) {
axis.tickInterval = 1;
} else if (isLinked && !tickIntervalOption &&
tickPixelIntervalOption === axis.linkedParent.options.tickPixelInterval) {
- axis.tickInterval = axis.linkedParent.tickInterval;
+ axis.tickInterval = tickIntervalOption = axis.linkedParent.tickInterval;
} else {
axis.tickInterval = pick(
tickIntervalOption,
this.tickAmount ? ((axis.max - axis.min) / mathMax(this.tickAmount - 1, 1)) : undefined,
categories ? // for categoried axis, 1 is default, for linear axis use tickPix
@@ -7414,24 +7419,22 @@
if (!tickIntervalOption && axis.tickInterval < minTickInterval) {
axis.tickInterval = minTickInterval;
}
// for linear axes, get magnitude and normalize the interval
- if (!isDatetimeAxis && !isLog) { // linear
- if (!tickIntervalOption) {
- axis.tickInterval = normalizeTickInterval(
- axis.tickInterval,
- null,
- getMagnitude(axis.tickInterval),
- // If the tick interval is between 0.5 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 > 0.5 && axis.tickInterval < 5 && axis.max > 1000 && axis.max < 9999)),
- !!this.tickAmount
- );
- }
+ if (!isDatetimeAxis && !isLog && !tickIntervalOption) {
+ axis.tickInterval = normalizeTickInterval(
+ axis.tickInterval,
+ null,
+ getMagnitude(axis.tickInterval),
+ // If the tick interval is between 0.5 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 > 0.5 && axis.tickInterval < 5 && axis.max > 1000 && axis.max < 9999)),
+ !!this.tickAmount
+ );
}
-
+
// Prevent ticks from getting so close that we can't draw the labels
if (!this.tickAmount && this.len) { // Color axis with disabled legend has no length
axis.tickInterval = axis.unsquish();
}
@@ -7459,11 +7462,11 @@
// get minorTickInterval
this.minorTickInterval = options.minorTickInterval === 'auto' && this.tickInterval ?
this.tickInterval / 5 : options.minorTickInterval;
// Find the tick positions
- this.tickPositions = tickPositions = options.tickPositions && options.tickPositions.slice(); // Work on a copy (#1565)
+ this.tickPositions = tickPositions = tickPositionsOption && tickPositionsOption.slice(); // Work on a copy (#1565)
if (!tickPositions) {
if (this.isDatetimeAxis) {
tickPositions = this.getTimeTicks(
this.normalizeTimeTickInterval(this.tickInterval, options.units),
@@ -7561,11 +7564,13 @@
var options = axis.options,
horiz = axis.horiz,
key = [horiz ? options.left : options.top, horiz ? options.width : options.height, options.pane].join(',');
if (others[key]) {
- hasOther = true;
+ if (axis.series.length) {
+ hasOther = true; // #4201
+ }
} else {
others[key] = 1;
}
});
@@ -7937,12 +7942,13 @@
tickPositions = this.tickPositions,
ticks = this.ticks,
labelOptions = this.options.labels,
horiz = this.horiz,
margin = chart.margin,
+ slotCount = this.categories ? tickPositions.length : tickPositions.length - 1,
slotWidth = this.slotWidth = (horiz && !labelOptions.step && !labelOptions.rotation &&
- ((this.staggerLines || 1) * chart.plotWidth) / tickPositions.length) ||
+ ((this.staggerLines || 1) * chart.plotWidth) / slotCount) ||
(!horiz && ((margin[3] && (margin[3] - chart.spacing[3])) || chart.chartWidth * 0.33)), // #1580, #1931,
innerWidth = mathMax(1, mathRound(slotWidth - 2 * (labelOptions.padding || 5))),
attr = {},
labelMetrics = renderer.fontMetrics(labelOptions.style.fontSize, ticks[0] && ticks[0].label),
css,
@@ -8206,10 +8212,12 @@
axisLength = this.len,
axisTitleOptions = this.options.title,
margin = horiz ? axisLeft : axisTop,
opposite = this.opposite,
offset = this.offset,
+ xOption = axisTitleOptions.x || 0,
+ yOption = axisTitleOptions.y || 0,
fontSize = pInt(axisTitleOptions.style.fontSize || 12),
// the position in the length direction of the axis
alongAxis = {
low: margin + (horiz ? 0 : axisLength),
@@ -8224,16 +8232,15 @@
this.axisTitleMargin +
(this.side === 2 ? fontSize : 0);
return {
x: horiz ?
- alongAxis :
- offAxis + (opposite ? this.width : 0) + offset +
- (axisTitleOptions.x || 0), // x
+ alongAxis + xOption :
+ offAxis + (opposite ? this.width : 0) + offset + xOption,
y: horiz ?
- offAxis - (opposite ? this.height : 0) + offset :
- alongAxis + (axisTitleOptions.y || 0) // y
+ offAxis + yOption - (opposite ? this.height : 0) + offset :
+ alongAxis + yOption
};
},
/**
* Render the axis
@@ -8509,11 +8516,13 @@
if (
// Disabled in options
!this.crosshair ||
// Snap
- ((defined(point) || !pick(this.crosshair.snap, true)) === false)
+ ((defined(point) || !pick(this.crosshair.snap, true)) === false) ||
+ // Not on this axis (#4095, #2888)
+ (point && point.series && point.series[this.coll] !== this)
) {
this.hideCrosshair();
} else {
@@ -8998,20 +9007,10 @@
this.hideTimer = setTimeout(function () {
tooltip.label.fadeOut();
tooltip.isHidden = true;
}, pick(delay, this.options.hideDelay, 500));
-
- // hide previous hoverPoints and set new
- if (hoverPoints) {
- each(hoverPoints, function (point) {
- point.setState();
- });
- }
-
- this.chart.hoverPoints = null;
- this.chart.hoverSeries = null;
}
},
/**
* Extendable method to get the anchor position of the tooltip
@@ -9074,11 +9073,11 @@
getPosition: function (boxWidth, boxHeight, point) {
var chart = this.chart,
distance = this.distance,
ret = {},
- h = point.h,
+ h = point.h || 0, // #4117
swapped,
first = ['y', chart.chartHeight, boxHeight, point.plotY + chart.plotTop],
second = ['x', chart.chartWidth, boxWidth, point.plotX + chart.plotLeft],
// The far side is right or bottom
preferFarSide = pick(point.ttBelow, (chart.inverted && !point.negative) || (!chart.inverted && point.negative)),
@@ -9268,11 +9267,11 @@
tooltip.updatePosition({
plotX: x,
plotY: y,
negative: point.negative,
ttBelow: point.ttBelow,
- h: (point.shapeArgs && point.shapeArgs.height) || 0
+ h: anchor[2] || 0
});
this.isHidden = false;
}
fireEvent(chart, 'tooltipRefresh', {
@@ -9297,11 +9296,11 @@
);
// do the move
this.move(
mathRound(pos.x),
- mathRound(pos.y),
+ mathRound(pos.y || 0), // can be undefined (#3977)
point.plotX + chart.plotLeft,
point.plotY + chart.plotTop
);
},
@@ -9320,11 +9319,11 @@
minute: 9,
hour: 6,
day: 3
},
date,
- lastN;
+ lastN = 'millisecond'; // for sub-millisecond data, #4223
if (closestPointRange) {
date = dateFormat('%m-%d %H:%M:%S.%L', point.x);
for (n in timeUnits) {
@@ -9530,20 +9529,17 @@
chart = pointer.chart,
series = chart.series,
tooltip = chart.tooltip,
shared = tooltip ? tooltip.shared : false,
followPointer,
- //point,
- //points,
hoverPoint = chart.hoverPoint,
hoverSeries = chart.hoverSeries,
i,
- //j,
distance = chart.chartWidth,
- rdistance = chart.chartWidth,
anchor,
noSharedTooltip,
+ directTouch,
kdpoints = [],
kdpoint,
kdpointT;
// For hovering over the empty parts of the plot area (hoverSeries is undefined).
@@ -9555,42 +9551,39 @@
}
}
}
// If it has a hoverPoint and that series requires direct touch (like columns),
- // use the hoverPoint (#3899). Otherwise, search the k-d tree.
+ // use the hoverPoint (#3899). Otherwise, search the k-d tree.
if (!shared && hoverSeries && hoverSeries.directTouch && hoverPoint) {
kdpoint = hoverPoint;
// Handle shared tooltip or cases where a series is not yet hovered
} else {
// Find nearest points on all series
each(series, function (s) {
// Skip hidden series
noSharedTooltip = s.noSharedTooltip && shared;
- if (s.visible && !noSharedTooltip && pick(s.options.enableMouseTracking, true)) { // #3821
- kdpointT = s.searchPoint(e); // #3828
+ directTouch = !shared && s.directTouch;
+ if (s.visible && !noSharedTooltip && !directTouch && pick(s.options.enableMouseTracking, true)) { // #3821
+ kdpointT = s.searchPoint(e, !noSharedTooltip && s.kdDimensions === 1); // #3828
if (kdpointT) {
kdpoints.push(kdpointT);
}
}
});
// Find absolute nearest point
each(kdpoints, function (p) {
- if (p && defined(p.plotX) && defined(p.plotY)) {
- if ((p.dist.distX < distance) || ((p.dist.distX === distance || p.series.kdDimensions > 1) &&
- p.dist.distR < rdistance)) {
- distance = p.dist.distX;
- rdistance = p.dist.distR;
- kdpoint = p;
- }
+ if (p && typeof p.dist === 'number' && p.dist < distance) {
+ distance = p.dist;
+ kdpoint = p;
}
});
}
- // Refresh tooltip for kdpoint if new hover point or tooltip was hidden // #3926
- if (kdpoint && (kdpoint !== hoverPoint || (tooltip && tooltip.isHidden))) {
+ // Refresh tooltip for kdpoint if new hover point or tooltip was hidden // #3926, #4200
+ if (kdpoint && (kdpoint !== this.prevKDPoint || (tooltip && tooltip.isHidden))) {
// Draw tooltip if necessary
if (shared && !kdpoint.series.noSharedTooltip) {
i = kdpoints.length;
while (i--) {
if (kdpoints[i].clientX !== kdpoint.clientX || kdpoints[i].series.noSharedTooltip) {
@@ -9613,10 +9606,11 @@
if (tooltip) {
tooltip.refresh(kdpoint, e);
}
kdpoint.onMouseOver(e);
}
+ this.prevKDPoint = kdpoint;
// Update positions (regardless of kdpoint or hoverPoint)
} else {
followPointer = hoverSeries && hoverSeries.tooltipOptions.followPointer;
if (tooltip && followPointer && !tooltip.isHidden) {
@@ -9637,11 +9631,12 @@
// Crosshair
each(chart.axes, function (axis) {
axis.drawCrosshair(e, pick(kdpoint, hoverPoint));
});
-
+
+
},
/**
@@ -9652,12 +9647,13 @@
reset: function (allowMove, delay) {
var pointer = this,
chart = pointer.chart,
hoverSeries = chart.hoverSeries,
hoverPoint = chart.hoverPoint,
+ hoverPoints = chart.hoverPoints,
tooltip = chart.tooltip,
- tooltipPoints = tooltip && tooltip.shared ? chart.hoverPoints : hoverPoint;
+ tooltipPoints = tooltip && tooltip.shared ? hoverPoints : hoverPoint;
// Narrow in allowMove
allowMove = allowMove && tooltip && tooltipPoints;
// Check if the points have moved outside the plot area, #1003
@@ -9669,11 +9665,11 @@
tooltip.refresh(tooltipPoints);
if (hoverPoint) { // #2500
hoverPoint.setState(hoverPoint.state, true);
each(chart.axes, function (axis) {
if (pick(axis.options.crosshair && axis.options.crosshair.snap, true)) {
- axis.drawCrosshair(null, allowMove);
+ axis.drawCrosshair(null, hoverPoint);
} else {
axis.hideCrosshair();
}
});
@@ -9684,10 +9680,16 @@
if (hoverPoint) {
hoverPoint.onMouseOut();
}
+ if (hoverPoints) {
+ each(hoverPoints, function (point) {
+ point.setState();
+ });
+ }
+
if (hoverSeries) {
hoverSeries.onMouseOut();
}
if (tooltip) {
@@ -9702,11 +9704,11 @@
// Remove crosshairs
each(chart.axes, function (axis) {
axis.hideCrosshair();
});
- pointer.hoverX = null;
+ pointer.hoverX = chart.hoverPoints = chart.hoverPoint = null;
}
},
/**
@@ -10005,11 +10007,10 @@
plotLeft = chart.plotLeft,
plotTop = chart.plotTop;
e = this.normalize(e);
e.originalEvent = e; // #3913
- e.cancelBubble = true; // IE specific
if (!chart.cancelClick) {
// On tracker click, fire the series and point events. #783, #1583
if (hoverPoint && this.inClass(e.target, PREFIX + 'tracker')) {
@@ -10206,12 +10207,18 @@
transform = {},
fireClickEvent = touchesLength === 1 && ((self.inClass(e.target, PREFIX + 'tracker') &&
chart.runTrackerClick) || self.runChartClick),
clip = {};
+ // Don't initiate panning until the user has pinched. This prevents us from
+ // blocking page scrolling as users scroll down a long page (#4210).
+ if (touchesLength > 1) {
+ self.initiated = true;
+ }
+
// On touch devices, only proceed to trigger click if a handler is defined
- if (hasZoom && !fireClickEvent) {
+ if (hasZoom && self.initiated && !fireClickEvent) {
e.preventDefault();
}
// Normalize each touch
map(touches, function (e) {
@@ -10269,11 +10276,14 @@
this.reset(false, 0);
}
}
},
- onContainerTouchStart: function (e) {
+ /**
+ * General touch handler shared by touchstart and touchmove.
+ */
+ touch: function (e, start) {
var chart = this.chart;
hoverChartIndex = chart.index;
if (e.touches.length === 1) {
@@ -10281,28 +10291,32 @@
e = this.normalize(e);
if (chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop) && !chart.openMenu) {
// Run mouse events and display tooltip etc
- this.runPointActions(e);
+ if (start) {
+ this.runPointActions(e);
+ }
this.pinch(e);
- } else {
+ } else if (start) {
// Hide the tooltip on touching outside the plot area (#1203)
this.reset();
}
} else if (e.touches.length === 2) {
this.pinch(e);
- }
+ }
},
+ onContainerTouchStart: function (e) {
+ this.touch(e, true);
+ },
+
onContainerTouchMove: function (e) {
- if (e.touches.length === 1 || e.touches.length === 2) {
- this.pinch(e);
- }
+ this.touch(e);
},
onDocumentTouchEnd: function (e) {
if (charts[hoverChartIndex]) {
charts[hoverChartIndex].pointer.drop(e);
@@ -10503,14 +10517,15 @@
symbolPadding = options.symbolPadding,
ltr = !options.rtl,
legendItemPos = item._legendItemPos,
itemX = legendItemPos[0],
itemY = legendItemPos[1],
- checkbox = item.checkbox;
+ checkbox = item.checkbox,
+ legendGroup = item.legendGroup;
- if (item.legendGroup) {
- item.legendGroup.translate(
+ if (legendGroup && legendGroup.element) {
+ legendGroup.translate(
ltr ? itemX : legend.legendWidth - itemX - 2 * symbolPadding - 4,
itemY
);
}
@@ -10538,20 +10553,10 @@
discardElement(item.checkbox);
}
},
/**
- * Destroy all items.
- */
- clearItems: function () {
- var legend = this;
- each(legend.getAllItems(), function (item) {
- legend.destroyItem(item);
- });
- },
-
- /**
* Destroys the legend.
*/
destroy: function () {
var legend = this,
legendGroup = legend.group,
@@ -10616,10 +10621,20 @@
}
this.titleHeight = titleHeight;
},
/**
+ * Set the legend item text
+ */
+ setText: function (item) {
+ var options = this.options;
+ item.legendItem.attr({
+ text: options.labelFormat ? format(options.labelFormat, item) : options.labelFormatter.call(item)
+ });
+ },
+
+ /**
* Render a single specific legend item
* @param {Object} item A series or point
*/
renderItem: function (item) {
var legend = this,
@@ -10655,11 +10670,11 @@
.attr({ zIndex: 1 })
.add(legend.scrollGroup);
// Generate the list item text and add it to the group
item.legendItem = li = renderer.text(
- options.labelFormat ? format(options.labelFormat, item) : options.labelFormatter.call(item),
+ '',
ltr ? symbolWidth + symbolPadding : -symbolPadding,
legend.baseline || 0,
useHTML
)
.css(merge(item.visible ? itemStyle : itemHiddenStyle)) // merge to prevent modifying original (#1021)
@@ -10690,10 +10705,13 @@
if (showCheckbox) {
legend.createCheckboxForItem(item);
}
}
+ // Always update the text
+ legend.setText(item);
+
// calculate the positions for the next line
bBox = li.getBBox();
itemWidth = item.checkboxOffset =
options.itemWidth ||
@@ -11400,14 +11418,17 @@
serie.isDirty = true;
}
}
}
- // handle updated data in the series
+ // Handle updated data in the series
each(series, function (serie) {
- if (serie.isDirty) { // prepare the data so axis can read it
+ if (serie.isDirty) {
if (serie.options.legendType === 'point') {
+ if (serie.updateTotals) {
+ serie.updateTotals();
+ }
redrawLegend = true;
}
}
});
@@ -12636,26 +12657,24 @@
plotWidth = chart.plotWidth - 2 * slicingRoom,
plotHeight = chart.plotHeight - 2 * slicingRoom,
centerOption = options.center,
positions = [pick(centerOption[0], '50%'), pick(centerOption[1], '50%'), options.size || '100%', options.innerSize || 0],
smallestSize = mathMin(plotWidth, plotHeight),
- isPercent,
i,
value;
for (i = 0; i < 4; ++i) {
value = positions[i];
- isPercent = /%$/.test(value);
- handleSlicingRoom = i < 2 || (i === 2 && isPercent);
- positions[i] = (isPercent ?
- // i == 0: centerX, relative to width
- // i == 1: centerY, relative to height
- // i == 2: size, relative to smallestSize
- // i == 3: innerSize, relative to size
- [plotWidth, plotHeight, smallestSize, positions[2]][i] *
- pInt(value) / 100 :
- pInt(value)) + (handleSlicingRoom ? slicingRoom : 0);
+ handleSlicingRoom = i < 2 || (i === 2 && /%$/.test(value));
+
+ // i == 0: centerX, relative to width
+ // i == 1: centerY, relative to height
+ // i == 2: size, relative to smallestSize
+ // i == 3: innerSize, relative to size
+ positions[i] = relativeLength(value, [plotWidth, plotHeight, smallestSize, positions[2]][i]) +
+ (handleSlicingRoom ? slicingRoom : 0);
+
}
return positions;
}
};
@@ -12726,11 +12745,11 @@
* Transform number or array configs into objects
*/
optionsToObject: function (options) {
var ret = {},
series = this.series,
- keys = series.options.keys, // docs: http://jsfiddle.net/ch4v7n8v/1
+ keys = series.options.keys,
pointArrayMap = keys || series.pointArrayMap || ['y'],
valueCount = pointArrayMap.length,
firstItemType,
i = 0,
j = 0;
@@ -13589,12 +13608,12 @@
y = yData[i];
// For points within the visible range, including the first point outside the
// visible range, consider y extremes
validValue = y !== null && y !== UNDEFINED && (!yAxis.isLog || (y.length || y > 0));
- withinRange = this.getExtremesFromAll || this.cropped || ((xData[i + 1] || x) >= xMin &&
- (xData[i - 1] || x) <= xMax);
+ withinRange = this.getExtremesFromAll || this.options.getExtremesFromAll || this.cropped ||
+ ((xData[i + 1] || x) >= xMin && (xData[i - 1] || x) <= xMax);
if (validValue && withinRange) {
j = y.length;
if (j) { // array, like ohlc or range data
@@ -13632,10 +13651,11 @@
hasModifyValue = !!series.modifyValue,
i,
pointPlacement = options.pointPlacement,
dynamicallyPlaced = pointPlacement === 'between' || isNumber(pointPlacement),
threshold = options.threshold,
+ stackThreshold = options.startFromThreshold ? threshold : 0,
plotX,
plotY,
lastPlotX,
closestPointRangePx = Number.MAX_VALUE;
@@ -13643,33 +13663,33 @@
for (i = 0; i < dataLength; i++) {
var point = points[i],
xValue = point.x,
yValue = point.y,
yBottom = point.low,
- stack = stacking && yAxis.stacks[(series.negStacks && yValue < threshold ? '-' : '') + series.stackKey],
+ stack = stacking && yAxis.stacks[(series.negStacks && yValue < (stackThreshold ? 0 : threshold) ? '-' : '') + series.stackKey],
pointStack,
stackValues;
// Discard disallowed y values for log axes (#3434)
if (yAxis.isLog && yValue !== null && yValue <= 0) {
point.y = yValue = null;
error(10);
}
// Get the plotX translation
- point.plotX = plotX = xAxis.translate(xValue, 0, 0, 0, 1, pointPlacement, this.type === 'flags'); // Math.round fixes #591
+ point.plotX = plotX = mathMin(mathMax(-1e5, xAxis.translate(xValue, 0, 0, 0, 1, pointPlacement, this.type === 'flags')), 1e5); // #3923
// Calculate the bottom y value for stacked series
if (stacking && series.visible && stack && stack[xValue]) {
pointStack = stack[xValue];
stackValues = pointStack.points[series.index + ',' + i];
yBottom = stackValues[0];
yValue = stackValues[1];
- if (yBottom === 0) {
+ if (yBottom === stackThreshold) {
yBottom = pick(threshold, yAxis.min);
}
if (yAxis.isLog && yBottom <= 0) { // #1200, #1232
yBottom = null;
}
@@ -14318,12 +14338,17 @@
graph = this.graph,
area = this.area,
chartSizeMax = mathMax(chart.chartWidth, chart.chartHeight),
zoneAxis = this.zoneAxis || 'y',
axis = this[zoneAxis + 'Axis'],
+ extremes,
reversed = axis.reversed,
+ inverted = chart.inverted,
horiz = axis.horiz,
+ pxRange,
+ pxPosMin,
+ pxPosMax,
ignoreZones = false;
if (zones.length && (graph || area)) {
// The use of the Color Threshold assumes there are no gaps
// so it is safe to hide the original graph and area
@@ -14333,52 +14358,54 @@
if (area) {
area.hide();
}
// Create the clips
+ extremes = axis.getExtremes();
each(zones, function (threshold, i) {
- translatedFrom = pick(translatedTo, (reversed ? (horiz ? chart.plotWidth : 0) : (horiz ? 0 : axis.toPixels(axis.min))));
- translatedTo = mathRound(axis.toPixels(pick(threshold.value, axis.max), true));
- if (axis.isXAxis) {
- translatedFrom = translatedFrom > translatedTo ? translatedTo : translatedFrom; //#4006 from should be less or equal then to
- } else {
- translatedFrom = translatedFrom < translatedTo ? translatedTo : translatedFrom; //#4006 from should be less or equal then to
- }
-
+ translatedFrom = reversed ?
+ (horiz ? chart.plotWidth : 0) :
+ (horiz ? 0 : axis.toPixels(extremes.min));
+ translatedFrom = mathMin(mathMax(pick(translatedTo, translatedFrom), 0), chartSizeMax);
+ translatedTo = mathMin(mathMax(mathRound(axis.toPixels(pick(threshold.value, extremes.max), true)), 0), chartSizeMax);
+
if (ignoreZones) {
- translatedFrom = translatedTo = axis.toPixels(axis.max);
+ translatedFrom = translatedTo = axis.toPixels(extremes.max);
}
+ pxRange = Math.abs(translatedFrom - translatedTo);
+ pxPosMin = mathMin(translatedFrom, translatedTo);
+ pxPosMax = mathMax(translatedFrom, translatedTo);
if (axis.isXAxis) {
clipAttr = {
- x: reversed ? translatedTo : translatedFrom,
+ x: inverted ? pxPosMax : pxPosMin,
y: 0,
- width: Math.abs(translatedFrom - translatedTo),
+ width: pxRange,
height: chartSizeMax
};
if (!horiz) {
clipAttr.x = chart.plotHeight - clipAttr.x;
}
} else {
clipAttr = {
x: 0,
- y: reversed ? translatedFrom : translatedTo,
+ y: inverted ? pxPosMax : pxPosMin,
width: chartSizeMax,
- height: Math.abs(translatedFrom - translatedTo)
- };
+ height: pxRange
+ };
if (horiz) {
clipAttr.y = chart.plotWidth - clipAttr.y;
}
- }
+ }
/// VML SUPPPORT
if (chart.inverted && renderer.isVML) {
if (axis.isXAxis) {
clipAttr = {
x: 0,
- y: reversed ? translatedFrom : translatedTo,
+ y: reversed ? pxPosMin : pxPosMax,
height: clipAttr.width,
width: chart.chartWidth
};
} else {
clipAttr = {
@@ -14403,11 +14430,11 @@
if (area) {
series['zoneArea' + i].clip(clips[i]);
}
}
// if this zone extends out of the axis, ignore the others
- ignoreZones = threshold.value > axis.max;
+ ignoreZones = threshold.value > extremes.max;
});
this.clips = clips;
}
},
@@ -14637,24 +14664,22 @@
/**
* KD Tree && PointSearching Implementation
*/
kdDimensions: 1,
- kdTree: null,
kdAxisArray: ['clientX', 'plotY'],
- kdComparer: 'distX',
- searchPoint: function (e) {
+ searchPoint: function (e, compareX) {
var series = this,
xAxis = series.xAxis,
yAxis = series.yAxis,
inverted = series.chart.inverted;
return this.searchKDTree({
clientX: inverted ? xAxis.len - e.chartY + xAxis.pos : e.chartX - xAxis.pos,
plotY: inverted ? yAxis.len - e.chartX + yAxis.pos : e.chartY - yAxis.pos
- });
+ }, compareX);
},
buildKDTree: function () {
var series = this,
dimensions = series.kdDimensions;
@@ -14673,11 +14698,11 @@
return a[axis] - b[axis];
});
median = Math.floor(length / 2);
- // build and return node
+ // build and return nod
return {
point: points[median],
left: _kdtree(points.slice(0, median), depth + 1, dimensions),
right: _kdtree(points.slice(median + 1), depth + 1, dimensions)
};
@@ -14688,70 +14713,68 @@
// Start the recursive build process with a clone of the points array and null points filtered out (#3873)
function startRecursive() {
var points = grep(series.points, function (point) {
return point.y !== null;
});
- series.kdTree = _kdtree(points, dimensions, dimensions);
- }
+ series.kdTree = _kdtree(points, dimensions, dimensions);
+ }
delete series.kdTree;
if (series.options.kdSync) { // For testing tooltips, don't build async
startRecursive();
} else {
setTimeout(startRecursive);
}
},
- searchKDTree: function (point) {
+ searchKDTree: function (point, compareX) {
var series = this,
- kdComparer = this.kdComparer,
kdX = this.kdAxisArray[0],
- kdY = this.kdAxisArray[1];
+ kdY = this.kdAxisArray[1],
+ kdComparer = compareX ? 'distX' : 'dist';
- // Internal function
- function _distance(p1, p2) {
+ // Set the one and two dimensional distance on the point object
+ function setDistance(p1, p2) {
var x = (defined(p1[kdX]) && defined(p2[kdX])) ? Math.pow(p1[kdX] - p2[kdX], 2) : null,
y = (defined(p1[kdY]) && defined(p2[kdY])) ? Math.pow(p1[kdY] - p2[kdY], 2) : null,
r = (x || 0) + (y || 0);
-
- return {
- distX: defined(x) ? Math.sqrt(x) : Number.MAX_VALUE,
- distY: defined(y) ? Math.sqrt(y) : Number.MAX_VALUE,
- distR: defined(r) ? Math.sqrt(r) : Number.MAX_VALUE
- };
+
+ p2.dist = defined(r) ? Math.sqrt(r) : Number.MAX_VALUE;
+ p2.distX = defined(x) ? Math.sqrt(x) : Number.MAX_VALUE;
}
function _search(search, tree, depth, dimensions) {
var point = tree.point,
axis = series.kdAxisArray[depth % dimensions],
tdist,
sideA,
sideB,
ret = point,
nPoint1,
nPoint2;
- point.dist = _distance(search, point);
+
+ setDistance(search, point);
// Pick side based on distance to splitting point
tdist = search[axis] - point[axis];
sideA = tdist < 0 ? 'left' : 'right';
+ sideB = tdist < 0 ? 'right' : 'left';
// End of tree
if (tree[sideA]) {
nPoint1 =_search(search, tree[sideA], depth + 1, dimensions);
- ret = (nPoint1.dist[kdComparer] < ret.dist[kdComparer] ? nPoint1 : point);
-
- sideB = tdist < 0 ? 'right' : 'left';
- if (tree[sideB]) {
- // compare distance to current best to splitting point to decide wether to check side B or not
- if (Math.sqrt(tdist*tdist) < ret.dist[kdComparer]) {
- nPoint2 = _search(search, tree[sideB], depth + 1, dimensions);
- ret = (nPoint2.dist[kdComparer] < ret.dist[kdComparer] ? nPoint2 : ret);
- }
+ ret = (nPoint1[kdComparer] < ret[kdComparer] ? nPoint1 : point);
+ }
+ if (tree[sideB]) {
+ // compare distance to current best to splitting point to decide wether to check side B or not
+ if (Math.sqrt(tdist * tdist) < ret[kdComparer]) {
+ nPoint2 = _search(search, tree[sideB], depth + 1, dimensions);
+ ret = (nPoint2[kdComparer] < ret[kdComparer] ? nPoint2 : ret);
}
}
+
return ret;
}
if (!this.kdTree) {
this.buildKDTree();
@@ -14945,10 +14968,11 @@
yData = series.processedYData,
stackedYData = [],
yDataLength = yData.length,
seriesOptions = series.options,
threshold = seriesOptions.threshold,
+ stackThreshold = seriesOptions.startFromThreshold ? threshold : 0,
stackOption = seriesOptions.stack,
stacking = seriesOptions.stacking,
stackKey = series.stackKey,
negKey = '-' + stackKey,
negStacks = series.negStacks,
@@ -14970,11 +14994,11 @@
y = yData[i];
pointKey = series.index + ',' + i;
// Read stacked values into a stack based on the x value,
// the sign of y and the stack key. Stacking is also handled for null values (#739)
- isNegative = negStacks && y < threshold;
+ isNegative = negStacks && y < (stackThreshold ? 0 : threshold);
key = isNegative ? negKey : stackKey;
// Create empty object for this stack if it doesn't exist yet
if (!stacks[key]) {
stacks[key] = {};
@@ -14990,12 +15014,15 @@
}
}
// If the StackItem doesn't exist, create it first
stack = stacks[key][x];
- stack.points[pointKey] = [stack.cum || 0];
+ //stack.points[pointKey] = [stack.cum || stackThreshold];
+ stack.points[pointKey] = [pick(stack.cum, stackThreshold)];
+
+
// Add value to the stack total
if (stacking === 'percent') {
// Percent stacked column, totals are the same for the positive and negative stacks
other = isNegative ? stackKey : negKey;
@@ -15009,11 +15036,11 @@
}
} else {
stack.total = correctFloat(stack.total + (y || 0));
}
- stack.cum = (stack.cum || 0) + (y || 0);
+ stack.cum = pick(stack.cum, stackThreshold) + (y || 0);
stack.points[pointKey].push(stack.cum);
stackedYData[i] = stack.cum;
}
@@ -15221,18 +15248,21 @@
function update() {
point.applyOptions(options);
// Update visuals
+ if (point.y === null && graphic) { // #4146
+ point.graphic = graphic.destroy();
+ }
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 || '']);
+ graphic.attr(point.pointAttr[point.state || ''])[point.visible ? 'show' : 'hide'](true); // #2430
}
}
if (options && options.dataLabels && point.dataLabel) { // #2468
point.dataLabel = point.dataLabel.destroy();
}
@@ -15253,13 +15283,12 @@
series.isDirty = series.isDirtyData = true;
if (!series.fixedBox && series.hasCartesianSeries) { // #1906, #2320
chart.isDirtyBox = true;
}
- if (chart.legend.display && seriesOptions.legendType === 'point') { // #1831, #1885, #3934
- series.updateTotals();
- chart.legend.clearItems();
+ if (seriesOptions.legendType === 'point') { // #1831, #1885
+ chart.isDirtyLegend = true;
}
if (redraw) {
chart.redraw(animation);
}
}
@@ -15984,10 +16013,11 @@
dataLabels: {
align: null, // auto
verticalAlign: null, // auto
y: null
},
+ startFromThreshold: true, // docs: http://jsfiddle.net/highcharts/hz8fopan/14/
stickyTracking: false,
tooltip: {
distance: 6
},
threshold: 0
@@ -16135,11 +16165,12 @@
Series.prototype.translate.apply(series);
// Record the new values
each(series.points, function (point) {
var yBottom = pick(point.yBottom, translatedThreshold),
- plotY = mathMin(mathMax(-999 - yBottom, point.plotY), yAxis.len + 999 + yBottom), // Don't draw too far outside plot area (#1303, #2241)
+ safeDistance = 999 + mathAbs(yBottom),
+ plotY = mathMin(mathMax(-safeDistance, point.plotY), yAxis.len + safeDistance), // Don't draw too far outside plot area (#1303, #2241, #4264)
barX = point.plotX + pointXOffset,
barW = seriesBarW,
barY = mathMin(plotY, yBottom),
right,
bottom,
@@ -16161,15 +16192,10 @@
// Cache for access in polar
point.barX = barX;
point.pointWidth = pointWidth;
- // Fix the tooltip on center of grouped columns (#1216, #424, #3648)
- point.tooltipPos = chart.inverted ?
- [yAxis.len + yAxis.pos - chart.plotLeft - 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;
@@ -16182,19 +16208,23 @@
if (fromTop) {
barY -= 1;
barH += 1;
}
+ // Fix the tooltip on center of grouped columns (#1216, #424, #3648)
+ point.tooltipPos = chart.inverted ?
+ [yAxis.len + yAxis.pos - chart.plotLeft - plotY, series.xAxis.len - barX - barW / 2, barH] :
+ [barX + barW / 2, plotY + yAxis.pos - chart.plotTop, barH];
+
// Register shape type and arguments to be used in drawPoints
point.shapeType = 'rect';
point.shapeArgs = {
x: barX,
y: barY,
width: barW,
height: barH
};
-
});
},
getSymbol: noop,
@@ -16349,11 +16379,10 @@
requireSorting: false,
noSharedTooltip: true,
trackerGroups: ['group', 'markerGroup', 'dataLabelsGroup'],
takeOrdinalPosition: false, // #2342
kdDimensions: 2,
- kdComparer: 'distR',
drawGraph: function () {
if (this.options.lineWidth) {
Series.prototype.drawGraph.call(this);
}
}
@@ -16435,44 +16464,42 @@
/**
* Toggle the visibility of the pie slice
* @param {Boolean} vis Whether to show the slice or not. If undefined, the
* visibility is toggled
*/
- setVisible: function (vis, force) {
+ setVisible: function (vis, redraw) {
var point = this,
series = point.series,
chart = series.chart,
- doRedraw = !series.isDirty && series.options.ignoreHiddenPoint;
+ ignoreHiddenPoint = series.options.ignoreHiddenPoint;
+
+ redraw = pick(redraw, ignoreHiddenPoint);
- // Only if the value has changed
- if (vis !== point.visible || force) {
-
+ if (vis !== point.visible) {
+
// If called without an argument, toggle visibility
point.visible = point.options.visible = vis = vis === UNDEFINED ? !point.visible : vis;
series.options.data[inArray(point, series.data)] = point.options; // update userOptions.data
- // Show and hide associated elements
+ // Show and hide associated elements. This is performed regardless of redraw or not,
+ // because chart.redraw only handles full series.
each(['graphic', 'dataLabel', 'connector', 'shadowGroup'], function (key) {
if (point[key]) {
point[key][vis ? 'show' : 'hide'](true);
}
});
if (point.legendItem) {
- if (chart.hasRendered) {
- series.updateTotals();
- chart.legend.clearItems();
- if (!doRedraw) {
- chart.legend.render();
- }
- }
chart.legend.colorizeItem(point, vis);
}
-
+
// Handle ignore hidden slices
- if (doRedraw) {
+ if (ignoreHiddenPoint) {
series.isDirty = true;
+ }
+
+ if (redraw) {
chart.redraw();
}
}
},
@@ -16527,10 +16554,11 @@
var PieSeries = {
type: 'pie',
isCartesian: false,
pointClass: PiePoint,
requireSorting: false,
+ directTouch: true,
noSharedTooltip: true,
trackerGroups: ['group', 'dataLabelsGroup'],
axisTypes: [],
pointAttrToOptions: { // mapping between SVG attributes and the corresponding options
stroke: 'borderColor',
@@ -16595,36 +16623,25 @@
* Recompute total chart sum and update percentages of points.
*/
updateTotals: function () {
var i,
total = 0,
- points,
- len,
+ points = this.points,
+ len = points.length,
point,
ignoreHiddenPoint = this.options.ignoreHiddenPoint;
- // Populate local vars
- points = this.points;
- len = points.length;
-
// Get the total sum
for (i = 0; i < len; i++) {
point = points[i];
-
- // Disallow negative values (#1530, #3623)
- if (point.y < 0) {
- point.y = null;
- }
-
total += (ignoreHiddenPoint && !point.visible) ? 0 : point.y;
}
this.total = total;
// Set each point's properties
for (i = 0; i < len; i++) {
point = points[i];
- //point.percentage = (total <= 0 || ignoreHiddenPoint && !point.visible) ? 0 : point.y / total * 100;
point.percentage = (total > 0 && (point.visible || !ignoreHiddenPoint)) ? point.y / total * 100 : 0;
point.total = total;
}
},
@@ -16760,22 +16777,20 @@
//center,
graphic,
//group,
shadow = series.options.shadow,
shadowGroup,
- shapeArgs;
+ shapeArgs,
+ attr;
if (shadow && !series.shadowGroup) {
series.shadowGroup = renderer.g('shadow')
.add(series.group);
}
// draw the slices
each(series.points, function (point) {
-
- var visible = point.options.visible;
-
graphic = point.graphic;
shapeArgs = point.shapeArgs;
shadowGroup = point.shadowGroup;
// put the shadow behind all points
@@ -16795,31 +16810,28 @@
shadowGroup.attr(groupTranslation);
}
// draw the slice
if (graphic) {
- graphic.animate(extend(shapeArgs, groupTranslation));
+ graphic.animate(extend(shapeArgs, groupTranslation));
} else {
+ attr = { 'stroke-linejoin': 'round' };
+ if (!point.visible) {
+ attr.visibility = 'hidden';
+ }
+
point.graphic = graphic = renderer[point.shapeType](shapeArgs)
.setRadialReference(series.center)
.attr(
point.pointAttr[point.selected ? SELECT_STATE : NORMAL_STATE]
)
- .attr({
- 'stroke-linejoin': 'round'
- //zIndex: 1 // #2722 (reversed)
- })
+ .attr(attr)
.attr(groupTranslation)
.add(series.group)
.shadow(shadow, shadowGroup);
}
- // Detect point specific visibility (#2430)
- if (visible !== undefined) {
- point.setVisible(visible, true);
- }
-
});
},
@@ -17368,11 +17380,10 @@
y = naturalY;
}
// get the x - use the natural x position for first and last slot, to prevent the top
// and botton slice connectors from touching each other on either side
- // Problem: Should check that it makes sense - http://jsfiddle.net/highcharts/n1y6ngxz/
x = options.justify ?
seriesCenter[0] + (i ? -1 : 1) * (radius + distanceOption) :
series.getX(y === centerY - radius - distanceOption || y === centerY + radius + distanceOption ? naturalY : y, i);
@@ -17426,11 +17437,11 @@
each(this.points, function (point) {
connector = point.connector;
labelPos = point.labelPos;
dataLabel = point.dataLabel;
- if (dataLabel && dataLabel._pos) {
+ if (dataLabel && dataLabel._pos && point.visible) {
visibility = dataLabel._attr.visibility;
x = dataLabel.connX;
y = dataLabel.connY;
connectorPath = softConnector ? [
M,
@@ -17477,11 +17488,11 @@
seriesTypes.pie.prototype.placeDataLabels = function () {
each(this.points, function (point) {
var dataLabel = point.dataLabel,
_pos;
- if (dataLabel) {
+ if (dataLabel && point.visible) {
_pos = dataLabel._pos;
if (_pos) {
dataLabel.attr(dataLabel._attr);
dataLabel[dataLabel.moved ? 'animate' : 'attr'](_pos);
dataLabel.moved = true;
@@ -17536,10 +17547,11 @@
}
// If the size must be decreased, we need to run translate and drawDataLabels again
if (newSize < center[2]) {
center[2] = newSize;
+ center[3] = relativeLength(options.innerSize || 0, newSize);
this.translate(center);
each(this.points, function (point) {
if (point.dataLabel) {
point.dataLabel._pos = null; // reset
}
@@ -17563,11 +17575,11 @@
*/
seriesTypes.column.prototype.alignDataLabel = function (point, dataLabel, options, alignTo, isNew) {
var inverted = this.chart.inverted,
series = point.series,
dlBox = point.dlBox || point.shapeArgs, // data label box for alignment
- below = point.below || (point.plotY > pick(this.translatedThreshold, series.yAxis.len)),
+ below = pick(point.below, point.plotY > pick(this.translatedThreshold, series.yAxis.len)), // point.below is used in range series
inside = pick(options.inside, !!this.options.stacking); // draw it inside the box?
// Align to the column itself, or the top of it
if (dlBox) { // Area range uses this method but not alignTo
alignTo = merge(dlBox);
@@ -17611,11 +17623,11 @@
}
/**
- * Highcharts JS v4.1.5 (2015-04-13)
+ * Highcharts JS v4.1.6 (2015-06-12)
* Highcharts module to hide overlapping data labels. This module is included by default in Highmaps.
*
* (c) 2010-2014 Torstein Honsi
*
* License: www.highcharts.com/license
@@ -17623,10 +17635,11 @@
/*global Highcharts, HighchartsAdapter */
(function (H) {
var Chart = H.Chart,
each = H.each,
+ pick = H.pick,
addEvent = HighchartsAdapter.addEvent;
// Collect potensial overlapping data labels. Stack labels probably don't need to be
// considered because they are usually accompanied by data labels that lie inside the columns.
Chart.prototype.callbacks.push(function (chart) {
@@ -17636,11 +17649,11 @@
each(chart.series, function (series) {
var dlOptions = series.options.dataLabels;
if ((dlOptions.enabled || series._hasPointLabels) && !dlOptions.allowOverlap && series.visible) { // #3866
each(series.points, function (point) {
if (point.dataLabel) {
- point.dataLabel.labelrank = point.labelrank;
+ point.dataLabel.labelrank = pick(point.labelrank, point.shapeArgs && point.shapeArgs.height); // #4118
labels.push(point.dataLabel);
}
});
}
});
@@ -17683,10 +17696,17 @@
label.oldOpacity = label.opacity;
label.newOpacity = 1;
}
}
+ // Prevent a situation in a gradually rising slope, that each label
+ // will hide the previous one because the previous one always has
+ // lower rank.
+ labels.sort(function (a, b) {
+ return b.labelrank - a.labelrank;
+ });
+
// Detect overlapping labels
for (i = 0; i < len; i++) {
label1 = labels[i];
for (j = i + 1; j < len; ++j) {
@@ -18351,10 +18371,12 @@
options = series.options,
chart = series.chart,
tooltip = chart.tooltip,
hoverPoint = chart.hoverPoint;
+ chart.hoverSeries = null; // #182, set to null before the mouseOut event fires
+
// trigger mouse out on the point, which must be in this series
if (hoverPoint) {
hoverPoint.onMouseOut();
}
@@ -18369,10 +18391,9 @@
tooltip.hide();
}
// set normal state
series.setState();
- chart.hoverSeries = null;
},
/**
* Set the state of the graph
*/