app/assets/javascripts/highcharts.js in highcharts-rails-4.1.1 vs app/assets/javascripts/highcharts.js in highcharts-rails-4.1.2

- old
+ new

@@ -1,10 +1,10 @@ // ==ClosureCompiler== // @compilation_level SIMPLE_OPTIMIZATIONS /** - * @license Highcharts JS v4.1.1 (2015-02-17) + * @license Highcharts JS v4.1.2 (2015-02-27) * * (c) 2009-2014 Torstein Honsi * * License: www.highcharts.com/license */ @@ -54,11 +54,11 @@ timeUnits, noop = function () { return UNDEFINED; }, charts = [], chartCount = 0, PRODUCT = 'Highcharts', - VERSION = '4.1.1', + VERSION = '4.1.2', // some constants for frequently used strings DIV = 'div', ABSOLUTE = 'absolute', RELATIVE = 'relative', @@ -742,11 +742,11 @@ Highcharts.numberFormat = function (number, decimals, decPoint, thousandsSep) { var lang = defaultOptions.lang, // http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_number_format/ n = +number || 0, c = decimals === -1 ? - (n.toString().split('.')[1] || '').length : // preserve decimals + mathMin((n.toString().split('.')[1] || '').length, 20) : // Preserve decimals. Not huge numbers (#3793). (isNaN(decimals = mathAbs(decimals)) ? 2 : decimals), d = decPoint === undefined ? lang.decimalPoint : decPoint, t = thousandsSep === undefined ? lang.thousandsSep : thousandsSep, s = n < 0 ? "-" : "", i = String(pInt(n = mathAbs(n).toFixed(c))), @@ -1257,12 +1257,12 @@ thousandsSep: ' ' }, global: { useUTC: true, //timezoneOffset: 0, - canvasToolsURL: 'http://code.highcharts.com/4.1.1/modules/canvas-tools.js', - VMLRadialGradientURL: 'http://code.highcharts.com/4.1.1/gfx/vml-radial-gradient.png' + canvasToolsURL: 'http://code.highcharts.com/4.1.2/modules/canvas-tools.js', + VMLRadialGradientURL: 'http://code.highcharts.com/4.1.2/gfx/vml-radial-gradient.png' }, chart: { //animation: true, //alignTicks: false, //reflow: true, @@ -1937,15 +1937,13 @@ */ applyTextShadow: function (textShadow) { var elem = this.element, tspans, hasContrast = textShadow.indexOf('contrast') !== -1, - // Safari suffers from the double display bug (#3649) - isSafari = userAgent.indexOf('Safari') > 0 && userAgent.indexOf('Chrome') === -1, // IE10 and IE11 report textShadow in elem.style even though it doesn't work. Check - // this again with new IE release. - supports = elem.style.textShadow !== UNDEFINED && !isIE && !isSafari; + // this again with new IE release. In exports, the rendering is passed to PhantomJS. + supports = this.renderer.forExport || (elem.style.textShadow !== UNDEFINED && !isIE); // When the text shadow is set to contrast, use dark stroke for light text and vice versa if (hasContrast) { textShadow = textShadow.replace(/contrast/g, this.renderer.getContrast(elem.style.fill)); } @@ -1964,10 +1962,12 @@ textShadow: textShadow }); } } else { + this.fakeTS = true; // Fake text shadow + // In order to get the right y position of the clones, // copy over the y setter this.ySetter = this.xSetter; tspans = [].slice.call(elem.getElementsByTagName('tspan')); @@ -1997,10 +1997,12 @@ } // Create the clone and apply shadow properties clone = tspan.cloneNode(1); attr(clone, { + 'class': PREFIX + 'text-shadow', + 'fill': color, 'stroke': color, 'stroke-opacity': 1 / mathMax(pInt(strokeWidth), 3), 'stroke-width': strokeWidth, 'stroke-linejoin': 'round' }); @@ -2450,10 +2452,13 @@ rotation = wrapper.rotation, element = wrapper.element, styles = wrapper.styles, rad = rotation * deg2rad, textStr = wrapper.textStr, + textShadow, + elemStyle = element.style, + toggleTextShadowShim, cacheKey; if (textStr !== UNDEFINED) { // Properties that affect bounding box @@ -2480,19 +2485,42 @@ // SVG elements if (element.namespaceURI === SVG_NS || renderer.forExport) { try { // Fails in Firefox if the container has display: none. + // When the text shadow shim is used, we need to hide the fake shadows + // to get the correct bounding box (#3872) + toggleTextShadowShim = this.fakeTS && function (display) { + each(element.querySelectorAll('.' + PREFIX + 'text-shadow'), function (tspan) { + tspan.style.display = display; + }); + }; + + // Workaround for #3842, Firefox reporting wrong bounding box for shadows + if (isFirefox && elemStyle.textShadow) { + textShadow = elemStyle.textShadow; + elemStyle.textShadow = ''; + } else if (toggleTextShadowShim) { + toggleTextShadowShim(NONE); + } + bBox = element.getBBox ? // SVG: use extend because IE9 is not allowed to change width and height in case // of rotation (below) extend({}, element.getBBox()) : // Canvas renderer and legacy IE in export mode { width: element.offsetWidth, height: element.offsetHeight }; + + // #3842 + if (textShadow) { + elemStyle.textShadow = textShadow; + } else if (toggleTextShadowShim) { + toggleTextShadowShim(''); + } } catch (e) {} // If the bBox is not set, the try-catch block above failed. The other condition // is for Opera that returns a width of -Infinity on hidden elements. if (!bBox || bBox.width < 0) { @@ -6292,11 +6320,11 @@ */ getPlotBandPath: function (from, to) { var toPath = this.getPlotLinePath(to, null, null, true), path = this.getPlotLinePath(from, null, null, true); - if (path && toPath) { + if (path && toPath && path.toString() !== toPath.toString()) { // #3836 path.push( toPath[4], toPath[5], toPath[1], toPath[2] @@ -6498,11 +6526,11 @@ //textAlign: dynamic, //rotation: 0, formatter: function () { return Highcharts.numberFormat(this.total, -1); }, - style: defaultPlotOptions.line.dataLabels.style + style: merge(defaultPlotOptions.line.dataLabels.style, { color: '#000000' }) } }, /** * These options extend the defaultOptions for left axes @@ -6604,11 +6632,11 @@ axis.reversed = options.reversed; axis.zoomEnabled = options.zoomEnabled !== false; // Initial categories axis.categories = options.categories || type === 'category'; - axis.names = []; + axis.names = axis.names || []; // Preserve on update (#3830) // Elements //axis.axisGroup = UNDEFINED; //axis.gridGroup = UNDEFINED; //axis.axisTitle = UNDEFINED; @@ -6869,11 +6897,11 @@ cvsOffset = 0, localA = old ? axis.oldTransA : axis.transA, localMin = old ? axis.oldMin : axis.min, returnValue, minPixelPadding = axis.minPixelPadding, - postTranslate = (axis.postTranslate || (axis.isLog && handleLog)) && axis.lin2val; + doPostTranslate = (axis.doPostTranslate || (axis.isLog && handleLog)) && axis.lin2val; if (!localA) { localA = axis.transA; } @@ -6894,17 +6922,17 @@ if (backwards) { // reverse translation val = val * sign + cvsOffset; val -= minPixelPadding; returnValue = val / localA + localMin; // from chart pixel to value - if (postTranslate) { // log and ordinal axes + if (doPostTranslate) { // log and ordinal axes returnValue = axis.lin2val(returnValue); } // From value to pixels } else { - if (postTranslate) { // log and ordinal axes + if (doPostTranslate) { // log and ordinal axes val = axis.val2lin(val); } if (pointPlacement === 'between') { pointPlacement = 0.5; } @@ -7040,14 +7068,15 @@ minorTickPositions = [], pos, i, min = axis.min, max = axis.max, + range = max - min, len; // If minor ticks get too dense, they are hard to read, and may cause long running script. So we don't draw them. - if ((max - min) / minorTickInterval < axis.len / 3) { + if (range && range / minorTickInterval < axis.len / 3) { // #3875 if (axis.isLog) { len = tickPositions.length; for (i = 1; i < len; i++) { minorTickPositions = minorTickPositions.concat( @@ -7061,22 +7090,12 @@ min, max, options.startOfWeek ) ); - - } else if (axis.isDatetimeAxis && options.minorTickInterval === 'auto') { // #1314 - minorTickPositions = minorTickPositions.concat( - axis.getTimeTicks( - axis.normalizeTimeTickInterval(minorTickInterval), - axis.min, - axis.max, - options.startOfWeek - ) - ); } else { - for (pos = axis.min + (tickPositions[0] - axis.min) % minorTickInterval; pos <= axis.max; pos += minorTickInterval) { + for (pos = min + (tickPositions[0] - min) % minorTickInterval; pos <= max; pos += minorTickInterval) { minorTickPositions.push(pos); } } } @@ -8465,25 +8484,23 @@ categorized; if ( // Disabled in options !this.crosshair || - // snap - ((defined(point) || !pick(this.crosshair.snap, true)) === false) || - // Do not draw the crosshair if this axis is not part of the point - (defined(point) && pick(this.crosshair.snap, true) && (!point.series || point.series[this.isXAxis ? 'xAxis' : 'yAxis'] !== this)) + // Snap + ((defined(point) || !pick(this.crosshair.snap, true)) === false) ) { this.hideCrosshair(); } else { // Get the path if (!pick(options.snap, true)) { pos = (this.horiz ? e.chartX - this.pos : this.len - e.chartY + this.pos); } else if (defined(point)) { /*jslint eqeq: true*/ - pos = (this.chart.inverted != this.horiz) ? point.plotX : this.len - point.plotY; + pos = this.isXAxis ? point.plotX : this.len - point.plotY; // #3834 /*jslint eqeq: false*/ } if (this.isRadial) { path = this.getPlotLinePath(this.isXAxis ? point.x : pick(point.stackY, point.y)) || null; // #3189 @@ -9496,11 +9513,12 @@ distance = chart.chartWidth, rdistance = chart.chartWidth, anchor, noSharedTooltip, kdpoints = [], - kdpoint; + kdpoint, + kdpointT; // For hovering over the empty parts of the plot area (hoverSeries is undefined). // If there is one series with point tracking (combo chart), don't go to nearest neighbour. if (!shared && !hoverSeries) { for (i = 0; i < series.length; i++) { @@ -9514,11 +9532,14 @@ // Find nearest points on all series each(series, function (s) { // Skip hidden series noSharedTooltip = s.noSharedTooltip && shared; if (s.visible && !noSharedTooltip && pick(s.options.enableMouseTracking, true)) { // #3821 - kdpoints.push(s.searchPoint(e)); + kdpointT = s.searchPoint(e); // #3828 + if (kdpointT) { + kdpoints.push(kdpointT); + } } }); // Find absolute nearest point each(kdpoints, function (p) { if (p && defined(p.plotX) && defined(p.plotY)) { @@ -9533,27 +9554,31 @@ } else { kdpoint = hoverSeries ? hoverSeries.searchPoint(e) : UNDEFINED; } // Refresh tooltip for kdpoint - if (kdpoint && tooltip && kdpoint !== hoverPoint) { + if (kdpoint && kdpoint !== hoverPoint) { // Draw tooltip if necessary if (shared && !kdpoint.series.noSharedTooltip) { i = kdpoints.length; - trueXkd = kdpoint.plotX + kdpoint.series.xAxis.left; + trueXkd = kdpoint.clientX; while (i--) { - trueX = kdpoints[i].plotX + kdpoints[i].series.xAxis.left; + trueX = kdpoints[i].clientX; if (kdpoints[i].x !== kdpoint.x || trueX !== trueXkd || !defined(kdpoints[i].y) || (kdpoints[i].series.noSharedTooltip || false)) { kdpoints.splice(i, 1); } } - tooltip.refresh(kdpoints, e); + if (tooltip) { + tooltip.refresh(kdpoints, e); + } each(kdpoints, function (point) { point.onMouseOver(e); }); } else { - tooltip.refresh(kdpoint, e); + if (tooltip) { + tooltip.refresh(kdpoint, e); + } kdpoint.onMouseOver(e); } // Update positions (regardless of kdpoint or hoverPoint) } else { @@ -9561,10 +9586,18 @@ if (tooltip && followPointer && !tooltip.isHidden) { anchor = tooltip.getAnchor([{}], e); tooltip.updatePosition({ plotX: anchor[0], plotY: anchor[1] }); } } + + // Start the event listener to pick up the tooltip + if (tooltip && !pointer._onDocumentMouseMove) { + pointer._onDocumentMouseMove = function (e) { + pointer.onDocumentMouseMove(e); + }; + addEvent(doc, 'mousemove', pointer._onDocumentMouseMove); + } // Crosshair each(chart.axes, function (axis) { axis.drawCrosshair(e, pick(kdpoint, hoverPoint)); }); @@ -9852,17 +9885,16 @@ * Special handler for mouse move that will hide the tooltip when the mouse leaves the plotarea. * Issue #149 workaround. The mouseleave event does not always fire. */ onDocumentMouseMove: function (e) { var chart = this.chart, - chartPosition = this.chartPosition, - hoverSeries = chart.hoverSeries; + chartPosition = this.chartPosition; e = this.normalize(e, chartPosition); // If we're outside, hide the tooltip - if (chartPosition && hoverSeries && !this.inClass(e.target, 'highcharts-tracker') && + if (chartPosition && !this.inClass(e.target, 'highcharts-tracker') && !chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop)) { this.reset(); } }, @@ -10467,10 +10499,20 @@ discardElement(item.checkbox); } }, /** + * Destroy all items. + */ + clearItems: function () { + var legend = this; + each(legend.getAllItems(), function (item) { + legend.destroyItem(item); + }); + }, + + /** * Destroys the legend. */ destroy: function () { var legend = this, legendGroup = legend.group, @@ -12554,24 +12596,28 @@ plotWidth = chart.plotWidth - 2 * slicingRoom, plotHeight = chart.plotHeight - 2 * slicingRoom, centerOption = options.center, positions = [pick(centerOption[0], '50%'), pick(centerOption[1], '50%'), options.size || '100%', options.innerSize || 0], smallestSize = mathMin(plotWidth, plotHeight), - isPercent; - - return map(positions, function (length, i) { - isPercent = /%$/.test(length); + isPercent, + i, + value; + + for (i = 0; i < 4; ++i) { + value = positions[i]; + isPercent = /%$/.test(value); handleSlicingRoom = i < 2 || (i === 2 && isPercent); - return (isPercent ? + positions[i] = (isPercent ? // i == 0: centerX, relative to width // i == 1: centerY, relative to height // i == 2: size, relative to smallestSize - // i == 4: innerSize, relative to smallestSize - [plotWidth, plotHeight, smallestSize, smallestSize][i] * - pInt(length) / 100 : - length) + (handleSlicingRoom ? slicingRoom : 0); - }); + // i == 3: innerSize, relative to size + [plotWidth, plotHeight, smallestSize, positions[2]][i] * + pInt(value) / 100 : + pInt(value)) + (handleSlicingRoom ? slicingRoom : 0); + } + return positions; } }; /** * The Point object and prototype. Inheritable and used as base for PiePoint @@ -13072,10 +13118,14 @@ options, zones; this.userOptions = itemOptions; + // General series options take precedence over type options because otherwise, default + // type options like column.animation would be overwritten by the general option. + // But issues have been raised here (#3881), and the solution may be to distinguish + // between default option and userOptions like in the tooltip below. options = merge( typeOptions, plotOptions.series, itemOptions ); @@ -14457,10 +14507,15 @@ // Handle inverted series and tracker groups if (chart.inverted) { series.invertGroups(); } + // Initial clipping, must be defined after inverting groups for VML. Applies to columns etc. (#3839). + if (options.clip !== false && !series.sharedClipKey && !hasRendered) { + group.clip(chart.clipRect); + } + // Run the animation if (animDuration) { series.animate(); } @@ -14509,10 +14564,11 @@ series.translate(); series.render(); if (wasDirtyData) { + delete this.kdTree; // #3868 recalculate the kdtree with dirty data fireEvent(series, 'updatedData'); } }, /** @@ -14564,12 +14620,13 @@ }; } } + // Start the recursive build process with a clone of the points array (#3873) function startRecursive() { - series.kdTree = _kdtree(series.points, dimensions, dimensions); + series.kdTree = _kdtree(series.points.slice(), dimensions, dimensions); } delete series.kdTree; if (series.options.kdSync) { // For testing tooltips, don't build async @@ -14635,12 +14692,10 @@ } if (this.kdTree) { return _search(point, this.kdTree, this.kdDimensions, this.kdDimensions); - } else { - return UNDEFINED; } } }; // end Series prototype @@ -15132,11 +15187,12 @@ if (!series.fixedBox && series.hasCartesianSeries) { // #1906, #2320 chart.isDirtyBox = true; } if (seriesOptions.legendType === 'point') { // #1831, #1885 - chart.legend.destroyItem(point); + series.updateTotals(); + chart.legend.clearItems(); } if (redraw) { chart.redraw(animation); } } @@ -15249,11 +15305,10 @@ dataOptions.shift(); } } // redraw - delete series.kdTree; // #3816 kdTree has to be rebuild. series.isDirty = true; series.isDirtyData = true; if (redraw) { series.getAttribs(); // #1937 chart.redraw(); @@ -15282,11 +15337,10 @@ if (point) { point.destroy(); } // redraw - delete series.kdTree; // #3816 kdTree has to be rebuild. series.isDirty = true; series.isDirtyData = true; if (redraw) { chart.redraw(); } @@ -16307,11 +16361,12 @@ * visibility is toggled */ setVisible: function (vis) { var point = this, series = point.series, - chart = series.chart; + chart = series.chart, + doRedraw = !series.isDirty && series.options.ignoreHiddenPoint; // if called without an argument, toggle visibility point.visible = point.options.visible = vis = vis === UNDEFINED ? !point.visible : vis; series.options.data[inArray(point, series.data)] = point.options; // update userOptions.data @@ -16321,15 +16376,22 @@ point[key][vis ? 'show' : 'hide'](true); } }); if (point.legendItem) { + if (chart.hasRendered) { + series.updateTotals(); + chart.legend.clearItems(); + if (!doRedraw) { + chart.legend.render(); + } + } chart.legend.colorizeItem(point, vis); } - + // Handle ignore hidden slices - if (!series.isDirty && series.options.ignoreHiddenPoint) { + if (doRedraw) { series.isDirty = true; chart.redraw(); } }, @@ -16447,22 +16509,20 @@ this.chart.redraw(animation); } }, /** - * Extend the generatePoints method by adding total and percentage properties to each point + * Recompute total chart sum and update percentages of points. */ - generatePoints: function () { + updateTotals: function () { var i, total = 0, points, len, point, ignoreHiddenPoint = this.options.ignoreHiddenPoint; - Series.prototype.generatePoints.call(this); - // Populate local vars points = this.points; len = points.length; // Get the total sum @@ -16479,15 +16539,23 @@ this.total = total; // Set each point's properties for (i = 0; i < len; i++) { point = points[i]; - point.percentage = total > 0 ? (point.y / total) * 100 : 0; + //point.percentage = (total <= 0 || ignoreHiddenPoint && !point.visible) ? 0 : point.y / total * 100; + point.percentage = (total > 0 && (point.visible || !ignoreHiddenPoint)) ? point.y / total * 100 : 0; point.total = total; } - }, + + /** + * Extend the generatePoints method by adding total and percentage properties to each point + */ + generatePoints: function () { + Series.prototype.generatePoints.call(this); + this.updateTotals(); + }, /** * Do translation for pie slices */ translate: function (positions) { @@ -16840,13 +16908,13 @@ dataLabel = point.dataLabel = renderer[rotation ? 'text' : 'label']( // labels don't support rotation str, 0, -999, + options.shape, null, null, - null, options.useHTML ) .attr(attr) .css(extend(style, moreStyle)) .add(dataLabelsGroup) @@ -16916,10 +16984,19 @@ } else if (pick(options.crop, true)) { // Now check that the data label is within the plot area visible = chart.isInsidePlot(alignAttr.x, alignAttr.y) && chart.isInsidePlot(alignAttr.x + bBox.width, alignAttr.y + bBox.height); } + + // When we're using a shape, make it possible with a connector or an arrow pointing to thie point + if (options.shape) { + dataLabel.attr({ + anchorX: point.plotX, + anchorY: point.plotY + }); + } + } } // Show or hide based on the final aligned position if (!visible) { @@ -17449,11 +17526,11 @@ } /** - * Highcharts JS v4.1.1 (2015-02-17) + * Highcharts JS v4.1.2 (2015-02-27) * 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 @@ -17471,11 +17548,11 @@ function collectAndHide() { var labels = []; each(chart.series, function (series) { var dlOptions = series.options.dataLabels; - if ((dlOptions.enabled || series._hasPointLabels) && !dlOptions.allowOverlap) { + if ((dlOptions.enabled || series._hasPointLabels) && !dlOptions.allowOverlap && series.visible) { // #3866 each(series.points, function (point) { if (point.dataLabel) { point.dataLabel.labelrank = point.labelrank; labels.push(point.dataLabel); } @@ -18275,10 +18352,10 @@ } }); // hide tooltip (#1361) - if (chart.hoverSeries === series) { + if (chart.hoverSeries === series || (chart.hoverPoint && chart.hoverPoint.series) === series) { series.onMouseOut(); } if (legendItem) {