app/assets/javascripts/highcharts.js in highcharts-rails-4.1.10 vs app/assets/javascripts/highcharts.js in highcharts-rails-4.2.0

- old
+ new

@@ -1,30 +1,27 @@ // ==ClosureCompiler== // @compilation_level SIMPLE_OPTIMIZATIONS /** - * @license Highcharts JS v4.1.10 (2015-12-07) + * @license Highcharts JS v4.2.0 (2105-12-15) * * (c) 2009-2014 Torstein Honsi * * License: www.highcharts.com/license */ (function (root, factory) { if (typeof module === 'object' && module.exports) { - module.exports = root.document ? - factory(root) : - function (w) { - return factory(w); - }; + module.exports = root.document ? + factory(root) : + factory; } else { - root.Highcharts = factory(); + root.Highcharts = factory(root); } -}(typeof window !== 'undefined' ? window : this, function (w) { +}(typeof window !== 'undefined' ? window : this, function (win) { // eslint-disable-line no-undef // encapsulated variables var UNDEFINED, - win = w || window, doc = win.document, math = Math, mathRound = math.round, mathFloor = math.floor, mathCeil = math.ceil, @@ -36,21 +33,21 @@ mathPI = math.PI, deg2rad = mathPI * 2 / 360, // some variables - userAgent = navigator.userAgent, + userAgent = (win.navigator && win.navigator.userAgent) || '', isOpera = win.opera, isMS = /(msie|trident|edge)/i.test(userAgent) && !isOpera, - docMode8 = doc.documentMode === 8, + docMode8 = doc && doc.documentMode === 8, isWebKit = !isMS && /AppleWebKit/.test(userAgent), isFirefox = /Firefox/.test(userAgent), isTouchDevice = /(Mobile|Android|Windows Phone)/.test(userAgent), SVG_NS = 'http://www.w3.org/2000/svg', - hasSVG = !!doc.createElementNS && !!doc.createElementNS(SVG_NS, 'svg').createSVGRect, + hasSVG = doc && doc.createElementNS && !!doc.createElementNS(SVG_NS, 'svg').createSVGRect, hasBidiBug = isFirefox && parseInt(userAgent.split('Firefox/')[1], 10) < 4, // issue #38 - useCanVG = !hasSVG && !isMS && !!doc.createElement('canvas').getContext, + useCanVG = doc && !hasSVG && !isMS && !!doc.createElement('canvas').getContext, Renderer, hasTouch, symbolSizes = {}, idCounter = 0, garbageBin, @@ -60,11 +57,11 @@ timeUnits, noop = function () {}, charts = [], chartCount = 0, PRODUCT = 'Highcharts', - VERSION = '4.1.10', + VERSION = '4.2.0', // some constants for frequently used strings DIV = 'div', ABSOLUTE = 'absolute', RELATIVE = 'relative', @@ -124,18 +121,254 @@ console.log(msg); // eslint-disable-line no-console } } // The Highcharts namespace - Highcharts = win.Highcharts ? error(16, true) : function (adapter) { - Highcharts.loadAdapter(adapter); - return Highcharts; - }; + Highcharts = win.Highcharts ? error(16, true) : { win: win }; Highcharts.seriesTypes = seriesTypes; + var timers = [], + getStyle, + // Previous adapter functions + inArray, + each, + grep, + offset, + map, + addEvent, + removeEvent, + fireEvent, + animate, + stop; + /** + * An animator object. One instance applies to one property (attribute or style prop) + * on one element. + * + * @param {object} elem The element to animate. May be a DOM element or a Highcharts SVGElement wrapper. + * @param {object} options Animation options, including duration, easing, step and complete. + * @param {object} prop The property to animate. + */ + function Fx(elem, options, prop) { + this.options = options; + this.elem = elem; + this.prop = prop; + } + Fx.prototype = { + + /** + * Animating a path definition on SVGElement + * @returns {undefined} + */ + dSetter: function () { + var start = this.paths[0], + end = this.paths[1], + ret = [], + now = this.now, + i = start.length, + startVal; + + if (now === 1) { // land on the final path without adjustment points appended in the ends + ret = this.toD; + + } else if (i === end.length && now < 1) { + while (i--) { + startVal = parseFloat(start[i]); + ret[i] = + isNaN(startVal) ? // a letter instruction like M or L + start[i] : + now * (parseFloat(end[i] - startVal)) + startVal; + + } + } else { // if animation is finished or length not matching, land on right value + ret = end; + } + this.elem.attr('d', ret); + }, + + /** + * Update the element with the current animation step + * @returns {undefined} + */ + update: function () { + var elem = this.elem, + prop = this.prop, // if destroyed, it is null + now = this.now, + step = this.options.step; + + // Animation setter defined from outside + if (this[prop + 'Setter']) { + this[prop + 'Setter'](); + + // Other animations on SVGElement + } else if (elem.attr) { + if (elem.element) { + elem.attr(prop, now); + } + + // HTML styles, raw HTML content like container size + } else { + elem.style[prop] = now + this.unit; + } + + if (step) { + step.call(elem, now, this); + } + + }, + + /** + * Run an animation + */ + run: function (from, to, unit) { + var self = this, + timer = function (gotoEnd) { + return timer.stopped ? false : self.step(gotoEnd); + }, + i; + + this.startTime = +new Date(); + this.start = from; + this.end = to; + this.unit = unit; + this.now = this.start; + this.pos = 0; + + timer.elem = this.elem; + + if (timer() && timers.push(timer) === 1) { + timer.timerId = setInterval(function () { + + for (i = 0; i < timers.length; i++) { + if (!timers[i]()) { + timers.splice(i--, 1); + } + } + + if (!timers.length) { + clearInterval(timer.timerId); + } + }, 13); + } + }, + + /** + * Run a single step in the animation + * @param {Boolean} gotoEnd Whether to go to then endpoint of the animation after abort + * @returns {Boolean} True if animation continues + */ + step: function (gotoEnd) { + var t = +new Date(), + ret, + done, + options = this.options, + elem = this.elem, + complete = options.complete, + duration = options.duration, + curAnim = options.curAnim, + i; + + if (elem.attr && !elem.element) { // #2616, element including flag is destroyed + ret = false; + + } else if (gotoEnd || t >= duration + this.startTime) { + this.now = this.end; + this.pos = 1; + this.update(); + + curAnim[this.prop] = true; + + done = true; + for (i in curAnim) { + if (curAnim[i] !== true) { + done = false; + } + } + + if (done && complete) { + complete.call(elem); + } + ret = false; + + } else { + this.pos = options.easing((t - this.startTime) / duration); + this.now = this.start + ((this.end - this.start) * this.pos); + this.update(); + ret = true; + } + return ret; + }, + + /** + * Prepare start and end values so that the path can be animated one to one + */ + initPath: function (elem, fromD, toD) { + fromD = fromD || ''; + var shift = elem.shift, + bezier = fromD.indexOf('C') > -1, + numParams = bezier ? 7 : 3, + endLength, + slice, + i, + start = fromD.split(' '), + end = [].concat(toD), // copy + startBaseLine, + endBaseLine, + sixify = function (arr) { // in splines make move points have six parameters like bezier curves + i = arr.length; + while (i--) { + if (arr[i] === M) { + arr.splice(i + 1, 0, arr[i + 1], arr[i + 2], arr[i + 1], arr[i + 2]); + } + } + }; + + if (bezier) { + sixify(start); + sixify(end); + } + + // pull out the base lines before padding + if (elem.isArea) { + startBaseLine = start.splice(start.length - 6, 6); + endBaseLine = end.splice(end.length - 6, 6); + } + + // if shifting points, prepend a dummy point to the end path + if (shift <= end.length / numParams && start.length === end.length) { + while (shift--) { + end = [].concat(end).splice(0, numParams).concat(end); + } + } + elem.shift = 0; // reset for following animations + + // copy and append last point until the length matches the end length + if (start.length) { + endLength = end.length; + while (start.length < endLength) { + + //bezier && sixify(start); + slice = [].concat(start).splice(start.length - numParams, numParams); + if (bezier) { // disable first control point + slice[numParams - 6] = slice[numParams - 2]; + slice[numParams - 5] = slice[numParams - 1]; + } + start = start.concat(slice); + } + } + + if (startBaseLine) { // append the base lines for areas + start = start.concat(startBaseLine); + end = end.concat(endBaseLine); + } + return [start, end]; + } + }; // End of Fx prototype + + + /** * Extend an object with the members of another * @param {Object} a The object to be extended * @param {Object} b The object to add to the first one */ var extend = Highcharts.extend = function (a, b) { @@ -265,11 +498,11 @@ } //return arr; } /** - * Returns true if the object is not null or undefined. Like MooTools' $.defined. + * Returns true if the object is not null or undefined. * @param {Object} obj */ function defined(obj) { return obj !== UNDEFINED && obj !== null; } @@ -304,12 +537,11 @@ } } return ret; } /** - * Check if an element is an array, and if not, make it into an array. Like - * MooTools' $.splat. + * Check if an element is an array, and if not, make it into an array. */ function splat(obj) { return isArray(obj) ? obj : [obj]; } @@ -327,11 +559,11 @@ fn.call(0, context); } /** - * Return the first value that is defined. Like MooTools' $.pick. + * Return the first value that is defined. */ var pick = Highcharts.pick = function () { var args = arguments, i, arg, @@ -786,512 +1018,447 @@ j = i.length > 3 ? i.length % 3 : 0; return (s + (j ? i.substr(0, j) + t : '') + i.substr(j).replace(/(\d{3})(?=\d)/g, '$1' + t) + (c ? d + mathAbs(n - i).toFixed(c).slice(2) : '')); }; + /** - * Path interpolation algorithm used across adapters + * Easing definition + * @param {Number} pos Current position, ranging from 0 to 1 */ - pathAnim = { - /** - * Prepare start and end values so that the path can be animated one to one - */ - init: function (elem, fromD, toD) { - fromD = fromD || ''; - var shift = elem.shift, - bezier = fromD.indexOf('C') > -1, - numParams = bezier ? 7 : 3, - endLength, - slice, - i, - start = fromD.split(' '), - end = [].concat(toD), // copy - startBaseLine, - endBaseLine, - sixify = function (arr) { // in splines make move points have six parameters like bezier curves - i = arr.length; - while (i--) { - if (arr[i] === M) { - arr.splice(i + 1, 0, arr[i + 1], arr[i + 2], arr[i + 1], arr[i + 2]); - } - } - }; + Math.easeInOutSine = function (pos) { + return -0.5 * (Math.cos(Math.PI * pos) - 1); + }; - if (bezier) { - sixify(start); - sixify(end); - } + /** + * Internal method to return CSS value for given element and property + */ + getStyle = function (el, prop) { + var style = win.getComputedStyle(el, undefined); + return style && pInt(style.getPropertyValue(prop)); + }; - // pull out the base lines before padding - if (elem.isArea) { - startBaseLine = start.splice(start.length - 6, 6); - endBaseLine = end.splice(end.length - 6, 6); - } + /** + * Return the index of an item in an array, or -1 if not found + */ + inArray = function (item, arr) { + return arr.indexOf ? arr.indexOf(item) : [].indexOf.call(arr, item); + }; - // if shifting points, prepend a dummy point to the end path - if (shift <= end.length / numParams && start.length === end.length) { - while (shift--) { - end = [].concat(end).splice(0, numParams).concat(end); - } - } - elem.shift = 0; // reset for following animations + /** + * Filter an array + */ + grep = function (elements, callback) { + return [].filter.call(elements, callback); + }; - // copy and append last point until the length matches the end length - if (start.length) { - endLength = end.length; - while (start.length < endLength) { + /** + * Map an array + */ + map = function (arr, fn) { + var results = [], i = 0, len = arr.length; - //bezier && sixify(start); - slice = [].concat(start).splice(start.length - numParams, numParams); - if (bezier) { // disable first control point - slice[numParams - 6] = slice[numParams - 2]; - slice[numParams - 5] = slice[numParams - 1]; - } - start = start.concat(slice); - } - } + for (; i < len; i++) { + results[i] = fn.call(arr[i], arr[i], i, arr); + } - if (startBaseLine) { // append the base lines for areas - start = start.concat(startBaseLine); - end = end.concat(endBaseLine); - } - return [start, end]; - }, + return results; + }; - /** - * Interpolate each value of the path and return the array - */ - step: function (start, end, pos, complete) { - var ret = [], - i = start.length, - startVal; + /** + * Get the element's offset position, corrected by overflow:auto. + */ + offset = function (el) { + var docElem = doc.documentElement, + box = el.getBoundingClientRect(); - if (pos === 1) { // land on the final path without adjustment points appended in the ends - ret = complete; + return { + top: box.top + (win.pageYOffset || docElem.scrollTop) - (docElem.clientTop || 0), + left: box.left + (win.pageXOffset || docElem.scrollLeft) - (docElem.clientLeft || 0) + }; + }; - } else if (i === end.length && pos < 1) { - while (i--) { - startVal = parseFloat(start[i]); - ret[i] = - isNaN(startVal) ? // a letter instruction like M or L - start[i] : - pos * (parseFloat(end[i] - startVal)) + startVal; + /** + * Stop running animation. + * A possible extension to this would be to stop a single property, when + * we want to continue animating others. Then assign the prop to the timer + * in the Fx.run method, and check for the prop here. This would be an improvement + * in all cases where we stop the animation from .attr. Instead of stopping + * everything, we can just stop the actual attributes we're setting. + */ + stop = function (el) { - } - } else { // if animation is finished or length not matching, land on right value - ret = end; + var i = timers.length; + + // Remove timers related to this element (#4519) + while (i--) { + if (timers[i].elem === el) { + timers[i].stopped = true; // #4667 } - return ret; } }; - function loadJQueryAdapter($) { - /** - * The default HighchartsAdapter for jQuery - */ - return { + /** + * Utility for iterating over an array. + * @param {Array} arr + * @param {Function} fn + */ + each = function (arr, fn) { // modern browsers + return Array.prototype.forEach.call(arr, fn); + }; - /** - * Initialize the adapter by applying some extensions to jQuery - */ - init: function (pathAnim) { + /** + * Add an event listener + */ + addEvent = function (el, type, fn) { + + var events = el.hcEvents = el.hcEvents || {}; - // extend the animate function to allow SVG animations - var Fx = $.fx; + function wrappedFn(e) { + e.target = e.srcElement || win; // #2820 + fn.call(el, e); + } - $.extend($.easing, { - easeOutQuad: function (x, t, b, c, d) { - return -c * (t /= d) * (t - 2) + b; - } - }); + // Handle DOM events in modern browsers + if (el.addEventListener) { + el.addEventListener(type, fn, 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 = Fx.step, - base; + // Handle old IE implementation + } else if (el.attachEvent) { - // Handle different parent objects - if (fn === 'cur') { - obj = Fx.prototype; // 'cur', the getter, relates to Fx.prototype + if (!el.hcEventsIE) { + el.hcEventsIE = {}; + } - } else if (fn === '_default' && $.Tween) { // jQuery 1.8 model - obj = $.Tween.propHooks[fn]; - fn = 'set'; - } + // Link wrapped fn with original fn, so we can get this in removeEvent + el.hcEventsIE[fn.toString()] = wrappedFn; - // Overwrite the method - base = obj[fn]; - if (base) { // step.width and step.height don't exist in jQuery < 1.7 + el.attachEvent('on' + type, wrappedFn); + } - // create the extended function replacement - obj[fn] = function (effects) { + if (!events[type]) { + events[type] = []; + } - var elem, fx; + events[type].push(fn); + }; - // Fx.prototype.cur does not use fx argument - fx = i ? effects : this; + /** + * Remove event added with addEvent + */ + removeEvent = function (el, type, fn) { + + var events, + hcEvents = el.hcEvents, + index; - // Don't run animations on textual properties like align (#1821) - if (fx.prop === 'align') { - return; - } + function removeOneEvent(type, fn) { + if (el.removeEventListener) { + el.removeEventListener(type, fn, false); + } else if (el.attachEvent) { + fn = el.hcEventsIE[fn.toString()]; + el.detachEvent('on' + type, fn); + } + } - // shortcut - elem = fx.elem; + function removeAllEvents() { + var types, + len, + n; - // Fx.prototype.cur returns the current value. The other ones are setters - // and returning a value has no effect. - if (elem.attr) { // is SVG element wrapper - return elem.attr( - fx.prop.replace('strokeWidth', 'stroke-width'), // #4721 - fn === 'cur' ? undefined : fx.now - ); - } + if (!el.nodeName) { + return; // break on non-DOM events + } - return base.apply(this, arguments); // use jQuery's built-in method - }; + if (type) { + types = {}; + types[type] = true; + } else { + types = hcEvents; + } + + for (n in types) { + if (hcEvents[n]) { + len = hcEvents[n].length; + while (len--) { + removeOneEvent(n, hcEvents[n][len]); } - }); + } + } + } - // Extend the opacity getter, needed for fading opacity with IE9 and jQuery 1.10+ - wrap($.cssHooks.opacity, 'get', function (proceed, elem, computed) { - return elem.attr ? (elem.opacity || 0) : proceed.call(this, elem, computed); - }); - - // Define the setter function for d (path definitions) - this.addAnimSetter('d', function (fx) { - var elem = fx.elem, - ends; - - // Normally start and end should be set in state == 0, but sometimes, - // for reasons unknown, this doesn't happen. Perhaps state == 0 is skipped - // in these cases - if (!fx.started) { - ends = pathAnim.init(elem, elem.d, elem.toD); - fx.start = ends[0]; - fx.end = ends[1]; - fx.started = true; + if (hcEvents) { + if (type) { + events = hcEvents[type] || []; + if (fn) { + index = inArray(fn, events); + if (index > -1) { + events.splice(index, 1); + hcEvents[type] = events; } + removeOneEvent(type, fn); - // Interpolate each value of the path - elem.attr('d', pathAnim.step(fx.start, fx.end, fx.pos, elem.toD)); - }); + } else { + removeAllEvents(); + hcEvents[type] = []; + } + } else { + removeAllEvents(); + el.hcEvents = {}; + } + } + }; - /** - * Utility for iterating over an array. Parameters are reversed compared to jQuery. - * @param {Array} arr - * @param {Function} fn - */ - this.each = Array.prototype.forEach ? - function each(arr, fn) { // modern browsers - return Array.prototype.forEach.call(arr, fn); + /** + * Fire an event on a custom object + */ + fireEvent = function (el, type, eventArguments, defaultFunction) { + var e, + hcEvents = el.hcEvents, + events, + len, + i, + preventDefault, + fn; - } : - function each(arr, fn) { // legacy - var i, - len = arr.length; - for (i = 0; i < len; i++) { - if (fn.call(arr[i], arr[i], i, arr) === false) { - return i; - } - } - }; + eventArguments = eventArguments || {}; - /** - * Register Highcharts as a plugin in the respective framework - */ - $.fn.highcharts = function () { - var constr = 'Chart', // default constructor - args = arguments, - options, - ret; + if (doc.createEvent && (el.dispatchEvent || el.fireEvent)) { + e = doc.createEvent('Events'); + e.initEvent(type, true, true); + e.target = el; - if (this[0]) { + extend(e, eventArguments); - if (isString(args[0])) { - constr = args[0]; - args = Array.prototype.slice.call(args, 1); - } - options = args[0]; + if (el.dispatchEvent) { + el.dispatchEvent(e); + } else { + el.fireEvent(type, e); + } - // Create the chart - if (options !== UNDEFINED) { - options.chart = options.chart || {}; - options.chart.renderTo = this[0]; - ret = new Highcharts[constr](options, args[1]); - ret = this; - } + } else if (hcEvents) { + + events = hcEvents[type] || []; + len = events.length; - // When called without parameters or with the return argument, get a predefined chart - if (options === UNDEFINED) { - ret = charts[attr(this[0], 'data-highcharts-chart')]; - } - } + // Attach a simple preventDefault function to skip default handler if called + preventDefault = function () { + eventArguments.defaultPrevented = true; + }; + + for (i = 0; i < len; i++) { + fn = events[i]; - return ret; - }; + // eventArguments is never null here + if (eventArguments.stopped) { + return; + } - }, + eventArguments.preventDefault = preventDefault; + eventArguments.target = el; - /** - * Add an animation setter for a specific property - */ - addAnimSetter: function (prop, setter) { - // jQuery 1.8 style - if ($.Tween) { - $.Tween.propHooks[prop] = { - set: setter - }; - // pre 1.8 - } else { - $.fx.step[prop] = setter; + // If the type is not set, we're running a custom event (#2297). If it is set, + // we're running a browser event, and setting it will cause en error in + // IE8 (#2465). + if (!eventArguments.type) { + eventArguments.type = type; } - }, + + // If the event handler return false, prevent the default handler from executing + if (fn.call(el, eventArguments) === false) { + eventArguments.preventDefault(); + } + } + } - /** - * Downloads a script and executes a callback when done. - * @param {String} scriptLocation - * @param {Function} callback - */ - getScript: $.getScript, + // Run the default if not prevented + if (defaultFunction && !eventArguments.defaultPrevented) { + defaultFunction(eventArguments); + } + }; - /** - * Return the index of an item in an array, or -1 if not found - */ - inArray: $.inArray, + /** + * The global animate method, which uses Fx to create individual animators. + */ + animate = function (el, params, opt) { + var start, + unit = '', + end, + fx, + args, + prop; - /** - * A direct link to jQuery methods. MooTools and Prototype adapters must be implemented for each case of method. - * @param {Object} elem The HTML element - * @param {String} method Which method to run on the wrapped element - */ - adapterRun: function (elem, method) { - return $(elem)[method](); - }, + if (!isObject(opt)) { // Number or undefined/null + args = arguments; + opt = { + duration: args[2], + easing: args[3], + complete: args[4] + }; + } + if (!isNumber(opt.duration)) { + opt.duration = 400; + } + opt.easing = Math[opt.easing] || Math.easeInOutSine; + opt.curAnim = merge(params); - /** - * Filter an array - */ - grep: $.grep, + for (prop in params) { + fx = new Fx(el, opt, prop); + end = null; - /** - * Map an array - * @param {Array} arr - * @param {Function} fn - */ - map: function (arr, fn) { - //return jQuery.map(arr, fn); - var results = [], - i, - len = arr.length; - for (i = 0; i < len; i++) { - results[i] = fn.call(arr[i], arr[i], i, arr); + if (prop === 'd') { + fx.paths = fx.initPath( + el, + el.d, + params.d + ); + fx.toD = params.d; + start = 0; + end = 1; + } else if (el.attr) { + start = el.attr(prop); + } else { + start = parseFloat(getStyle(el, prop)) || 0; + if (prop !== 'opacity') { + unit = 'px'; } - return results; + } - }, + if (!end) { + end = params[prop]; + } + if (end.match && end.match('px')) { + end = end.replace(/px/g, ''); // #4351 + } + fx.run(start, end, unit); + } + }; - /** - * Get the position of an element relative to the top left of the page - */ - offset: function (el) { - return $(el).offset(); - }, + /** + * Register Highcharts as a plugin in jQuery + */ + if (win.jQuery) { + win.jQuery.fn.highcharts = function () { + var args = [].slice.call(arguments); - /** - * Add an event listener - * @param {Object} el A HTML element or custom object - * @param {String} event The event type - * @param {Function} fn The event handler - */ - addEvent: function (el, event, fn) { - $(el).bind(event, fn); - }, + if (this[0]) { // this[0] is the renderTo div - /** - * Remove event added with addEvent - * @param {Object} el The object - * @param {String} eventType The event type. Leave blank to remove all events. - * @param {Function} handler The function to remove - */ - removeEvent: function (el, eventType, handler) { - // workaround for jQuery issue with unbinding custom events: - // http://forum.jQuery.com/topic/javascript-error-when-unbinding-a-custom-event-using-jQuery-1-4-2 - var func = doc.removeEventListener ? 'removeEventListener' : 'detachEvent'; - if (doc[func] && el && !el[func]) { - el[func] = function () {}; + // Create the chart + if (args[0]) { + new Highcharts[ // eslint-disable-line no-new + isString(args[0]) ? args.shift() : 'Chart' // Constructor defaults to Chart + ](this[0], args[0], args[1]); + return this; } - $(el).unbind(eventType, handler); - }, + // When called without parameters or with the return argument, return an existing chart + return charts[attr(this[0], 'data-highcharts-chart')]; + } + }; + } - /** - * Fire an event on a custom object - * @param {Object} el - * @param {String} type - * @param {Object} eventArguments - * @param {Function} defaultFunction - */ - fireEvent: function (el, type, eventArguments, defaultFunction) { - var event = $.Event(type), - detachedType = 'detached' + type, - defaultPrevented; - // Remove warnings in Chrome when accessing returnValue (#2790), layerX and layerY. Although Highcharts - // never uses these properties, Chrome includes them in the default click event and - // raises the warning when they are copied over in the extend statement below. - // - // To avoid problems in IE (see #1010) where we cannot delete the properties and avoid - // testing if they are there (warning in chrome) the only option is to test if running IE. - if (!isMS && eventArguments) { - delete eventArguments.layerX; - delete eventArguments.layerY; - delete eventArguments.returnValue; - } + /** + * Compatibility section to add support for legacy IE. This can be removed if old IE + * support is not needed. + */ + if (doc && !doc.defaultView) { + getStyle = function (el, prop) { + var val, + alias = { width: 'clientWidth', height: 'clientHeight' }[prop]; + + if (el.style[prop]) { + return pInt(el.style[prop]); + } + if (prop === 'opacity') { + prop = 'filter'; + } - extend(event, eventArguments); + // Getting the rendered width and height + if (alias) { + el.style.zoom = 1; + return el[alias] - 2 * getStyle(el, 'padding'); + } + + val = el.currentStyle[prop.replace(/\-(\w)/g, function (a, b) { + return b.toUpperCase(); + })]; + if (prop === 'filter') { + val = val.replace( + /alpha\(opacity=([0-9]+)\)/, + function (a, b) { + return b / 100; + } + ); + } + + return val === '' ? 1 : pInt(val); + }; + } - // Prevent jQuery from triggering the object method that is named the - // same as the event. For example, if the event is 'select', jQuery - // attempts calling el.select and it goes into a loop. - if (el[type]) { - el[detachedType] = el[type]; - el[type] = null; + if (!Array.prototype.forEach) { + each = function (arr, fn) { // legacy + var i = 0, + len = arr.length; + for (; i < len; i++) { + if (fn.call(arr[i], arr[i], i, arr) === false) { + return i; } + } + }; + } - // Wrap preventDefault and stopPropagation in try/catch blocks in - // order to prevent JS errors when cancelling events on non-DOM - // objects. #615. - $.each(['preventDefault', 'stopPropagation'], function (i, fn) { - var base = event[fn]; - event[fn] = function () { - try { - base.call(event); - } catch (e) { - if (fn === 'preventDefault') { - defaultPrevented = true; - } - } - }; - }); + if (!Array.prototype.indexOf) { + inArray = function (item, arr) { + var len, + i = 0; - // trigger it - $(el).trigger(event); - - // attach the method - if (el[detachedType]) { - el[type] = el[detachedType]; - el[detachedType] = null; + if (arr) { + len = arr.length; + + for (; i < len; i++) { + if (arr[i] === item) { + return i; + } } + } - if (defaultFunction && !event.isDefaultPrevented() && !defaultPrevented) { - defaultFunction(event); - } - }, + return -1; + }; + } - /** - * Extension method needed for MooTools - */ - washMouseEvent: function (e) { - var ret = e.originalEvent || e; + if (!Array.prototype.filter) { + grep = function (elements, fn) { + var ret = [], + i = 0, + length = elements.length; - // computed by jQuery, needed by IE8 - if (ret.pageX === UNDEFINED) { // #1236 - ret.pageX = e.pageX; - ret.pageY = e.pageY; + for (; i < length; i++) { + if (fn(elements[i], i)) { + ret.push(elements[i]); } - - return ret; - }, - - /** - * Animate a HTML element or SVG element wrapper - * @param {Object} el - * @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 (#2161) - } - el.hasAnim = 1; // #3342 - $el.animate(params, options); - - }, - /** - * Stop running animation - */ - stop: function (el) { - if (el.hasAnim) { // #3342, memory leak on calling $(el) from destroy - $(el).stop(); - } } + + return ret; }; } - // Utility functions. If the HighchartsAdapter is not defined, adapter is an empty object - // and all the utility functions will be null. In that case they are populated by the - // default adapters below. - var adapterRun, - inArray, - each, - grep, - offset, - map, - addEvent, - removeEvent, - fireEvent, - washMouseEvent, - animate, - stop; - /** - * Helper function to load and extend Highcharts with adapter functionality. - * @param {object|function} adapter - HighchartsAdapter or jQuery - */ - Highcharts.loadAdapter = function (adapter) { - - if (adapter) { - // If jQuery, then load our default jQueryAdapter - if (adapter.fn && adapter.fn.jquery) { - adapter = loadJQueryAdapter(adapter); - } - // Initialize the adapter. - if (adapter.init) { - adapter.init(pathAnim); - delete adapter.init; // Avoid copying to Highcharts object - } - // Extend Highcharts with adapter functionality. - Highcharts.extend(Highcharts, adapter); + //--- End compatibility section --- - // Assign values to local functions. - adapterRun = Highcharts.adapterRun; - inArray = Highcharts.inArray; - each = Highcharts.each; - grep = Highcharts.grep; - offset = Highcharts.offset; - map = Highcharts.map; - addEvent = Highcharts.addEvent; - removeEvent = Highcharts.removeEvent; - fireEvent = Highcharts.fireEvent; - washMouseEvent = Highcharts.washMouseEvent; - animate = Highcharts.animate; - stop = Highcharts.stop; - } - }; + // Expose utilities + Highcharts.Fx = Fx; + Highcharts.inArray = inArray; + Highcharts.each = each; + Highcharts.grep = grep; + Highcharts.offset = offset; + Highcharts.map = map; + Highcharts.addEvent = addEvent; + Highcharts.removeEvent = removeEvent; + Highcharts.fireEvent = fireEvent; + Highcharts.animate = animate; + Highcharts.stop = stop; - // Load adapter if HighchartsAdapter or jQuery is set on the window. - Highcharts.loadAdapter(win.HighchartsAdapter || win.jQuery); /* **************************************************************************** * Handle the options * *****************************************************************************/ defaultOptions = { colors: ['#7cb5ec', '#434348', '#90ed7d', '#f7a35c', @@ -1312,11 +1479,11 @@ }, global: { useUTC: true, //timezoneOffset: 0, canvasToolsURL: 'http://code.highcharts.com/modules/canvas-tools.js', - VMLRadialGradientURL: 'http://code.highcharts.com/4.1.10/gfx/vml-radial-gradient.png' + VMLRadialGradientURL: 'http://code.highcharts.com/4.2.0/gfx/vml-radial-gradient.png' }, chart: { //animation: true, //alignTicks: false, //reflow: true, @@ -1637,11 +1804,11 @@ useUTC = globalOptions.useUTC, GET = useUTC ? 'getUTC' : 'get', SET = useUTC ? 'setUTC' : 'set'; - Date = globalOptions.Date || window.Date; + Date = globalOptions.Date || win.Date; timezoneOffset = useUTC && globalOptions.timezoneOffset; getTimezoneOffset = useUTC && globalOptions.getTimezoneOffset; makeTime = function (year, month, date, hours, minutes, seconds) { var d; if (useUTC) { @@ -1876,11 +2043,11 @@ }, /** * Animate a given attribute * @param {Object} params - * @param {Number} options The same options as in jQuery animation + * @param {Number} options Options include duration, easing, step and complete * @param {Function} complete Function to perform at the end of animation */ animate: function (params, options, complete) { var animOptions = pick(options, this.renderer.globalAnimation, true); stop(this); // stop regardless of animation actually running, or reverting to .attr (#607) @@ -3075,11 +3242,10 @@ * @param {Number} height * @param {Boolean} forExport */ init: function (container, width, height, style, forExport, allowHTML) { var renderer = this, - loc = location, boxWrapper, element, desc; boxWrapper = renderer.createElement('svg') @@ -3101,11 +3267,11 @@ renderer.boxWrapper = boxWrapper; renderer.alignedObjects = []; // Page url used for internal references. #24, #672, #1070 renderer.url = (isFirefox || isWebKit) && doc.getElementsByTagName('base').length ? - loc.href + win.location.href .replace(/#.*?$/, '') // remove the hash .replace(/([\('\)])/g, '\\$1') // escape parantheses and quotes .replace(/ /g, '%20') : // replace spaces (needed for Safari only) ''; @@ -3913,11 +4079,11 @@ if (this.width === 0) { css(this, { position: ABSOLUTE, top: '-999em' }); - document.body.appendChild(this); + doc.body.appendChild(this); } // Center the image centerImage(obj, symbolSizes[imageSrc] = [this.width, this.height]); @@ -5224,21 +5390,29 @@ } else if (nodeName !== 'IMG') { // #1336 element.filled = value !== NONE; this.setAttr('fillcolor', this.renderer.color(value, element, key, this)); } }, + 'fill-opacitySetter': function (value, key, element) { + createElement( + this.renderer.prepVML(['<', key.split('-')[0], ' opacity="', value, '"/>']), + null, + null, + element + ); + }, opacitySetter: noop, // Don't bother - animation is too slow and filters introduce artifacts rotationSetter: function (value, key, element) { var style = element.style; this[key] = style[key] = value; // style is for #1873 // Correction for the 1x1 size of the shape container. Used in gauge needles. style.left = -mathRound(mathSin(value * deg2rad) + 1) + PX; style.top = mathRound(mathCos(value * deg2rad)) + PX; }, strokeSetter: function (value, key, element) { - this.setAttr('strokecolor', this.renderer.color(value, element, key)); + this.setAttr('strokecolor', this.renderer.color(value, element, key, this)); }, 'stroke-widthSetter': function (value, key, element) { element.stroked = !!value; // VML "stroked" attribute this[key] = value; // used in getter, issue #113 if (isNumber(value)) { @@ -5300,10 +5474,12 @@ }, zIndexSetter: function (value, key, element) { element.style[key] = value; } }; + VMLElement['stroke-opacitySetter'] = VMLElement['fill-opacitySetter']; + Highcharts.VMLElement = VMLElement = extendClass(SVGElement, VMLElement); // Some shared setters VMLElement.prototype.ySetter = VMLElement.prototype.widthSetter = @@ -5590,18 +5766,17 @@ // Gradients are not supported for VML stroke, return the first color. #722. } else { ret = stopColor; } - // if the color is an rgba color, split it and add a fill node + // If the color is an rgba color, split it and add a fill node // to hold the opacity component } else if (regexRgba.test(color) && elem.tagName !== 'IMG') { colorObject = Color(color); - markup = ['<', prop, ' opacity="', colorObject.get('a'), '"/>']; - createElement(this.prepVML(markup), null, null, elem); + wrapper[prop + '-opacitySetter'](colorObject.get('a'), prop, elem); ret = colorObject.get('rgb'); } else { @@ -5897,10 +6072,26 @@ * * *****************************************************************************/ var CanVGRenderer, CanVGController; + /** + * Downloads a script and executes a callback when done. + * @param {String} scriptLocation + * @param {Function} callback + */ + function getScript(scriptLocation, callback) { + var head = doc.getElementsByTagName('head')[0], + script = doc.createElement('script'); + + script.type = 'text/javascript'; + script.src = scriptLocation; + script.onload = callback; + + head.appendChild(script); + } + if (useCanVG) { /** * The CanVGRenderer is empty from start to keep the source footprint small. * When requested, the CanVGController downloads the rest of the source packaged * together with the canvg library. @@ -5940,11 +6131,11 @@ return { push: function (func, scriptLocation) { // Only get the script once if (deferredRenderCalls.length === 0) { - Highcharts.getScript(scriptLocation, drawDeferred); + getScript(scriptLocation, drawDeferred); } // Register render call deferredRenderCalls.push(func); } }; @@ -9683,11 +9874,11 @@ }; var hoverChartIndex; // Global flag for touch support - hasTouch = doc.documentElement.ontouchstart !== UNDEFINED; + hasTouch = doc && doc.documentElement.ontouchstart !== UNDEFINED; /** * The mouse tracker object. All methods starting with "on" are primary DOM event handlers. * Subsequent methods should be named differently from what they are doing. * @param {Object} chart The Chart instance @@ -9742,17 +9933,12 @@ normalize: function (e, chartPosition) { var chartX, chartY, ePos; - // common IE normalizing - e = e || window.event; - - // Framework specific normalizing (#1165) - e = washMouseEvent(e); - - // More IE normalizing, needs to go after washMouseEvent + // IE normalizing + e = e || win.event; if (!e.target) { e.target = e.srcElement; } // iOS (#2757) @@ -10142,12 +10328,11 @@ hasPinched = this.hasPinched; if (this.selectionMarker) { var selectionData = { xAxis: [], - yAxis: [], - originalEvent: e.originalEvent || e + yAxis: [] }, selectionBox = this.selectionMarker, selectionLeft = selectionBox.attr ? selectionBox.attr('x') : selectionBox.x, selectionTop = selectionBox.attr ? selectionBox.attr('y') : selectionBox.y, selectionWidth = selectionBox.attr ? selectionBox.attr('width') : selectionBox.width, @@ -10303,11 +10488,10 @@ hoverPoint = chart.hoverPoint, plotLeft = chart.plotLeft, plotTop = chart.plotTop; e = this.normalize(e); - e.originalEvent = e; // #3913 if (!chart.cancelClick) { // On tracker click, fire the series and point events. #783, #1583 if (hoverPoint && this.inClass(e.target, PREFIX + 'tracker')) { @@ -10643,11 +10827,10 @@ } return fake; }, translateMSPointer = function (e, method, wktype, func) { var p; - e = e.originalEvent || e; if ((e.pointerType === 'touch' || e.pointerType === e.MSPOINTER_TYPE_TOUCH) && charts[hoverChartIndex]) { func(e); p = charts[hoverChartIndex].pointer; p[method]({ type: wktype, @@ -10875,20 +11058,21 @@ * Position the checkboxes after the width is determined */ positionCheckboxes: function (scrollOffset) { var alignAttr = this.group.alignAttr, translateY, - clipHeight = this.clipHeight || this.legendHeight; + clipHeight = this.clipHeight || this.legendHeight, + titleHeight = this.titleHeight; if (alignAttr) { translateY = alignAttr.translateY; each(this.allItems, function (item) { var checkbox = item.checkbox, top; if (checkbox) { - top = (translateY + checkbox.y + (scrollOffset || 0) + 3); + top = translateY + titleHeight + checkbox.y + (scrollOffset || 0) + 3; css(checkbox, { left: (alignAttr.translateX + item.checkboxOffset + checkbox.x - 20) + PX, top: top + PX, display: top > translateY - 6 && top < translateY + clipHeight - 6 ? '' : NONE }); @@ -11495,11 +11679,12 @@ this.legendSymbol = legendSymbol = renderer.symbol( this.symbol, (symbolWidth / 2) - radius, verticalCenter - radius, 2 * radius, - 2 * radius + 2 * radius, + markerOptions ) .add(legendItemGroup); legendSymbol.isMarker = true; } } @@ -11524,26 +11709,46 @@ // Do it after to work around the core issue setTimeout(runPositionItem); }); } /** - * The chart class + * The Chart class + * @param {String|Object} renderTo The DOM element to render to, or its id * @param {Object} options * @param {Function} callback Function to run when the chart has loaded */ var Chart = Highcharts.Chart = function () { - this.init.apply(this, arguments); + this.getArgs.apply(this, arguments); }; + Highcharts.chart = function (a, b, c) { + return new Chart(a, b, c); + }; + Chart.prototype = { /** * Hook for modules */ callbacks: [], /** + * Handle the arguments passed to the constructor + * @returns {Array} Arguments without renderTo + */ + getArgs: function () { + var args = [].slice.call(arguments); + + // Remove the optional first argument, renderTo, and + // set it on this. + if (isString(args[0]) || args[0].nodeName) { + this.renderTo = args.shift(); + } + this.init(args[0], args[1]); + }, + + /** * Initialize the chart */ init: function (userOptions, callback) { // Handle regular options @@ -11817,11 +12022,11 @@ // redraw if canvas renderer.draw(); // fire the event - fireEvent(chart, 'redraw'); // jQuery breaks this when calling it from addEvent. Overwrites chart.redraw + fireEvent(chart, 'redraw'); if (isHiddenChart) { chart.cloneRenderTo(true); } @@ -12027,16 +12232,16 @@ optionsChart = chart.options.chart, widthOption = optionsChart.width, heightOption = optionsChart.height, renderTo = chart.renderToClone || chart.renderTo; - // get inner width and height from jQuery (#824) + // Get inner width and height if (!defined(widthOption)) { - chart.containerWidth = adapterRun(renderTo, 'width'); + chart.containerWidth = getStyle(renderTo, 'width'); } if (!defined(heightOption)) { - chart.containerHeight = adapterRun(renderTo, 'height'); + chart.containerHeight = getStyle(renderTo, 'height'); } chart.chartWidth = mathMax(0, widthOption || chart.containerWidth || 600); // #1393, 1460 chart.chartHeight = mathMax(0, pick(heightOption, // the offsetHeight of an empty container is 0 in standard browsers, but 19 in IE7: @@ -12089,19 +12294,20 @@ container, options = chart.options, optionsChart = options.chart, chartWidth, chartHeight, - renderTo, + renderTo = chart.renderTo, indexAttrName = 'data-highcharts-chart', oldChartIndex, Ren, - containerId; + containerId = 'highcharts-' + idCounter++; - chart.renderTo = renderTo = optionsChart.renderTo; - containerId = PREFIX + idCounter++; - + if (!renderTo) { + chart.renderTo = renderTo = optionsChart.renderTo; + } + if (isString(renderTo)) { chart.renderTo = renderTo = doc.getElementById(renderTo); } // Display an error if the renderTo is wrong @@ -12244,13 +12450,13 @@ */ reflow: function (e) { var chart = this, optionsChart = chart.options.chart, renderTo = chart.renderTo, - width = optionsChart.width || adapterRun(renderTo, 'width'), - height = optionsChart.height || adapterRun(renderTo, 'height'), - target = e ? e.target : win; // #805 - MooTools doesn't supply e + width = optionsChart.width || getStyle(renderTo, 'width'), + height = optionsChart.height || getStyle(renderTo, 'height'), + target = e ? e.target : win; // Width and height checks for display:none. Target is doc in IE8 and Opera, // win in Firefox, Chrome and IE9. if (!chart.hasUserSize && !chart.isPrinting && width && height && (target === win || target === doc)) { // #1093 if (width !== chart.containerWidth || height !== chart.containerHeight) { @@ -12751,11 +12957,11 @@ 0, 0 ) .on('click', function () { if (credits.href) { - location.href = credits.href; + win.location.href = credits.href; } }) .attr({ align: credits.position.align, zIndex: 8 @@ -13193,12 +13399,11 @@ series: this.series }); }, /** - * Fire an event on the Point object. Must not be renamed to fireEvent, as this - * causes a name clash in MooTools + * Fire an event on the Point object. * @param {String} eventType * @param {Object} eventArgs Additional event arguments * @param {Function} defaultFunction Default event handler */ firePointEvent: function (eventType, eventArgs, defaultFunction) { @@ -14811,20 +15016,19 @@ // Generate it on first call if (isNew) { this[prop] = group = this.chart.renderer.g(name) .attr({ - visibility: visibility, zIndex: zIndex || 0.1 // IE8 needs this }) .add(parent); group.addClass('highcharts-series-' + this.index); } // Place it on first and subsequent (redraw) calls - group[isNew ? 'attr' : 'animate'](this.getPlotBox()); + group.attr({ visibility: visibility })[isNew ? 'attr' : 'animate'](this.getPlotBox()); return group; }, /** * Get the translation and scale for the plot area of this series @@ -15871,39 +16075,28 @@ * * @param {Boolean} redraw Whether to redraw the chart or wait for an explicit call * @param {Boolean|Object} animation Whether to apply animation, and optionally animation * configuration */ - remove: function (redraw, animation) { var series = this, chart = series.chart; - redraw = pick(redraw, true); - if (!series.isRemoving) { /* prevent triggering native event in jQuery - (calling the remove function from the remove event) */ - series.isRemoving = true; + // Fire the event with a default handler of removing the point + fireEvent(series, 'remove', null, function () { - // fire the event with a default handler of removing the point - fireEvent(series, 'remove', null, function () { + // Destroy elements + series.destroy(); + // Redraw + chart.isDirtyLegend = chart.isDirtyBox = true; + chart.linkSeries(); - // destroy elements - series.destroy(); - - - // redraw - chart.isDirtyLegend = chart.isDirtyBox = true; - chart.linkSeries(); - - if (redraw) { - chart.redraw(animation); - } - }); - - } - series.isRemoving = false; + if (pick(redraw, true)) { + chart.redraw(animation); + } + }); }, /** * Update the series with a new set of options */ @@ -16223,25 +16416,28 @@ each(zones, function (threshold, i) { props.push(['zoneArea' + i, threshold.color || series.color, threshold.fillColor || options.fillColor]); }); each(props, function (prop) { var areaKey = prop[0], - area = series[areaKey]; + area = series[areaKey], + attr; // Create or update the area if (area) { // update area.animate({ d: areaPath }); } else { // create + attr = { + fill: prop[2] || prop[1], + zIndex: 0 // #1069 + }; + if (!prop[2]) { + attr['fill-opacity'] = options.fillOpacity || 0.75; + } series[areaKey] = series.chart.renderer.path(areaPath) - .attr({ - fill: pick( - prop[2], - Color(prop[1]).setOpacity(pick(options.fillOpacity, 0.75)).get() - ), - zIndex: 0 // #1069 - }).add(series.group); + .attr(attr) + .add(series.group); } }); }, drawLegendSymbol: LegendSymbolMixin.drawRectangle @@ -16484,22 +16680,22 @@ xAxis = series.xAxis, yAxis = series.yAxis, reversedXAxis = xAxis.reversed, stackKey, stackGroups = {}, - columnIndex, columnCount = 0; // Get the total number of column type series. // This is called on every series. Consider moving this logic to a // chart.orderStacks() function and call it on init, addSeries and removeSeries if (options.grouping === false) { columnCount = 1; } else { each(series.chart.series, function (otherSeries) { var otherOptions = otherSeries.options, - otherYAxis = otherSeries.yAxis; + otherYAxis = otherSeries.yAxis, + columnIndex; if (otherSeries.type === series.type && otherSeries.visible && yAxis.len === otherYAxis.len && yAxis.pos === otherYAxis.pos) { // #642, #2086 if (otherOptions.stacking) { stackKey = otherSeries.stackKey; if (stackGroups[stackKey] === UNDEFINED) { @@ -18544,13 +18740,13 @@ halfPointRange = (axis.pointRange || 0) / 2, extremes = axis.getExtremes(), newMin = axis.toValue(startPos - mousePos, true) + halfPointRange, newMax = axis.toValue(startPos + chart[isX ? 'plotWidth' : 'plotHeight'] - mousePos, true) - halfPointRange, goingLeft = startPos > mousePos; // #3613 - + if (axis.series.length && - (goingLeft || newMin > mathMin(extremes.dataMin, extremes.min)) && + (goingLeft || newMin > mathMin(extremes.dataMin, extremes.min)) && (!goingLeft || newMax < mathMax(extremes.dataMax, extremes.max))) { axis.setExtremes(newMin, newMax, false, false, { trigger: 'pan' }); doRedraw = true; } @@ -18579,11 +18775,11 @@ series = point.series, chart = series.chart; selected = pick(selected, !point.selected); - // fire the event with the defalut handler + // fire the event with the default handler point.firePointEvent(selected ? 'select' : 'unselect', { accumulate: accumulate }, function () { point.selected = point.options.selected = selected; series.options.data[inArray(point, series.data)] = point.options; point.setState(selected && SELECT_STATE); @@ -18786,15 +18982,12 @@ if (haloOptions && haloOptions.size) { if (!halo) { series.halo = halo = chart.renderer.path() .add(chart.seriesGroup); } - halo.attr(extend(hasSVG ? { + halo.attr(extend({ fill: point.color || series.color, 'fill-opacity': haloOptions.opacity - } : { - // Old IE doesn't take fill-opacity - fill: Color(point.color || series.color).setOpacity(haloOptions.opacity).get() }, haloOptions.attributes))[move ? 'animate' : 'attr']({ d: point.haloPath(haloOptions.size) }); } else if (halo) {