app/assets/javascripts/highcharts.js in highcharts-rails-5.0.9 vs app/assets/javascripts/highcharts.js in highcharts-rails-5.0.10

- old
+ new

@@ -1,7 +1,7 @@ /** - * @license Highcharts JS v5.0.9 (2017-03-08) + * @license Highcharts JS v5.0.10 (2017-03-31) * * (c) 2009-2016 Torstein Honsi * * License: www.highcharts.com/license */ @@ -15,11 +15,11 @@ root.Highcharts = factory(root); } }(typeof window !== 'undefined' ? window : this, function(win) { var Highcharts = (function() { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ /* global window */ var win = window, @@ -33,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.9', + version: '5.0.10', deg2rad: Math.PI * 2 / 360, doc: doc, hasBidiBug: hasBidiBug, hasTouch: doc && doc.documentElement.ontouchstart !== undefined, isMS: isMS, @@ -59,11 +59,11 @@ }; return Highcharts; }()); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ /* eslint max-len: ["warn", 80, 4] */ @@ -2089,11 +2089,11 @@ //--- End compatibility section --- }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var each = H.each, isNumber = H.isNumber, @@ -2137,12 +2137,12 @@ parse: function(result) { return [pInt(result[1]), pInt(result[2]), pInt(result[3]), 1]; } }], - // Collection of named colors. Can be extended from the outside by adding colors - // to Highcharts.Color.prototype.names. + // Collection of named colors. Can be extended from the outside by adding + // colors to Highcharts.Color.prototype.names. names: { white: '#ffffff', black: '#000000' }, @@ -2155,11 +2155,15 @@ rgba, i, parser, len; - this.input = input = this.names[input] || input; + this.input = input = this.names[ + input && input.toLowerCase ? + input.toLowerCase() : + '' + ] || input; // Gradients if (input && input.stops) { this.stops = map(input.stops, function(stop) { return new H.Color(stop[1]); @@ -2286,11 +2290,11 @@ }; }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var SVGElement, SVGRenderer, @@ -2482,14 +2486,14 @@ id, key = [], value; // Apply linear or radial gradients - if (color.linearGradient) { - gradName = 'linearGradient'; - } else if (color.radialGradient) { + if (color.radialGradient) { gradName = 'radialGradient'; + } else if (color.linearGradient) { + gradName = 'linearGradient'; } if (gradName) { gradAttr = color[gradName]; gradients = renderer.gradients; @@ -2506,13 +2510,18 @@ gradientUnits: 'userSpaceOnUse' }; } // Correct the radial gradient for the radial reference system - if (gradName === 'radialGradient' && radialReference && !defined(gradAttr.gradientUnits)) { + if ( + gradName === 'radialGradient' && + radialReference && + !defined(gradAttr.gradientUnits) + ) { radAttr = gradAttr; // Save the radial attributes for updating - gradAttr = merge(gradAttr, + gradAttr = merge( + gradAttr, renderer.getRadialAttr(radialReference, radAttr), { gradientUnits: 'userSpaceOnUse' } ); } @@ -2597,57 +2606,62 @@ * }); */ applyTextOutline: function(textOutline) { var elem = this.element, tspans, + tspan, hasContrast = textOutline.indexOf('contrast') !== -1, styles = {}, color, strokeWidth, - firstRealChild; + firstRealChild, + i; // When the text shadow is set to contrast, use dark stroke for light // text and vice versa. if (hasContrast) { styles.textOutline = textOutline = textOutline.replace( /contrast/g, this.renderer.getContrast(elem.style.fill) ); } - this.fakeTS = true; // Fake text shadow - - // In order to get the right y position of the clone, - // copy over the y setter - this.ySetter = this.xSetter; - - tspans = [].slice.call(elem.getElementsByTagName('tspan')); - // Extract the stroke width and color textOutline = textOutline.split(' '); color = textOutline[textOutline.length - 1]; strokeWidth = textOutline[0]; - if (strokeWidth && strokeWidth !== 'none') { + if (strokeWidth && strokeWidth !== 'none' && H.svg) { + this.fakeTS = true; // Fake text shadow + + tspans = [].slice.call(elem.getElementsByTagName('tspan')); + + // In order to get the right y position of the clone, + // copy over the y setter + this.ySetter = this.xSetter; + // Since the stroke is applied on center of the actual outline, we // need to double it to get the correct stroke-width outside the // glyphs. strokeWidth = strokeWidth.replace( /(^[\d\.]+)(.*?)$/g, function(match, digit, unit) { return (2 * digit) + unit; } ); - // Remove shadows from previous runs - each(tspans, function(tspan) { + // Remove shadows from previous runs. Iterate from the end to + // support removing items inside the cycle (#6472). + i = tspans.length; + while (i--) { + tspan = tspans[i]; if (tspan.getAttribute('class') === 'highcharts-text-outline') { // Remove then erase erase(tspans, elem.removeChild(tspan)); } - }); + } // For each of the tspans, create a stroked copy behind it. firstRealChild = elem.firstChild; each(tspans, function(tspan, y) { var clone; @@ -2767,11 +2781,16 @@ // running animation of this property if (!continueAnimation) { stop(this, key); } - if (this.symbolName && /^(x|y|width|height|r|start|end|innerR|anchorX|anchorY)/.test(key)) { + // Special handling of symbol attributes + if ( + this.symbolName && + /^(x|y|width|height|r|start|end|innerR|anchorX|anchorY)$/ + .test(key) + ) { if (!hasSetSymbolSize) { this.symbolAttr(hash); hasSetSymbolSize = true; } skipAttr = true; @@ -2984,12 +3003,12 @@ hyphenate, hasNew = !oldStyles, // These CSS properties are interpreted internally by the SVG // renderer, but are not supported by SVG and should not be added to // the DOM. In styled mode, no CSS should find its way to the DOM - // whatsoever (#6173). - svgPseudoProps = ['textOverflow', 'width']; + // whatsoever (#6173, #6474). + svgPseudoProps = ['textOutline', 'textOverflow', 'width']; // convert legacy if (styles && styles.color) { styles.fill = styles.color; } @@ -3617,10 +3636,21 @@ // remove events element.onclick = element.onmouseout = element.onmouseover = element.onmousemove = element.point = null; stop(wrapper); // stop running animations if (wrapper.clipPath) { + // Look for existing references to this clipPath and remove them + // before destroying the element (#6196). + each( + wrapper.element.ownerSVGElement.querySelectorAll('[clip-path]'), + function(el) { + if (el.getAttribute('clip-path') + .indexOf(wrapper.clipPath.element.id) > -1) { + el.removeAttribute('clip-path'); + } + } + ); wrapper.clipPath = wrapper.clipPath.destroy(); } // Destroy stops in case this is a gradient object if (wrapper.stops) { @@ -4059,11 +4089,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.9')); + desc.element.appendChild(doc.createTextNode('Created with Highcharts 5.0.10')); renderer.defs = this.createElement('defs').add(); renderer.allowHTML = allowHTML; renderer.forExport = forExport; @@ -4206,10 +4236,62 @@ cy: (radialReference[1] - radialReference[2] / 2) + gradAttr.cy * radialReference[2], r: gradAttr.r * radialReference[2] }; }, + getSpanWidth: function(wrapper, tspan) { + var renderer = this, + bBox = wrapper.getBBox(true), + actualWidth = bBox.width; + + // Old IE cannot measure the actualWidth for SVG elements (#2314) + if (!svg && renderer.forExport) { + actualWidth = renderer.measureSpanWidth(tspan.firstChild.data, wrapper.styles); + } + return actualWidth; + }, + + applyEllipsis: function(wrapper, tspan, text, width) { + var renderer = this, + actualWidth = renderer.getSpanWidth(wrapper, tspan), + wasTooLong = actualWidth > width, + str = text, + currentIndex, + minIndex = 0, + maxIndex = text.length, + updateTSpan = function(s) { + tspan.removeChild(tspan.firstChild); + if (s) { + tspan.appendChild(doc.createTextNode(s)); + } + }; + if (wasTooLong) { + while (minIndex <= maxIndex) { + currentIndex = Math.ceil((minIndex + maxIndex) / 2); + str = text.substring(0, currentIndex) + '\u2026'; + updateTSpan(str); + actualWidth = renderer.getSpanWidth(wrapper, tspan); + if (minIndex === maxIndex) { + // Complete + minIndex = maxIndex + 1; + } else if (actualWidth > width) { + // Too large. Set max index to current. + maxIndex = currentIndex - 1; + } else { + // Within width. Set min index to current. + minIndex = currentIndex; + } + } + // If max index was 0 it means just ellipsis was also to large. + if (maxIndex === 0) { + // Remove ellipses. + updateTSpan(''); + } + } + return wasTooLong; + }, + /** * Parse a simple HTML string into SVG tspans. Called internally when text * is set on an SVGElement. The function supports a subset of HTML tags, * CSS text features like `width`, `text-overflow`, `white-space`, and * also attributes like `href` and `style`. @@ -4235,10 +4317,11 @@ textOutline = textStyles && textStyles.textOutline, ellipsis = textStyles && textStyles.textOverflow === 'ellipsis', noWrap = textStyles && textStyles.whiteSpace === 'nowrap', fontSize = textStyles && textStyles.fontSize, textCache, + isSubsequentLine, i = childNodes.length, tempParent = width && !wrapper.added && this.box, getLineHeight = function(tspan) { var fontSizeStyle; @@ -4369,11 +4452,11 @@ // Append it textNode.appendChild(tspan); // first span on subsequent line, add the line height - if (!spanNo && lineNo) { + if (!spanNo && isSubsequentLine) { // allow getting the right offset height in exporting in IE if (!svg && forExport) { css(tspan, { display: 'block' @@ -4396,48 +4479,32 @@ // Check width and apply soft breaks or ellipsis if (width) { var words = span.replace(/([^\^])-/g, '$1- ').split(' '), // #1273 hasWhiteSpace = spans.length > 1 || lineNo || (words.length > 1 && !noWrap), tooLong, - actualWidth, rest = [], + actualWidth, dy = getLineHeight(tspan), - rotation = wrapper.rotation, - wordStr = span, // for ellipsis - cursor = wordStr.length, // binary search cursor - bBox; + rotation = wrapper.rotation; - while ((hasWhiteSpace || ellipsis) && (words.length || rest.length)) { - wrapper.rotation = 0; // discard rotation when computing box - bBox = wrapper.getBBox(true); - actualWidth = bBox.width; + if (ellipsis) { + wasTooLong = renderer.applyEllipsis(wrapper, tspan, span, width); + } - // Old IE cannot measure the actualWidth for SVG elements (#2314) - if (!svg && renderer.forExport) { - actualWidth = renderer.measureSpanWidth(tspan.firstChild.data, wrapper.styles); - } - + while (!ellipsis && hasWhiteSpace && (words.length || rest.length)) { + wrapper.rotation = 0; // discard rotation when computing box + actualWidth = renderer.getSpanWidth(wrapper, tspan); tooLong = actualWidth > width; // For ellipsis, do a binary search for the correct string length if (wasTooLong === undefined) { wasTooLong = tooLong; // First time } - if (ellipsis && wasTooLong) { - cursor /= 2; - if (wordStr === '' || (!tooLong && cursor < 0.5)) { - words = []; // All ok, break out - } else { - wordStr = span.substring(0, wordStr.length + (tooLong ? -1 : 1) * Math.ceil(cursor)); - words = [wordStr + (width > 3 ? '\u2026' : '')]; - tspan.removeChild(tspan.firstChild); - } - - // Looping down, this is the first word sequence that is not too long, - // so we can move on to build the next line. - } else if (!tooLong || words.length === 1) { + // Looping down, this is the first word sequence that is not too long, + // so we can move on to build the next line. + if (!tooLong || words.length === 1) { words = rest; rest = []; if (words.length && !noWrap) { tspan = doc.createElementNS(SVG_NS, 'tspan'); @@ -4466,10 +4533,12 @@ spanNo++; } } }); + // To avoid beginning lines that doesn't add to the textNode (#6144) + isSubsequentLine = isSubsequentLine || textNode.childNodes.length; }); if (wasTooLong) { wrapper.attr('title', wrapper.textStr); } @@ -5805,11 +5874,11 @@ H.Renderer = SVGRenderer; }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var attr = H.attr, createElement = H.createElement, @@ -6167,11 +6236,11 @@ }); }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var VMLRenderer, @@ -7318,11 +7387,11 @@ }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var color = H.color, each = H.each, @@ -7364,11 +7433,11 @@ }, global: { useUTC: true, //timezoneOffset: 0, - VMLRadialGradientURL: 'http://code.highcharts.com/5.0.9/gfx/vml-radial-gradient.png' + VMLRadialGradientURL: 'http://code.highcharts.com/5.0.10/gfx/vml-radial-gradient.png' }, chart: { //animation: true, //alignTicks: false, @@ -7706,11 +7775,11 @@ setTimeMethods(); }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var arrayMax = H.arrayMax, arrayMin = H.arrayMin, @@ -7799,13 +7868,13 @@ // Grouping and zIndex groupAttribs.zIndex = zIndex; groupName += '-' + zIndex; - group = axis[groupName]; + group = axis.plotLinesAndBandsGroups[groupName]; if (!group) { - axis[groupName] = group = renderer.g('plot-' + groupName) + axis.plotLinesAndBandsGroups[groupName] = group = renderer.g('plot-' + groupName) .attr(groupAttribs).add(); } // Create the path if (isNew) { @@ -7954,23 +8023,30 @@ /** * Create the path for a plot band */ getPlotBandPath: function(from, to) { var toPath = this.getPlotLinePath(to, null, null, true), - path = this.getPlotLinePath(from, null, null, true); + path = this.getPlotLinePath(from, null, null, true), + // #4964 check if chart is inverted or plotband is on yAxis + horiz = this.horiz, + plus = 1, + outside = + (from < this.min && to < this.min) || + (from > this.max && to > this.max); if (path && toPath) { // Flat paths don't need labels (#3836) - path.flat = path.toString() === toPath.toString(); + if (outside) { + path.flat = path.toString() === toPath.toString(); + plus = 0; + } + // Add 1 pixel, when coordinates are the same path.push( - toPath[4], - toPath[5], - toPath[1], - toPath[2], - 'z' // #5909 + horiz && toPath[4] === path[4] ? toPath[4] + plus : toPath[4], !horiz && toPath[5] === path[5] ? toPath[5] + plus : toPath[5], + horiz && toPath[1] === path[1] ? toPath[1] + plus : toPath[1], !horiz && toPath[2] === path[2] ? toPath[2] + plus : toPath[2] ); } else { // outside the axis area path = null; } @@ -8032,11 +8108,11 @@ }; }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var correctFloat = H.correctFloat, defined = H.defined, @@ -8542,11 +8618,11 @@ }; }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var addEvent = H.addEvent, @@ -8842,10 +8918,13 @@ //axis.axisGroup = undefined; //axis.gridGroup = undefined; //axis.axisTitle = undefined; //axis.axisLine = undefined; + // Placeholder for plotlines and plotbands groups + axis.plotLinesAndBandsGroups = {}; + // Shorthand types axis.isLog = type === 'logarithmic'; axis.isDatetimeAxis = isDatetimeAxis; axis.positiveValuesOnly = axis.isLog && !axis.allowNegativeLog; @@ -9463,11 +9542,13 @@ } else { x = nameX; } // Write the last point's name to the names array - this.names[x] = point.name; + if (x !== undefined) { + this.names[x] = point.name; + } return x; }, /** @@ -9492,11 +9573,11 @@ each(series.points, function(point, i) { var x; if (point.options) { x = axis.nameToX(point); - if (x !== point.x) { + if (x !== undefined && x !== point.x) { point.x = x; series.xData[i] = x; } } }); @@ -9701,22 +9782,25 @@ axis.max += length * maxPadding; } } } - // Handle options for floor, ceiling, softMin and softMax + // Handle options for floor, ceiling, softMin and softMax (#6359) + if (isNumber(options.softMin)) { + axis.min = Math.min(axis.min, options.softMin); + } + if (isNumber(options.softMax)) { + axis.max = Math.max(axis.max, options.softMax); + } if (isNumber(options.floor)) { axis.min = Math.max(axis.min, options.floor); - } else if (isNumber(options.softMin)) { - axis.min = Math.min(axis.min, options.softMin); } if (isNumber(options.ceiling)) { axis.max = Math.min(axis.max, options.ceiling); - } else if (isNumber(options.softMax)) { - axis.max = Math.max(axis.max, options.softMax); } + // When the threshold is soft, adjust the extreme value only if // the data extreme and the padded extreme land on either side of the threshold. For example, // a series of [0, 1, 2, 3] would make the yAxis add a tick for -1 because of the // default minPadding and startOnTick options. This is prevented by the softThreshold // option. @@ -9887,11 +9971,11 @@ var roundedMin = tickPositions[0], roundedMax = tickPositions[tickPositions.length - 1], minPointOffset = this.minPointOffset || 0; if (!this.isLinked) { - if (startOnTick) { + if (startOnTick && roundedMin !== -Infinity) { // #6502 this.min = roundedMin; } else { while (this.min - minPointOffset > tickPositions[0]) { tickPositions.shift(); } @@ -11051,10 +11135,11 @@ destroy: function(keepEvents) { var axis = this, stacks = axis.stacks, stackKey, plotLinesAndBands = axis.plotLinesAndBands, + plotGroup, i, n; // Remove the events if (!keepEvents) { @@ -11084,10 +11169,15 @@ if (axis[prop]) { axis[prop] = axis[prop].destroy(); } }); + // Destroy each generated group for plotlines and plotbands + for (plotGroup in axis.plotLinesAndBandsGroups) { + axis.plotLinesAndBandsGroups[plotGroup] = axis.plotLinesAndBandsGroups[plotGroup].destroy(); + } + // Delete all properties and fall back to the prototype. for (n in axis) { if (axis.hasOwnProperty(n) && inArray(n, axis.keepProps) === -1) { delete axis[n]; } @@ -11201,11 +11291,11 @@ extend(H.Axis.prototype, AxisPlotLineOrBandExtension); }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var Axis = H.Axis, Date = H.Date, @@ -11461,11 +11551,11 @@ }; }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var Axis = H.Axis, getMagnitude = H.getMagnitude, @@ -11584,11 +11674,11 @@ }; }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var dateFormat = H.dateFormat, each = H.each, @@ -12336,11 +12426,11 @@ }; }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var addEvent = H.addEvent, attr = H.attr, @@ -12495,12 +12585,15 @@ // 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 (s.visible && !directTouch && pick(s.options.enableMouseTracking, true)) { // #3821 + // #3828 + kdpointT = s.searchPoint( + e, !noSharedTooltip && s.options.findNearestPointBy.indexOf('y') < 0 + ); if (kdpointT && kdpointT.series) { // Point.series becomes null when reset and before redraw (#5197) kdpoints.push(kdpointT); } } }); @@ -12529,11 +12622,11 @@ } return result; }); // Remove points with different x-positions, required for shared tooltip and crosshairs (#4645): - if (shared) { + if (shared && kdpoints[0] && !kdpoints[0].series.noSharedTooltip) { i = kdpoints.length; while (i--) { if (kdpoints[i].x !== kdpoints[0].x || kdpoints[i].series.noSharedTooltip) { kdpoints.splice(i, 1); } @@ -12551,13 +12644,13 @@ } return point; }, getHoverData: function(existingHoverPoint, existingHoverSeries, series, isDirectTouch, shared, e) { - var i, - hoverPoint = existingHoverPoint, + var hoverPoint = existingHoverPoint, hoverSeries = existingHoverSeries, + searchSeries, 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. @@ -12585,33 +12678,32 @@ hoverPoints = [hoverPoint]; } } else { hoverPoints = [hoverPoint]; } - } else if (hoverSeries && !hoverSeries.options.stickyTracking) { - hoverPoints = this.getKDPoints([hoverSeries], shared, e); - hoverPoint = hoverPoints[0]; - hoverSeries = hoverPoint && hoverPoint.series; - } else { + // When the hovered series has stickyTracking false. + } else if (hoverSeries && !hoverSeries.stickyTracking) { 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]; - } + series = [hoverSeries]; } hoverPoints = this.getKDPoints(series, shared, e); + hoverPoint = H.find(hoverPoints, function(p) { + return p.series === hoverSeries; + }); + // When the hoverSeries has stickyTracking or there is no series hovered. + } else { + // Avoid series with stickyTracking + searchSeries = H.grep(series, function(s) { + return s.stickyTracking; + }); + hoverPoints = this.getKDPoints(searchSeries, shared, e); hoverPoint = hoverPoints[0]; hoverSeries = hoverPoint && hoverPoint.series; + // If + if (shared) { + hoverPoints = this.getKDPoints(series, shared, e); + } } // 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; @@ -12634,11 +12726,11 @@ 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), + isDirectTouch = !!p || (!shared && hoverSeries && hoverSeries.directTouch), hoverData = this.getHoverData(hoverPoint, hoverSeries, series, isDirectTouch, shared, e), useSharedTooltip, followPointer, anchor, points; @@ -12646,11 +12738,14 @@ // 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]; + points = (useSharedTooltip ? + hoverData.hoverPoints : + (hoverPoint ? [hoverPoint] : []) + ); // Refresh tooltip for kdpoint if new hover point or tooltip was hidden // #3926, #4200 if ( hoverPoint && // !(hoverSeries && hoverSeries.directTouch) && @@ -12701,19 +12796,24 @@ chart.pointer.onDocumentMouseMove(e); } }); } - // Crosshair. For each hover point, loop over axes and draw cross if that point - // belongs to the axis (#4927). - 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); - } - }); + // Draw crosshairs (#4927, #5269 #5066, #5658) + each(chart.axes, function drawAxisCrosshair(axis) { + // Snap is true. For each hover point, loop over the axes and draw a + // crosshair if that point belongs to the axis. + // @todo Consider only one crosshair per axis. + if (pick(axis.crosshair.snap, true)) { + each(points, function(p) { + if (p.series[axis.coll] === axis) { + axis.drawCrosshair(e, p); + } + }); + } else { + axis.drawCrosshair(e); + } }); }, /** * Reset the tracking by hiding the tooltip, the hover series state and the hover point @@ -13089,11 +13189,11 @@ onTrackerMouseOut: function(e) { var series = this.chart.hoverSeries, relatedTarget = e.relatedTarget || e.toElement; - if (series && relatedTarget && !series.options.stickyTracking && + if (series && relatedTarget && !series.stickyTracking && !this.inClass(relatedTarget, 'highcharts-tooltip') && (!this.inClass(relatedTarget, 'highcharts-series-' + series.index) || // #2499, #4465 !this.inClass(relatedTarget, 'highcharts-tracker') // #5553 ) ) { @@ -13179,11 +13279,19 @@ * Destroys the Pointer object and disconnects DOM events. */ destroy: function() { var prop; - removeEvent(this.chart.container, 'mouseleave', this.onContainerMouseLeave); + if (this.unDocMouseMove) { + this.unDocMouseMove(); + } + + removeEvent( + this.chart.container, + 'mouseleave', + this.onContainerMouseLeave + ); if (!H.chartCount) { removeEvent(doc, 'mouseup', this.onDocumentMouseUp); removeEvent(doc, 'touchend', this.onDocumentTouchEnd); } @@ -13197,11 +13305,11 @@ }; }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var charts = H.charts, each = H.each, @@ -13474,11 +13582,11 @@ }); }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var addEvent = H.addEvent, charts = H.charts, @@ -13593,11 +13701,11 @@ } }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var Legend, @@ -13655,22 +13763,21 @@ this.itemStyle = options.itemStyle; this.itemHiddenStyle = merge(this.itemStyle, options.itemHiddenStyle); this.itemMarginTop = options.itemMarginTop || 0; this.padding = padding; - this.initialItemX = padding; - this.initialItemY = padding - 5; // 5 is the number of pixels above the text + this.initialItemY = padding - 5; // 5 is pixels above the text this.maxItemWidth = 0; this.itemHeight = 0; this.symbolWidth = pick(options.symbolWidth, 16); this.pages = []; }, /** - * Update the legend with new options. Equivalent to running chart.update with a legend - * configuration option. + * Update the legend with new options. Equivalent to running chart.update + * with a legend configuration option. * @param {Object} options Legend options * @param {Boolean} redraw Whether to redraw the chart, defaults to true. */ update: function(options, redraw) { var chart = this.chart; @@ -13687,11 +13794,13 @@ * Set the colors for the legend item * @param {Object} item A Series or Point instance * @param {Object} visible Dimmed or colored */ colorizeItem: function(item, visible) { - item.legendGroup[visible ? 'removeClass' : 'addClass']('highcharts-legend-item-hidden'); + item.legendGroup[visible ? 'removeClass' : 'addClass']( + 'highcharts-legend-item-hidden' + ); var legend = this, options = legend.options, legendItem = item.legendItem, @@ -13707,12 +13816,12 @@ key; if (legendItem) { legendItem.css({ fill: textColor, - color: textColor - }); // color for #1553, oldIE + color: textColor // #1553, oldIE + }); } if (legendLine) { legendLine.attr({ stroke: symbolColor }); @@ -13751,11 +13860,13 @@ checkbox = item.checkbox, legendGroup = item.legendGroup; if (legendGroup && legendGroup.element) { legendGroup.translate( - ltr ? itemX : legend.legendWidth - itemX - 2 * symbolPadding - 4, + ltr ? + itemX : + legend.legendWidth - itemX - 2 * symbolPadding - 4, itemY ); } if (checkbox) { @@ -13770,15 +13881,18 @@ */ destroyItem: function(item) { var checkbox = item.checkbox; // destroy SVG elements - each(['legendItem', 'legendLine', 'legendSymbol', 'legendGroup'], function(key) { - if (item[key]) { - item[key] = item[key].destroy(); + each( + ['legendItem', 'legendLine', 'legendSymbol', 'legendGroup'], + function(key) { + if (item[key]) { + item[key] = item[key].destroy(); + } } - }); + ); if (checkbox) { discardElement(item.checkbox); } }, @@ -13826,15 +13940,18 @@ each(this.allItems, function(item) { var checkbox = item.checkbox, top; if (checkbox) { - top = translateY + titleHeight + checkbox.y + (scrollOffset || 0) + 3; + top = translateY + titleHeight + checkbox.y + + (scrollOffset || 0) + 3; css(checkbox, { - left: (alignAttr.translateX + item.checkboxOffset + checkbox.x - 20) + 'px', + left: (alignAttr.translateX + item.checkboxOffset + + checkbox.x - 20) + 'px', top: top + 'px', - display: top > translateY - 6 && top < translateY + clipHeight - 6 ? '' : 'none' + display: top > translateY - 6 && top < translateY + + clipHeight - 6 ? '' : 'none' }); } }); } }, @@ -13849,11 +13966,21 @@ titleHeight = 0, bBox; if (titleOptions.text) { if (!this.title) { - this.title = this.chart.renderer.label(titleOptions.text, padding - 3, padding - 4, null, null, null, null, null, 'legend-title') + 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) @@ -13874,11 +14001,12 @@ * Set the legend item text */ setText: function(item) { var options = this.options; item.legendItem.attr({ - text: options.labelFormat ? H.format(options.labelFormat, item) : options.labelFormatter.call(item) + text: options.labelFormat ? + H.format(options.labelFormat, item) : options.labelFormatter.call(item) }); }, /** * Render a single specific legend item @@ -13901,28 +14029,34 @@ ltr = !options.rtl, itemHeight, widthOption = options.width, itemMarginBottom = options.itemMarginBottom || 0, itemMarginTop = legend.itemMarginTop, - initialItemX = legend.initialItemX, bBox, itemWidth, li = item.legendItem, isSeries = !item.series, - series = !isSeries && item.series.drawLegendSymbol ? item.series : item, + series = !isSeries && item.series.drawLegendSymbol ? + item.series : + item, seriesOptions = series.options, - showCheckbox = legend.createCheckboxForItem && seriesOptions && seriesOptions.showCheckbox, + showCheckbox = legend.createCheckboxForItem && + seriesOptions && + seriesOptions.showCheckbox, useHTML = options.useHTML, - fontSize = 12; + fontSize = 12, + itemClassName = item.options.className; if (!li) { // generate it once, later move it - // Generate the group box - // A group to hold the symbol and text. Text is to be appended in Legend class. + // Generate the group box, a group to hold the symbol and text. Text + // is to be appended in Legend class. item.legendGroup = renderer.g('legend-item') - .addClass('highcharts-' + series.type + '-series highcharts-color-' + item.colorIndex + - (item.options.className ? ' ' + item.options.className : '') + + .addClass( + 'highcharts-' + series.type + '-series ' + + 'highcharts-color-' + item.colorIndex + + (itemClassName ? ' ' + itemClassName : '') + (isSeries ? ' highcharts-series-' + item.index : '') ) .attr({ zIndex: 1 }) @@ -13934,19 +14068,21 @@ ltr ? symbolWidth + symbolPadding : -symbolPadding, legend.baseline || 0, useHTML ) - .css(merge(item.visible ? itemStyle : itemHiddenStyle)) // merge to prevent modifying original (#1021) + // merge to prevent modifying original (#1021) + .css(merge(item.visible ? itemStyle : itemHiddenStyle)) .attr({ align: ltr ? 'left' : 'right', zIndex: 2 }) .add(item.legendGroup); - // Get the baseline for the first item - the font size is equal for all + // Get the baseline for the first item - the font size is equal for + // all if (!legend.baseline) { fontSize = itemStyle.fontSize; legend.fontMetrics = renderer.fontMetrics( @@ -13981,32 +14117,46 @@ bBox = li.getBBox(); itemWidth = item.checkboxOffset = options.itemWidth || item.legendItemWidth || - symbolWidth + symbolPadding + bBox.width + itemDistance + (showCheckbox ? 20 : 0); - legend.itemHeight = itemHeight = Math.round(item.legendItemHeight || bBox.height); + symbolWidth + symbolPadding + bBox.width + itemDistance + + (showCheckbox ? 20 : 0); + legend.itemHeight = itemHeight = Math.round( + item.legendItemHeight || bBox.height || legend.symbolHeight + ); - // if the item exceeds the width, start a new line - if (horizontal && legend.itemX - initialItemX + itemWidth > - (widthOption || (chart.chartWidth - 2 * padding - initialItemX - options.x))) { - legend.itemX = initialItemX; - legend.itemY += itemMarginTop + legend.lastLineHeight + itemMarginBottom; + // If the item exceeds the width, start a new line + if ( + horizontal && + legend.itemX - padding + itemWidth > ( + widthOption || ( + chart.spacingBox.width - 2 * padding - options.x + ) + ) + ) { + legend.itemX = padding; + legend.itemY += itemMarginTop + legend.lastLineHeight + + itemMarginBottom; legend.lastLineHeight = 0; // reset for next line (#915, #3976) } // If the item exceeds the height, start a new column - /*if (!horizontal && legend.itemY + options.y + itemHeight > chart.chartHeight - spacingTop - spacingBottom) { + /*if (!horizontal && legend.itemY + options.y + + itemHeight > chart.chartHeight - spacingTop - spacingBottom) { legend.itemY = legend.initialItemY; legend.itemX += legend.maxItemWidth; legend.maxItemWidth = 0; }*/ // Set the edge positions legend.maxItemWidth = Math.max(legend.maxItemWidth, itemWidth); legend.lastItemY = itemMarginTop + legend.itemY + itemMarginBottom; - legend.lastLineHeight = Math.max(itemHeight, legend.lastLineHeight); // #915 + legend.lastLineHeight = Math.max( // #915 + itemHeight, + legend.lastLineHeight + ); // cache the position of the newly generated or reordered items item._legendItemPos = [legend.itemX, legend.itemY]; // advance @@ -14018,65 +14168,82 @@ legend.lastLineHeight = itemHeight; } // the width of the widest item legend.offsetWidth = widthOption || Math.max( - (horizontal ? legend.itemX - initialItemX - itemDistance : itemWidth) + padding, + (horizontal ? legend.itemX - padding - itemDistance : itemWidth) + + padding, legend.offsetWidth ); }, /** - * Get all items, which is one item per series for normal series and one item per point - * for pie series. + * Get all items, which is one item per series for normal series and one + * item per point for pie series. */ getAllItems: function() { var allItems = []; each(this.chart.series, function(series) { var seriesOptions = series && series.options; - // Handle showInLegend. If the series is linked to another series, defaults to false. - if (series && pick(seriesOptions.showInLegend, !defined(seriesOptions.linkedTo) ? undefined : false, true)) { + // Handle showInLegend. If the series is linked to another series, + // defaults to false. + if (series && pick( + seriesOptions.showInLegend, !defined(seriesOptions.linkedTo) ? undefined : false, true + )) { - // Use points or series for the legend item depending on legendType + // Use points or series for the legend item depending on + // legendType allItems = allItems.concat( series.legendItems || - (seriesOptions.legendType === 'point' ? + ( + seriesOptions.legendType === 'point' ? series.data : - series) + series + ) ); } }); return allItems; }, /** - * Adjust the chart margins by reserving space for the legend on only one side - * of the chart. If the position is set to a corner, top or bottom is reserved - * for horizontal legends and left or right for vertical ones. + * Adjust the chart margins by reserving space for the legend on only one + * side of the chart. If the position is set to a corner, top or bottom is + * reserved for horizontal legends and left or right for vertical ones. */ adjustMargins: function(margin, spacing) { var chart = this.chart, options = this.options, - // Use the first letter of each alignment option in order to detect the side - alignment = options.align.charAt(0) + options.verticalAlign.charAt(0) + options.layout.charAt(0); // #4189 - use charAt(x) notation instead of [x] for IE7 + // Use the first letter of each alignment option in order to detect + // the side. (#4189 - use charAt(x) notation instead of [x] for IE7) + alignment = options.align.charAt(0) + + options.verticalAlign.charAt(0) + + options.layout.charAt(0); if (!options.floating) { each([ /(lth|ct|rth)/, /(rtv|rm|rbv)/, /(rbh|cb|lbh)/, /(lbv|lm|ltv)/ ], function(alignments, side) { if (alignments.test(alignment) && !defined(margin[side])) { - // Now we have detected on which side of the chart we should reserve space for the legend + // Now we have detected on which side of the chart we should + // reserve space for the legend chart[marginNames[side]] = Math.max( chart[marginNames[side]], - chart.legend[(side + 1) % 2 ? 'legendHeight' : 'legendWidth'] + [1, -1, -1, 1][side] * options[(side % 2) ? 'x' : 'y'] + - pick(options.margin, 12) + - spacing[side] + ( + chart.legend[ + (side + 1) % 2 ? 'legendHeight' : 'legendWidth' + ] + [1, -1, -1, 1][side] * options[ + (side % 2) ? 'x' : 'y' + ] + + pick(options.margin, 12) + + spacing[side] + ) ); } }); } }, @@ -14097,11 +14264,11 @@ legendHeight, box = legend.box, options = legend.options, padding = legend.padding; - legend.itemX = legend.initialItemX; + legend.itemX = padding; legend.itemY = legend.initialItemY; legend.offsetWidth = 0; legend.lastItemY = 0; if (!legendGroup) { @@ -14124,11 +14291,12 @@ // add each series or point allItems = legend.getAllItems(); // sort by legendIndex stableSort(allItems, function(a, b) { - return ((a.options && a.options.legendIndex) || 0) - ((b.options && b.options.legendIndex) || 0); + return ((a.options && a.options.legendIndex) || 0) - + ((b.options && b.options.legendIndex) || 0); }); // reversed legend if (options.reversed) { allItems.reverse(); @@ -14143,11 +14311,12 @@ legend.renderItem(item); }); // Get the box legendWidth = (options.width || legend.offsetWidth) + padding; - legendHeight = legend.lastItemY + legend.lastLineHeight + legend.titleHeight; + legendHeight = legend.lastItemY + legend.lastLineHeight + + legend.titleHeight; legendHeight = legend.handleOverflow(legendHeight); legendHeight += padding; // Draw the border and/or background if (!box) { @@ -14189,12 +14358,12 @@ legend.legendWidth = legendWidth; legend.legendHeight = legendHeight; - // Now that the legend width and height are established, put the items in the - // final position + // Now that the legend width and height are established, put the items + // in the final position each(allItems, function(item) { legend.positionItem(item); }); // 1.x compatibility: positioning based on style @@ -14203,11 +14372,12 @@ i = 4; while (i--) { prop = props[i]; if (options.style[prop] && options.style[prop] !== 'auto') { options[i < 2 ? 'align' : 'verticalAlign'] = prop; - options[i < 2 ? 'x' : 'y'] = pInt(options.style[prop]) * (i % 2 ? -1 : 1); + options[i < 2 ? 'x' : 'y'] = + pInt(options.style[prop]) * (i % 2 ? -1 : 1); } }*/ if (display) { legendGroup.align(merge(options, { @@ -14220,30 +14390,31 @@ this.positionCheckboxes(); } }, /** - * Set up the overflow handling by adding navigation with up and down arrows below the - * legend. + * Set up the overflow handling by adding navigation with up and down arrows + * below the legend. */ handleOverflow: function(legendHeight) { var legend = this, chart = this.chart, renderer = chart.renderer, options = this.options, optionsY = options.y, alignTop = options.verticalAlign === 'top', - spaceHeight = chart.spacingBox.height + (alignTop ? -optionsY : optionsY) - this.padding, + padding = this.padding, + spaceHeight = chart.spacingBox.height + + (alignTop ? -optionsY : optionsY) - padding, maxHeight = options.maxHeight, clipHeight, clipRect = this.clipRect, navOptions = options.navigation, animation = pick(navOptions.animation, true), arrowSize = navOptions.arrowSize || 12, nav = this.nav, pages = this.pages, - padding = this.padding, lastY, allItems = this.allItems, clipToHeight = function(height) { if (height) { clipRect.attr({ @@ -14263,70 +14434,98 @@ } }; // Adjust the height - if (options.layout === 'horizontal' && options.verticalAlign !== 'middle' && !options.floating) { + if ( + options.layout === 'horizontal' && + options.verticalAlign !== 'middle' && + !options.floating + ) { spaceHeight /= 2; } if (maxHeight) { spaceHeight = Math.min(spaceHeight, maxHeight); } // Reset the legend height and adjust the clipping rectangle pages.length = 0; if (legendHeight > spaceHeight && navOptions.enabled !== false) { - this.clipHeight = clipHeight = Math.max(spaceHeight - 20 - this.titleHeight - padding, 0); + this.clipHeight = clipHeight = + Math.max(spaceHeight - 20 - this.titleHeight - padding, 0); this.currentPage = pick(this.currentPage, 1); this.fullHeight = legendHeight; - // Fill pages with Y positions so that the top of each a legend item defines - // the scroll top for each page (#2098) + // Fill pages with Y positions so that the top of each a legend item + // defines the scroll top for each page (#2098) each(allItems, function(item, i) { var y = item._legendItemPos[1], h = Math.round(item.legendItem.getBBox().height), len = pages.length; - if (!len || (y - pages[len - 1] > clipHeight && (lastY || y) !== pages[len - 1])) { + if (!len || (y - pages[len - 1] > clipHeight && + (lastY || y) !== pages[len - 1])) { pages.push(lastY || y); len++; } - if (i === allItems.length - 1 && y + h - pages[len - 1] > clipHeight) { + if (i === allItems.length - 1 && + y + h - pages[len - 1] > clipHeight) { pages.push(y); } if (y !== lastY) { lastY = y; } }); - // Only apply clipping if needed. Clipping causes blurred legend in PDF export (#1787) + // Only apply clipping if needed. Clipping causes blurred legend in + // PDF export (#1787) if (!clipRect) { - clipRect = legend.clipRect = renderer.clipRect(0, padding, 9999, 0); + clipRect = legend.clipRect = + renderer.clipRect(0, padding, 9999, 0); legend.contentGroup.clip(clipRect); } clipToHeight(clipHeight); // Add navigation elements if (!nav) { - this.nav = nav = renderer.g().attr({ - zIndex: 1 - }).add(this.group); - this.up = renderer.symbol('triangle', 0, 0, arrowSize, arrowSize) + this.nav = nav = renderer.g() + .attr({ + zIndex: 1 + }) + .add(this.group); + + this.up = renderer + .symbol( + 'triangle', + 0, + 0, + arrowSize, + arrowSize + ) .on('click', function() { legend.scroll(-1, animation); }) .add(nav); + this.pager = renderer.text('', 15, 10) .addClass('highcharts-legend-navigation') .css(navOptions.style) .add(nav); - this.down = renderer.symbol('triangle-down', 0, 0, arrowSize, arrowSize) + + this.down = renderer + .symbol( + 'triangle-down', + 0, + 0, + arrowSize, + arrowSize + ) .on('click', function() { legend.scroll(1, animation); }) .add(nav); } @@ -14379,31 +14578,35 @@ translateX: padding, translateY: clipHeight + this.padding + 7 + this.titleHeight, visibility: 'visible' }); this.up.attr({ - 'class': currentPage === 1 ? 'highcharts-legend-nav-inactive' : 'highcharts-legend-nav-active' + 'class': currentPage === 1 ? + 'highcharts-legend-nav-inactive' : 'highcharts-legend-nav-active' }); pager.attr({ text: currentPage + '/' + pageCount }); this.down.attr({ 'x': 18 + this.pager.getBBox().width, // adjust to text width - 'class': currentPage === pageCount ? 'highcharts-legend-nav-inactive' : 'highcharts-legend-nav-active' + 'class': currentPage === pageCount ? + 'highcharts-legend-nav-inactive' : 'highcharts-legend-nav-active' }); this.up .attr({ - fill: currentPage === 1 ? navOptions.inactiveColor : navOptions.activeColor + fill: currentPage === 1 ? + navOptions.inactiveColor : navOptions.activeColor }) .css({ cursor: currentPage === 1 ? 'default' : 'pointer' }); this.down .attr({ - fill: currentPage === pageCount ? navOptions.inactiveColor : navOptions.activeColor + fill: currentPage === pageCount ? + navOptions.inactiveColor : navOptions.activeColor }) .css({ cursor: currentPage === pageCount ? 'default' : 'pointer' }); @@ -14453,12 +14656,13 @@ }).add(item.legendGroup); }, /** - * Get the series' symbol in the legend. This method should be overridable to create custom - * symbols through Highcharts.seriesTypes[type].prototype.drawLegendSymbols. + * Get the series' symbol in the legend. This method should be overridable + * to create custom symbols through + * Highcharts.seriesTypes[type].prototype.drawLegendSymbols. * * @param {Object} legend The legend object */ drawLineMarker: function(legend) { @@ -14469,11 +14673,12 @@ symbolWidth = legend.symbolWidth, symbolHeight = legend.symbolHeight, generalRadius = symbolHeight / 2, renderer = this.chart.renderer, legendItemGroup = this.legendGroup, - verticalCenter = legend.baseline - Math.round(legend.fontMetrics.b * 0.3), + verticalCenter = legend.baseline - + Math.round(legend.fontMetrics.b * 0.3), attr = {}; // Draw the line attr = { @@ -14530,15 +14735,17 @@ }; // Workaround for #2030, horizontal legend items not displaying in IE11 Preview, // and for #2580, a similar drawing flaw in Firefox 26. // Explore if there's a general cause for this. The problem may be related - // to nested group elements, as the legend item texts are within 4 group elements. + // to nested group elements, as the legend item texts are within 4 group + // elements. if (/Trident\/7\.0/.test(win.navigator.userAgent) || isFirefox) { wrap(Legend.prototype, 'positionItem', function(proceed, item) { var legend = this, - runPositionItem = function() { // If chart destroyed in sync, this is undefined (#2030) + // If chart destroyed in sync, this is undefined (#2030) + runPositionItem = function() { if (item._legendItemPos) { proceed.call(legend, item); } }; @@ -14551,11 +14758,11 @@ } }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var addEvent = H.addEvent, animate = H.animate, @@ -15102,11 +15309,12 @@ }); chart.layOutTitles(redraw); }, /** - * Lay out the chart titles and cache the full offset height for use in getMargins + * Lay out the chart titles and cache the full offset height for use + * in getMargins */ layOutTitles: function(redraw) { var titleOffset = 0, requiresDirtyBox, renderer = this.renderer, @@ -15124,18 +15332,23 @@ titleSize = renderer.fontMetrics(titleSize, title).b; title .css({ - width: (titleOptions.width || spacingBox.width + titleOptions.widthAdjust) + 'px' + width: (titleOptions.width || + spacingBox.width + titleOptions.widthAdjust) + 'px' }) .align(extend({ y: titleOffset + titleSize + (key === 'title' ? -3 : 2) }, titleOptions), false, 'spacingBox'); if (!titleOptions.floating && !titleOptions.verticalAlign) { - titleOffset = Math.ceil(titleOffset + title.getBBox().height); + titleOffset = Math.ceil( + titleOffset + + // Skip the cache for HTML (#3481) + title.getBBox(titleOptions.useHTML).height + ); } } }, this); requiresDirtyBox = this.titleOffset !== titleOffset; @@ -16154,11 +16367,11 @@ }; // end Chart }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var Point, @@ -16504,11 +16717,11 @@ }; }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var addEvent = H.addEvent, animObject = H.animObject, @@ -16667,14 +16880,14 @@ //valueDecimals: null, //xDateFormat: '%A, %b %e, %Y', //valuePrefix: '', //ySuffix: '' //} - turboThreshold: 1000 + turboThreshold: 1000, // zIndex: null + findNearestPointBy: 'x' - }, /** @lends Series.prototype */ { isCartesian: true, pointClass: Point, sorted: true, // requires the data to be sorted requireSorting: true, @@ -16913,10 +17126,23 @@ userPlotOptions.series && userPlotOptions.series.tooltip, userPlotOptions[this.type] && userPlotOptions[this.type].tooltip, itemOptions.tooltip ); + // When shared tooltip, stickyTracking is true by default, + // unless user says otherwise. + this.stickyTracking = pick( + itemOptions.stickyTracking, + userPlotOptions[this.type] && userPlotOptions[this.type].stickyTracking, + userPlotOptions.series && userPlotOptions.series.stickyTracking, + ( + this.tooltipOptions.shared && !this.noSharedTooltip ? + true : + options.stickyTracking + ) + ); + // Delete marker object if not allowed (#1125) if (typeOptions.marker === null) { delete options.marker; } @@ -17298,12 +17524,14 @@ } else { // splat the y data in case of ohlc data array point = (new PointClass()).init(series, [processedXData[i]].concat(splat(processedYData[i]))); point.dataGroup = series.groupMap[i]; } - point.index = cursor; // For faster access in Point.update - points[i] = point; + if (point) { // #6279 + point.index = cursor; // For faster access in Point.update + points[i] = point; + } } // Hide cropped-away points - this only runs when the number of points is above cropThreshold, or when // swithching view from non-grouped data to grouped data (#637) if (data && (processedDataLength !== (dataLength = data.length) || hasGroupedData)) { @@ -17352,11 +17580,11 @@ // For points within the visible range, including the first point outside the // visible range, consider y extremes 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); + ((xData[i] || x) >= xMin && (xData[i] || x) <= xMax); if (validValue && withinRange) { j = y.length; if (j) { // array, like ohlc or range data @@ -17368,10 +17596,11 @@ } else { activeYData[activeCounter++] = y; } } } + this.dataMin = arrayMin(activeYData); this.dataMax = arrayMax(activeYData); }, /** @@ -17580,11 +17809,10 @@ if (clipRect.count.length === 0 && sharedClipKey && chart[sharedClipKey]) { if (!seriesClipBox) { chart[sharedClipKey] = chart[sharedClipKey].destroy(); } if (chart[sharedClipKey + 'm']) { - this.markerGroup.clip(); chart[sharedClipKey + 'm'] = chart[sharedClipKey + 'm'].destroy(); } } } }, @@ -18424,11 +18652,10 @@ /** * KD Tree && PointSearching Implementation */ - kdDimensions: 1, kdAxisArray: ['clientX', 'plotY'], searchPoint: function(e, compareX) { var series = this, xAxis = series.xAxis, @@ -18451,11 +18678,12 @@ // Prevent multiple k-d-trees from being built simultaneously (#6235) this.buildingKdTree = true; var series = this, - dimensions = series.kdDimensions; + dimensions = series.options.findNearestPointBy.indexOf('y') > -1 ? + 2 : 1; // Internal function function _kdtree(points, depth, dimensions) { var axis, median, @@ -18502,11 +18730,13 @@ searchKDTree: function(point, compareX) { var series = this, kdX = this.kdAxisArray[0], kdY = this.kdAxisArray[1], - kdComparer = compareX ? 'distX' : 'dist'; + kdComparer = compareX ? 'distX' : 'dist', + kdDimensions = series.options.findNearestPointBy.indexOf('y') > -1 ? + 2 : 1; // Set the one and two dimensional distance on the point object function setDistance(p1, p2) { var x = (defined(p1[kdX]) && defined(p2[kdX])) ? Math.pow(p1[kdX] - p2[kdX], 2) : null, y = (defined(p1[kdY]) && defined(p2[kdY])) ? Math.pow(p1[kdY] - p2[kdY], 2) : null, @@ -18553,21 +18783,20 @@ if (!this.kdTree && !this.buildingKdTree) { this.buildKDTree(); } if (this.kdTree) { - return _search(point, - this.kdTree, this.kdDimensions, this.kdDimensions); + return _search(point, this.kdTree, kdDimensions, kdDimensions); } } }); // end Series prototype }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var Axis = H.Axis, Chart = H.Chart, @@ -19048,11 +19277,11 @@ }; }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var addEvent = H.addEvent, animate = H.animate, @@ -19268,14 +19497,20 @@ if ('className' in optionsChart) { this.setClassName(optionsChart.className); } if ('inverted' in optionsChart || 'polar' in optionsChart) { - this.propFromSeries(); // Parses options.chart.inverted and options.chart.polar together with the available series + // Parse options.chart.inverted and options.chart.polar together + // with the available series. + this.propFromSeries(); updateAllAxes = true; } + if ('alignTicks' in optionsChart) { // #6452 + updateAllAxes = true; + } + for (key in optionsChart) { if (optionsChart.hasOwnProperty(key)) { if (inArray('chart.' + key, this.propsRequireUpdateSeries) !== -1) { updateAllSeries = true; } @@ -19332,11 +19567,11 @@ // 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) // chart.update and responsive. - each(['xAxis', 'yAxis', 'series'], function(coll) { + each(['xAxis', 'yAxis', 'series', 'colorAxis', 'pane'], function(coll) { if (options[coll]) { each(splat(options[coll]), function(newOptions, i) { var item = ( defined(newOptions.id) && this.get(newOptions.id) @@ -19772,11 +20007,11 @@ }); }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var color = H.color, each = H.each, @@ -20103,11 +20338,11 @@ }); }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var pick = H.pick, seriesType = H.seriesType; @@ -20241,11 +20476,11 @@ }); }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var areaProto = H.seriesTypes.area.prototype, defaultPlotOptions = H.defaultPlotOptions, @@ -20263,11 +20498,11 @@ }); }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var animObject = H.animObject, color = H.color, @@ -20288,10 +20523,11 @@ * @augments Series */ seriesType('column', 'line', { borderRadius: 0, //colorByPoint: undefined, + crisp: true, groupPadding: 0.2, //grouping: true, marker: null, // point options are specified in the base options pointPadding: 0.1, //pointWidth: null, @@ -20446,13 +20682,15 @@ yCrisp += 1; } // Horizontal. We need to first compute the exact right edge, then round it // and compute the width from there. - right = Math.round(x + w) + xCrisp; - x = Math.round(x) + xCrisp; - w = right - x; + if (this.options.crisp) { + right = Math.round(x + w) + xCrisp; + x = Math.round(x) + xCrisp; + w = right - x; + } // Vertical bottom = Math.round(y + h) + yCrisp; fromTop = Math.abs(y) <= 0.5 && bottom > 0.5; // #4504, #4656 y = Math.round(y) + yCrisp; @@ -20729,11 +20967,11 @@ }); }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var seriesType = H.seriesType; @@ -20746,21 +20984,22 @@ }); }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var Series = H.Series, seriesType = H.seriesType; /** * The scatter series type */ seriesType('scatter', 'line', { lineWidth: 0, + findNearestPointBy: 'xy', marker: { enabled: true // Overrides auto-enabling in line series (#3647) }, tooltip: { @@ -20775,22 +21014,21 @@ sorted: false, requireSorting: false, noSharedTooltip: true, trackerGroups: ['group', 'markerGroup', 'dataLabelsGroup'], takeOrdinalPosition: false, // #2342 - kdDimensions: 2, drawGraph: function() { if (this.options.lineWidth) { Series.prototype.drawGraph.call(this); } } }); }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var pick = H.pick, relativeLength = H.relativeLength; @@ -20835,11 +21073,11 @@ }; }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var addEvent = H.addEvent, CenteredSeriesMixin = H.CenteredSeriesMixin, @@ -21336,11 +21574,11 @@ }); }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var addEvent = H.addEvent, arrayMax = H.arrayMax, @@ -21535,13 +21773,13 @@ // Determine the color style.color = pick(options.color, style.color, series.color, '#000000'); // Get automated contrast color if (style.color === 'contrast') { + point.contrastColor = renderer.getContrast(point.color || series.color); style.color = options.inside || options.distance < 0 || !!seriesOptions.stacking ? - renderer.getContrast(point.color || series.color) : - '#000000'; + point.contrastColor : '#000000'; } if (seriesOptions.cursor) { style.cursor = seriesOptions.cursor; } @@ -21703,11 +21941,18 @@ alignAttr = dataLabel.alignAttr; } // Handle justify or crop if (justify) { - this.justifyDataLabel(dataLabel, options, alignAttr, bBox, alignTo, isNew); + point.isLabelJustified = this.justifyDataLabel( + dataLabel, + options, + alignAttr, + bBox, + alignTo, + isNew + ); // Now check that the data label is within the plot area } else if (pick(options.crop, true)) { visible = chart.isInsidePlot(alignAttr.x, alignAttr.y) && chart.isInsidePlot(alignAttr.x + bBox.width, alignAttr.y + bBox.height); } @@ -21789,10 +22034,12 @@ if (justified) { dataLabel.placed = !isNew; dataLabel.align(options, null, alignTo); } + + return justified; }; /** * Override the base drawDataLabels method by pie specific functionality */ @@ -22209,42 +22456,60 @@ inverted || inside ? 'middle' : below ? 'top' : 'bottom' ); // Call the parent method Series.prototype.alignDataLabel.call(this, point, dataLabel, options, alignTo, isNew); + + // If label was justified and we have contrast, set it: + if (point.isLabelJustified && point.contrastColor) { + point.dataLabel.css({ + color: point.contrastColor + }); + } }; } }(Highcharts)); (function(H) { /** - * (c) 2009-2016 Torstein Honsi + * (c) 2009-2017 Torstein Honsi * * License: www.highcharts.com/license */ /** - * Highcharts module to hide overlapping data labels. This module is included in Highcharts. + * Highcharts module to hide overlapping data labels. This module is included in + * Highcharts. */ var Chart = H.Chart, each = H.each, pick = H.pick, addEvent = H.addEvent; - // Collect potensial overlapping data labels. Stack labels probably don't need to be - // considered because they are usually accompanied by data labels that lie inside the columns. + // Collect potensial overlapping data labels. Stack labels probably don't need + // to be 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) { var dlOptions = series.options.dataLabels, - collections = series.dataLabelCollections || ['dataLabel']; // Range series have two collections - if ((dlOptions.enabled || series._hasPointLabels) && !dlOptions.allowOverlap && series.visible) { // #3866 + // Range series have two collections + collections = series.dataLabelCollections || ['dataLabel']; + + if ( + (dlOptions.enabled || series._hasPointLabels) && + !dlOptions.allowOverlap && + series.visible + ) { // #3866 each(collections, function(coll) { each(series.points, function(point) { if (point[coll]) { - point[coll].labelrank = pick(point.labelrank, point.shapeArgs && point.shapeArgs.height); // #4118 + point[coll].labelrank = pick( + point.labelrank, + point.shapeArgs && point.shapeArgs.height + ); // #4118 labels.push(point[coll]); } }); }); } @@ -22259,12 +22524,12 @@ addEvent(chart, 'redraw', collectAndHide); }); /** - * Hide overlapping labels. Labels are moved and faded in and out on zoom to provide a smooth - * visual imression. + * Hide overlapping labels. Labels are moved and faded in and out on zoom to + * provide a smooth visual imression. */ Chart.prototype.hideOverlappingLabels = function(labels) { var len = labels.length, label, @@ -22294,29 +22559,35 @@ label.oldOpacity = label.opacity; label.newOpacity = 1; } } - // Prevent a situation in a gradually rising slope, that each label - // will hide the previous one because the previous one always has - // lower rank. + // Prevent a situation in a gradually rising slope, that each label will + // hide the previous one because the previous one always has lower rank. labels.sort(function(a, b) { return (b.labelrank || 0) - (a.labelrank || 0); }); // Detect overlapping labels for (i = 0; i < len; i++) { label1 = labels[i]; for (j = i + 1; j < len; ++j) { label2 = labels[j]; - if (label1 && label2 && label1.placed && label2.placed && label1.newOpacity !== 0 && label2.newOpacity !== 0) { + if ( + label1 && label2 && + label1 !== label2 && // #6465, polar chart with connectEnds + label1.placed && label2.placed && + label1.newOpacity !== 0 && label2.newOpacity !== 0 + ) { pos1 = label1.alignAttr; pos2 = label2.alignAttr; - parent1 = label1.parentGroup; // Different panes have different positions + // Different panes have different positions + parent1 = label1.parentGroup; parent2 = label2.parentGroup; - padding = 2 * (label1.box ? 0 : label1.padding); // Substract the padding if no background or border (#4333) + // Substract the padding if no background or border (#4333) + padding = 2 * (label1.box ? 0 : label1.padding); isIntersecting = intersectRect( pos1.x + parent1.translateX, pos1.y + parent1.translateY, label1.width - padding, label1.height - padding, @@ -22325,11 +22596,12 @@ label2.width - padding, label2.height - padding ); if (isIntersecting) { - (label1.labelrank < label2.labelrank ? label1 : label2).newOpacity = 0; + (label1.labelrank < label2.labelrank ? label1 : label2) + .newOpacity = 0; } } } } @@ -22341,33 +22613,38 @@ if (label) { newOpacity = label.newOpacity; if (label.oldOpacity !== newOpacity && label.placed) { - // Make sure the label is completely hidden to avoid catching clicks (#4362) + // Make sure the label is completely hidden to avoid catching + // clicks (#4362) if (newOpacity) { label.show(true); } else { complete = function() { label.hide(); }; } // Animate or set the opacity label.alignAttr.opacity = newOpacity; - label[label.isOld ? 'animate' : 'attr'](label.alignAttr, null, complete); + label[label.isOld ? 'animate' : 'attr']( + label.alignAttr, + null, + complete + ); } label.isOld = true; } }); }; }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var addEvent = H.addEvent, Chart = H.Chart, @@ -23143,11 +23420,11 @@ fireEvent(series, 'mouseOut'); } // hide the tooltip - if (tooltip && !options.stickyTracking && (!tooltip.shared || series.noSharedTooltip)) { + if (tooltip && !series.stickyTracking && (!tooltip.shared || series.noSharedTooltip)) { tooltip.hide(); } // set normal state series.setState(); @@ -23322,10 +23599,10 @@ }); }(Highcharts)); (function(H) { /** - * (c) 2010-2016 Torstein Honsi + * (c) 2010-2017 Torstein Honsi * * License: www.highcharts.com/license */ var Chart = H.Chart, each = H.each,