app/assets/javascripts/highcharts.js in highcharts-rails-5.0.7 vs app/assets/javascripts/highcharts.js in highcharts-rails-5.0.8
- old
+ new
@@ -1,7 +1,7 @@
/**
- * @license Highcharts JS v5.0.7 (2017-01-17)
+ * @license Highcharts JS v5.0.8 (2017-03-08)
*
* (c) 2009-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
@@ -19,11 +19,10 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
/* global window */
var win = window,
doc = win.document;
var SVG_NS = 'http://www.w3.org/2000/svg',
@@ -34,11 +33,11 @@
isFirefox = /Firefox/.test(userAgent),
hasBidiBug = isFirefox && parseInt(userAgent.split('Firefox/')[1], 10) < 4; // issue #38
var Highcharts = win.Highcharts ? win.Highcharts.error(16, true) : {
product: 'Highcharts',
- version: '5.0.7',
+ version: '5.0.8',
deg2rad: Math.PI * 2 / 360,
doc: doc,
hasBidiBug: hasBidiBug,
hasTouch: doc && doc.documentElement.ontouchstart !== undefined,
isMS: isMS,
@@ -65,11 +64,10 @@
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
/* eslint max-len: ["warn", 80, 4] */
- 'use strict';
/**
* The Highcharts object is the placeholder for all other members, and various
* utility functions.
* @namespace Highcharts
@@ -1595,13 +1593,14 @@
* Iterate over an array.
*
* @function #each
* @memberOf Highcharts
* @param {Array} arr - The array to iterate over.
- * @param {Function} fn - The iterator callback. It passes two arguments:
+ * @param {Function} fn - The iterator callback. It passes three arguments:
* * item - The array item.
* * index - The item's index in the array.
+ * * arr - The array that each is being applied to.
* @param {Object} [ctx] The context.
*/
H.each = function(arr, fn, ctx) { // modern browsers
return Array.prototype.forEach.call(arr, fn, ctx);
};
@@ -1888,11 +1887,11 @@
}
if (!end) {
end = params[prop];
}
- if (end.match && end.match('px')) {
+ if (end && end.match && end.match('px')) {
end = end.replace(/px/g, ''); // #4351
}
fx.run(start, end, unit);
}
};
@@ -2094,11 +2093,10 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var each = H.each,
isNumber = H.isNumber,
map = H.map,
merge = H.merge,
pInt = H.pInt;
@@ -2132,16 +2130,10 @@
regex: /rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]?(?:\.[0-9]+)?)\s*\)/,
parse: function(result) {
return [pInt(result[1]), pInt(result[2]), pInt(result[3]), parseFloat(result[4], 10)];
}
}, {
- // HEX color
- regex: /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/,
- parse: function(result) {
- return [pInt(result[1], 16), pInt(result[2], 16), pInt(result[3], 16), 1];
- }
- }, {
// RGB color
regex: /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/,
parse: function(result) {
return [pInt(result[1]), pInt(result[2]), pInt(result[3]), 1];
}
@@ -2160,11 +2152,12 @@
*/
init: function(input) {
var result,
rgba,
i,
- parser;
+ parser,
+ len;
this.input = input = this.names[input] || input;
// Gradients
if (input && input.stops) {
@@ -2172,18 +2165,52 @@
return new H.Color(stop[1]);
});
// Solid colors
} else {
- i = this.parsers.length;
- while (i-- && !rgba) {
- parser = this.parsers[i];
- result = parser.regex.exec(input);
- if (result) {
- rgba = parser.parse(result);
+
+ // Check if it's possible to do bitmasking instead of regex
+ if (input && input[0] === '#') {
+
+ len = input.length;
+ input = parseInt(input.substr(1), 16);
+
+ // Handle long-form, e.g. #AABBCC
+ if (len === 7) {
+
+ rgba = [
+ (input & 0xFF0000) >> 16,
+ (input & 0xFF00) >> 8,
+ (input & 0xFF),
+ 1
+ ];
+
+ // Handle short-form, e.g. #ABC
+ // In short form, the value is assumed to be the same
+ // for both nibbles for each component. e.g. #ABC = #AABBCC
+ } else if (len === 4) {
+
+ rgba = [
+ ((input & 0xF00) >> 4) | (input & 0xF00) >> 8,
+ ((input & 0xF0) >> 4) | (input & 0xF0),
+ ((input & 0xF) << 4) | (input & 0xF),
+ 1
+ ];
}
}
+
+ // Otherwise, check regex parsers
+ if (!rgba) {
+ i = this.parsers.length;
+ while (i-- && !rgba) {
+ parser = this.parsers[i];
+ result = parser.regex.exec(input);
+ if (result) {
+ rgba = parser.parse(result);
+ }
+ }
+ }
}
this.rgba = rgba || [];
},
/**
@@ -2263,11 +2290,10 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var SVGElement,
SVGRenderer,
addEvent = H.addEvent,
animate = H.animate,
@@ -2334,12 +2360,12 @@
/**
* For labels, these CSS properties are applied to the `text` node directly.
* @type {Array.<string>}
*/
textProps: ['direction', 'fontSize', 'fontWeight', 'fontFamily',
- 'fontStyle', 'color', 'lineHeight', 'width', 'textDecoration',
- 'textOverflow', 'textOutline'
+ 'fontStyle', 'color', 'lineHeight', 'width', 'textAlign',
+ 'textDecoration', 'textOverflow', 'textOutline'
],
/**
* Initialize the SVG renderer. This function only exists to make the
* initiation process overridable. It should not be called directly.
@@ -2383,10 +2409,13 @@
animOptions.complete = complete;
}
animate(this, params, animOptions);
} else {
this.attr(params, null, complete);
+ if (animOptions.step) {
+ animOptions.step.call(this);
+ }
}
return this;
},
/**
@@ -2944,14 +2973,13 @@
* elements.
* @param {CSSObject} styles The new CSS styles.
* @returns {SVGElement} Return the SVG element for chaining.
*/
css: function(styles) {
- var elemWrapper = this,
- oldStyles = elemWrapper.styles,
+ var oldStyles = this.styles,
newStyles = {},
- elem = elemWrapper.element,
+ elem = this.element,
textWidth,
n,
serializedCss = '',
hyphenate,
hasNew = !oldStyles,
@@ -2974,32 +3002,38 @@
hasNew = true;
}
}
}
if (hasNew) {
- textWidth = elemWrapper.textWidth =
- (styles && styles.width && elem.nodeName.toLowerCase() === 'text' && pInt(styles.width)) ||
- elemWrapper.textWidth; // #3501
// Merge the new styles with the old ones
if (oldStyles) {
styles = extend(
oldStyles,
newStyles
);
}
+ // Get the text width from style
+ textWidth = this.textWidth = (
+ styles &&
+ styles.width &&
+ styles.width !== 'auto' &&
+ elem.nodeName.toLowerCase() === 'text' &&
+ pInt(styles.width)
+ );
+
// store object
- elemWrapper.styles = styles;
+ this.styles = styles;
- if (textWidth && (!svg && elemWrapper.renderer.forExport)) {
+ if (textWidth && (!svg && this.renderer.forExport)) {
delete styles.width;
}
// serialize and set style attribute
if (isMS && !svg) {
- css(elemWrapper.element, styles);
+ css(this.element, styles);
} else {
hyphenate = function(a, b) {
return '-' + b.toLowerCase();
};
for (n in styles) {
@@ -3013,24 +3047,26 @@
attr(elem, 'style', serializedCss); // #1881
}
}
- if (elemWrapper.added) {
- // Rebuild text after added
- if (textWidth) {
- elemWrapper.renderer.buildText(elemWrapper);
+ if (this.added) {
+
+ // Rebuild text after added. Cache mechanisms in the buildText
+ // will prevent building if there are no significant changes.
+ if (this.element.nodeName === 'text') {
+ this.renderer.buildText(this);
}
// Apply text outline after added
if (styles && styles.textOutline) {
- elemWrapper.applyTextOutline(styles.textOutline);
+ this.applyTextOutline(styles.textOutline);
}
}
}
- return elemWrapper;
+ return this;
},
/**
* Get the current stroke width. In classic mode, the setter registers it
@@ -3984,11 +4020,11 @@
.attr({
'version': '1.1',
'class': 'highcharts-root'
})
- .css(this.getStyle(style));
+ .css(this.getStyle(style));
element = boxWrapper.element;
container.appendChild(element);
// For browsers other than IE, add the namespace attribute (#1978)
if (container.innerHTML.indexOf('xmlns') === -1) {
@@ -4023,11 +4059,11 @@
.replace(/ /g, '%20') : // replace spaces (needed for Safari only)
'';
// Add description
desc = this.createElement('desc').add();
- desc.element.appendChild(doc.createTextNode('Created with Highcharts 5.0.7'));
+ desc.element.appendChild(doc.createTextNode('Created with Highcharts 5.0.8'));
renderer.defs = this.createElement('defs').add();
renderer.allowHTML = allowHTML;
renderer.forExport = forExport;
@@ -4260,12 +4296,14 @@
tempParent.appendChild(textNode); // attach it to the DOM to read offset width
}
if (hasMarkup) {
lines = textStr
+
.replace(/<(b|strong)>/g, '<span style="font-weight:bold">')
.replace(/<(i|em)>/g, '<span style="font-style:italic">')
+
.replace(/<a/g, '<span')
.replace(/<\/(b|strong|i|em|a)>/g, '</span>')
.split(/<br.*?>/g);
} else {
@@ -4721,28 +4759,32 @@
* Draw and return an arc. Overloaded function that takes arguments object.
* @param {SVGAttributes} attribs Initial SVG attributes.
* @returns {SVGElement} The generated wrapper element.
*/
arc: function(x, y, r, innerR, start, end) {
- var arc;
+ var arc,
+ options;
if (isObject(x)) {
- y = x.y;
- r = x.r;
- innerR = x.innerR;
- start = x.start;
- end = x.end;
- x = x.x;
+ options = x;
+ y = options.y;
+ r = options.r;
+ innerR = options.innerR;
+ start = options.start;
+ end = options.end;
+ x = options.x;
+ } else {
+ options = {
+ innerR: innerR,
+ start: start,
+ end: end
+ };
}
// Arcs are defined as symbols for the ability to set
// attributes in attr and animate
- arc = this.symbol('arc', x || 0, y || 0, r || 0, r || 0, {
- innerR: innerR || 0,
- start: start || 0,
- end: end || 0
- });
+ arc = this.symbol('arc', x, y, r, r, options);
arc.r = r; // #959
return arc;
},
/**
@@ -5594,11 +5636,11 @@
* Add specific attribute setters.
*/
// only change local variables
wrapper.widthSetter = function(value) {
- width = value;
+ width = H.isNumber(value) ? value : null; // width:auto => null
};
wrapper.heightSetter = function(value) {
height = value;
};
wrapper['text-alignSetter'] = function(value) {
@@ -5767,11 +5809,10 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var attr = H.attr,
createElement = H.createElement,
css = H.css,
defined = H.defined,
each = H.each,
@@ -6130,11 +6171,10 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var VMLRenderer,
VMLRendererExtension,
VMLElement,
@@ -7282,11 +7322,10 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var color = H.color,
each = H.each,
getTZOffset = H.getTZOffset,
isTouchDevice = H.isTouchDevice,
merge = H.merge,
@@ -7325,11 +7364,11 @@
},
global: {
useUTC: true,
//timezoneOffset: 0,
- VMLRadialGradientURL: 'http://code.highcharts.com/5.0.7/gfx/vml-radial-gradient.png'
+ VMLRadialGradientURL: 'http://code.highcharts.com/5.0.8/gfx/vml-radial-gradient.png'
},
chart: {
//animation: true,
//alignTicks: false,
@@ -7375,12 +7414,12 @@
// fontSize: '12px'
//},
backgroundColor: '#ffffff',
//plotBackgroundColor: null,
plotBorderColor: '#cccccc'
- //plotBorderWidth: 0,
- //plotShadow: false
+ //plotBorderWidth: 0,
+ //plotShadow: false
},
title: {
text: 'Chart title',
align: 'center',
@@ -7671,11 +7710,10 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var arrayMax = H.arrayMax,
arrayMin = H.arrayMin,
defined = H.defined,
destroyObjectProperties = H.destroyObjectProperties,
each = H.each,
@@ -7998,11 +8036,10 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var correctFloat = H.correctFloat,
defined = H.defined,
destroyObjectProperties = H.destroyObjectProperties,
isNumber = H.isNumber,
merge = H.merge,
@@ -8082,14 +8119,14 @@
0,
0,
labelOptions.useHTML
)
- // without position absolute, IE export sometimes is wrong
- .css(merge(labelOptions.style))
+ // without position absolute, IE export sometimes is wrong
+ .css(merge(labelOptions.style))
- .add(axis.labelGroup):
+ .add(axis.labelGroup) :
null;
tick.labelLength = label && label.getBBox().width; // Un-rotated length
tick.rotation = 0; // Base value to detect change for new calls to getBBox
// update
@@ -8259,55 +8296,35 @@
y + (horiz ? tickLength : 0)
], tickWidth);
},
/**
- * Put everything in place
- *
- * @param index {Number}
- * @param old {Boolean} Use old coordinates to prepare an animation into new position
+ * Renders the gridLine.
+ * @param {Boolean} old Whether or not the tick is old
+ * @param {number} opacity The opacity of the grid line
+ * @param {number} reverseCrisp Modifier for avoiding overlapping 1 or -1
+ * @return {undefined}
*/
- render: function(index, old, opacity) {
+ renderGridLine: function(old, opacity, reverseCrisp) {
var tick = this,
axis = tick.axis,
options = axis.options,
- chart = axis.chart,
- renderer = chart.renderer,
- horiz = axis.horiz,
- type = tick.type,
- label = tick.label,
- pos = tick.pos,
- labelOptions = options.labels,
gridLine = tick.gridLine,
- tickPrefix = type ? type + 'Tick' : 'tick',
- tickSize = axis.tickSize(tickPrefix),
gridLinePath,
- mark = tick.mark,
- isNewMark = !mark,
- step = labelOptions.step,
attribs = {},
- show = true,
+ pos = tick.pos,
+ type = tick.type,
tickmarkOffset = axis.tickmarkOffset,
- xy = tick.getPosition(horiz, pos, tickmarkOffset, old),
- x = xy.x,
- y = xy.y,
- reverseCrisp = ((horiz && x === axis.pos + axis.len) ||
- (!horiz && y === axis.pos)) ? -1 : 1; // #1480, #1687
+ renderer = axis.chart.renderer;
var gridPrefix = type ? type + 'Grid' : 'grid',
gridLineWidth = options[gridPrefix + 'LineWidth'],
gridLineColor = options[gridPrefix + 'LineColor'],
- dashStyle = options[gridPrefix + 'LineDashStyle'],
- tickWidth = pick(options[tickPrefix + 'Width'], !type && axis.isXAxis ? 1 : 0), // X axis defaults to 1
- tickColor = options[tickPrefix + 'Color'];
+ dashStyle = options[gridPrefix + 'LineDashStyle'];
- opacity = pick(opacity, 1);
- this.isActive = true;
-
- // Create the grid line
if (!gridLine) {
attribs.stroke = gridLineColor;
attribs['stroke-width'] = gridLineWidth;
if (dashStyle) {
@@ -8320,27 +8337,62 @@
if (old) {
attribs.opacity = 0;
}
tick.gridLine = gridLine = renderer.path()
.attr(attribs)
- .addClass('highcharts-' + (type ? type + '-' : '') + 'grid-line')
+ .addClass(
+ 'highcharts-' + (type ? type + '-' : '') + 'grid-line'
+ )
.add(axis.gridGroup);
}
// If the parameter 'old' is set, the current call will be followed
// by another call, therefore do not do any animations this time
if (!old && gridLine) {
- gridLinePath = axis.getPlotLinePath(pos + tickmarkOffset, gridLine.strokeWidth() * reverseCrisp, old, true);
+ gridLinePath = axis.getPlotLinePath(
+ pos + tickmarkOffset,
+ gridLine.strokeWidth() * reverseCrisp,
+ old, true
+ );
if (gridLinePath) {
gridLine[tick.isNew ? 'attr' : 'animate']({
d: gridLinePath,
opacity: opacity
});
}
}
+ },
- // create the tick mark
+ /**
+ * Renders the tick mark.
+ * @param {Object} xy The position vector of the mark
+ * @param {number} xy.x The x position of the mark
+ * @param {number} xy.y The y position of the mark
+ * @param {number} opacity The opacity of the mark
+ * @param {number} reverseCrisp Modifier for avoiding overlapping 1 or -1
+ * @return {undefined}
+ */
+ renderMark: function(xy, opacity, reverseCrisp) {
+ var tick = this,
+ axis = tick.axis,
+ options = axis.options,
+ renderer = axis.chart.renderer,
+ type = tick.type,
+ tickPrefix = type ? type + 'Tick' : 'tick',
+ tickSize = axis.tickSize(tickPrefix),
+ mark = tick.mark,
+ isNewMark = !mark,
+ x = xy.x,
+ y = xy.y;
+
+
+ var tickWidth = pick(
+ options[tickPrefix + 'Width'], !type && axis.isXAxis ? 1 : 0
+ ), // X axis defaults to 1
+ tickColor = options[tickPrefix + 'Color'];
+
+
if (tickSize) {
// negate the length
if (axis.opposite) {
tickSize[0] = -tickSize[0];
@@ -8358,24 +8410,74 @@
'stroke-width': tickWidth
});
}
mark[isNewMark ? 'attr' : 'animate']({
- d: tick.getMarkPath(x, y, tickSize[0], mark.strokeWidth() * reverseCrisp, horiz, renderer),
+ d: tick.getMarkPath(
+ x,
+ y,
+ tickSize[0],
+ mark.strokeWidth() * reverseCrisp,
+ axis.horiz,
+ renderer),
opacity: opacity
});
}
+ },
- // the label is created on init - now move it into place
+ /**
+ * Renders the tick label.
+ * Note: The label should already be created in init(), so it should only
+ * have to be moved into place.
+ * @param {Object} xy The position vector of the label
+ * @param {number} xy.x The x position of the label
+ * @param {number} xy.y The y position of the label
+ * @param {Boolean} old Whether or not the tick is old
+ * @param {number} opacity The opacity of the label
+ * @param {number} index The index of the tick
+ * @return {undefined}
+ */
+ renderLabel: function(xy, old, opacity, index) {
+ var tick = this,
+ axis = tick.axis,
+ horiz = axis.horiz,
+ options = axis.options,
+ label = tick.label,
+ labelOptions = options.labels,
+ step = labelOptions.step,
+ tickmarkOffset = axis.tickmarkOffset,
+ show = true,
+ x = xy.x,
+ y = xy.y;
if (label && isNumber(x)) {
- label.xy = xy = tick.getLabelPosition(x, y, label, horiz, labelOptions, tickmarkOffset, index, step);
+ label.xy = xy = tick.getLabelPosition(
+ x,
+ y,
+ label,
+ horiz,
+ labelOptions,
+ tickmarkOffset,
+ index,
+ step
+ );
- // Apply show first and show last. If the tick is both first and last, it is
- // a single centered tick, in which case we show the label anyway (#2100).
- if ((tick.isFirst && !tick.isLast && !pick(options.showFirstLabel, 1)) ||
- (tick.isLast && !tick.isFirst && !pick(options.showLastLabel, 1))) {
+ // Apply show first and show last. If the tick is both first and
+ // last, it is a single centered tick, in which case we show the
+ // label anyway (#2100).
+ if (
+ (
+ tick.isFirst &&
+ !tick.isLast &&
+ !pick(options.showFirstLabel, 1)
+ ) ||
+ (
+ tick.isLast &&
+ !tick.isFirst &&
+ !pick(options.showLastLabel, 1)
+ )
+ ) {
show = false;
// Handle label overflow and show or hide accordingly
} else if (horiz && !axis.isRadial && !labelOptions.step &&
!labelOptions.rotation && !old && opacity !== 0) {
@@ -8398,10 +8500,42 @@
tick.isNew = false;
}
},
/**
+ * Put everything in place
+ *
+ * @param index {Number}
+ * @param old {Boolean} Use old coordinates to prepare an animation into new
+ * position
+ */
+ render: function(index, old, opacity) {
+ var tick = this,
+ axis = tick.axis,
+ horiz = axis.horiz,
+ pos = tick.pos,
+ tickmarkOffset = axis.tickmarkOffset,
+ xy = tick.getPosition(horiz, pos, tickmarkOffset, old),
+ x = xy.x,
+ y = xy.y,
+ reverseCrisp = ((horiz && x === axis.pos + axis.len) ||
+ (!horiz && y === axis.pos)) ? -1 : 1; // #1480, #1687
+
+ opacity = pick(opacity, 1);
+ this.isActive = true;
+
+ // Create the grid line
+ this.renderGridLine(old, opacity, reverseCrisp);
+
+ // create the tick mark
+ this.renderMark(xy, opacity, reverseCrisp);
+
+ // the label is created on init - now move it into place
+ this.renderLabel(xy, old, opacity, index);
+ },
+
+ /**
* Destructor for the tick prototype
*/
destroy: function() {
destroyObjectProperties(this, this.axis);
}
@@ -8412,11 +8546,10 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var addEvent = H.addEvent,
animObject = H.animObject,
arrayMax = H.arrayMax,
arrayMin = H.arrayMin,
@@ -8489,14 +8622,14 @@
cursor: 'default',
fontSize: '11px'
},
x: 0
- //y: undefined
- /*formatter: function () {
- return this.value;
- },*/
+ //y: undefined
+ /*formatter: function () {
+ return this.value;
+ },*/
},
//linkedTo: null,
//max: undefined,
//min: undefined,
minPadding: 0.01,
@@ -8554,11 +8687,11 @@
lineWidth: 1,
gridLineColor: '#e6e6e6',
// gridLineDashStyle: 'solid',
// gridLineWidth: 0,
tickColor: '#ccd6eb'
- // tickWidth: 1
+ // tickWidth: 1
},
/**
* This options set extends the defaultOptions for Y axes
@@ -8598,11 +8731,11 @@
},
gridLineWidth: 1,
lineWidth: 0
- // tickWidth: 0
+ // tickWidth: 0
},
/**
* These options extend the defaultOptions for left axes
@@ -8633,12 +8766,12 @@
*/
defaultBottomAxisOptions: {
labels: {
autoRotation: [-45],
x: 0
- // overflow: undefined,
- // staggerLines: null
+ // overflow: undefined,
+ // staggerLines: null
},
title: {
rotation: 0
}
},
@@ -8647,12 +8780,12 @@
*/
defaultTopAxisOptions: {
labels: {
autoRotation: [-45],
x: 0
- // overflow: undefined
- // staggerLines: null
+ // overflow: undefined
+ // staggerLines: null
},
title: {
rotation: 0
}
},
@@ -8712,10 +8845,11 @@
//axis.axisLine = undefined;
// Shorthand types
axis.isLog = type === 'logarithmic';
axis.isDatetimeAxis = isDatetimeAxis;
+ axis.positiveValuesOnly = axis.isLog && !axis.allowNegativeLog;
// Flag, if axis is linked to another axis
axis.isLinked = defined(options.linkedTo);
// Linked axis.
//axis.linkedParent = undefined;
@@ -8810,10 +8944,11 @@
for (eventType in events) {
addEvent(axis, eventType, events[eventType]);
}
// extend logarithmic axis
+ axis.lin2log = options.linearToLogConverter || axis.lin2log;
if (axis.isLog) {
axis.val2lin = axis.log2lin;
axis.lin2val = axis.lin2log;
}
},
@@ -8849,11 +8984,11 @@
multi,
ret,
formatOption = axis.options.labels.format,
// make sure the same symbol is added for all labels on a linear axis
- numericSymbolDetector = axis.isLog ? value : axis.tickInterval;
+ numericSymbolDetector = axis.isLog ? Math.abs(value) : axis.tickInterval;
if (formatOption) {
ret = format(formatOption, this);
} else if (categories) {
@@ -8913,11 +9048,11 @@
seriesDataMax;
axis.hasVisibleSeries = true;
// Validate threshold in logarithmic axes
- if (axis.isLog && threshold <= 0) {
+ if (axis.positiveValuesOnly && threshold <= 0) {
threshold = null;
}
// Get dataMin and dataMax for X axes
if (axis.isXAxis) {
@@ -8958,11 +9093,11 @@
// Adjust to threshold
if (defined(threshold)) {
axis.threshold = threshold;
}
// If any series has a hard threshold, it takes precedence
- if (!seriesOptions.softThreshold || axis.isLog) {
+ if (!seriesOptions.softThreshold || axis.positiveValuesOnly) {
axis.softThreshold = false;
}
}
}
});
@@ -9105,12 +9240,13 @@
lastPos,
roundedMin = correctFloat(Math.floor(min / tickInterval) * tickInterval),
roundedMax = correctFloat(Math.ceil(max / tickInterval) * tickInterval),
tickPositions = [];
- // For single points, add a tick regardless of the relative position (#2662)
- if (min === max && isNumber(min)) {
+ // For single points, add a tick regardless of the relative position
+ // (#2662, #6274)
+ if (this.single) {
return [min];
}
// Populate the intermediate values
pos = roundedMin;
@@ -9143,27 +9279,35 @@
options = axis.options,
tickPositions = axis.tickPositions,
minorTickInterval = axis.minorTickInterval,
minorTickPositions = [],
pos,
- i,
pointRangePadding = axis.pointRangePadding || 0,
min = axis.min - pointRangePadding, // #1498
max = axis.max + pointRangePadding, // #1498
- range = max - min,
- len;
+ range = max - min;
// If minor ticks get too dense, they are hard to read, and may cause long running script. So we don't draw them.
if (range && range / minorTickInterval < axis.len / 3) { // #3875
if (axis.isLog) {
- len = tickPositions.length;
- for (i = 1; i < len; i++) {
- minorTickPositions = minorTickPositions.concat(
- axis.getLogTickPositions(minorTickInterval, tickPositions[i - 1], tickPositions[i], true)
- );
- }
+ // For each interval in the major ticks, compute the minor ticks
+ // separately.
+ each(this.paddedTicks, function(pos, i, paddedTicks) {
+ if (i) {
+ minorTickPositions.push.apply(
+ minorTickPositions,
+ axis.getLogTickPositions(
+ minorTickInterval,
+ paddedTicks[i - 1],
+ paddedTicks[i],
+ true
+ )
+ );
+ }
+ });
+
} else if (axis.isDatetimeAxis && options.minorTickInterval === 'auto') { // #1314
minorTickPositions = minorTickPositions.concat(
axis.getTimeTicks(
axis.normalizeTimeTickInterval(minorTickInterval),
min,
@@ -9182,12 +9326,12 @@
minorTickPositions.push(pos);
}
}
}
- if (minorTickPositions.length !== 0) { // don't change the extremes, when there is no minor ticks
- axis.trimTicks(minorTickPositions, options.startOnTick, options.endOnTick); // #3652 #3743 #1498
+ if (minorTickPositions.length !== 0) {
+ axis.trimTicks(minorTickPositions); // #3652 #3743 #1498 #6330
}
return minorTickPositions;
},
/**
@@ -9433,11 +9577,13 @@
// Secondary values
if (saveOld) {
axis.oldTransA = transA;
}
- axis.translationSlope = axis.transA = transA = axis.len / ((range + pointRangePadding) || 1);
+ axis.translationSlope = axis.transA = transA =
+ axis.options.staticScale ||
+ axis.len / ((range + pointRangePadding) || 1);
axis.transB = axis.horiz ? axis.left : axis.bottom; // translation addend
axis.minPixelPadding = transA * minPointOffset;
},
minFromRange: function() {
@@ -9508,11 +9654,15 @@
axis.max = pick(hardMax, thresholdMax, axis.dataMax);
}
if (isLog) {
- if (!secondPass && Math.min(axis.min, pick(axis.dataMin, axis.min)) <= 0) { // #978
+ if (
+ axis.positiveValuesOnly &&
+ !secondPass &&
+ Math.min(axis.min, pick(axis.dataMin, axis.min)) <= 0
+ ) { // #978
H.error(10, 1); // Can't plot negative values on log axis
}
// The correctFloat cures #934, float errors on full tens. But it
// was too aggressive for #4360 because of conversion back to lin,
// therefore use precision 15.
@@ -9657,22 +9807,28 @@
var options = this.options,
tickPositions,
tickPositionsOption = options.tickPositions,
tickPositioner = options.tickPositioner,
startOnTick = options.startOnTick,
- endOnTick = options.endOnTick,
- single;
+ endOnTick = options.endOnTick;
// Set the tickmarkOffset
this.tickmarkOffset = (this.categories && options.tickmarkPlacement === 'between' &&
this.tickInterval === 1) ? 0.5 : 0; // #3202
// get minorTickInterval
this.minorTickInterval = options.minorTickInterval === 'auto' && this.tickInterval ?
this.tickInterval / 5 : options.minorTickInterval;
+ // When there is only one point, or all points have the same value on
+ // this axis, then min and max are equal and tickPositions.length is 0
+ // or 1. In this case, add some padding in order to center the point,
+ // but leave it with one tick. #1337.
+ this.single = this.min === this.max && defined(this.min) &&
+ !this.tickAmount && options.allowDecimals !== false;
+
// Find the tick positions
this.tickPositions = tickPositions = tickPositionsOption && tickPositionsOption.slice(); // Work on a copy (#1565)
if (!tickPositions) {
if (this.isDatetimeAxis) {
@@ -9706,23 +9862,20 @@
}
}
}
- // reset min/max or remove extremes based on start/end on tick
+ // Reset min/max or remove extremes based on start/end on tick
+ this.paddedTicks = tickPositions.slice(0); // Used for logarithmic minor
this.trimTicks(tickPositions, startOnTick, endOnTick);
if (!this.isLinked) {
- // When there is only one point, or all points have the same value on this axis, then min
- // and max are equal and tickPositions.length is 0 or 1. In this case, add some padding
- // in order to center the point, but leave it with one tick. #1337.
- if (this.min === this.max && defined(this.min) && !this.tickAmount) {
- // Substract half a unit (#2619, #2846, #2515, #3390)
- single = true;
+
+ // Substract half a unit (#2619, #2846, #2515, #3390)
+ if (this.single) {
this.min -= 0.5;
this.max += 0.5;
}
- this.single = single;
if (!tickPositionsOption && !tickPositioner) {
this.adjustTickAmount();
}
}
},
@@ -10390,13 +10543,13 @@
rotation: axisTitleOptions.rotation || 0,
align: textAlign
})
.addClass('highcharts-axis-title')
- .css(axisTitleOptions.style)
+ .css(axisTitleOptions.style)
- .add(axis.axisGroup);
+ .add(axis.axisGroup);
axis.axisTitle.isNew = true;
}
// hide or show the title depending on whether showEmpty is set
axis.axisTitle[display ? 'show' : 'hide'](true);
@@ -10555,11 +10708,13 @@
axisOffset[side] = Math.max(
axisOffset[side],
axis.axisTitleMargin + titleOffset + directionFactor * axis.offset,
labelOffsetPadded, // #3027
- hasData && tickPositions.length && tickSize ? tickSize[0] : 0 // #4866
+ hasData && tickPositions.length && tickSize ?
+ tickSize[0] + directionFactor * axis.offset :
+ 0 // #4866
);
// Decide the clipping needed to keep the graph inside the plot area and axis lines
clip = options.offset ? 0 : Math.floor(axis.axisLine.strokeWidth() / 2) * 2; // #4308, #4371
clipOffset[invertedSide] = Math.max(clipOffset[invertedSide], clip);
@@ -11050,11 +11205,10 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var Axis = H.Axis,
Date = H.Date,
dateFormat = H.dateFormat,
defaultOptions = H.defaultOptions,
defined = H.defined,
@@ -11080,11 +11234,11 @@
var tickPositions = [],
i,
higherRanks = {},
useUTC = defaultOptions.global.useUTC,
minYear, // used in months and years as a basis for Date.UTC()
- minDate = new Date(min - getTZOffset(min)),
+ minDate = new Date(min - Math.abs(getTZOffset(min))), // #6278
makeTime = Date.hcMakeTime,
interval = normalizedInterval.unitRange,
count = normalizedInterval.count,
variableDayLength;
@@ -11311,11 +11465,10 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var Axis = H.Axis,
getMagnitude = H.getMagnitude,
map = H.map,
normalizeTickInterval = H.normalizeTickInterval,
pick = H.pick;
@@ -11435,11 +11588,10 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var dateFormat = H.dateFormat,
each = H.each,
extend = H.extend,
format = H.format,
isNumber = H.isNumber,
@@ -11826,25 +11978,24 @@
return s;
},
/**
* Refresh the tooltip's text and position.
- * @param {Object} point
+ * @param {Object|Array} pointOrPoints Rither a point or an array of points
*/
- refresh: function(point, mouseEvent) {
+ refresh: function(pointOrPoints, mouseEvent) {
var tooltip = this,
- chart = tooltip.chart,
label,
options = tooltip.options,
x,
y,
+ point = pointOrPoints,
anchor,
textConfig = {},
text,
pointConfig = [],
formatter = options.formatter || tooltip.defaultFormatter,
- hoverPoints = chart.hoverPoints,
shared = tooltip.shared,
currentSeries;
clearTimeout(this.hideTimer);
@@ -11854,20 +12005,10 @@
x = anchor[0];
y = anchor[1];
// shared tooltip, array is sent over
if (shared && !(point.series && point.series.noSharedTooltip)) {
-
- // hide previous hoverPoints and set new
-
- chart.hoverPoints = point;
- if (hoverPoints) {
- each(hoverPoints, function(point) {
- point.setState();
- });
- }
-
each(point, function(item) {
item.setState('hover');
pointConfig.push(item.getLabelConfig());
});
@@ -11904,11 +12045,11 @@
}).show();
}
// update text
if (tooltip.split) {
- this.renderSplit(text, chart.hoverPoints);
+ this.renderSplit(text, pointOrPoints);
} else {
label.attr({
text: text && text.join ? text.join('') : text
});
@@ -12199,11 +12340,10 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var addEvent = H.addEvent,
attr = H.attr,
charts = H.charts,
color = H.color,
css = H.css,
@@ -12336,156 +12476,238 @@
value: axis.toValue(e[axis.horiz ? 'chartX' : 'chartY'])
});
});
return coordinates;
},
-
/**
- * With line type charts with a single tracker, get the point closest to the mouse.
- * Run Point.onMouseOver and display tooltip for the point or points.
+ * Collects the points closest to a mouseEvent
+ * @param {Array} series Array of series to gather points from
+ * @param {Boolean} shared True if shared tooltip, otherwise false
+ * @param {Object} e Mouse event which possess a position to compare against
+ * @return {Array} KDPoints sorted by distance
*/
- runPointActions: function(e) {
-
- var pointer = this,
- chart = pointer.chart,
- series = chart.series,
- tooltip = chart.tooltip,
- shared = tooltip ? tooltip.shared : false,
- followPointer,
- updatePosition = true,
- hoverPoint = chart.hoverPoint,
- hoverSeries = chart.hoverSeries,
- i,
- anchor,
+ getKDPoints: function(series, shared, e) {
+ var kdpoints = [],
noSharedTooltip,
- stickToHoverSeries,
directTouch,
- kdpoints = [],
- kdpointT;
+ kdpointT,
+ i;
- // 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++) {
- if (series[i].directTouch || !series[i].options.stickyTracking) {
- series = [];
+ // Find nearest points on all series
+ each(series, function(s) {
+ // Skip hidden series
+ noSharedTooltip = s.noSharedTooltip && shared;
+ directTouch = !shared && s.directTouch;
+ if (s.visible && !noSharedTooltip && !directTouch && pick(s.options.enableMouseTracking, true)) { // #3821
+ kdpointT = s.searchPoint(e, !noSharedTooltip && s.kdDimensions === 1); // #3828
+ if (kdpointT && kdpointT.series) { // Point.series becomes null when reset and before redraw (#5197)
+ kdpoints.push(kdpointT);
}
}
- }
+ });
- // 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) {
- kdpoints = [hoverPoint];
+ // Sort kdpoints by distance to mouse pointer
+ kdpoints.sort(function(p1, p2) {
+ var isCloserX = p1.distX - p2.distX,
+ isCloser = p1.dist - p2.dist,
+ isAbove =
+ (p2.series.group && p2.series.group.zIndex) -
+ (p1.series.group && p1.series.group.zIndex),
+ result;
- // Handle shared tooltip or cases where a series is not yet hovered
- } else {
- // When we have non-shared tooltip and sticky tracking is disabled,
- // search for the closest point only on hovered series: #5533, #5476
- if (!shared && hoverSeries && !hoverSeries.options.stickyTracking) {
- series = [hoverSeries];
- }
- // Find nearest points on all series
- each(series, function(s) {
- // Skip hidden series
- noSharedTooltip = s.noSharedTooltip && shared;
- directTouch = !shared && s.directTouch;
- if (s.visible && !noSharedTooltip && !directTouch && pick(s.options.enableMouseTracking, true)) { // #3821
- kdpointT = s.searchPoint(e, !noSharedTooltip && s.kdDimensions === 1); // #3828
- if (kdpointT && kdpointT.series) { // Point.series becomes null when reset and before redraw (#5197)
- kdpoints.push(kdpointT);
- }
- }
- });
-
- // Sort kdpoints by distance to mouse pointer
- kdpoints.sort(function(p1, p2) {
- var isCloserX = p1.distX - p2.distX,
- isCloser = p1.dist - p2.dist,
- isAbove = (p2.series.group && p2.series.group.zIndex) -
- (p1.series.group && p1.series.group.zIndex);
-
- // We have two points which are not in the same place on xAxis and shared tooltip:
- if (isCloserX !== 0 && shared) { // #5721
- return isCloserX;
- }
+ // We have two points which are not in the same place on xAxis and shared tooltip:
+ if (isCloserX !== 0 && shared) { // #5721
+ result = isCloserX;
// Points are not exactly in the same place on x/yAxis:
- if (isCloser !== 0) {
- return isCloser;
- }
+ } else if (isCloser !== 0) {
+ result = isCloser;
// The same xAxis and yAxis position, sort by z-index:
- if (isAbove !== 0) {
- return isAbove;
- }
-
+ } else if (isAbove !== 0) {
+ result = isAbove;
// The same zIndex, sort by array index:
- return p1.series.index > p2.series.index ? -1 : 1;
- });
- }
+ } else {
+ result = p1.series.index > p2.series.index ? -1 : 1;
+ }
+ return result;
+ });
// Remove points with different x-positions, required for shared tooltip and crosshairs (#4645):
if (shared) {
i = kdpoints.length;
while (i--) {
if (kdpoints[i].x !== kdpoints[0].x || kdpoints[i].series.noSharedTooltip) {
kdpoints.splice(i, 1);
}
}
}
+ return kdpoints;
+ },
+ getPointFromEvent: function(e) {
+ var target = e.target,
+ point;
- // Refresh tooltip for kdpoint if new hover point or tooltip was hidden // #3926, #4200
- if (kdpoints[0] && (kdpoints[0] !== this.prevKDPoint || (tooltip && tooltip.isHidden))) {
- // Draw tooltip if necessary
- if (shared && !kdpoints[0].series.noSharedTooltip) {
- // Do mouseover on all points (#3919, #3985, #4410, #5622)
- for (i = 0; i < kdpoints.length; i++) {
- kdpoints[i].onMouseOver(e, kdpoints[i] !== ((hoverSeries && hoverSeries.directTouch && hoverPoint) || kdpoints[0]));
- }
+ while (target && !point) {
+ point = target.point;
+ target = target.parentNode;
+ }
+ return point;
+ },
- if (kdpoints.length && tooltip) {
- // Keep the order of series in tooltip:
- tooltip.refresh(kdpoints.sort(function(p1, p2) {
- return p1.series.index - p2.series.index;
- }), e);
+ getHoverData: function(existingHoverPoint, existingHoverSeries, series, isDirectTouch, shared, e) {
+ var i,
+ hoverPoint = existingHoverPoint,
+ hoverSeries = existingHoverSeries,
+ hoverPoints;
+
+ // 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.
+ // Handle shared tooltip or cases where a series is not yet hovered
+ if (isDirectTouch) {
+ if (shared) {
+ hoverPoints = [];
+ each(series, function(s) {
+ // Skip hidden series
+ var noSharedTooltip = s.noSharedTooltip && shared,
+ directTouch = !shared && s.directTouch,
+ kdpointT;
+ if (s.visible && !noSharedTooltip && !directTouch && pick(s.options.enableMouseTracking, true)) { // #3821
+ kdpointT = s.searchKDTree({
+ clientX: hoverPoint.clientX,
+ plotY: hoverPoint.plotY
+ }, !noSharedTooltip && s.kdDimensions === 1);
+ if (kdpointT && kdpointT.series) { // Point.series becomes null when reset and before redraw (#5197)
+ hoverPoints.push(kdpointT);
+ }
+ }
+ });
+ // If kdTree is not built
+ if (hoverPoints.length === 0) {
+ hoverPoints = [hoverPoint];
}
} else {
- if (tooltip) {
- tooltip.refresh(kdpoints[0], e);
+ hoverPoints = [hoverPoint];
+ }
+ } else if (hoverSeries && !hoverSeries.options.stickyTracking) {
+ hoverPoints = this.getKDPoints([hoverSeries], shared, e);
+ hoverPoint = hoverPoints[0];
+ hoverSeries = hoverPoint && hoverPoint.series;
+ } else {
+ if (!shared) {
+ // 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 (!hoverSeries) {
+ for (i = 0; i < series.length; i++) {
+ if (series[i].directTouch || !series[i].options.stickyTracking) {
+ series = [];
+ }
+ }
+ // When we have non-shared tooltip and sticky tracking is disabled,
+ // search for the closest point only on hovered series: #5533, #5476
+ } else if (!hoverSeries.options.stickyTracking) {
+ series = [hoverSeries];
}
- if (!hoverSeries || !hoverSeries.directTouch) { // #4448
- kdpoints[0].onMouseOver(e);
- }
}
- this.prevKDPoint = kdpoints[0];
- updatePosition = false;
+ hoverPoints = this.getKDPoints(series, shared, e);
+ hoverPoint = hoverPoints[0];
+ hoverSeries = hoverPoint && hoverPoint.series;
}
- // Update positions (regardless of kdpoint or hoverPoint)
- if (updatePosition) {
- followPointer = hoverSeries && hoverSeries.tooltipOptions.followPointer;
- if (tooltip && followPointer && !tooltip.isHidden) {
- anchor = tooltip.getAnchor([{}], e);
- tooltip.updatePosition({
- plotX: anchor[0],
- plotY: anchor[1]
- });
+ // Keep the order of series in tooltip
+ // Must be done after assigning of hoverPoint
+ hoverPoints.sort(function(p1, p2) {
+ return p1.series.index - p2.series.index;
+ });
+
+ return {
+ hoverPoint: hoverPoint,
+ hoverSeries: hoverSeries,
+ hoverPoints: hoverPoints
+ };
+ },
+ /**
+ * With line type charts with a single tracker, get the point closest to the mouse.
+ * Run Point.onMouseOver and display tooltip for the point or points.
+ */
+ runPointActions: function(e, p) {
+ var pointer = this,
+ chart = pointer.chart,
+ series = chart.series,
+ tooltip = chart.tooltip,
+ shared = tooltip ? tooltip.shared : false,
+ hoverPoint = p || chart.hoverPoint,
+ hoverSeries = hoverPoint && hoverPoint.series || chart.hoverSeries,
+ // onMouseOver or already hovering a series with directTouch
+ isDirectTouch = !!p || (hoverSeries && hoverSeries.directTouch),
+ hoverData = this.getHoverData(hoverPoint, hoverSeries, series, isDirectTouch, shared, e),
+ useSharedTooltip,
+ followPointer,
+ anchor,
+ points;
+
+ // Update variables from hoverData.
+ hoverPoint = hoverData.hoverPoint;
+ hoverSeries = hoverData.hoverSeries;
+ followPointer = hoverSeries && hoverSeries.tooltipOptions.followPointer;
+ useSharedTooltip = shared && hoverPoint && !hoverPoint.series.noSharedTooltip;
+ points = useSharedTooltip ? hoverData.hoverPoints : [hoverPoint];
+
+ // Refresh tooltip for kdpoint if new hover point or tooltip was hidden // #3926, #4200
+ if (
+ hoverPoint &&
+ // !(hoverSeries && hoverSeries.directTouch) &&
+ (hoverPoint !== chart.hoverPoint || (tooltip && tooltip.isHidden))
+ ) {
+ each(chart.hoverPoints || [], function(p) {
+ if (H.inArray(p, points) === -1) {
+ p.setState();
+ }
+ });
+ // Do mouseover on all points (#3919, #3985, #4410, #5622)
+ each(points || [], function(p) {
+ p.setState('hover');
+ });
+ // set normal state to previous series
+ if (chart.hoverSeries !== hoverSeries) {
+ hoverSeries.onMouseOver();
}
+
+ // If tracking is on series in stead of on each point,
+ // fire mouseOver on hover point.
+ if (hoverSeries && !hoverSeries.directTouch) { // #4448
+ if (chart.hoverPoint) {
+ chart.hoverPoint.firePointEvent('mouseOut');
+ }
+ hoverPoint.firePointEvent('mouseOver');
+ }
+ chart.hoverPoints = points;
+ chart.hoverPoint = hoverPoint;
+ // Draw tooltip if necessary
+ if (tooltip) {
+ tooltip.refresh(useSharedTooltip ? points : hoverPoint, e);
+ }
+ // Update positions (regardless of kdpoint or hoverPoint)
+ } else if (followPointer && tooltip && !tooltip.isHidden) {
+ anchor = tooltip.getAnchor([{}], e);
+ tooltip.updatePosition({
+ plotX: anchor[0],
+ plotY: anchor[1]
+ });
}
// Start the event listener to pick up the tooltip and crosshairs
if (!pointer.unDocMouseMove) {
pointer.unDocMouseMove = addEvent(doc, 'mousemove', function(e) {
- if (charts[H.hoverChartIndex]) {
- charts[H.hoverChartIndex].pointer.onDocumentMouseMove(e);
+ var chart = charts[H.hoverChartIndex];
+ if (chart) {
+ chart.pointer.onDocumentMouseMove(e);
}
});
}
// Crosshair. For each hover point, loop over axes and draw cross if that point
// belongs to the axis (#4927).
- each(shared ? kdpoints : [pick(hoverPoint, kdpoints[0])], function drawPointCrosshair(point) { // #5269
+ each(points, function drawPointCrosshair(point) { // #5269
each(chart.axes, function drawAxisCrosshair(axis) {
// In case of snap = false, point is undefined, and we draw the crosshair anyway (#5066)
if (!point || point.series && point.series[axis.coll] === axis) { // #5658
axis.drawCrosshair(e, point);
}
@@ -12558,11 +12780,11 @@
// Remove crosshairs
each(chart.axes, function(axis) {
axis.hideCrosshair();
});
- pointer.hoverX = pointer.prevKDPoint = chart.hoverPoints = chart.hoverPoint = null;
+ pointer.hoverX = chart.hoverPoints = chart.hoverPoint = null;
}
},
/**
* Scale series groups to a certain scale and translation
@@ -12979,11 +13201,10 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var charts = H.charts,
each = H.each,
extend = H.extend,
map = H.map,
noop = H.noop,
@@ -13257,11 +13478,10 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var addEvent = H.addEvent,
charts = H.charts,
css = H.css,
doc = H.doc,
extend = H.extend,
@@ -13377,19 +13597,17 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var Legend,
addEvent = H.addEvent,
css = H.css,
discardElement = H.discardElement,
defined = H.defined,
each = H.each,
- extend = H.extend,
isFirefox = H.isFirefox,
marginNames = H.marginNames,
merge = H.merge,
pick = H.pick,
setAnimation = H.setAnimation,
@@ -13578,11 +13796,21 @@
// Destroy items
each(this.getAllItems(), function(item) {
each(['legendItem', 'legendGroup'], destroyItems, item);
});
- each(['box', 'title', 'group'], destroyItems, this);
+ // Destroy legend elements
+ each([
+ 'clipRect',
+ 'up',
+ 'down',
+ 'pager',
+ 'nav',
+ 'box',
+ 'title',
+ 'group'
+ ], destroyItems, this);
this.display = null; // Reset in .render on update.
},
/**
* Position the checkboxes after the width is determined
@@ -13626,13 +13854,13 @@
this.title = this.chart.renderer.label(titleOptions.text, padding - 3, padding - 4, null, null, null, null, null, 'legend-title')
.attr({
zIndex: 1
})
- .css(titleOptions.style)
+ .css(titleOptions.style)
- .add(this.group);
+ .add(this.group);
}
bBox = this.title.getBBox();
titleHeight = bBox.height;
this.offsetWidth = bBox.width; // #1717
this.contentGroup.attr({
@@ -13700,19 +13928,19 @@
})
.add(legend.scrollGroup);
// Generate the list item text and add it to the group
item.legendItem = li = renderer.text(
- '',
- ltr ? symbolWidth + symbolPadding : -symbolPadding,
- legend.baseline || 0,
- useHTML
- )
+ '',
+ ltr ? symbolWidth + symbolPadding : -symbolPadding,
+ legend.baseline || 0,
+ useHTML
+ )
- .css(merge(item.visible ? itemStyle : itemHiddenStyle)) // merge to prevent modifying original (#1021)
+ .css(merge(item.visible ? itemStyle : itemHiddenStyle)) // merge to prevent modifying original (#1021)
- .attr({
+ .attr({
align: ltr ? 'left' : 'right',
zIndex: 2
})
.add(item.legendGroup);
@@ -13980,14 +14208,14 @@
options[i < 2 ? 'x' : 'y'] = pInt(options.style[prop]) * (i % 2 ? -1 : 1);
}
}*/
if (display) {
- legendGroup.align(extend({
+ legendGroup.align(merge(options, {
width: legendWidth,
height: legendHeight
- }, options), true, 'spacingBox');
+ }), true, 'spacingBox');
}
if (!chart.isResizing) {
this.positionCheckboxes();
}
@@ -14091,13 +14319,13 @@
})
.add(nav);
this.pager = renderer.text('', 15, 10)
.addClass('highcharts-legend-navigation')
- .css(navOptions.style)
+ .css(navOptions.style)
- .add(nav);
+ .add(nav);
this.down = renderer.symbol('triangle-down', 0, 0, arrowSize, arrowSize)
.on('click', function() {
legend.scroll(1, animation);
})
.add(nav);
@@ -14109,11 +14337,11 @@
legendHeight = spaceHeight;
// Reset
} else if (nav) {
clipToHeight();
- nav.hide();
+ this.nav = nav.destroy(); // #6322
this.scrollGroup.attr({
translateY: 1
});
this.clipHeight = 0; // #1379
}
@@ -14327,11 +14555,10 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var addEvent = H.addEvent,
animate = H.animate,
animObject = H.animObject,
attr = H.attr,
doc = H.doc,
@@ -14414,11 +14641,10 @@
userOptions.series = null;
options = merge(defaultOptions, userOptions); // do the merge
options.series = userOptions.series = seriesOptions; // set back the series data
this.userOptions = userOptions;
- this.respRules = [];
var optionsChart = options.chart;
var chartEvents = optionsChart.events;
@@ -14563,12 +14789,11 @@
redrawLegend = chart.isDirtyLegend,
hasStackedSeries,
hasDirtyStacks,
hasCartesianSeries = chart.hasCartesianSeries,
isDirtyBox = chart.isDirtyBox,
- seriesLength = series.length,
- i = seriesLength,
+ i,
serie,
renderer = chart.renderer,
isHiddenChart = renderer.isHidden(),
afterRedraw = [];
@@ -14585,10 +14810,11 @@
// Adjust title layout (reflow multiline text)
chart.layOutTitles();
// link stacked series
+ i = series.length;
while (i--) {
serie = series[i];
if (serie.options.stacking) {
hasStackedSeries = true;
@@ -14598,11 +14824,11 @@
break;
}
}
}
if (hasDirtyStacks) { // mark others as dirty
- i = seriesLength;
+ i = series.length;
while (i--) {
serie = series[i];
if (serie.options.stacking) {
serie.isDirty = true;
}
@@ -14946,11 +15172,14 @@
0,
widthOption || chart.containerWidth || 600 // #1460
);
chart.chartHeight = Math.max(
0,
- heightOption || chart.containerHeight || 400
+ H.relativeLength(
+ heightOption,
+ chart.chartWidth
+ ) || chart.containerHeight || 400
);
},
/**
* Create a clone of the chart's renderTo div and place it outside the viewport to allow
@@ -15733,13 +15962,13 @@
.attr({
align: credits.position.align,
zIndex: 8
})
- .css(credits.style)
+ .css(credits.style)
- .add()
+ .add()
.align(credits.position);
// Dynamically update
this.credits.update = function(options) {
chart.credits = chart.credits.destroy();
@@ -15929,11 +16158,10 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var Point,
each = H.each,
extend = H.extend,
erase = H.erase,
@@ -16280,11 +16508,10 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var addEvent = H.addEvent,
animObject = H.animObject,
arrayMax = H.arrayMax,
arrayMin = H.arrayMin,
correctFloat = H.correctFloat,
@@ -16413,10 +16640,13 @@
//showInLegend: null, // auto: true for standalone series, false for linked series
softThreshold: true,
states: { // states for the entire series
hover: {
//enabled: false,
+ animation: {
+ duration: 50
+ },
lineWidthPlus: 1,
marker: {
// lineWidth: base + 1,
// radius: base + 1
},
@@ -16438,11 +16668,11 @@
//xDateFormat: '%A, %b %e, %Y',
//valuePrefix: '',
//ySuffix: ''
//}
turboThreshold: 1000
- // zIndex: null
+ // zIndex: null
}, /** @lends Series.prototype */ {
isCartesian: true,
pointClass: Point,
@@ -17120,11 +17350,11 @@
x = xData[i];
y = yData[i];
// For points within the visible range, including the first point outside the
// visible range, consider y extremes
- validValue = (isNumber(y, true) || isArray(y)) && (!yAxis.isLog || (y.length || y > 0));
+ validValue = (isNumber(y, true) || isArray(y)) && (!yAxis.positiveValuesOnly || (y.length || y > 0));
withinRange = this.getExtremesFromAll || this.options.getExtremesFromAll || this.cropped ||
((xData[i + 1] || x) >= xMin && (xData[i - 1] || x) <= xMax);
if (validValue && withinRange) {
@@ -17194,11 +17424,11 @@
stack = stacking && yAxis.stacks[(series.negStacks && yValue < (stackThreshold ? 0 : threshold) ? '-' : '') + series.stackKey],
pointStack,
stackValues;
// Discard disallowed y values for log axes (#3434)
- if (yAxis.isLog && yValue !== null && yValue <= 0) {
+ if (yAxis.positiveValuesOnly && yValue !== null && yValue <= 0) {
point.isNull = true;
}
// Get the plotX translation
point.plotX = plotX = correctFloat( // #5236
@@ -17222,11 +17452,11 @@
yValue = stackValues[1];
if (yBottom === stackThreshold && stackIndicator.key === stack[xValue].base) {
yBottom = pick(threshold, yAxis.min);
}
- if (yAxis.isLog && yBottom <= 0) { // #1200, #1232
+ if (yAxis.positiveValuesOnly && yBottom <= 0) { // #1200, #1232
yBottom = null;
}
point.total = point.stackTotal = pointStack.total;
point.percentage = pointStack.total && (point.y / pointStack.total * 100);
@@ -17429,11 +17659,12 @@
xAxis = series.xAxis,
markerAttribs,
globallyEnabled = pick(
seriesMarkerOptions.enabled,
xAxis.isRadial ? true : null,
- series.closestPointRangePx > 2 * seriesMarkerOptions.radius
+ // Use larger or equal as radius is null in bubbles (#6321)
+ series.closestPointRangePx >= 2 * seriesMarkerOptions.radius
);
if (seriesMarkerOptions.enabled !== false || series._hasPointMarkers) {
for (i = 0; i < points.length; i++) {
@@ -17980,10 +18211,19 @@
remover;
function setInvert() {
each(['group', 'markerGroup'], function(groupName) {
if (series[groupName]) {
+
+ // VML/HTML needs explicit attributes for flipping
+ if (chart.renderer.isVML) {
+ series[groupName].attr({
+ width: series.yAxis.len,
+ height: series.xAxis.len
+ });
+ }
+
series[groupName].width = series.yAxis.len;
series[groupName].height = series.xAxis.len;
series[groupName].invert(inverted);
}
});
@@ -18327,11 +18567,10 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var Axis = H.Axis,
Chart = H.Chart,
correctFloat = H.correctFloat,
defined = H.defined,
destroyObjectProperties = H.destroyObjectProperties,
@@ -18813,11 +19052,10 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var addEvent = H.addEvent,
animate = H.animate,
Axis = H.Axis,
Chart = H.Chart,
createElement = H.createElement,
@@ -19061,10 +19299,11 @@
// options.credits => chart.credits
// options.legend => chart.legend
// options.title => chart.title
// options.tooltip => chart.tooltip
// options.subtitle => chart.subtitle
+ // options.mapNavigation => chart.mapNavigation
// options.navigator => chart.navigator
// options.scrollbar => chart.scrollbar
for (key in options) {
if (this[key] && typeof this[key].update === 'function') {
this[key].update(options[key], false);
@@ -19092,11 +19331,10 @@
// Setters for collections. For axes and series, each item is referred
// by an id. If the id is not found, it defaults to the corresponding
// item in the collection, so setting one series without an id, will
// update the first series in the chart. Setting two series without
// an id will update the first and the second respectively (#6019)
- // // docs: New behaviour for unidentified items, add it to docs for
// chart.update and responsive.
each(['xAxis', 'yAxis', 'series'], function(coll) {
if (options[coll]) {
each(splat(options[coll]), function(newOptions, i) {
var item = (
@@ -19192,13 +19430,19 @@
// record changes in the parallel arrays
i = point.index;
series.updateParallelArrays(point, i);
- // Record the options to options.data. If there is an object from before,
- // use point options, otherwise use raw options. (#4701)
- seriesOptions.data[i] = isObject(seriesOptions.data[i], true) ? point.options : options;
+ // Record the options to options.data. If the old or the new config
+ // is an object, use point options, otherwise use raw options
+ // (#4701, #4916).
+ seriesOptions.data[i] = (
+ isObject(seriesOptions.data[i], true) ||
+ isObject(options, true)
+ ) ?
+ point.options :
+ options;
// redraw
series.isDirty = series.isDirtyData = true;
if (!series.fixedBox && series.hasCartesianSeries) { // #1906, #2320
chart.isDirtyBox = true;
@@ -19401,11 +19645,11 @@
var series = this,
chart = this.chart,
// must use user options when changing type because this.options is merged
// in with type specific plotOptions
oldOptions = this.userOptions,
- oldType = this.type,
+ oldType = this.oldType || this.type,
newType = newOptions.type || oldOptions.type || chart.options.chart.type,
proto = seriesTypes[oldType].prototype,
preserve = ['group', 'markerGroup', 'dataLabelsGroup'],
n;
@@ -19441,10 +19685,11 @@
each(preserve, function(prop) {
series[prop] = preserve[prop];
});
this.init(chart, newOptions);
+ this.oldType = oldType;
chart.linkSeries(); // Links are lost in this.remove (#3028)
if (pick(redraw, true)) {
chart.redraw(false);
}
}
@@ -19531,11 +19776,10 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var color = H.color,
each = H.each,
LegendSymbolMixin = H.LegendSymbolMixin,
map = H.map,
pick = H.pick,
@@ -19548,14 +19792,14 @@
* @extends {Series}
*/
seriesType('area', 'line', {
softThreshold: false,
threshold: 0
- // trackByArea: false,
- // lineColor: null, // overrides color, but lets fillColor be unaltered
- // fillOpacity: 0.75,
- // fillColor: null
+ // trackByArea: false,
+ // lineColor: null, // overrides color, but lets fillColor be unaltered
+ // fillOpacity: 0.75,
+ // fillColor: null
}, /** @lends seriesTypes.area.prototype */ {
singleStacks: false,
/**
* Return an array of stacked points, where null and missing points are replaced by
* dummy points in order for gaps to be drawn correctly in stacks.
@@ -19656,15 +19900,15 @@
break;
}
// When reversedStacks is true, loop up, else loop down
i += upOrDown;
}
-
- y = yAxis.toPixels(y, true);
+ y = yAxis.translate(y, 0, 1, 0, 1); // #6272
segment.push({
isNull: true,
- plotX: xAxis.toPixels(x, true),
+ plotX: xAxis.translate(x, 0, 0, 0, 1), // #6272
+ x: x,
plotY: y,
yBottom: y
});
}
});
@@ -19721,11 +19965,12 @@
// Add to the top and bottom line of the area
if (top !== undefined) {
graphPoints.push({
plotX: plotX,
plotY: top === null ? translatedThreshold : yAxis.getThreshold(top),
- isNull: isNull
+ isNull: isNull,
+ isCliff: true
});
bottomPoints.push({
plotX: plotX,
plotY: bottom === null ? translatedThreshold : yAxis.getThreshold(bottom),
doCurve: false // #1041, gaps in areaspline areas
@@ -19778,10 +20023,11 @@
areaPath = topPath.concat(bottomPath);
graphPath = getGraphPath.call(this, graphPoints, false, connectNulls); // TODO: don't set leftCliff and rightCliff when connectNulls?
areaPath.xMap = topPath.xMap;
this.areaPath = areaPath;
+
return graphPath;
},
/**
* Draw the graph and the underlying area. This method calls the Series base
@@ -19861,11 +20107,10 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var pick = H.pick,
seriesType = H.seriesType;
/**
* Spline series type.
@@ -19888,11 +20133,14 @@
rightContX,
rightContY,
ret;
function doCurve(otherPoint) {
- return otherPoint && !otherPoint.isNull && otherPoint.doCurve !== false;
+ return otherPoint &&
+ !otherPoint.isNull &&
+ otherPoint.doCurve !== false &&
+ !point.isCliff; // #6387, area splines next to null
}
// Find control points
if (doCurve(lastPoint) && doCurve(nextPoint)) {
var lastX = lastPoint.plotX,
@@ -19997,11 +20245,10 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var areaProto = H.seriesTypes.area.prototype,
defaultPlotOptions = H.defaultPlotOptions,
LegendSymbolMixin = H.LegendSymbolMixin,
seriesType = H.seriesType;
/**
@@ -20020,11 +20267,10 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var animObject = H.animObject,
color = H.color,
each = H.each,
extend = H.extend,
isNumber = H.isNumber,
@@ -20080,11 +20326,11 @@
distance: 6
},
threshold: 0,
borderColor: '#ffffff'
- // borderWidth: 1
+ // borderWidth: 1
}, /** @lends seriesTypes.column.prototype */ {
cropShoulder: 0,
directTouch: true, // When tooltip is not shared, this series (and derivatives) requires direct touch/hover. KD-tree does not apply.
@@ -20341,11 +20587,14 @@
fill = (zone && zone.color) || point.options.color || this.color; // When zones are present, don't use point.color (#4267)
}
// Select or hover states
if (state) {
- stateOptions = options.states[state];
+ stateOptions = merge(
+ options.states[state],
+ point.options.states && point.options.states[state] || {} // #6401
+ );
brightness = stateOptions.brightness;
fill = stateOptions.color ||
(brightness !== undefined && color(fill).brighten(stateOptions.brightness).get()) ||
fill;
stroke = stateOptions[strokeOption] || stroke;
@@ -20396,23 +20645,23 @@
merge(shapeArgs)
);
} else {
point.graphic = graphic = renderer[point.shapeType](shapeArgs)
- .attr({
- 'class': point.getClassName()
- })
.add(point.group || series.group);
}
// Presentational
graphic
.attr(series.pointAttribs(point, point.selected && 'select'))
.shadow(options.shadow, null, options.stacking && !options.borderRadius);
+ graphic.addClass(point.getClassName(), true);
+
+
} else if (graphic) {
point.graphic = graphic.destroy(); // #1269
}
});
},
@@ -20484,11 +20733,10 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var seriesType = H.seriesType;
/**
* The Bar series class
@@ -20502,11 +20750,10 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var Series = H.Series,
seriesType = H.seriesType;
/**
* The scatter series type
*/
@@ -20543,11 +20790,10 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var pick = H.pick,
relativeLength = H.relativeLength;
H.CenteredSeriesMixin = {
/**
@@ -20593,11 +20839,10 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var addEvent = H.addEvent,
CenteredSeriesMixin = H.CenteredSeriesMixin,
defined = H.defined,
each = H.each,
extend = H.extend,
@@ -20631,11 +20876,11 @@
formatter: function() { // #2945
return this.y === null ? undefined : this.point.name;
},
// softConnector: true,
x: 0
- // y: 0
+ // y: 0
},
ignoreHiddenPoint: true,
//innerSize: 0,
legendType: 'point',
marker: null, // point options are specified in the base options
@@ -20878,12 +21123,13 @@
if (point.y !== null) {
graphic = point.graphic;
shapeArgs = point.shapeArgs;
- // if the point is sliced, use special translation, else use plot area traslation
- groupTranslation = point.sliced ? point.slicedTranslation : {};
+ // If the point is sliced, use special translation, else use
+ // plot area traslation
+ groupTranslation = point.getTranslate();
// Put the shadow behind all points
var shadowGroup = point.shadowGroup;
if (shadow && !shadowGroup) {
@@ -20900,17 +21146,16 @@
// Draw the slice
if (graphic) {
graphic
.setRadialReference(series.center)
- .attr(pointAttr)
+ .attr(pointAttr)
- .animate(extend(shapeArgs, groupTranslation));
+ .animate(extend(shapeArgs, groupTranslation));
} else {
point.graphic = graphic = renderer[point.shapeType](shapeArgs)
- .addClass(point.getClassName())
.setRadialReference(series.center)
.attr(groupTranslation)
.add(series.group);
if (!point.visible) {
@@ -20926,10 +21171,13 @@
'stroke-linejoin': 'round'
})
.shadow(shadow, shadowGroup);
}
+
+ graphic.addClass(point.getClassName());
+
}
});
},
@@ -21041,36 +21289,37 @@
* @param {Boolean} redraw Whether to redraw the chart. True by default.
*/
slice: function(sliced, redraw, animation) {
var point = this,
series = point.series,
- chart = series.chart,
- translation;
+ chart = series.chart;
setAnimation(animation, chart);
// redraw is true by default
redraw = pick(redraw, true);
// if called without an argument, toggle
point.sliced = point.options.sliced = sliced = defined(sliced) ? sliced : !point.sliced;
series.options.data[inArray(point, series.data)] = point.options; // update userOptions.data
- translation = sliced ? point.slicedTranslation : {
- translateX: 0,
- translateY: 0
- };
+ point.graphic.animate(this.getTranslate());
- point.graphic.animate(translation);
-
if (point.shadowGroup) {
- point.shadowGroup.animate(translation);
+ point.shadowGroup.animate(this.getTranslate());
}
},
+ getTranslate: function() {
+ return this.sliced ? this.slicedTranslation : {
+ translateX: 0,
+ translateY: 0
+ };
+ },
+
haloPath: function(size) {
var shapeArgs = this.shapeArgs;
return this.sliced || !this.visible ? [] :
this.series.chart.renderer.symbols.arc(
@@ -21091,11 +21340,10 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var addEvent = H.addEvent,
arrayMax = H.arrayMax,
defined = H.defined,
each = H.each,
extend = H.extend,
@@ -21582,10 +21830,25 @@
// get out if not enabled
if (!series.visible || (!options.enabled && !series._hasPointLabels)) {
return;
}
+ // Reset all labels that have been shortened
+ each(data, function(point) {
+ if (point.dataLabel && point.visible && point.dataLabel.shortened) {
+ point.dataLabel
+ .attr({
+ width: 'auto'
+ }).css({
+ width: 'auto',
+ textOverflow: 'clip'
+ });
+ point.dataLabel.shortened = false;
+ }
+ });
+
+
// run parent method
Series.prototype.drawDataLabels.apply(series);
each(data, function(point) {
if (point.dataLabel && point.visible) { // #407, #2510
@@ -21606,10 +21869,11 @@
var top,
bottom,
length = points.length,
positions,
naturalY,
+ sideOverflow,
size;
if (!length) {
return;
}
@@ -21681,28 +21945,43 @@
labelPos.y = y;
// Detect overflowing data labels
if (series.options.size === null) {
- dataLabelWidth = dataLabel.width;
+ dataLabelWidth = dataLabel.getBBox().width;
+
+ sideOverflow = null;
// Overflow left
if (x - dataLabelWidth < connectorPadding) {
- overflow[3] = Math.max(Math.round(dataLabelWidth - x + connectorPadding), overflow[3]);
+ sideOverflow = Math.round(
+ dataLabelWidth - x + connectorPadding
+ );
+ overflow[3] = Math.max(sideOverflow, overflow[3]);
// Overflow right
} else if (x + dataLabelWidth > plotWidth - connectorPadding) {
- overflow[1] = Math.max(Math.round(x + dataLabelWidth - plotWidth + connectorPadding), overflow[1]);
+ sideOverflow = Math.round(
+ x + dataLabelWidth - plotWidth + connectorPadding
+ );
+ overflow[1] = Math.max(sideOverflow, overflow[1]);
}
// Overflow top
if (y - labelHeight / 2 < 0) {
- overflow[0] = Math.max(Math.round(-y + labelHeight / 2), overflow[0]);
+ overflow[0] = Math.max(
+ Math.round(-y + labelHeight / 2),
+ overflow[0]
+ );
// Overflow left
} else if (y + labelHeight / 2 > plotHeight) {
- overflow[2] = Math.max(Math.round(y + labelHeight / 2 - plotHeight), overflow[2]);
+ overflow[2] = Math.max(
+ Math.round(y + labelHeight / 2 - plotHeight),
+ overflow[2]
+ );
}
+ dataLabel.sideOverflow = sideOverflow;
}
} // for each point
}); // for each half
// Do not apply the final placement and draw the connectors until we have verified
@@ -21782,24 +22061,36 @@
*/
seriesTypes.pie.prototype.placeDataLabels = function() {
each(this.points, function(point) {
var dataLabel = point.dataLabel,
_pos;
-
if (dataLabel && point.visible) {
_pos = dataLabel._pos;
if (_pos) {
+
+ // Shorten data labels with ellipsis if they still overflow
+ // after the pie has reached minSize (#223).
+ if (dataLabel.sideOverflow) {
+ dataLabel._attr.width =
+ dataLabel.getBBox().width - dataLabel.sideOverflow;
+ dataLabel.css({
+ width: dataLabel._attr.width + 'px',
+ textOverflow: 'ellipsis'
+ });
+ dataLabel.shortened = true;
+ }
+
dataLabel.attr(dataLabel._attr);
dataLabel[dataLabel.moved ? 'animate' : 'attr'](_pos);
dataLabel.moved = true;
} else if (dataLabel) {
dataLabel.attr({
y: -9999
});
}
}
- });
+ }, this);
};
seriesTypes.pie.prototype.alignDataLabel = noop;
/**
@@ -21928,11 +22219,10 @@
/**
* (c) 2009-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
/**
* Highcharts module to hide overlapping data labels. This module is included in Highcharts.
*/
var Chart = H.Chart,
each = H.each,
@@ -21943,11 +22233,11 @@
// considered because they are usually accompanied by data labels that lie inside the columns.
Chart.prototype.callbacks.push(function(chart) {
function collectAndHide() {
var labels = [];
- each(chart.series, function(series) {
+ each(chart.series || [], function(series) {
var dlOptions = series.options.dataLabels,
collections = series.dataLabelCollections || ['dataLabel']; // Range series have two collections
if ((dlOptions.enabled || series._hasPointLabels) && !dlOptions.allowOverlap && series.visible) { // #3866
each(collections, function(coll) {
each(series.points, function(point) {
@@ -22077,11 +22367,10 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var addEvent = H.addEvent,
Chart = H.Chart,
createElement = H.createElement,
css = H.css,
defaultOptions = H.defaultOptions,
@@ -22114,19 +22403,14 @@
drawTrackerPoint: function() {
var series = this,
chart = series.chart,
pointer = chart.pointer,
onMouseOver = function(e) {
- var target = e.target,
- point;
+ var point = pointer.getPointFromEvent(e);
- while (target && !point) {
- point = target.point;
- target = target.parentNode;
- }
-
- if (point !== undefined && point !== chart.hoverPoint) { // undefined on graph in scatterchart
+ // undefined on graph in scatterchart
+ if (point !== undefined) {
point.onMouseOver(e);
}
};
// Add reference to the point
@@ -22297,19 +22581,19 @@
*/
extend(Legend.prototype, {
setItemEvents: function(item, legendItem, useHTML) {
var legend = this,
- chart = legend.chart,
+ boxWrapper = legend.chart.renderer.boxWrapper,
activeClass = 'highcharts-legend-' + (item.series ? 'point' : 'series') + '-active';
// Set the events on the item group, or in case of useHTML, the item itself (#1249)
(useHTML ? legendItem : item.legendGroup).on('mouseover', function() {
item.setState('hover');
// A CSS class to dim or hide other than the hovered series
- chart.seriesGroup.addClass(activeClass);
+ boxWrapper.addClass(activeClass);
legendItem.css(legend.options.itemHoverStyle);
})
@@ -22317,11 +22601,11 @@
legendItem.css(item.visible ? legend.itemStyle : legend.itemHiddenStyle);
// A CSS class to dim or hide other than the hovered series
- chart.seriesGroup.removeClass(activeClass);
+ boxWrapper.removeClass(activeClass);
item.setState();
})
.on('click', function(event) {
var strLegendItemClick = 'legendItemClick',
@@ -22499,12 +22783,18 @@
panMax = axis.toValue(startPos + axis.len - mousePos, true) -
halfPointRange,
flipped = panMax < panMin,
newMin = flipped ? panMax : panMin,
newMax = flipped ? panMin : panMax,
- distMin = Math.min(extremes.dataMin, extremes.min) - newMin,
- distMax = newMax - Math.max(extremes.dataMax, extremes.max);
+ paddedMin = axis.toValue(
+ axis.toPixels(extremes.min) - axis.minPixelPadding
+ ),
+ paddedMax = axis.toValue(
+ axis.toPixels(extremes.max) + axis.minPixelPadding
+ ),
+ distMin = Math.min(extremes.dataMin, paddedMin) - newMin,
+ distMax = newMax - Math.max(extremes.dataMax, paddedMax);
// Negative distMin and distMax means that we're still inside the
// data range.
if (axis.series.length && distMin < 0 && distMax < 0) {
axis.setExtremes(
@@ -22570,62 +22860,33 @@
});
},
/**
* Runs on mouse over the point
- *
+ *
* @param {Object} e The event arguments
- * @param {Boolean} byProximity Falsy for kd points that are closest to the mouse, or to
- * actually hovered points. True for other points in shared tooltip.
*/
- onMouseOver: function(e, byProximity) {
+ onMouseOver: function(e) {
var point = this,
series = point.series,
chart = series.chart,
- tooltip = chart.tooltip,
- hoverPoint = chart.hoverPoint;
-
- if (point.series) { // It may have been destroyed, #4130
- // In shared tooltip, call mouse over when point/series is actually hovered: (#5766)
- if (!byProximity) {
- // set normal state to previous series
- if (hoverPoint && hoverPoint !== point) {
- hoverPoint.onMouseOut();
- }
- if (chart.hoverSeries !== series) {
- series.onMouseOver();
- }
- chart.hoverPoint = point;
- }
-
- // update the tooltip
- if (tooltip && (!tooltip.shared || series.noSharedTooltip)) {
- // hover point only for non shared points: (#5766)
- point.setState('hover');
- tooltip.refresh(point, e);
- } else if (!tooltip) {
- point.setState('hover');
- }
-
- // trigger the event
- point.firePointEvent('mouseOver');
- }
+ pointer = chart.pointer;
+ point.firePointEvent('mouseOver');
+ pointer.runPointActions(e, point);
},
/**
* Runs on mouse out from the point
*/
onMouseOut: function() {
- var chart = this.series.chart,
- hoverPoints = chart.hoverPoints;
-
- this.firePointEvent('mouseOut');
-
- if (!hoverPoints || inArray(this, hoverPoints) === -1) { // #887, #2240
- this.setState();
- chart.hoverPoint = null;
- }
+ var point = this,
+ chart = point.series.chart;
+ point.firePointEvent('mouseOut');
+ each(chart.hoverPoints || [], function(p) {
+ p.setState();
+ });
+ chart.hoverPoints = chart.hoverPoint = null;
},
/**
* Import events from the series' and point's options. Only do it on
* demand, to save processing time on hovering.
@@ -22907,11 +23168,15 @@
state = state || '';
if (series.state !== state) {
// Toggle class names
- each([series.group, series.markerGroup], function(group) {
+ each([
+ series.group,
+ series.markerGroup,
+ series.dataLabelsGroup
+ ], function(group) {
if (group) {
// Old state
if (series.state) {
group.removeClass('highcharts-series-' + series.state);
}
@@ -22936,12 +23201,20 @@
if (graph && !graph.dashstyle) { // hover is turned off for dashed lines in VML
attribs = {
'stroke-width': lineWidth
};
- // use attr because animate will cause any other animation on the graph to stop
- graph.attr(attribs);
+
+ // Animate the graph stroke-width. By default a quick animation
+ // to hover, slower to un-hover.
+ graph.animate(
+ attribs,
+ pick(
+ series.chart.options.chart.animation,
+ stateOptions[state] && stateOptions[state].animation
+ )
+ );
while (series['zone-graph-' + i]) {
series['zone-graph-' + i].attr(attribs);
i = i + 1;
}
}
@@ -23053,66 +23326,91 @@
/**
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
- 'use strict';
var Chart = H.Chart,
each = H.each,
inArray = H.inArray,
+ isArray = H.isArray,
isObject = H.isObject,
pick = H.pick,
splat = H.splat;
/**
- * Update the chart based on the current chart/document size and options for responsiveness
+ * Update the chart based on the current chart/document size and options for
+ * responsiveness.
*/
Chart.prototype.setResponsive = function(redraw) {
- var options = this.options.responsive;
+ var options = this.options.responsive,
+ ruleIds = [],
+ currentResponsive = this.currentResponsive,
+ currentRuleIds;
if (options && options.rules) {
each(options.rules, function(rule) {
- this.matchResponsiveRule(rule, redraw);
+ if (rule._id === undefined) {
+ rule._id = H.uniqueKey();
+ }
+
+ this.matchResponsiveRule(rule, ruleIds, redraw);
}, this);
}
+
+ // Merge matching rules
+ var mergedOptions = H.merge.apply(0, H.map(ruleIds, function(ruleId) {
+ return H.find(options.rules, function(rule) {
+ return rule._id === ruleId;
+ }).chartOptions;
+ }));
+
+ // Stringified key for the rules that currently apply.
+ ruleIds = ruleIds.toString() || undefined;
+ currentRuleIds = currentResponsive && currentResponsive.ruleIds;
+
+
+ // Changes in what rules apply
+ if (ruleIds !== currentRuleIds) {
+
+ // Undo previous rules. Before we apply a new set of rules, we need to
+ // roll back completely to base options (#6291).
+ if (currentResponsive) {
+ this.update(currentResponsive.undoOptions, redraw);
+ }
+
+ if (ruleIds) {
+ // Get undo-options for matching rules
+ this.currentResponsive = {
+ ruleIds: ruleIds,
+ mergedOptions: mergedOptions,
+ undoOptions: this.currentOptions(mergedOptions)
+ };
+
+ this.update(mergedOptions, redraw);
+
+ } else {
+ this.currentResponsive = undefined;
+ }
+ }
};
/**
* Handle a single responsiveness rule
*/
- Chart.prototype.matchResponsiveRule = function(rule, redraw) {
- var respRules = this.respRules,
- condition = rule.condition,
- matches,
+ Chart.prototype.matchResponsiveRule = function(rule, matches) {
+ var condition = rule.condition,
fn = condition.callback || function() {
return this.chartWidth <= pick(condition.maxWidth, Number.MAX_VALUE) &&
this.chartHeight <= pick(condition.maxHeight, Number.MAX_VALUE) &&
this.chartWidth >= pick(condition.minWidth, 0) &&
this.chartHeight >= pick(condition.minHeight, 0);
};
-
- if (rule._id === undefined) {
- rule._id = H.uniqueKey();
+ if (fn.call(this)) {
+ matches.push(rule._id);
}
- matches = fn.call(this);
- // Apply a rule
- if (!respRules[rule._id] && matches) {
-
- // Store the current state of the options
- if (rule.chartOptions) {
- respRules[rule._id] = this.currentOptions(rule.chartOptions);
- this.update(rule.chartOptions, redraw);
- }
-
- // Unapply a rule based on the previous options before the rule
- // was applied
- } else if (respRules[rule._id] && !matches) {
- this.update(respRules[rule._id], redraw);
- delete respRules[rule._id];
- }
};
/**
* Get the current values for a given set of options. Used before we update
* the chart with a new responsiveness rule.
@@ -23131,20 +23429,25 @@
for (key in options) {
if (!depth && inArray(key, ['series', 'xAxis', 'yAxis']) > -1) {
options[key] = splat(options[key]);
ret[key] = [];
+
+ // Iterate over collections like series, xAxis or yAxis and map
+ // the items by index.
for (i = 0; i < options[key].length; i++) {
- ret[key][i] = {};
- getCurrent(
- options[key][i],
- curr[key][i],
- ret[key][i],
- depth + 1
- );
+ if (curr[key][i]) { // Item exists in current data (#6347)
+ ret[key][i] = {};
+ getCurrent(
+ options[key][i],
+ curr[key][i],
+ ret[key][i],
+ depth + 1
+ );
+ }
}
} else if (isObject(options[key])) {
- ret[key] = {};
+ ret[key] = isArray(options[key]) ? [] : {};
getCurrent(
options[key],
curr[key] || {},
ret[key],
depth + 1