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,