gopheracademy/gcon

View on GitHub
assets/admin/global/plugins/highcharts/js/adapters/standalone-framework.src.js

Summary

Maintainability
F
1 mo
Test Coverage
/**
 * @license Highcharts JS v4.1.9 (2015-10-07)
 *
 * Standalone Highcharts Framework
 *
 * License: MIT License
 */


/*global Highcharts */
var HighchartsAdapter = (function () {

var UNDEFINED,
    doc = document,
    emptyArray = [],
    timers = [],
    animSetters = {},
    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

                // Animation setter defined from outside
                if (animSetters[this.prop]) {
                    animSetters[this.prop](this);

                // Animating a path definition on SVGElement
                } else 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) {
                    t.timerId = setInterval(function () {
                        
                        for (i = 0; i < timers.length; i++) {
                            if (!timers[i]()) {
                                timers.splice(i--, 1);
                            }
                        }

                        if (!timers.length) {
                            clearInterval(t.timerId);
                        }
                    }, 13);
                }
            },
            
            step: function (gotoEnd) {
                var t = +new Date(),
                    ret,
                    done,
                    options = this.options,
                    elem = this.elem,
                    i;
                
                if (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,
                PX = 'px';

            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 = prop[name];
                }
                if (end.match && end.match(PX)) {
                    end = end.replace(/px/g, ''); // #4351
                }
                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);
    },

    /**
     * Add an animation setter for a specific property
     */
    addAnimSetter: function (prop, fn) {
        animSetters[prop] = fn;
    },

    /**
     * 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) {

        var i = timers.length,
            timer;

        // Remove timers related to this element (#4519)
        while (i--) {
            timer = timers[i];
            if (timer.elem === el) {
                timers.splice(i, 1);
            }
        }
    },

    /**
     * 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);
    }
};
}());