app/assets/javascripts/highcharts.js in highcharts-rails-4.1.8 vs app/assets/javascripts/highcharts.js in highcharts-rails-4.1.9
- old
+ new
@@ -1,10 +1,10 @@
// ==ClosureCompiler==
// @compilation_level SIMPLE_OPTIMIZATIONS
/**
- * @license Highcharts JS v4.1.8 (2015-08-20)
+ * @license Highcharts JS v4.1.9 (2015-10-07)
*
* (c) 2009-2014 Torstein Honsi
*
* License: www.highcharts.com/license
*/
@@ -31,19 +31,19 @@
// some variables
userAgent = navigator.userAgent,
isOpera = win.opera,
- isIE = /(msie|trident)/i.test(userAgent) && !isOpera,
+ isMS = /(msie|trident|edge)/i.test(userAgent) && !isOpera,
docMode8 = doc.documentMode === 8,
- isWebKit = /AppleWebKit/.test(userAgent),
+ isWebKit = !isMS && /AppleWebKit/.test(userAgent),
isFirefox = /Firefox/.test(userAgent),
isTouchDevice = /(Mobile|Android|Windows Phone)/.test(userAgent),
SVG_NS = 'http://www.w3.org/2000/svg',
hasSVG = !!doc.createElementNS && !!doc.createElementNS(SVG_NS, 'svg').createSVGRect,
hasBidiBug = isFirefox && parseInt(userAgent.split('Firefox/')[1], 10) < 4, // issue #38
- useCanVG = !hasSVG && !isIE && !!doc.createElement('canvas').getContext,
+ useCanVG = !hasSVG && !isMS && !!doc.createElement('canvas').getContext,
Renderer,
hasTouch,
symbolSizes = {},
idCounter = 0,
garbageBin,
@@ -53,11 +53,11 @@
timeUnits,
noop = function () { return UNDEFINED; },
charts = [],
chartCount = 0,
PRODUCT = 'Highcharts',
- VERSION = '4.1.8',
+ VERSION = '4.1.9',
// some constants for frequently used strings
DIV = 'div',
ABSOLUTE = 'absolute',
RELATIVE = 'relative',
@@ -310,11 +310,11 @@
* Set CSS on a given element
* @param {Object} el
* @param {Object} styles Style object with camel case property names
*/
function css(el, styles) {
- if (isIE && !hasSVG) { // #2686
+ if (isMS && !hasSVG) { // #2686
if (styles && styles.opacity !== UNDEFINED) {
styles.filter = 'alpha(opacity=' + (styles.opacity * 100) + ')';
}
}
extend(el.style, styles);
@@ -1116,11 +1116,11 @@
// never uses these properties, Chrome includes them in the default click event and
// raises the warning when they are copied over in the extend statement below.
//
// To avoid problems in IE (see #1010) where we cannot delete the properties and avoid
// testing if they are there (warning in chrome) the only option is to test if running IE.
- if (!isIE && eventArguments) {
+ if (!isMS && eventArguments) {
delete eventArguments.layerX;
delete eventArguments.layerY;
delete eventArguments.returnValue;
}
@@ -1267,12 +1267,12 @@
thousandsSep: ' '
},
global: {
useUTC: true,
//timezoneOffset: 0,
- canvasToolsURL: 'http://code.highcharts.com/4.1.8/modules/canvas-tools.js',
- VMLRadialGradientURL: 'http://code.highcharts.com/4.1.8/gfx/vml-radial-gradient.png'
+ canvasToolsURL: 'http://code.highcharts.com/4.1.9/modules/canvas-tools.js',
+ VMLRadialGradientURL: 'http://code.highcharts.com/4.1.9/gfx/vml-radial-gradient.png'
},
chart: {
//animation: true,
//alignTicks: false,
//reflow: true,
@@ -1411,10 +1411,11 @@
cropThreshold: 300, // draw points outside the plot area when the number of points is less than this
pointRange: 0,
//pointStart: 0,
//pointInterval: 1,
//showInLegend: null, // auto: true for standalone series, false for linked series
+ softThreshold: true,
states: { // states for the entire series
hover: {
//enabled: false,
lineWidthPlus: 1,
marker: {
@@ -1552,10 +1553,11 @@
style: {
color: '#333333',
cursor: 'default',
fontSize: '12px',
padding: '8px',
+ pointerEvents: 'none', // #1686 http://caniuse.com/#feat=pointer-events
whiteSpace: 'nowrap'
}
//xDateFormat: '%A, %b %e, %Y',
//valueDecimals: null,
//valuePrefix: '',
@@ -1839,10 +1841,11 @@
colorGradient: function (color, prop, elem) {
var renderer = this.renderer,
colorObject,
gradName,
gradAttr,
+ radAttr,
gradients,
gradientObject,
stops,
stopColor,
stopOpacity,
@@ -1875,16 +1878,15 @@
};
}
// Correct the radial gradient for the radial reference system
if (gradName === 'radialGradient' && radialReference && !defined(gradAttr.gradientUnits)) {
- gradAttr = merge(gradAttr, {
- cx: (radialReference[0] - radialReference[2] / 2) + gradAttr.cx * radialReference[2],
- cy: (radialReference[1] - radialReference[2] / 2) + gradAttr.cy * radialReference[2],
- r: gradAttr.r * radialReference[2],
- gradientUnits: 'userSpaceOnUse'
- });
+ radAttr = gradAttr; // Save the radial attributes for updating
+ gradAttr = merge(gradAttr,
+ renderer.getRadialAttr(radialReference, radAttr),
+ { gradientUnits: 'userSpaceOnUse' }
+ );
}
// Build the unique key to detect whether we need to create a new element (#1282)
for (n in gradAttr) {
if (n !== 'id') {
@@ -1906,10 +1908,11 @@
gradAttr.id = id = PREFIX + idCounter++;
gradients[key] = gradientObject = renderer.createElement(gradName)
.attr(gradAttr)
.add(renderer.defs);
+ gradientObject.radAttr = radAttr;
// The gradient needs to keep a list of stops to be able to destroy them
gradientObject.stops = [];
each(stops, function (stop) {
var stopObject;
@@ -1932,10 +1935,11 @@
});
}
// Set the reference to the gradient object
elem.setAttribute(prop, 'url(' + renderer.url + '#' + id + ')');
+ elem.gradient = key;
}
},
/**
* Apply a polyfill to the text-stroke CSS property, by copying the text element
@@ -1948,22 +1952,23 @@
applyTextShadow: function (textShadow) {
var elem = this.element,
tspans,
hasContrast = textShadow.indexOf('contrast') !== -1,
styles = {},
+ forExport = this.renderer.forExport,
// 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);
+ supports = forExport || (elem.style.textShadow !== UNDEFINED && !isMS);
// When the text shadow is set to contrast, use dark stroke for light text and vice versa
if (hasContrast) {
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) {
+ if (isWebKit || forExport) {
styles.textRendering = 'geometricPrecision';
}
/* Selective side-by-side testing in supported browser (http://jsfiddle.net/highcharts/73L1ptrh/)
if (elem.textContent.indexOf('2.') === 0) {
@@ -1972,11 +1977,11 @@
}
// */
// No reason to polyfill, we've got native support
if (supports) {
- css(elem, styles); // Apply altered textShadow or textRendering workaround
+ this.css(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,
@@ -2248,11 +2253,11 @@
if (textWidth && (useCanVG || (!hasSVG && elemWrapper.renderer.forExport))) {
delete styles.width;
}
// serialize and set style attribute
- if (isIE && !hasSVG) {
+ if (isMS && !hasSVG) {
css(elemWrapper.element, styles);
} else {
/*jslint unparam: true*/
hyphenate = function (a, b) { return '-' + b.toLowerCase(); };
/*jslint unparam: false*/
@@ -2304,11 +2309,25 @@
* Set the coordinates needed to draw a consistent radial gradient across
* pie slices regardless of positioning inside the chart. The format is
* [centerX, centerY, diameter] in pixels.
*/
setRadialReference: function (coordinates) {
+ var existingGradient = this.renderer.gradients[this.element.gradient];
+
this.element.radialReference = coordinates;
+
+ // On redrawing objects with an existing gradient, the gradient needs
+ // to be repositioned (#3801)
+ if (existingGradient && existingGradient.radAttr) {
+ existingGradient.animate(
+ this.renderer.getRadialAttr(
+ coordinates,
+ existingGradient.radAttr
+ )
+ );
+ }
+
return this;
},
/**
* Move an object and its children by x and y values
@@ -2558,11 +2577,11 @@
if (renderer.isSVG) {
width = bBox.width;
height = bBox.height;
// Workaround for wrong bounding box in IE9 and IE10 (#1101, #1505, #1669, #2568)
- if (isIE && styles && styles.fontSize === '11px' && height.toPrecision(3) === '16.9') {
+ if (isMS && styles && styles.fontSize === '11px' && height.toPrecision(3) === '16.9') {
bBox.height = height = 14;
}
// Adjust for rotated text
if (rotation) {
@@ -2868,11 +2887,11 @@
},
visibilitySetter: function (value, key, element) {
// IE9-11 doesn't handle visibilty:inherit well, so we remove the attribute instead (#2881, #3909)
if (value === 'inherit') {
element.removeAttribute(key);
- } else {
+ } else {
element.setAttribute(key, value);
}
},
zIndexSetter: function (value, key) {
var renderer = this.renderer,
@@ -2972,11 +2991,11 @@
* @param {Object} container
* @param {Number} width
* @param {Number} height
* @param {Boolean} forExport
*/
- init: function (container, width, height, style, forExport) {
+ init: function (container, width, height, style, forExport, allowHTML) {
var renderer = this,
loc = location,
boxWrapper,
element,
desc;
@@ -3012,10 +3031,11 @@
desc = this.createElement('desc').add();
desc.element.appendChild(doc.createTextNode('Created with ' + PRODUCT + ' ' + VERSION));
renderer.defs = this.createElement('defs').add();
+ renderer.allowHTML = allowHTML;
renderer.forExport = forExport;
renderer.gradients = {}; // Object where gradient SvgElements are stored
renderer.cache = {}; // Cache for numerical bounding boxes
renderer.setSize(width, height, false);
@@ -3107,10 +3127,21 @@
* Dummy function for use in canvas renderer
*/
draw: function () {},
/**
+ * Get converted radial gradient attributes
+ */
+ getRadialAttr: function (radialReference, gradAttr) {
+ return {
+ cx: (radialReference[0] - radialReference[2] / 2) + gradAttr.cx * radialReference[2],
+ cy: (radialReference[1] - radialReference[2] / 2) + gradAttr.cy * radialReference[2],
+ r: gradAttr.r * radialReference[2]
+ };
+ },
+
+ /**
* Parse a simple HTML string into SVG tspans
*
* @param {Object} textNode The parent text SVG node
*/
buildText: function (wrapper) {
@@ -3466,17 +3497,17 @@
}, disabledState);
disabledStyle = disabledState.style;
delete disabledState.style;
// Add the events. IE9 and IE10 need mouseover and mouseout to funciton (#667).
- addEvent(label.element, isIE ? 'mouseover' : 'mouseenter', function () {
+ addEvent(label.element, isMS ? 'mouseover' : 'mouseenter', function () {
if (curState !== 3) {
label.attr(hoverState)
.css(hoverStyle);
}
});
- addEvent(label.element, isIE ? 'mouseout' : 'mouseleave', function () {
+ addEvent(label.element, isMS ? 'mouseout' : 'mouseleave', function () {
if (curState !== 3) {
stateOptions = [normalState, hoverState, pressedState][curState];
stateStyle = [normalStyle, hoverStyle, pressedStyle][curState];
label.attr(stateOptions)
.css(stateStyle);
@@ -3798,11 +3829,28 @@
// 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', {
onload: function () {
+
+ // Special case for SVGs on IE11, the width is not accessible until the image is
+ // part of the DOM (#2854).
+ if (this.width === 0) {
+ css(this, {
+ position: ABSOLUTE,
+ top: '-999em'
+ });
+ document.body.appendChild(this);
+ }
+
+ // Center the image
centerImage(obj, symbolSizes[imageSrc] = [this.width, this.height]);
+
+ // Clean up after #2854 workaround.
+ if (this.parentNode) {
+ this.parentNode.removeChild(this);
+ }
},
src: imageSrc
});
}
}
@@ -3998,11 +4046,11 @@
var renderer = this,
fakeSVG = useCanVG || (!hasSVG && renderer.forExport),
wrapper,
attr = {};
- if (useHTML && !renderer.forExport) {
+ if (useHTML && (renderer.allowHTML || !renderer.forExport)) {
return renderer.html(str, x, y);
}
attr.x = Math.round(x || 0); // X is always needed for line-wrap logic
if (y) {
@@ -4049,11 +4097,11 @@
var lineHeight,
baseline,
style;
fontSize = fontSize || this.style.fontSize;
- if (elem && win.getComputedStyle) {
+ if (!fontSize && elem && win.getComputedStyle) {
elem = elem.element || elem; // SVGElement
style = win.getComputedStyle(elem, "");
fontSize = style && style.fontSize; // #4309, the style doesn't exist inside a hidden iframe in Firefox
}
fontSize = /px/.test(fontSize) ? pInt(fontSize) : /em/.test(fontSize) ? parseFloat(fontSize) * 12 : 12;
@@ -4523,11 +4571,11 @@
/**
* Set the rotation of an individual HTML span
*/
setSpanRotation: function (rotation, alignCorrection, baseline) {
var rotationStyle = {},
- cssTransformKey = isIE ? '-ms-transform' : isWebKit ? '-webkit-transform' : isFirefox ? 'MozTransform' : isOpera ? '-o-transform' : '';
+ cssTransformKey = isMS ? '-ms-transform' : isWebKit ? '-webkit-transform' : isFirefox ? 'MozTransform' : isOpera ? '-o-transform' : '';
rotationStyle[cssTransformKey] = rotationStyle.transform = 'rotate(' + rotation + 'deg)';
rotationStyle[cssTransformKey + (isFirefox ? 'Origin' : '-origin')] = rotationStyle.transformOrigin = (alignCorrection * 100) + '% ' + baseline + 'px';
css(this.element, rotationStyle);
},
@@ -4650,13 +4698,18 @@
htmlGroupStyle.top = value + PX;
parentGroup[key] = value;
parentGroup.doTransform = true;
}
});
- wrap(parentGroup, 'visibilitySetter', function (proceed, value, key, elem) {
- proceed.call(this, value, key, elem);
- htmlGroupStyle[key] = value;
+
+ // These properties are set as attributes on the SVG group, and as
+ // identical CSS properties on the div. (#3542)
+ each(['opacity', 'visibility'], function (prop) {
+ wrap(parentGroup, prop + 'Setter', function (proceed, value, key, elem) {
+ proceed.call(this, value, key, elem);
+ htmlGroupStyle[key] = value;
+ });
});
});
}
} else {
@@ -6541,10 +6594,11 @@
}
//x: 0,
//y: 0
},
type: 'linear' // linear, logarithmic or datetime
+ //visible: true
},
/**
* This options set extends the defaultOptions for Y axes
*/
@@ -6674,14 +6728,13 @@
// Flag, stagger lines or not
axis.userOptions = userOptions;
//axis.axisTitleMargin = UNDEFINED,// = options.title.margin,
axis.minPixelPadding = 0;
- //axis.ignoreMinPadding = UNDEFINED; // can be set to true by a column or bar series
- //axis.ignoreMaxPadding = UNDEFINED;
axis.reversed = options.reversed;
+ axis.visible = options.visible !== false;
axis.zoomEnabled = options.zoomEnabled !== false;
// Initial categories
axis.categories = options.categories || type === 'category';
axis.names = axis.names || []; // Preserve on update (#3830)
@@ -6873,11 +6926,12 @@
chart = axis.chart;
axis.hasVisibleSeries = false;
// Reset properties in case we're redrawing (#3353)
- axis.dataMin = axis.dataMax = axis.ignoreMinPadding = axis.ignoreMaxPadding = null;
+ axis.dataMin = axis.dataMax = axis.threshold = null;
+ axis.softThreshold = !axis.isXAxis;
if (axis.buildStacks) {
axis.buildStacks();
}
@@ -6923,18 +6977,16 @@
axis.dataMax = mathMax(pick(axis.dataMax, seriesDataMax), seriesDataMax);
}
// Adjust to threshold
if (defined(threshold)) {
- if (axis.dataMin >= threshold) {
- axis.dataMin = threshold;
- axis.ignoreMinPadding = true;
- } else if (axis.dataMax < threshold) {
- axis.dataMax = threshold;
- axis.ignoreMaxPadding = true;
- }
+ axis.threshold = threshold;
}
+ // If any series has a hard threshold, it takes precedence
+ if (!seriesOptions.softThreshold || axis.isLog) {
+ axis.softThreshold = false;
+ }
}
}
});
},
@@ -7174,11 +7226,12 @@
i,
distance,
xData,
loopLength,
minArgs,
- maxArgs;
+ maxArgs,
+ minRange;
// Set the automatic minimum range based on the closest point distance
if (axis.isXAxis && axis.minRange === UNDEFINED && !axis.isLog) {
if (defined(options.min) || defined(options.max)) {
@@ -7202,11 +7255,11 @@
}
}
// if minRange is exceeded, adjust
if (max - min < axis.minRange) {
- var minRange = axis.minRange;
+ minRange = axis.minRange;
zoomOffset = (minRange - max + min) / 2;
// if min and max options have been set, don't go beyond it
minArgs = [min - zoomOffset, pick(options.min, min - zoomOffset)];
if (spaceAvailable) { // if space is available, stay within the data range
@@ -7260,13 +7313,10 @@
each(axis.series, function (series) {
var seriesPointRange = hasCategories ? 1 : (isXAxis ? series.pointRange : (axis.axisPointRange || 0)), // #2806
pointPlacement = series.options.pointPlacement,
seriesClosestPointRange = series.closestPointRange;
- if (seriesPointRange > range) { // #1446
- seriesPointRange = 0;
- }
pointRange = mathMax(pointRange, seriesPointRange);
if (!axis.single) {
// minPointOffset is the value padding to the left of the axis in order to make
// room for points with a pointRange, typically columns. When the pointPlacement option
@@ -7339,28 +7389,53 @@
length,
linkedParentExtremes,
tickIntervalOption = options.tickInterval,
minTickInterval,
tickPixelIntervalOption = options.tickPixelInterval,
- categories = axis.categories;
+ categories = axis.categories,
+ threshold = axis.threshold,
+ softThreshold = axis.softThreshold,
+ thresholdMin,
+ thresholdMax,
+ hardMin,
+ hardMax;
if (!isDatetimeAxis && !categories && !isLinked) {
this.getTickAmount();
}
- // linked axis gets the extremes from the parent axis
+ // Min or max set either by zooming/setExtremes or initial options
+ hardMin = pick(axis.userMin, options.min);
+ hardMax = pick(axis.userMax, options.max);
+
+ // Linked axis gets the extremes from the parent axis
if (isLinked) {
axis.linkedParent = chart[axis.coll][options.linkedTo];
linkedParentExtremes = axis.linkedParent.getExtremes();
axis.min = pick(linkedParentExtremes.min, linkedParentExtremes.dataMin);
axis.max = pick(linkedParentExtremes.max, linkedParentExtremes.dataMax);
if (options.type !== axis.linkedParent.options.type) {
error(11, 1); // Can't link axes of different type
}
- } else { // initial min and max from the extreme data values
- axis.min = pick(axis.userMin, options.min, axis.dataMin);
- axis.max = pick(axis.userMax, options.max, axis.dataMax);
+
+ // Initial min and max from the extreme data values
+ } else {
+
+ // Adjust to hard threshold
+ if (!softThreshold && defined(threshold)) {
+ if (axis.dataMin >= threshold) {
+ thresholdMin = threshold;
+ minPadding = 0;
+ } else if (axis.dataMax <= threshold) {
+ thresholdMax = threshold;
+ maxPadding = 0;
+ }
+ }
+
+ axis.min = pick(hardMin, thresholdMin, axis.dataMin);
+ axis.max = pick(hardMax, thresholdMax, axis.dataMax);
+
}
if (isLog) {
if (!secondPass && mathMin(axis.min, pick(axis.dataMin, axis.min)) <= 0) { // #978
error(10, 1); // Can't plot negative values on log axis
@@ -7372,12 +7447,12 @@
axis.max = correctFloat(log2lin(axis.max), 15);
}
// handle zoomed range
if (axis.range && defined(axis.max)) {
- axis.userMin = axis.min = mathMax(axis.min, axis.minFromRange()); // #618
- axis.userMax = axis.max;
+ axis.userMin = axis.min = hardMin = mathMax(axis.min, axis.minFromRange()); // #618
+ axis.userMax = hardMax = axis.max;
axis.range = null; // don't use it when running setExtremes
}
// Hook for adjusting this.min and this.max. Used by bubble series.
@@ -7391,14 +7466,14 @@
// Pad the values to get clear of the chart's edges. To avoid tickInterval taking the padding
// into account, we do this after computing tick interval (#1337).
if (!categories && !axis.axisPointRange && !axis.usePercentage && !isLinked && defined(axis.min) && defined(axis.max)) {
length = axis.max - axis.min;
if (length) {
- if (!defined(options.min) && !defined(axis.userMin) && minPadding && (axis.dataMin < 0 || !axis.ignoreMinPadding)) {
+ if (!defined(hardMin) && minPadding) {
axis.min -= length * minPadding;
}
- if (!defined(options.max) && !defined(axis.userMax) && maxPadding && (axis.dataMax > 0 || !axis.ignoreMaxPadding)) {
+ if (!defined(hardMax) && maxPadding) {
axis.max += length * maxPadding;
}
}
}
@@ -7408,10 +7483,25 @@
}
if (isNumber(options.ceiling)) {
axis.max = mathMin(axis.max, options.ceiling);
}
+ // When the threshold is soft, adjust the extreme value only if
+ // the data extreme and the padded extreme land on either side of the threshold. For example,
+ // a series of [0, 1, 2, 3] would make the yAxis add a tick for -1 because of the
+ // default minPadding and startOnTick options. This is prevented by the softThreshold
+ // option.
+ if (softThreshold && defined(axis.dataMin)) {
+ threshold = threshold || 0;
+ if (!defined(hardMin) && axis.min < threshold && axis.dataMin >= threshold) {
+ axis.min = threshold;
+ } else if (!defined(hardMax) && axis.max > threshold && axis.dataMax <= threshold) {
+ axis.max = threshold;
+ }
+ }
+
+
// 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) {
@@ -7607,16 +7697,16 @@
each(this.chart[this.coll], function (axis) {
var options = axis.options,
horiz = axis.horiz,
key = [horiz ? options.left : options.top, horiz ? options.width : options.height, options.pane].join(',');
- if (others[key]) {
- if (axis.series.length) {
+ if (axis.series.length) { // #4442
+ if (others[key]) {
hasOther = true; // #4201
+ } else {
+ others[key] = 1;
}
- } else {
- others[key] = 1;
}
});
if (hasOther) {
// Add 1 because 4 tick intervals require 5 ticks (including first and last)
@@ -7925,13 +8015,15 @@
step = step > 1 ? mathCeil(step) : 1;
return step * tickInterval;
};
if (horiz) {
- autoRotation = defined(rotationOption) ?
- [rotationOption] :
- slotSize < pick(labelOptions.autoRotationLimit, 80) && !labelOptions.staggerLines && !labelOptions.step && labelOptions.autoRotation;
+ autoRotation = !labelOptions.staggerLines && !labelOptions.step && ( // #3971
+ defined(rotationOption) ?
+ [rotationOption] :
+ slotSize < pick(labelOptions.autoRotationLimit, 80) && labelOptions.autoRotation
+ );
if (autoRotation) {
// Loop over the given autoRotation options, and determine which gives the best score. The
// best score is that with the lowest number of steps and a rotation closest to horizontal.
@@ -7956,11 +8048,11 @@
} else if (!labelOptions.step) { // #4411
newTickInterval = getStep(labelMetrics.h);
}
this.autoRotation = autoRotation;
- this.labelRotation = rotation;
+ this.labelRotation = pick(rotation, rotationOption);
return newTickInterval;
},
renderUnsquish: function () {
@@ -8052,15 +8144,15 @@
// Apply general and specific CSS
each(tickPositions, function (pos) {
var tick = ticks[pos],
label = tick && tick.label;
if (label) {
+ label.attr(attr); // This needs to go before the CSS in old IE (#4502)
if (css) {
label.css(merge(css, label.specCss));
}
delete label.specCss;
- label.attr(attr);
tick.rotation = attr.rotation;
}
});
// TODO: Why not part of getLabelPosition?
@@ -8099,10 +8191,11 @@
axisOffset = chart.axisOffset,
clipOffset = chart.clipOffset,
clip,
directionFactor = [-1, 1, 1, -1][side],
n,
+ axisParent = axis.axisParent, // Used in color axis
lineHeightCorrection;
// For reuse in Axis.render
hasData = axis.hasData();
axis.showAxis = showAxis = hasData || pick(options.showEmpty, true);
@@ -8112,18 +8205,18 @@
// Create the axisGroup and gridGroup elements on first iteration
if (!axis.axisGroup) {
axis.gridGroup = renderer.g('grid')
.attr({ zIndex: options.gridZIndex || 1 })
- .add();
+ .add(axisParent);
axis.axisGroup = renderer.g('axis')
.attr({ zIndex: options.zIndex || 2 })
- .add();
+ .add(axisParent);
axis.labelGroup = renderer.g('axis-labels')
.attr({ zIndex: labelOptions.zIndex || 7 })
.addClass(PREFIX + axis.coll.toLowerCase() + '-labels')
- .add();
+ .add(axisParent);
}
if (hasData || axis.isLinked) {
// Generate ticks
@@ -8379,16 +8472,16 @@
}
// alternate grid color
if (alternateGridColor) {
each(tickPositions, function (pos, i) {
- if (i % 2 === 0 && pos < axis.max) {
+ to = tickPositions[i + 1] !== UNDEFINED ? tickPositions[i + 1] + tickmarkOffset : axis.max - tickmarkOffset;
+ if (i % 2 === 0 && pos < axis.max && to <= axis.max - tickmarkOffset) { // #2248
if (!alternateBands[pos]) {
alternateBands[pos] = new Highcharts.PlotLineOrBand(axis);
}
from = pos + tickmarkOffset; // #949
- to = tickPositions[i + 1] !== UNDEFINED ? tickPositions[i + 1] + tickmarkOffset : axis.max;
alternateBands[pos].options = {
from: isLog ? lin2log(from) : from,
to: isLog ? lin2log(to) : to,
color: alternateGridColor
};
@@ -8486,17 +8579,19 @@
/**
* Redraw the axis to reflect changes in the data or axis extremes
*/
redraw: function () {
- // render the axis
- this.render();
+ if (this.visible) {
+ // render the axis
+ this.render();
- // move plot lines and bands
- each(this.plotLinesAndBands, function (plotLine) {
- plotLine.render();
- });
+ // move plot lines and bands
+ each(this.plotLinesAndBands, function (plotLine) {
+ plotLine.render();
+ });
+ }
// mark associated series as dirty and ready for redraw
each(this.series, function (series) {
series.isDirty = true;
});
@@ -9577,13 +9672,14 @@
shared = tooltip ? tooltip.shared : false,
followPointer,
hoverPoint = chart.hoverPoint,
hoverSeries = chart.hoverSeries,
i,
- distance = chart.chartWidth,
+ distance = Number.MAX_VALUE, // #4511
anchor,
noSharedTooltip,
+ stickToHoverSeries,
directTouch,
kdpoints = [],
kdpoint,
kdpointT;
@@ -9595,13 +9691,15 @@
series = [];
}
}
}
- // If it has a hoverPoint and that series requires direct touch (like columns),
- // use the hoverPoint (#3899). Otherwise, search the k-d tree.
- if (!shared && hoverSeries && hoverSeries.directTouch && hoverPoint) {
+ // If it has a hoverPoint and that series requires direct touch (like columns, #3899), or we're on
+ // a noSharedTooltip series among shared tooltip series (#4546), use the hoverPoint . Otherwise,
+ // search the k-d tree.
+ stickToHoverSeries = hoverSeries && (shared ? hoverSeries.noSharedTooltip : hoverSeries.directTouch);
+ if (stickToHoverSeries && hoverPoint) {
kdpoint = hoverPoint;
// Handle shared tooltip or cases where a series is not yet hovered
} else {
// Find nearest points on all series
@@ -10040,15 +10138,15 @@
}
},
onTrackerMouseOut: function (e) {
var series = this.chart.hoverSeries,
- relatedTarget = e.relatedTarget || e.toElement,
- relatedSeries = relatedTarget && relatedTarget.point && relatedTarget.point.series; // #2499
+ relatedTarget = e.relatedTarget || e.toElement;
- if (series && !series.options.stickyTracking && !this.inClass(relatedTarget, PREFIX + 'tooltip') &&
- relatedSeries !== series) {
+ if (series && !series.options.stickyTracking &&
+ !this.inClass(relatedTarget, PREFIX + 'tooltip') &&
+ !this.inClass(relatedTarget, PREFIX + 'series-' + series.index)) { // #2499, #4465
series.onMouseOut();
}
},
onContainerClick: function (e) {
@@ -11749,11 +11847,11 @@
}
if (subtitle) {
subtitle
.css({ width: (subtitleOptions.width || autoWidth) + PX })
.align(extend({
- y: titleOffset + (titleOptions.margin - 13) + renderer.fontMetrics(titleOptions.style.fontSize, subtitle).b
+ y: titleOffset + (titleOptions.margin - 13) + renderer.fontMetrics(subtitleOptions.style.fontSize, title).b
}, subtitleOptions), false, 'spacingBox');
if (!subtitleOptions.floating && !subtitleOptions.verticalAlign) {
titleOffset = mathCeil(titleOffset + subtitle.getBBox().height);
}
@@ -11837,16 +11935,18 @@
* div to hold the chart
*/
getContainer: function () {
var chart = this,
container,
- optionsChart = chart.options.chart,
+ options = chart.options,
+ optionsChart = options.chart,
chartWidth,
chartHeight,
renderTo,
indexAttrName = 'data-highcharts-chart',
oldChartIndex,
+ Ren,
containerId;
chart.renderTo = renderTo = optionsChart.renderTo;
containerId = PREFIX + idCounter++;
@@ -11909,14 +12009,19 @@
// cache the cursor (#1650)
chart._cursor = container.style.cursor;
// Initialize the renderer
- chart.renderer =
- optionsChart.forExport ? // force SVG, used for SVG export
- new SVGRenderer(container, chartWidth, chartHeight, optionsChart.style, true) :
- new Renderer(container, chartWidth, chartHeight, optionsChart.style);
+ Ren = Highcharts[optionsChart.renderer] || Renderer;
+ chart.renderer = new Ren(
+ container,
+ chartWidth,
+ chartHeight,
+ optionsChart.style,
+ optionsChart.forExport,
+ options.exporting && options.exporting.allowHTML
+ );
if (useCanVG) {
// If we need canvg library, extend and configure the renderer
// to get the tracker for translating mouse events
chart.renderer.create(chart, container, chartWidth, chartHeight);
@@ -11965,11 +12070,13 @@
margin = chart.margin;
// pre-render axes to get labels offset width
if (chart.hasCartesianSeries) {
each(chart.axes, function (axis) {
- axis.getOffset();
+ if (axis.visible) {
+ axis.getOffset();
+ }
});
}
// Add the axis offsets
each(marginNames, function (m, side) {
@@ -12041,11 +12148,11 @@
var chart = this,
chartWidth,
chartHeight,
fireEndResize,
renderer = chart.renderer,
- globalAnimation = renderer.globalAnimation;
+ globalAnimation;
// Handle the isResizing counter
chart.isResizing += 1;
fireEndResize = function () {
if (chart) {
@@ -12067,10 +12174,11 @@
if (defined(height)) {
chart.chartHeight = chartHeight = mathMax(0, mathRound(height));
}
// Resize the container with the global animation applied if enabled (#2503)
+ globalAnimation = renderer.globalAnimation;
(globalAnimation ? animate : css)(chart.container, {
width: chartWidth + PX,
height: chartHeight + PX
}, globalAnimation);
@@ -12099,12 +12207,12 @@
chart.oldChartHeight = null;
fireEvent(chart, 'resize');
- // fire endResize and set isResizing back
- // If animation is disabled, fire without delay
+ // Fire endResize and set isResizing back. If animation is disabled, fire without delay
+ globalAnimation = renderer.globalAnimation; // Reassign it before using it, it may have changed since the top of this function.
if (globalAnimation === false) {
fireEndResize();
} else { // else set a timeout with the animation duration
setTimeout(fireEndResize, (globalAnimation && globalAnimation.duration) || 500);
}
@@ -12466,11 +12574,13 @@
// Axes
if (chart.hasCartesianSeries) {
each(axes, function (axis) {
- axis.render();
+ if (axis.visible) {
+ axis.render();
+ }
});
}
// The series
if (!chart.seriesGroup) {
@@ -12718,10 +12828,14 @@
// i == 3: innerSize, relative to size
positions[i] = relativeLength(value, [plotWidth, plotHeight, smallestSize, positions[2]][i]) +
(handleSlicingRoom ? slicingRoom : 0);
}
+ // innerSize cannot be larger than size (#3632)
+ if (positions[3] > positions[2]) {
+ positions[3] = positions[2];
+ }
return positions;
}
};
/**
@@ -12813,11 +12927,15 @@
ret.x = options[0];
}
i++;
}
while (j < valueCount) {
- ret[pointArrayMap[j++]] = options[i++];
+ if (!keys || options[i] !== undefined) { // Skip undefined positions for keys
+ ret[pointArrayMap[j]] = options[i];
+ }
+ i++;
+ j++;
}
} else if (typeof options === 'object') {
ret = options;
// This is the fastest way to detect if there are individual point dataLabels that need
@@ -12878,11 +12996,11 @@
/**
* Destroy SVG elements associated with the point
*/
destroyElements: function () {
var point = this,
- props = ['graphic', 'dataLabel', 'dataLabelUpper', 'group', 'connector', 'shadowGroup'],
+ props = ['graphic', 'dataLabel', 'dataLabelUpper', 'connector', 'shadowGroup'],
prop,
i = 6;
while (i--) {
prop = props[i];
if (point[prop]) {
@@ -12962,11 +13080,12 @@
}
};
}
fireEvent(this, eventType, eventArgs, defaultFunction);
- }
+ },
+ visible: true
};/**
* @classDescription The base function which all other series types inherit from. The data in the series is stored
* in various arrays.
*
* - First, series.options.data contains all the original config options for
@@ -12997,10 +13116,11 @@
stroke: 'lineColor',
'stroke-width': 'lineWidth',
fill: 'fillColor',
r: 'radius'
},
+ directTouch: false,
axisTypes: ['xAxis', 'yAxis'],
colorCounter: 0,
parallelArrays: ['x', 'y'], // each point's x and y values are stored in this.xData and this.yData
init: function (chart, options) {
var series = this,
@@ -13446,10 +13566,17 @@
// redraw
series.isDirty = series.isDirtyData = chart.isDirtyBox = true;
animation = false;
}
+ // Typically for pie series, points need to be processed and generated
+ // prior to rendering the legend
+ if (options.legendType === 'point') { // docs: legendType now supported on more series types (at least column and pie)
+ this.processData();
+ this.generatePoints();
+ }
+
if (redraw) {
chart.redraw(animation);
}
},
@@ -13469,10 +13596,11 @@
closestPointRange,
xAxis = series.xAxis,
i, // loop variable
options = series.options,
cropThreshold = options.cropThreshold,
+ getExtremesFromAll = series.getExtremesFromAll || options.getExtremesFromAll, // #4599
isCartesian = series.isCartesian,
xExtremes,
min,
max;
@@ -13487,11 +13615,11 @@
min = xExtremes.min;
max = xExtremes.max;
}
// optionally filter out points outside the plot area
- if (isCartesian && series.sorted && (!cropThreshold || dataLength > cropThreshold || series.forceCrop)) {
+ if (isCartesian && series.sorted && !getExtremesFromAll && (!cropThreshold || dataLength > cropThreshold || series.forceCrop)) {
// it's outside current extremes
if (processedXData[dataLength - 1] < min || processedXData[0] > max) {
processedXData = [];
processedYData = [];
@@ -13707,10 +13835,11 @@
threshold = options.threshold,
stackThreshold = options.startFromThreshold ? threshold : 0,
plotX,
plotY,
lastPlotX,
+ stackIndicator,
closestPointRangePx = Number.MAX_VALUE;
// Translate each point
for (i = 0; i < dataLength; i++) {
var point = points[i],
@@ -13731,13 +13860,13 @@
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]) {
-
+ stackIndicator = series.getStackIndicator(stackIndicator, xValue, series.index);
pointStack = stack[xValue];
- stackValues = pointStack.points[series.index + ',' + i];
+ stackValues = pointStack.points[stackIndicator.key];
yBottom = stackValues[0];
yValue = stackValues[1];
if (yBottom === stackThreshold) {
yBottom = pick(threshold, yAxis.min);
@@ -13800,15 +13929,16 @@
* Set the clipping for the series. For animated series it is called twice, first to initiate
* animating the clip then the second time without the animation to set the final clip.
*/
setClip: function (animation) {
var chart = this.chart,
+ options = this.options,
renderer = chart.renderer,
inverted = chart.inverted,
seriesClipBox = this.clipBox,
clipBox = seriesClipBox || chart.clipBox,
- sharedClipKey = this.sharedClipKey || ['_sharedClip', animation && animation.duration, animation && animation.easing, clipBox.height].join(','),
+ sharedClipKey = this.sharedClipKey || ['_sharedClip', animation && animation.duration, animation && animation.easing, clipBox.height, options.xAxis, options.yAxis].join(','), // #4526
clipRect = chart[sharedClipKey],
markerClipRect = chart[sharedClipKey + 'm'];
// If a clipping rectangle with the same properties is currently present in the chart, use that.
if (!clipRect) {
@@ -13829,11 +13959,11 @@
}
if (animation) {
clipRect.count += 1;
}
- if (this.options.clip !== false) {
+ if (options.clip !== false) {
this.group.clip(animation || seriesClipBox ? clipRect : chart.clipRect);
this.markerGroup.clip(markerClipRect);
this.sharedClipKey = sharedClipKey;
}
@@ -14095,13 +14225,12 @@
threshold = zones[j];
while (point[zoneAxis] >= threshold.value) {
threshold = zones[++j];
}
- if (threshold.color) {
- point.color = point.fillColor = threshold.color;
- }
+ point.color = point.fillColor = pick(threshold.color, series.color); // #3636, #4267, #4430 - inherit color from series, when color is undefined
+
}
hasPointSpecificOptions = seriesOptions.colorByPoint || point.color; // #868
// check if the point has specific visual options
@@ -14120,13 +14249,13 @@
pointAttr = [];
stateOptions = normalOptions.states || {}; // reassign for individual point
pointStateOptionsHover = stateOptions[HOVER_STATE] = stateOptions[HOVER_STATE] || {};
// Handle colors for column and pies
- if (!seriesOptions.marker) { // column, bar, point
+ if (!seriesOptions.marker || (point.negative && !pointStateOptionsHover.fillColor && !stateOptionsHover.fillColor)) { // column, bar, point or negative threshold for series with markers (#3636)
// If no hover color is given, brighten the normal color. #1619, #2579
- pointStateOptionsHover.color = pointStateOptionsHover.color || (!point.options.color && stateOptionsHover[(point.negative && seriesNegativeColor ? 'negativeColor' : 'color')]) ||
+ pointStateOptionsHover[series.pointAttrToOptions.fill] = pointStateOptionsHover.color || (!point.options.color && stateOptionsHover[(point.negative && seriesNegativeColor ? 'negativeColor' : 'color')]) ||
Color(point.color)
.brighten(pointStateOptionsHover.brightness || stateOptionsHover.brightness)
.get();
}
@@ -14269,25 +14398,29 @@
if (step && i) {
lastPoint = segment[i - 1];
if (step === 'right') {
segmentPath.push(
lastPoint.plotX,
- plotY
+ plotY,
+ L
);
} else if (step === 'center') {
segmentPath.push(
(lastPoint.plotX + plotX) / 2,
lastPoint.plotY,
+ L,
(lastPoint.plotX + plotX) / 2,
- plotY
+ plotY,
+ L
);
} else {
segmentPath.push(
plotX,
- lastPoint.plotY
+ lastPoint.plotY,
+ L
);
}
}
// normal line to next point
@@ -14544,11 +14677,14 @@
.attr({
visibility: visibility,
zIndex: zIndex || 0.1 // IE8 needs this
})
.add(parent);
+
+ group.addClass('highcharts-series-' + this.index);
}
+
// Place it on first and subsequent (redraw) calls
group[isNew ? 'attr' : 'animate'](this.getPlotBox());
return group;
},
@@ -15094,10 +15230,11 @@
negKey = '-' + stackKey,
negStacks = series.negStacks,
yAxis = series.yAxis,
stacks = yAxis.stacks,
oldStacks = yAxis.oldStacks,
+ stackIndicator,
isNegative,
stack,
other,
key,
pointKey,
@@ -15110,12 +15247,12 @@
// loop over the non-null y values and read them into a local array
for (i = 0; i < yDataLength; i++) {
x = xData[i];
y = yData[i];
- pointKey = series.index + ',' + i;
-
+ stackIndicator = series.getStackIndicator(stackIndicator, x, series.index);
+ pointKey = stackIndicator.key;
// 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 < (stackThreshold ? 0 : threshold);
key = isNegative ? negKey : stackKey;
@@ -15180,33 +15317,53 @@
*/
Series.prototype.setPercentStacks = function () {
var series = this,
stackKey = series.stackKey,
stacks = series.yAxis.stacks,
- processedXData = series.processedXData;
+ processedXData = series.processedXData,
+ stackIndicator;
each([stackKey, '-' + stackKey], function (key) {
var i = processedXData.length,
x,
stack,
pointExtremes,
totalFactor;
while (i--) {
x = processedXData[i];
+ stackIndicator = series.getStackIndicator(stackIndicator, x, series.index);
stack = stacks[key] && stacks[key][x];
- pointExtremes = stack && stack.points[series.index + ',' + i];
+ pointExtremes = stack && stack.points[stackIndicator.key];
if (pointExtremes) {
totalFactor = stack.total ? 100 / stack.total : 0;
pointExtremes[0] = correctFloat(pointExtremes[0] * totalFactor); // Y bottom value
pointExtremes[1] = correctFloat(pointExtremes[1] * totalFactor); // Y value
series.stackedYData[i] = pointExtremes[1];
}
}
});
};
+/**
+* Get stack indicator, according to it's x-value, to determine points with the same x-value
+*/
+Series.prototype.getStackIndicator = function(stackIndicator, x, index) {
+ if (!defined(stackIndicator) || stackIndicator.x !== x) {
+ stackIndicator = {
+ x: x,
+ index: 0
+ };
+ } else {
+ stackIndicator.index++;
+ }
+
+ stackIndicator.key = [index, x, stackIndicator.index].join(',');
+
+ return stackIndicator;
+};
+
// Extend the Chart prototype for dynamic methods
extend(Chart.prototype, {
/**
* Add a series dynamically after time
@@ -15466,11 +15623,11 @@
while (i--) {
shiftShapes.push('zoneGraph' + i, 'zoneArea' + i);
}
each(shiftShapes, function (shape) {
if (series[shape]) {
- series[shape].shift = currentShift + 1;
+ series[shape].shift = currentShift + (seriesOptions.step ? 2 : 1);
}
});
}
if (area) {
area.isArea = true; // needed in animation, both with and without shift
@@ -15747,10 +15904,11 @@
/**
* Set the default options for area
*/
defaultPlotOptions.area = merge(defaultSeriesOptions, {
+ softThreshold: false,
threshold: 0
// trackByArea: false,
// lineColor: null, // overrides color, but lets fillColor be unaltered
// fillOpacity: 0.75,
// fillColor: null
@@ -15777,10 +15935,11 @@
pointMap = {},
plotX,
plotY,
points = this.points,
connectNulls = this.options.connectNulls,
+ stackIndicator,
i,
x;
if (this.options.stacking && !this.cropped) { // cropped causes artefacts in Stock, and perf issue
// Create a map where we can quickly look up the points by their X value.
@@ -15797,11 +15956,11 @@
keys.sort(function (a, b) {
return a - b;
});
each(keys, function (x) {
- var y = 0,
+ var threshold = null,
stackPoint;
if (connectNulls && (!pointMap[x] || pointMap[x].y === null)) { // #1836
return;
@@ -15814,20 +15973,21 @@
// correctly.
} else {
// Loop down the stack to find the series below this one that has
// a value (#1991)
- for (i = series.index; i <= yAxis.series.length; i++) {
- stackPoint = stack[x].points[i + ',' + x];
+ for (i = series.index; i <= yAxis.series.length; i++) {
+ stackIndicator = series.getStackIndicator(null, x, i);
+ stackPoint = stack[x].points[stackIndicator.key];
if (stackPoint) {
- y = stackPoint[1];
+ threshold = stackPoint[1];
break;
}
}
plotX = xAxis.translate(x);
- plotY = yAxis.toPixels(y, true);
+ plotY = yAxis.getThreshold(threshold);
segment.push({
y: null,
plotX: plotX,
clientX: plotX,
plotY: plotY,
@@ -16131,11 +16291,12 @@
dataLabels: {
align: null, // auto
verticalAlign: null, // auto
y: null
},
- startFromThreshold: true, // docs: http://jsfiddle.net/highcharts/hz8fopan/14/
+ softThreshold: false,
+ startFromThreshold: true, // docs (but false doesn't work well): http://jsfiddle.net/highcharts/hz8fopan/14/
stickyTracking: false,
tooltip: {
distance: 6
},
threshold: 0
@@ -16244,10 +16405,52 @@
});
},
/**
+ * Make the columns crisp. The edges are rounded to the nearest full pixel.
+ */
+ crispCol: function (x, y, w, h) {
+ var chart = this.chart,
+ borderWidth = this.borderWidth,
+ xCrisp = -(borderWidth % 2 ? 0.5 : 0),
+ yCrisp = borderWidth % 2 ? 0.5 : 1,
+ right,
+ bottom,
+ fromTop;
+
+ if (chart.inverted && chart.renderer.isVML) {
+ yCrisp += 1;
+ }
+
+ // Horizontal. We need to first compute the exact right edge, then round it
+ // and compute the width from there.
+ right = Math.round(x + w) + xCrisp;
+ x = Math.round(x) + xCrisp;
+ w = right - x;
+
+ // Vertical
+ fromTop = mathAbs(y) <= 0.5; // #4504
+ bottom = Math.round(y + h) + yCrisp;
+ y = Math.round(y) + yCrisp;
+ h = bottom - y;
+
+ // Top edges are exceptions
+ if (fromTop) {
+ y -= 1;
+ h += 1;
+ }
+
+ return {
+ x: x,
+ y: y,
+ width: w,
+ height: h
+ };
+ },
+
+ /**
* Translate each point to the plot area coordinate system and find shape positions
*/
translate: function () {
var series = this,
chart = series.chart,
@@ -16261,19 +16464,14 @@
translatedThreshold = series.translatedThreshold = yAxis.getThreshold(threshold),
minPointLength = pick(options.minPointLength, 5),
metrics = series.getColumnMetrics(),
pointWidth = metrics.width,
seriesBarW = series.barW = mathMax(pointWidth, 1 + 2 * borderWidth), // postprocessed for border width
- pointXOffset = series.pointXOffset = metrics.offset,
- xCrisp = -(borderWidth % 2 ? 0.5 : 0),
- yCrisp = borderWidth % 2 ? 0.5 : 1;
+ pointXOffset = series.pointXOffset = metrics.offset;
if (chart.inverted) {
translatedThreshold -= 0.5; // #3355
- if (chart.renderer.isVML) {
- yCrisp += 1;
- }
}
// When the pointPadding is 0, we want the columns to be packed tightly, so we allow individual
// columns to have individual sizes. When pointPadding is greater, we strive for equal-width
// columns (#2694).
@@ -16283,67 +16481,42 @@
Series.prototype.translate.apply(series);
// Record the new values
each(series.points, function (point) {
- var yBottom = pick(point.yBottom, translatedThreshold),
+ var yBottom = mathMin(pick(point.yBottom, translatedThreshold), 9e4), // #3575
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,
- fromTop,
up,
barH = mathMax(plotY, yBottom) - barY;
// Handle options.minPointLength
if (mathAbs(barH) < minPointLength) {
if (minPointLength) {
barH = minPointLength;
up = (!yAxis.reversed && !point.negative) || (yAxis.reversed && point.negative);
- barY =
- mathRound(mathAbs(barY - translatedThreshold) > minPointLength ? // stacked
+ barY = mathAbs(barY - translatedThreshold) > minPointLength ? // stacked
yBottom - minPointLength : // keep position
- translatedThreshold - (up ? minPointLength : 0)); // #1485, #4051
+ translatedThreshold - (up ? minPointLength : 0); // #1485, #4051
}
}
// Cache for access in polar
point.barX = barX;
point.pointWidth = pointWidth;
- // Round off to obtain crisp edges and avoid overlapping with neighbours (#2694)
- right = mathRound(barX + barW) + xCrisp;
- barX = mathRound(barX) + xCrisp;
- barW = right - barX;
-
- fromTop = mathAbs(barY) < 0.5;
- bottom = mathMin(mathRound(barY + barH) + yCrisp, 9e4); // #3575
- barY = mathRound(barY) + yCrisp;
- barH = bottom - barY;
-
- // Top edges are exceptions
- 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
- };
+ point.shapeArgs = series.crispCol(barX, barY, barW, barH);
});
},
getSymbol: noop,
@@ -16394,11 +16567,11 @@
} else {
point.graphic = graphic = renderer[point.shapeType](shapeArgs)
.attr(borderAttr)
.attr(pointAttr)
- .add(series.group)
+ .add(point.group || series.group)
.shadow(options.shadow, null, options.stacking && !options.borderRadius);
}
} else if (graphic) {
point.graphic = graphic.destroy(); // #1269
@@ -16563,14 +16736,11 @@
Point.prototype.init.apply(this, arguments);
var point = this,
toggleSlice;
- extend(point, {
- visible: point.visible !== false,
- name: pick(point.name, 'Slice')
- });
+ point.name = pick(point.name, 'Slice');
// add event listener for select
toggleSlice = function (e) {
point.slice(e.type === 'select');
};
@@ -16724,23 +16894,10 @@
series.animate = null;
}
},
/**
- * Extend the basic setData method by running processData and generatePoints immediately,
- * in order to access the points from the legend.
- */
- setData: function (data, redraw, animation, updatePoints) {
- Series.prototype.setData.call(this, data, false, animation, updatePoints);
- this.processData();
- this.generatePoints();
- if (pick(redraw, true)) {
- this.chart.redraw(animation);
- }
- },
-
- /**
* Recompute total chart sum and update percentages of points.
*/
updateTotals: function () {
var i,
total = 0,
@@ -16930,11 +17087,13 @@
shadowGroup.attr(groupTranslation);
}
// draw the slice
if (graphic) {
- graphic.animate(extend(shapeArgs, groupTranslation));
+ graphic
+ .setRadialReference(series.center)
+ .animate(extend(shapeArgs, groupTranslation));
} else {
attr = { 'stroke-linejoin': 'round' };
if (!point.visible) {
attr.visibility = 'hidden';
}
@@ -17214,10 +17373,11 @@
}
}
// Show or hide based on the final aligned position
if (!visible) {
+ stop(dataLabel);
dataLabel.attr({ y: -999 });
dataLabel.placed = false; // don't animate back in
}
};
@@ -17667,11 +17827,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);
+ center[3] = Math.min(relativeLength(options.innerSize || 0, newSize), newSize); // #3632
this.translate(center);
each(this.points, function (point) {
if (point.dataLabel) {
point.dataLabel._pos = null; // reset
}
@@ -17743,11 +17903,11 @@
}
/**
- * Highcharts JS v4.1.8 (2015-08-20)
+ * Highcharts JS v4.1.9 (2015-10-07)
* 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
@@ -18073,11 +18233,13 @@
item.setState();
})
.on('click', function (event) {
var strLegendItemClick = 'legendItemClick',
fnLegendItemClick = function () {
- item.setVisible();
+ if (item.setVisible) {
+ item.setVisible();
+ }
};
// Pass over the click/touch event. #4.
event = {
browserEvent: event
@@ -18369,10 +18531,10 @@
* Set the point's state
* @param {String} state
*/
setState: function (state, move) {
var point = this,
- plotX = point.plotX,
+ plotX = mathFloor(point.plotX), // #4586
plotY = point.plotY,
series = point.series,
stateOptions = series.options.states,
markerOptions = defaultPlotOptions[series.type].marker && series.options.marker,
normalDisabled = markerOptions && !markerOptions.enabled,