app/assets/javascripts/highcharts.js in highcharts-rails-3.0.1.5 vs app/assets/javascripts/highcharts.js in highcharts-rails-3.0.2

- old
+ new

@@ -1,18 +1,18 @@ // ==ClosureCompiler== // @compilation_level SIMPLE_OPTIMIZATIONS /** - * @license Highcharts JS v3.0.1 (2013-04-09) + * @license Highcharts JS v3.0.2 (2013-06-05) * * (c) 2009-2013 Torstein Hønsi * * License: www.highcharts.com/license */ // JSLint options: -/*global Highcharts, document, window, navigator, setInterval, clearInterval, clearTimeout, setTimeout, location, jQuery, $, console */ +/*global Highcharts, document, window, navigator, setInterval, clearInterval, clearTimeout, setTimeout, location, jQuery, $, console, each, grep */ (function () { // encapsulated variables var UNDEFINED, doc = document, @@ -53,11 +53,11 @@ pathAnim, timeUnits, noop = function () {}, charts = [], PRODUCT = 'Highcharts', - VERSION = '3.0.1', + VERSION = '3.0.2', // some constants for frequently used strings DIV = 'div', ABSOLUTE = 'absolute', RELATIVE = 'relative', @@ -780,27 +780,29 @@ time = makeTime(minYear, minMonth, minDateDate + i * count * (interval === timeUnits[DAY] ? 1 : 7)); // else, the interval is fixed and we use simple addition } else { - - // mark new days if the time is dividable by day - if (interval <= timeUnits[HOUR] && time % timeUnits[DAY] === timezoneOffset) { - higherRanks[time] = DAY; - } - time += interval * count; - } i++; } // push the last time tickPositions.push(time); + + + // mark new days if the time is dividible by day (#1649, #1760) + each(grep(tickPositions, function (time) { + return interval <= timeUnits[HOUR] && time % timeUnits[DAY] === timezoneOffset; + }), function (time) { + higherRanks[time] = DAY; + }); } + // record information on the chosen unit - for dynamic label formatter tickPositions.info = extend(normalizedInterval, { higherRanks: higherRanks, totalRange: interval * count }); @@ -1092,21 +1094,21 @@ // extend the animate function to allow SVG animations var Fx = $.fx, Step = Fx.step, dSetter, Tween = $.Tween, - propHooks = Tween && Tween.propHooks; + propHooks = Tween && Tween.propHooks, + opacityHook = $.cssHooks.opacity; /*jslint unparam: true*//* allow unused param x in this function */ $.extend($.easing, { easeOutQuad: function (x, t, b, c, d) { return -c * (t /= d) * (t - 2) + b; } }); /*jslint unparam: false*/ - // extend some methods to check for elem.attr, which means it is a Highcharts SVG object $.each(['cur', '_default', 'width', 'height', 'opacity'], function (i, fn) { var obj = Step, base, elem; @@ -1139,10 +1141,15 @@ elem.attr(fx.prop, fn === 'cur' ? UNDEFINED : fx.now) : // apply the SVG wrapper's method base.apply(this, arguments); // use jQuery's built-in method }; } }); + + // Extend the opacity getter, needed for fading opacity with IE9 and jQuery 1.10+ + wrap(opacityHook, 'get', function (proceed, elem, computed) { + return elem.attr ? (elem.opacity || 0) : proceed.call(this, elem, computed); + }); // Define the setter function for d (path definitions) dSetter = function (fx) { var elem = fx.elem, @@ -1394,19 +1401,19 @@ * @param {Object} params * @param {Object} options jQuery-like animation options: duration, easing, callback */ animate: function (el, params, options) { var $el = $(el); + if (!el.style) { + el.style = {}; // #1881 + } if (params.d) { el.toD = params.d; // keep the array form for paths, used in $.fx.step.d params.d = 1; // because in jQuery, animating to an array has a different meaning } $el.stop(); - if (params.opacity !== UNDEFINED && el.attr) { - params.opacity += 'px'; // force jQuery to use same logic as width and height - } $el.animate(params, options); }, /** * Stop running animation @@ -1485,12 +1492,12 @@ resetZoomTitle: 'Reset zoom level 1:1', thousandsSep: ',' }, global: { useUTC: true, - canvasToolsURL: 'http://code.highcharts.com/3.0.1/modules/canvas-tools.js', - VMLRadialGradientURL: 'http://code.highcharts.com/3.0.1/gfx/vml-radial-gradient.png' + canvasToolsURL: 'http://code.highcharts.com/3.0.2/modules/canvas-tools.js', + VMLRadialGradientURL: 'http://code.highcharts.com/3.0.2/gfx/vml-radial-gradient.png' }, chart: { //animation: true, //alignTicks: false, //reflow: true, @@ -1601,11 +1608,11 @@ events: {} }, dataLabels: merge(defaultLabelOptions, { enabled: false, formatter: function () { - return this.y; + return numberFormat(this.y, -1); }, verticalAlign: 'bottom', // above singular point y: 0 // backgroundColor: undefined, // borderColor: undefined, @@ -2289,11 +2296,17 @@ each(['x', 'y', 'r', 'start', 'end', 'width', 'height', 'innerR', 'anchorX', 'anchorY'], function (key) { wrapper[key] = pick(hash[key], wrapper[key]); }); wrapper.attr({ - d: wrapper.renderer.symbols[wrapper.symbolName](wrapper.x, wrapper.y, wrapper.width, wrapper.height, wrapper) + d: wrapper.renderer.symbols[wrapper.symbolName]( + wrapper.x, + wrapper.y, + wrapper.width, + wrapper.height, + wrapper + ) }); }, /** * Apply a clipping path to this object @@ -2381,13 +2394,11 @@ css(elemWrapper.element, styles); } else { for (n in styles) { serializedCss += n.replace(/([A-Z])/g, hyphenate) + ':' + styles[n] + ';'; } - elemWrapper.attr({ - style: serializedCss - }); + attr(elem, 'style', serializedCss); // #1881 } // re-build text if (textWidth && elemWrapper.added) { @@ -2523,23 +2534,21 @@ alignCorrection = { left: 0, center: 0.5, right: 1 }[align], nonLeft = align && align !== 'left', shadows = wrapper.shadows; // apply translate - if (translateX || translateY) { - css(elem, { - marginLeft: translateX, - marginTop: translateY - }); - if (shadows) { // used in labels/tooltip - each(shadows, function (shadow) { - css(shadow, { - marginLeft: translateX + 1, - marginTop: translateY + 1 - }); + css(elem, { + marginLeft: translateX, + marginTop: translateY + }); + if (shadows) { // used in labels/tooltip + each(shadows, function (shadow) { + css(shadow, { + marginLeft: translateX + 1, + marginTop: translateY + 1 }); - } + }); } // apply inversion if (wrapper.inverted) { // wrapper is a group each(elem.childNodes, function (child) { @@ -2653,22 +2662,21 @@ translateY = wrapper.translateY || 0, scaleX = wrapper.scaleX, scaleY = wrapper.scaleY, inverted = wrapper.inverted, rotation = wrapper.rotation, - transform = []; + transform; // flipping affects translate as adjustment for flipping around the group's axis if (inverted) { translateX += wrapper.attr('width'); translateY += wrapper.attr('height'); } - // apply translate - if (translateX || translateY) { - transform.push('translate(' + translateX + ',' + translateY + ')'); - } + // Apply translate. Nearly all transformed elements have translation, so instead + // of checking for translate = 0, do it always (#1767, #1846). + transform = ['translate(' + translateX + ',' + translateY + ')']; // apply rotation if (inverted) { transform.push('rotate(90) scale(-1,1)'); } else if (rotation) { // text rotation @@ -4372,11 +4380,11 @@ */ css: function (styles) { if (styles) { var textStyles = {}; styles = merge(styles); // create a copy to avoid altering the original object (#537) - each(['fontSize', 'fontWeight', 'fontFamily', 'color', 'lineHeight', 'width'], function (prop) { + each(['fontSize', 'fontWeight', 'fontFamily', 'color', 'lineHeight', 'width', 'textDecoration'], function (prop) { if (styles[prop] !== UNDEFINED) { textStyles[prop] = styles[prop]; delete styles[prop]; } }); @@ -5301,16 +5309,18 @@ * @param {Number} x * @param {Number} y * @param {Number} r */ circle: function (x, y, r) { + var circle = this.symbol('circle'); if (isObject(x)) { r = x.r; y = x.y; x = x.x; } - return this.symbol('circle').attr({ x: x - r, y: y - r, width: 2 * r, height: 2 * r }); + circle.isCircle = true; // Causes x and y to mean center (#1682) + return circle.attr({ x: x, y: y, width: 2 * r, height: 2 * r }); }, /** * Create a group using an outer div and an inner v:group to allow rotating * and flipping. A simple v:group would have problems with positioning @@ -5448,12 +5458,18 @@ ret.isArc = true; return ret; }, // Add circle symbol path. This performs significantly faster than v:oval. - circle: function (x, y, w, h) { + circle: function (x, y, w, h, wrapper) { + // Center correction, #1682 + if (wrapper && wrapper.isCircle) { + x -= w / 2; + y -= h / 2; + } + // Return the path return [ 'wa', // clockwisearcto x, // left y, // top x + w, // right @@ -6242,11 +6258,14 @@ /** * Renders the stack total label and adds it to the stack label group. */ render: function (group) { var options = this.options, - str = options.formatter.call(this); // format the text in the label + formatOption = options.format, // docs: added stackLabel.format option + str = formatOption ? + format(formatOption, this) : + options.formatter.call(this); // format the text in the label // Change the text to reflect the new total and set visibility to hidden in case the serie is hidden if (this.label) { this.label.attr({text: str, visibility: HIDDEN}); // Create new label @@ -6422,11 +6441,11 @@ //x: dynamic, //verticalAlign: dynamic, //textAlign: dynamic, //rotation: 0, formatter: function () { - return this.total; + return numberFormat(this.total, -1); }, style: defaultLabelOptions.style } }, @@ -6697,10 +6716,13 @@ // Remove the axis erase(chart.axes, this); erase(chart[key], this); chart.options[key].splice(this.options.index, 1); + each(chart[key], function (axis, i) { // Re-index, #1706 + axis.options.index = i; + }); this.destroy(); chart.isDirtyBox = true; if (pick(redraw, true)) { chart.redraw(); @@ -7397,11 +7419,11 @@ } }); } // Record minPointOffset and pointRangePadding - ordinalCorrection = axis.ordinalSlope ? axis.ordinalSlope / closestPointRange : 1; // #988 + ordinalCorrection = axis.ordinalSlope && closestPointRange ? axis.ordinalSlope / closestPointRange : 1; // #988, #1853 axis.minPointOffset = minPointOffset = minPointOffset * ordinalCorrection; axis.pointRangePadding = pointRangePadding = pointRangePadding * ordinalCorrection; // pointRange means the width reserved for each point, like in a column chart axis.pointRange = mathMin(pointRange, range); @@ -8460,10 +8482,12 @@ // Destroy and clear local variables if (this.label) { this.label = this.label.destroy(); } + clearTimeout(this.hideTimer); + clearTimeout(this.tooltipTimeout); }, /** * Provide a soft movement for the tooltip * @@ -8509,11 +8533,12 @@ * Hide the tooltip */ hide: function () { var tooltip = this, hoverPoints; - + + clearTimeout(this.hideTimer); // disallow duplicate timers (#1728, #1766) if (!this.isHidden) { hoverPoints = this.chart.hoverPoints; this.hideTimer = setTimeout(function () { tooltip.label.fadeOut(); @@ -9074,11 +9099,11 @@ var chart = this.chart; // Scale each series each(chart.series, function (series) { - if (series.xAxis.zoomEnabled) { + if (series.xAxis && series.xAxis.zoomEnabled) { series.group.attr(attribs); if (series.markerGroup) { series.markerGroup.attr(attribs); series.markerGroup.clip(clip ? chart.clipRect : null); } @@ -9177,11 +9202,11 @@ pinch: function (e) { var self = this, chart = self.chart, pinchDown = self.pinchDown, - followTouchMove = chart.tooltip.options.followTouchMove, + followTouchMove = chart.tooltip && chart.tooltip.options.followTouchMove, touches = e.touches, touchesLength = touches.length, lastValidTouch = self.lastValidTouch, zoomHor = self.zoomHor || self.pinchHor, zoomVert = self.zoomVert || self.pinchVert, @@ -9189,16 +9214,12 @@ selectionMarker = self.selectionMarker, transform = {}, clip = {}; // On touch devices, only proceed to trigger click if a handler is defined - if (e.type === 'touchstart' && followTouchMove) { - if (self.inClass(e.target, PREFIX + 'tracker')) { - if (!chart.runTrackerClick || touchesLength > 1) { - e.preventDefault(); - } - } else if (!chart.runChartClick || touchesLength > 1) { + if (e.type === 'touchstart') { + if (followTouchMove || hasZoom) { e.preventDefault(); } } // Normalize each touch @@ -9381,13 +9402,12 @@ // record each axis' min and max each(chart.axes, function (axis) { if (axis.zoomEnabled) { var horiz = axis.horiz, - minPixelPadding = axis.minPixelPadding, - selectionMin = axis.toValue((horiz ? selectionLeft : selectionTop) + minPixelPadding), - selectionMax = axis.toValue((horiz ? selectionLeft + selectionBox.width : selectionTop + selectionBox.height) - minPixelPadding); + selectionMin = axis.toValue((horiz ? selectionLeft : selectionTop)), + selectionMax = axis.toValue((horiz ? selectionLeft + selectionBox.width : selectionTop + selectionBox.height)); if (!isNaN(selectionMin) && !isNaN(selectionMax)) { // #859 selectionData[axis.xOrY + 'Axis'].push({ axis: axis, min: mathMin(selectionMin, selectionMax), // for reversed axes, @@ -9418,11 +9438,11 @@ } // Reset all if (chart) { // it may be destroyed on mouse up - #877 css(chart.container, { cursor: chart._cursor }); - chart.cancelClick = this.hasDragged; // #370 + chart.cancelClick = this.hasDragged > 10; // #370 chart.mouseIsDown = this.hasDragged = this.hasPinched = false; this.pinchDown = []; } }, @@ -9486,12 +9506,12 @@ if (chart.mouseIsDown === 'mousedown') { this.drag(e); } - // Show the tooltip and run mouse over events (#977) - if (chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop)) { + // Show the tooltip and run mouse over events (#977) + if (chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop) && !chart.openMenu) { this.runPointActions(e); } }, /** @@ -9554,11 +9574,13 @@ fireEvent(hoverPoint.series, 'click', extend(e, { point: hoverPoint })); // the point click event - hoverPoint.firePointEvent('click', e); + if (chart.hoverPoint) { // it may be destroyed (#1844) + hoverPoint.firePointEvent('click', e); + } // When clicking outside a tracker, fire a chart event } else { extend(e, this.getCoordinates(e)); @@ -10063,12 +10085,10 @@ legend.contentGroup = renderer.g() .attr({ zIndex: 1 }) // above background .add(legendGroup); legend.scrollGroup = renderer.g() .add(legend.contentGroup); - legend.clipRect = renderer.clipRect(0, 0, 9999, chart.chartHeight); - legend.contentGroup.clip(legend.clipRect); } legend.renderTitle(); // add each series or point @@ -10208,16 +10228,21 @@ spaceHeight = mathMin(spaceHeight, maxHeight); } // Reset the legend height and adjust the clipping rectangle if (legendHeight > spaceHeight && !options.useHTML) { - + this.clipHeight = clipHeight = spaceHeight - 20 - this.titleHeight; this.pageCount = pageCount = mathCeil(legendHeight / clipHeight); this.currentPage = pick(this.currentPage, 1); this.fullHeight = legendHeight; + // Only apply clipping if needed. Clipping causes blurred legend in PDF export (#1787) + if (!clipRect) { + clipRect = legend.clipRect = renderer.clipRect(0, 0, 9999, 0); + legend.contentGroup.clip(clipRect); + } clipRect.attr({ height: clipHeight }); // Add navigation elements @@ -11110,11 +11135,12 @@ // content overflow in IE width: chartWidth + PX, height: chartHeight + PX, textAlign: 'left', lineHeight: 'normal', // #427 - zIndex: 0 // #1072 + zIndex: 0, // #1072 + '-webkit-tap-highlight-color': 'rgba(0,0,0,0)' }, optionsChart.style), chart.renderToClone || renderTo ); // cache the cursor (#1650) @@ -12115,11 +12141,11 @@ tooltipFormatter: function (pointFormat) { // Insert options for valueDecimals, valuePrefix, and valueSuffix var series = this.series, seriesTooltipOptions = series.tooltipOptions, - valueDecimals = pick(seriesTooltipOptions.valueDecimals, ''), + valueDecimals = pick(seriesTooltipOptions.valueDecimals, ''), valuePrefix = seriesTooltipOptions.valuePrefix || '', valueSuffix = seriesTooltipOptions.valueSuffix || ''; // Loop over the point array map and replace unformatted values with sprintf formatting markup each(series.pointArrayMap || ['y'], function (key) { @@ -12516,11 +12542,11 @@ } }); // The series needs an X and an Y axis if (!series[AXIS]) { - error(17, true); + error(18, true); } }); } }, @@ -13127,17 +13153,22 @@ yAxis = series.yAxis, points = series.points, dataLength = points.length, hasModifyValue = !!series.modifyValue, isBottomSeries, - allStackSeries = yAxis.series, - i = allStackSeries.length, + allStackSeries, + i, placeBetween = options.pointPlacement === 'between', threshold = options.threshold; //nextSeriesDown; - // Is it the last visible series? + // Is it the last visible series? (#809, #1722). + // TODO: After merging in the 'stacking' branch, this logic should probably be moved to Chart.getStacks + allStackSeries = yAxis.series.sort(function (a, b) { + return a.index - b.index; + }); + i = allStackSeries.length; while (i--) { if (allStackSeries[i].visible) { if (allStackSeries[i] === series) { // #809 isBottomSeries = true; } @@ -13276,23 +13307,29 @@ */ tooltipHeaderFormatter: function (point) { var series = this, tooltipOptions = series.tooltipOptions, xDateFormat = tooltipOptions.xDateFormat, + dateTimeLabelFormats = tooltipOptions.dateTimeLabelFormats, xAxis = series.xAxis, isDateTime = xAxis && xAxis.options.type === 'datetime', headerFormat = tooltipOptions.headerFormat, + closestPointRange = xAxis && xAxis.closestPointRange, n; // Guess the best date format based on the closest point distance (#568) if (isDateTime && !xDateFormat) { - for (n in timeUnits) { - if (timeUnits[n] >= xAxis.closestPointRange) { - xDateFormat = tooltipOptions.dateTimeLabelFormats[n]; - break; - } - } + if (closestPointRange) { + for (n in timeUnits) { + if (timeUnits[n] >= closestPointRange) { + xDateFormat = dateTimeLabelFormats[n]; + break; + } + } + } else { + xDateFormat = dateTimeLabelFormats.day; + } } // Insert the header date format if any if (isDateTime && xDateFormat && isNumber(point.key)) { headerFormat = headerFormat.replace('{point.key}', '{point.key:' + xDateFormat + '}'); @@ -13478,11 +13515,11 @@ plotX = point.plotX; plotY = point.plotY; graphic = point.graphic; pointMarkerOptions = point.marker || {}; enabled = (seriesMarkerOptions.enabled && pointMarkerOptions.enabled === UNDEFINED) || pointMarkerOptions.enabled; - isInside = chart.isInsidePlot(plotX, plotY, chart.inverted); + isInside = chart.isInsidePlot(mathRound(plotX), plotY, chart.inverted); // #1858 // only draw the point if y is defined if (enabled && plotY !== UNDEFINED && !isNaN(plotY) && point.y !== null) { // shortcuts @@ -13667,11 +13704,11 @@ seriesPointAttr[SELECT_STATE], pointAttr[NORMAL_STATE] ); // Force the fill to negativeColor on markers - if (point.negative && seriesOptions.marker) { + if (point.negative && seriesOptions.marker && negativeColor) { pointAttr[NORMAL_STATE].fill = pointAttr[HOVER_STATE].fill = pointAttr[SELECT_STATE].fill = series.convertAttribs({ fillColor: negativeColor }).fill; } @@ -14108,19 +14145,21 @@ renderer = chart.renderer, negativeColor = options.negativeColor, translatedThreshold, posAttr, negAttr, + graph = this.graph, + area = this.area, posClip = this.posClip, negClip = this.negClip, chartWidth = chart.chartWidth, chartHeight = chart.chartHeight, chartSizeMax = mathMax(chartWidth, chartHeight), above, below; - if (negativeColor && this.graph) { + if (negativeColor && (graph || area)) { translatedThreshold = mathCeil(this.yAxis.len - this.yAxis.translate(options.threshold || 0)); above = { x: 0, y: 0, width: chartSizeMax, @@ -14158,18 +14197,21 @@ if (posClip) { // update posClip.animate(posAttr); negClip.animate(negAttr); } else { - this.posClip = posClip = renderer.clipRect(posAttr); - this.graph.clip(posClip); + this.posClip = posClip = renderer.clipRect(posAttr); this.negClip = negClip = renderer.clipRect(negAttr); - this.graphNeg.clip(negClip); - if (this.area) { - this.area.clip(posClip); + if (graph) { + graph.clip(posClip); + this.graphNeg.clip(negClip); + } + + if (area) { + area.clip(posClip); this.areaNeg.clip(negClip); } } } }, @@ -14178,10 +14220,15 @@ * Initialize and perform group inversion on series.group and series.markerGroup */ invertGroups: function () { var series = this, chart = series.chart; + + // Pie, go away (#1736) + if (!series.xAxis) { + return; + } // A fixed size is needed for inversion to work function setInvert() { var size = { width: series.yAxis.len, @@ -14232,11 +14279,10 @@ translateX: xAxis ? xAxis.left : chart.plotLeft, translateY: yAxis ? yAxis.top : chart.plotTop, scaleX: 1, // #1623 scaleY: 1 }); - return group; }, /** @@ -14279,12 +14325,12 @@ } // cache attributes for shapes series.getAttribs(); - // SVGRenderer needs to know this before drawing elements (#1089) - group.inverted = chart.inverted; + // SVGRenderer needs to know this before drawing elements (#1089, #1795) + group.inverted = series.isCartesian ? chart.inverted : false; // draw the graph if any if (series.drawGraph) { series.drawGraph(); series.clipNeg(); @@ -16075,9 +16121,10 @@ connector = point.connector; labelPos = point.labelPos; dataLabel = point.dataLabel; if (dataLabel && dataLabel._pos) { + visibility = dataLabel._attr.visibility; x = dataLabel.connX; y = dataLabel.connY; connectorPath = softConnector ? [ M, x + (labelPos[6] === 'left' ? 5 : -5), y, // end of the string at the label