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) {