/** * @license Highcharts JS v4.0.1 (2014-04-24) * * Standalone Highcharts Framework * * License: MIT License */ /*global Highcharts */ var HighchartsAdapter = (function () { var UNDEFINED, doc = document, emptyArray = [], timers = [], timerId, Fx; Math.easeInOutSine = function (t, b, c, d) { return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b; }; /** * Extend given object with custom events */ function augment(obj) { function removeOneEvent(el, type, fn) { el.removeEventListener(type, fn, false); } function IERemoveOneEvent(el, type, fn) { fn = el.HCProxiedMethods[fn.toString()]; el.detachEvent('on' + type, fn); } function removeAllEvents(el, type) { var events = el.HCEvents, remove, types, len, n; if (el.removeEventListener) { remove = removeOneEvent; } else if (el.attachEvent) { remove = IERemoveOneEvent; } else { return; // break on non-DOM events } if (type) { types = {}; types[type] = true; } else { types = events; } for (n in types) { if (events[n]) { len = events[n].length; while (len--) { remove(el, n, events[n][len]); } } } } if (!obj.HCExtended) { Highcharts.extend(obj, { HCExtended: true, HCEvents: {}, bind: function (name, fn) { var el = this, events = this.HCEvents, wrappedFn; // handle DOM events in modern browsers if (el.addEventListener) { el.addEventListener(name, fn, false); // handle old IE implementation } else if (el.attachEvent) { wrappedFn = function (e) { e.target = e.srcElement || window; // #2820 fn.call(el, e); }; if (!el.HCProxiedMethods) { el.HCProxiedMethods = {}; } // link wrapped fn with original fn, so we can get this in removeEvent el.HCProxiedMethods[fn.toString()] = wrappedFn; el.attachEvent('on' + name, wrappedFn); } if (events[name] === UNDEFINED) { events[name] = []; } events[name].push(fn); }, unbind: function (name, fn) { var events, index; if (name) { events = this.HCEvents[name] || []; if (fn) { index = HighchartsAdapter.inArray(fn, events); if (index > -1) { events.splice(index, 1); this.HCEvents[name] = events; } if (this.removeEventListener) { removeOneEvent(this, name, fn); } else if (this.attachEvent) { IERemoveOneEvent(this, name, fn); } } else { removeAllEvents(this, name); this.HCEvents[name] = []; } } else { removeAllEvents(this); this.HCEvents = {}; } }, trigger: function (name, args) { var events = this.HCEvents[name] || [], target = this, len = events.length, i, preventDefault, fn; // Attach a simple preventDefault function to skip default handler if called preventDefault = function () { args.defaultPrevented = true; }; for (i = 0; i < len; i++) { fn = events[i]; // args is never null here if (args.stopped) { return; } args.preventDefault = preventDefault; args.target = target; // 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 (!args.type) { args.type = name; } // If the event handler return false, prevent the default handler from executing if (fn.call(this, args) === false) { args.preventDefault(); } } } }); } return obj; } return { /** * Initialize the adapter. This is run once as Highcharts is first run. */ init: function (pathAnim) { /** * Compatibility section to add support for legacy IE. This can be removed if old IE * support is not needed. */ if (!doc.defaultView) { this._getStyle = function (el, prop) { var val; if (el.style[prop]) { return el.style[prop]; } else { if (prop === 'opacity') { prop = 'filter'; } /*jslint unparam: true*/ 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; } ); } /*jslint unparam: false*/ return val === '' ? 1 : val; } }; this.adapterRun = function (elem, method) { var alias = { width: 'clientWidth', height: 'clientHeight' }[method]; if (alias) { elem.style.zoom = 1; return elem[alias] - 2 * parseInt(HighchartsAdapter._getStyle(elem, 'padding'), 10); } }; } if (!Array.prototype.forEach) { this.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; } } }; } if (!Array.prototype.indexOf) { this.inArray = function (item, arr) { var len, i = 0; if (arr) { len = arr.length; for (; i < len; i++) { if (arr[i] === item) { return i; } } } return -1; }; } if (!Array.prototype.filter) { this.grep = function (elements, callback) { var ret = [], i = 0, length = elements.length; for (; i < length; i++) { if (!!callback(elements[i], i)) { ret.push(elements[i]); } } return ret; }; } //--- End compatibility section --- /** * Start of animation specific code */ Fx = function (elem, options, prop) { this.options = options; this.elem = elem; this.prop = prop; }; Fx.prototype = { update: function () { var styles, paths = this.paths, elem = this.elem, elemelem = elem.element; // if destroyed, it is null // Animating a path definition on SVGElement if (paths && elemelem) { elem.attr('d', pathAnim.step(paths[0], paths[1], this.now, this.toD)); // Other animations on SVGElement } else if (elem.attr) { if (elemelem) { elem.attr(this.prop, this.now); } // HTML styles } else { styles = {}; styles[this.prop] = this.now + this.unit; Highcharts.css(elem, styles); } if (this.options.step) { this.options.step.call(this.elem, this.now, this); } }, custom: function (from, to, unit) { var self = this, t = function (gotoEnd) { return self.step(gotoEnd); }, i; this.startTime = +new Date(); this.start = from; this.end = to; this.unit = unit; this.now = this.start; this.pos = this.state = 0; t.elem = this.elem; if (t() && timers.push(t) === 1) { timerId = setInterval(function () { for (i = 0; i < timers.length; i++) { if (!timers[i]()) { timers.splice(i--, 1); } } if (!timers.length) { clearInterval(timerId); } }, 13); } }, step: function (gotoEnd) { var t = +new Date(), ret, done, options = this.options, elem = this.elem, i; if (elem.stopAnimation || (elem.attr && !elem.element)) { // #2616, element including flag is destroyed ret = false; } else if (gotoEnd || t >= options.duration + this.startTime) { this.now = this.end; this.pos = this.state = 1; this.update(); this.options.curAnim[this.prop] = true; done = true; for (i in options.curAnim) { if (options.curAnim[i] !== true) { done = false; } } if (done) { if (options.complete) { options.complete.call(elem); } } ret = false; } else { var n = t - this.startTime; this.state = n / options.duration; this.pos = options.easing(n, 0, 1, options.duration); this.now = this.start + ((this.end - this.start) * this.pos); this.update(); ret = true; } return ret; } }; /** * The adapter animate method */ this.animate = function (el, prop, opt) { var start, unit = '', end, fx, args, name; el.stopAnimation = false; // ready for new if (typeof opt !== 'object' || opt === null) { args = arguments; opt = { duration: args[2], easing: args[3], complete: args[4] }; } if (typeof opt.duration !== 'number') { opt.duration = 400; } opt.easing = Math[opt.easing] || Math.easeInOutSine; opt.curAnim = Highcharts.extend({}, prop); for (name in prop) { fx = new Fx(el, opt, name); end = null; if (name === 'd') { fx.paths = pathAnim.init( el, el.d, prop.d ); fx.toD = prop.d; start = 0; end = 1; } else if (el.attr) { start = el.attr(name); } else { start = parseFloat(HighchartsAdapter._getStyle(el, name)) || 0; if (name !== 'opacity') { unit = 'px'; } } if (!end) { end = parseFloat(prop[name]); } fx.custom(start, end, unit); } }; }, /** * Internal method to return CSS value for given element and property */ _getStyle: function (el, prop) { return window.getComputedStyle(el, undefined).getPropertyValue(prop); }, /** * Downloads a script and executes a callback when done. * @param {String} scriptLocation * @param {Function} callback */ getScript: function (scriptLocation, callback) { // We cannot assume that Assets class from mootools-more is available so instead insert a script tag to download script. var head = doc.getElementsByTagName('head')[0], script = doc.createElement('script'); script.type = 'text/javascript'; script.src = scriptLocation; script.onload = callback; head.appendChild(script); }, /** * Return the index of an item in an array, or -1 if not found */ inArray: function (item, arr) { return arr.indexOf ? arr.indexOf(item) : emptyArray.indexOf.call(arr, item); }, /** * A direct link to adapter methods */ adapterRun: function (elem, method) { return parseInt(HighchartsAdapter._getStyle(elem, method), 10); }, /** * Filter an array */ grep: function (elements, callback) { return emptyArray.filter.call(elements, callback); }, /** * Map an array */ map: function (arr, fn) { var results = [], i = 0, len = arr.length; for (; i < len; i++) { results[i] = fn.call(arr[i], arr[i], i, arr); } return results; }, /** * Get the element's offset position, corrected by overflow:auto. Loosely based on jQuery's offset method. */ offset: function (el) { var docElem = document.documentElement, box = el.getBoundingClientRect(); return { top: box.top + (window.pageYOffset || docElem.scrollTop) - (docElem.clientTop || 0), left: box.left + (window.pageXOffset || docElem.scrollLeft) - (docElem.clientLeft || 0) }; }, /** * Add an event listener */ addEvent: function (el, type, fn) { augment(el).bind(type, fn); }, /** * Remove event added with addEvent */ removeEvent: function (el, type, fn) { augment(el).unbind(type, fn); }, /** * Fire an event on a custom object */ fireEvent: function (el, type, eventArguments, defaultFunction) { var e; if (doc.createEvent && (el.dispatchEvent || el.fireEvent)) { e = doc.createEvent('Events'); e.initEvent(type, true, true); e.target = el; Highcharts.extend(e, eventArguments); if (el.dispatchEvent) { el.dispatchEvent(e); } else { el.fireEvent(type, e); } } else if (el.HCExtended === true) { eventArguments = eventArguments || {}; el.trigger(type, eventArguments); } if (eventArguments && eventArguments.defaultPrevented) { defaultFunction = null; } if (defaultFunction) { defaultFunction(eventArguments); } }, washMouseEvent: function (e) { return e; }, /** * Stop running animation */ stop: function (el) { el.stopAnimation = true; }, /** * Utility for iterating over an array. Parameters are reversed compared to jQuery. * @param {Array} arr * @param {Function} fn */ each: function (arr, fn) { // modern browsers return Array.prototype.forEach.call(arr, fn); } }; }());