/**
 * @license MIT or GPL-2.0
 * @fileOverview Favico animations
 * @author Miroslav Magda, http://blog.ejci.net
 * @source: https://github.com/ejci/favico.js
 * @version 0.3.10
 */

/**
 * Create new favico instance
 * @param {Object} Options
 * @return {Object} Favico object
 * @example
 * var favico = new Favico({
 *    bgColor : '#d00',
 *    textColor : '#fff',
 *    fontFamily : 'sans-serif',
 *    fontStyle : 'bold',
 *    type : 'circle',
 *    position : 'down',
 *    animation : 'slide',
 *    elementId: false,
 *    element: null,
 *    dataUrl: function(url){},
 *    win: window
 * });
 */
(function () {

    var Favico = (function (opt) {
        'use strict';
        opt = (opt) ? opt : {};
        var _def = {
            bgColor: '#d00',
            textColor: '#fff',
            fontFamily: 'sans-serif', //Arial,Verdana,Times New Roman,serif,sans-serif,...
            fontStyle: 'bold', //normal,italic,oblique,bold,bolder,lighter,100,200,300,400,500,600,700,800,900
            type: 'circle',
            position: 'down', // down, up, left, leftup (upleft)
            animation: 'slide',
            elementId: false,
            element: null,
            dataUrl: false,
            win: window
        };
        var _opt, _orig, _h, _w, _canvas, _context, _img, _ready, _lastBadge, _running, _readyCb, _stop, _browser, _animTimeout, _drawTimeout, _doc;

        _browser = {};
        _browser.ff = typeof InstallTrigger != 'undefined';
        _browser.chrome = !!window.chrome;
        _browser.opera = !!window.opera || navigator.userAgent.indexOf('Opera') >= 0;
        _browser.ie = /*@cc_on!@*/false;
        _browser.safari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0;
        _browser.supported = (_browser.chrome || _browser.ff || _browser.opera);

        var _queue = [];
        _readyCb = function () {
        };
        _ready = _stop = false;
        /**
         * Initialize favico
         */
        var init = function () {
            //merge initial options
            _opt = merge(_def, opt);
            _opt.bgColor = hexToRgb(_opt.bgColor);
            _opt.textColor = hexToRgb(_opt.textColor);
            _opt.position = _opt.position.toLowerCase();
            _opt.animation = (animation.types['' + _opt.animation]) ? _opt.animation : _def.animation;

            _doc = _opt.win.document;

            var isUp = _opt.position.indexOf('up') > -1;
            var isLeft = _opt.position.indexOf('left') > -1;

            //transform the animations
            if (isUp || isLeft) {
                for (var a in animation.types) {
                    for (var i = 0; i < animation.types[a].length; i++) {
                        var step = animation.types[a][i];

                        if (isUp) {
                            if (step.y < 0.6) {
                                step.y = step.y - 0.4;
                            } else {
                                step.y = step.y - 2 * step.y + (1 - step.w);
                            }
                        }

                        if (isLeft) {
                            if (step.x < 0.6) {
                                step.x = step.x - 0.4;
                            } else {
                                step.x = step.x - 2 * step.x + (1 - step.h);
                            }
                        }

                        animation.types[a][i] = step;
                    }
                }
            }
            _opt.type = (type['' + _opt.type]) ? _opt.type : _def.type;

            _orig = link. getIcons();
            //create temp canvas
            _canvas = document.createElement('canvas');
            //create temp image
            _img = document.createElement('img');
            var lastIcon = _orig[_orig.length - 1];
            if (lastIcon.hasAttribute('href')) {
                _img.setAttribute('crossOrigin', 'anonymous');
                //get width/height
                _img.onload = function () {
                    _h = (_img.height > 0) ? _img.height : 32;
                    _w = (_img.width > 0) ? _img.width : 32;
                    _canvas.height = _h;
                    _canvas.width = _w;
                    _context = _canvas.getContext('2d');
                    icon.ready();
                };
                _img.setAttribute('src', lastIcon.getAttribute('href'));
            } else {
                _h = 32;
                _w = 32;
                _img.height = _h;
                _img.width = _w;
                _canvas.height = _h;
                _canvas.width = _w;
                _context = _canvas.getContext('2d');
                icon.ready();
            }

        };
        /**
         * Icon namespace
         */
        var icon = {};
        /**
         * Icon is ready (reset icon) and start animation (if ther is any)
         */
        icon.ready = function () {
            _ready = true;
            icon.reset();
            _readyCb();
        };
        /**
         * Reset icon to default state
         */
        icon.reset = function () {
            //reset
            if (!_ready) {
                return;
            }
            _queue = [];
            _lastBadge = false;
            _running = false;
            _context.clearRect(0, 0, _w, _h);
            _context.drawImage(_img, 0, 0, _w, _h);
            //_stop=true;
            link.setIcon(_canvas);
            //webcam('stop');
            //video('stop');
            window.clearTimeout(_animTimeout);
            window.clearTimeout(_drawTimeout);
        };
        /**
         * Start animation
         */
        icon.start = function () {
            if (!_ready || _running) {
                return;
            }
            var finished = function () {
                _lastBadge = _queue[0];
                _running = false;
                if (_queue.length > 0) {
                    _queue.shift();
                    icon.start();
                } else {

                }
            };
            if (_queue.length > 0) {
                _running = true;
                var run = function () {
                    // apply options for this animation
                    ['type', 'animation', 'bgColor', 'textColor', 'fontFamily', 'fontStyle'].forEach(function (a) {
                        if (a in _queue[0].options) {
                            _opt[a] = _queue[0].options[a];
                        }
                    });
                    animation.run(_queue[0].options, function () {
                        finished();
                    }, false);
                };
                if (_lastBadge) {
                    animation.run(_lastBadge.options, function () {
                        run();
                    }, true);
                } else {
                    run();
                }
            }
        };

        /**
         * Badge types
         */
        var type = {};
        var options = function (opt) {
            opt.n = ((typeof opt.n) === 'number') ? Math.abs(opt.n | 0) : opt.n;
            opt.x = _w * opt.x;
            opt.y = _h * opt.y;
            opt.w = _w * opt.w;
            opt.h = _h * opt.h;
            opt.len = ("" + opt.n).length;
            return opt;
        };

        /**
         * Generate circle
         * @param {Object} opt Badge options
         */
        type.circle = function (opt) {
            opt = options(opt);
            var more = false;
            if (opt.len === 2) {
                opt.x = opt.x - opt.w * 0.4;
                opt.w = opt.w * 1.4;
                more = true;
            } else if (opt.len >= 3) {
                opt.x = opt.x - opt.w * 0.65;
                opt.w = opt.w * 1.65;
                more = true;
            }

            // begin patch
            // draw a smaller badge without text
            opt.w = 14;
            opt.h = 14;
            opt.x = (_w - opt.w);
            opt.y = (_h - opt.h);
            // end patch

            _context.clearRect(0, 0, _w, _h);
            _context.drawImage(_img, 0, 0, _w, _h);
            _context.beginPath();
            _context.font = _opt.fontStyle + " " + Math.floor(opt.h * (opt.n > 99 ? 0.85 : 1)) + "px " + _opt.fontFamily;
            _context.textAlign = 'center';
            if (more) {
                _context.moveTo(opt.x + opt.w / 2, opt.y);
                _context.lineTo(opt.x + opt.w - opt.h / 2, opt.y);
                _context.quadraticCurveTo(opt.x + opt.w, opt.y, opt.x + opt.w, opt.y + opt.h / 2);
                _context.lineTo(opt.x + opt.w, opt.y + opt.h - opt.h / 2);
                _context.quadraticCurveTo(opt.x + opt.w, opt.y + opt.h, opt.x + opt.w - opt.h / 2, opt.y + opt.h);
                _context.lineTo(opt.x + opt.h / 2, opt.y + opt.h);
                _context.quadraticCurveTo(opt.x, opt.y + opt.h, opt.x, opt.y + opt.h - opt.h / 2);
                _context.lineTo(opt.x, opt.y + opt.h / 2);
                _context.quadraticCurveTo(opt.x, opt.y, opt.x + opt.h / 2, opt.y);
            } else {
                _context.arc(opt.x + opt.w / 2, opt.y + opt.h / 2, opt.h / 2, 0, 2 * Math.PI);
            }
            _context.fillStyle = 'rgba(' + _opt.bgColor.r + ',' + _opt.bgColor.g + ',' + _opt.bgColor.b + ',' + opt.o + ')';
            _context.fill();
            _context.closePath();
            _context.beginPath();
            _context.stroke();
            _context.fillStyle = 'rgba(' + _opt.textColor.r + ',' + _opt.textColor.g + ',' + _opt.textColor.b + ',' + opt.o + ')';
            //_context.fillText((more) ? '9+' : opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15));
            if ((typeof opt.n) === 'number' && opt.n > 999) {
                _context.fillText(((opt.n > 9999) ? 9 : Math.floor(opt.n / 1000)) + 'k+', Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.2));
            } else {
                _context.fillText(opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15));
            }
            _context.closePath();
        };
        /**
         * Generate rectangle
         * @param {Object} opt Badge options
         */
        type.rectangle = function (opt) {
            opt = options(opt);
            var more = false;
            if (opt.len === 2) {
                opt.x = opt.x - opt.w * 0.4;
                opt.w = opt.w * 1.4;
                more = true;
            } else if (opt.len >= 3) {
                opt.x = opt.x - opt.w * 0.65;
                opt.w = opt.w * 1.65;
                more = true;
            }
            _context.clearRect(0, 0, _w, _h);
            _context.drawImage(_img, 0, 0, _w, _h);
            _context.beginPath();
            _context.font = _opt.fontStyle + " " + Math.floor(opt.h * (opt.n > 99 ? 0.9 : 1)) + "px " + _opt.fontFamily;
            _context.textAlign = 'center';
            _context.fillStyle = 'rgba(' + _opt.bgColor.r + ',' + _opt.bgColor.g + ',' + _opt.bgColor.b + ',' + opt.o + ')';
            _context.fillRect(opt.x, opt.y, opt.w, opt.h);
            _context.fillStyle = 'rgba(' + _opt.textColor.r + ',' + _opt.textColor.g + ',' + _opt.textColor.b + ',' + opt.o + ')';
            //_context.fillText((more) ? '9+' : opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15));
            if ((typeof opt.n) === 'number' && opt.n > 999) {
                _context.fillText(((opt.n > 9999) ? 9 : Math.floor(opt.n / 1000)) + 'k+', Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.2));
            } else {
                _context.fillText(opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15));
            }
            _context.closePath();
        };

        /**
         * Set badge
         */
        var badge = function (number, opts) {
            opts = ((typeof opts) === 'string' ? {
                animation: opts
            } : opts) || {};
            _readyCb = function () {
                try {
                    if (typeof (number) === 'number' ? (number > 0) : (number !== '')) {
                        var q = {
                            type: 'badge',
                            options: {
                                n: number
                            }
                        };
                        if ('animation' in opts && animation.types['' + opts.animation]) {
                            q.options.animation = '' + opts.animation;
                        }
                        if ('type' in opts && type['' + opts.type]) {
                            q.options.type = '' + opts.type;
                        }
                        ['bgColor', 'textColor'].forEach(function (o) {
                            if (o in opts) {
                                q.options[o] = hexToRgb(opts[o]);
                            }
                        });
                        ['fontStyle', 'fontFamily'].forEach(function (o) {
                            if (o in opts) {
                                q.options[o] = opts[o];
                            }
                        });
                        _queue.push(q);
                        if (_queue.length > 100) {
                            throw new Error('Too many badges requests in queue.');
                        }
                        icon.start();
                    } else {
                        icon.reset();
                    }
                } catch (e) {
                    throw new Error('Error setting badge. Message: ' + e.message);
                }
            };
            if (_ready) {
                _readyCb();
            }
        };

        /**
         * Set image as icon
         */
        var image = function (imageElement) {
            _readyCb = function () {
                try {
                    var w = imageElement.width;
                    var h = imageElement.height;
                    var newImg = document.createElement('img');
                    var ratio = (w / _w < h / _h) ? (w / _w) : (h / _h);
                    newImg.setAttribute('crossOrigin', 'anonymous');
                    newImg.onload=function(){
                        _context.clearRect(0, 0, _w, _h);
                        _context.drawImage(newImg, 0, 0, _w, _h);
                        link.setIcon(_canvas);
                    };
                    newImg.setAttribute('src', imageElement.getAttribute('src'));
                    newImg.height = (h / ratio);
                    newImg.width = (w / ratio);
                } catch (e) {
                    throw new Error('Error setting image. Message: ' + e.message);
                }
            };
            if (_ready) {
                _readyCb();
            }
        };
        /**
         * Set the icon from a source url. Won't work with badges.
         */
        var rawImageSrc = function (url) {
            _readyCb = function() {
                link.setIconSrc(url);
            };
            if (_ready) {
                _readyCb();
            }
        };
        /**
         * Set video as icon
         */
        var video = function (videoElement) {
            _readyCb = function () {
                try {
                    if (videoElement === 'stop') {
                        _stop = true;
                        icon.reset();
                        _stop = false;
                        return;
                    }
                    //var w = videoElement.width;
                    //var h = videoElement.height;
                    //var ratio = (w / _w < h / _h) ? (w / _w) : (h / _h);
                    videoElement.addEventListener('play', function () {
                        drawVideo(this);
                    }, false);

                } catch (e) {
                    throw new Error('Error setting video. Message: ' + e.message);
                }
            };
            if (_ready) {
                _readyCb();
            }
        };
        /**
         * Set video as icon
         */
        var webcam = function (action) {
            //UR
            if (!window.URL || !window.URL.createObjectURL) {
                window.URL = window.URL || {};
                window.URL.createObjectURL = function (obj) {
                    return obj;
                };
            }
            if (_browser.supported) {
                var newVideo = false;
                navigator.getUserMedia = navigator.getUserMedia || navigator.oGetUserMedia || navigator.msGetUserMedia || navigator.mozGetUserMedia || navigator.webkitGetUserMedia;
                _readyCb = function () {
                    try {
                        if (action === 'stop') {
                            _stop = true;
                            icon.reset();
                            _stop = false;
                            return;
                        }
                        newVideo = document.createElement('video');
                        newVideo.width = _w;
                        newVideo.height = _h;
                        navigator.getUserMedia({
                            video: true,
                            audio: false
                        }, function (stream) {
                            newVideo.src = URL.createObjectURL(stream);
                            newVideo.play();
                            drawVideo(newVideo);
                        }, function () {
                        });
                    } catch (e) {
                        throw new Error('Error setting webcam. Message: ' + e.message);
                    }
                };
                if (_ready) {
                    _readyCb();
                }
            }

        };

        var setOpt = function (key, value) {
            var opts = key;
            if (!(value == null && Object.prototype.toString.call(key) == '[object Object]')) {
                opts = {};
                opts[key] = value;
            }

            var keys = Object.keys(opts);
            for (var i = 0; i < keys.length; i++) {
                if (keys[i] == 'bgColor' || keys[i] == 'textColor') {
                    _opt[keys[i]] = hexToRgb(opts[keys[i]]);
                } else {
                    _opt[keys[i]] = opts[keys[i]];
                }
            }

            _queue.push(_lastBadge);
            icon.start();
        };

        /**
         * Draw video to context and repeat :)
         */
        function drawVideo(video) {
            if (video.paused || video.ended || _stop) {
                return false;
            }
            //nasty hack for FF webcam (Thanks to Julian Ćwirko, kontakt@redsunmedia.pl)
            try {
                _context.clearRect(0, 0, _w, _h);
                _context.drawImage(video, 0, 0, _w, _h);
            } catch (e) {

            }
            _drawTimeout = setTimeout(function () {
                drawVideo(video);
            }, animation.duration);
            link.setIcon(_canvas);
        }

        var link = {};
        /**
         * Get icons from HEAD tag or create a new <link> element
         */
        link.getIcons = function () {
            var elms = [];
            //get link element
            var getLinks = function () {
                var icons = [];
                var links = _doc.getElementsByTagName('head')[0].getElementsByTagName('link');
                for (var i = 0; i < links.length; i++) {
                    if ((/(^|\s)icon(\s|$)/i).test(links[i].getAttribute('rel'))) {
                        icons.push(links[i]);
                    }
                }
                return icons;
            };
            if (_opt.element) {
                elms = [_opt.element];
            } else if (_opt.elementId) {
                //if img element identified by elementId
                elms = [_doc.getElementById(_opt.elementId)];
                elms[0].setAttribute('href', elms[0].getAttribute('src'));
            } else {
                //if link element
                elms = getLinks();
                if (elms.length === 0) {
                    elms = [_doc.createElement('link')];
                    elms[0].setAttribute('rel', 'icon');
                    _doc.getElementsByTagName('head')[0].appendChild(elms[0]);
                }
            }
            elms.forEach(function(item) {
                item.setAttribute('type', 'image/png');
            });
            return elms;
        };
        link.setIcon = function (canvas) {
            var url = canvas.toDataURL('image/png');
            link.setIconSrc(url);
        };
        link.setIconSrc = function (url) {
            if (_opt.dataUrl) {
                //if using custom exporter
                _opt.dataUrl(url);
            }
            if (_opt.element) {
                _opt.element.setAttribute('href', url);
                _opt.element.setAttribute('src', url);
            } else if (_opt.elementId) {
                //if is attached to element (image)
                var elm = _doc.getElementById(_opt.elementId);
                elm.setAttribute('href', url);
                elm.setAttribute('src', url);
            } else {
                //if is attached to fav icon
                if (_browser.ff || _browser.opera) {
                    //for FF we need to "recreate" element, atach to dom and remove old <link>
                    //var originalType = _orig.getAttribute('rel');
                    var old = _orig[_orig.length - 1];
                    var newIcon = _doc.createElement('link');
                    _orig = [newIcon];
                    //_orig.setAttribute('rel', originalType);
                    if (_browser.opera) {
                        newIcon.setAttribute('rel', 'icon');
                    }
                    newIcon.setAttribute('rel', 'icon');
                    newIcon.setAttribute('type', 'image/png');
                    _doc.getElementsByTagName('head')[0].appendChild(newIcon);
                    newIcon.setAttribute('href', url);
                    if (old.parentNode) {
                        old.parentNode.removeChild(old);
                    }
                } else {
                    _orig.forEach(function(icon) {
                        icon.setAttribute('href', url);
                    });
                }
            }
        };

        //http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb#answer-5624139
        //HEX to RGB convertor
        function hexToRgb(hex) {
            var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
            hex = hex.replace(shorthandRegex, function (m, r, g, b) {
                return r + r + g + g + b + b;
            });
            var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
            return result ? {
                r: parseInt(result[1], 16),
                g: parseInt(result[2], 16),
                b: parseInt(result[3], 16)
            } : false;
        }

        /**
         * Merge options
         */
        function merge(def, opt) {
            var mergedOpt = {};
            var attrname;
            for (attrname in def) {
                mergedOpt[attrname] = def[attrname];
            }
            for (attrname in opt) {
                mergedOpt[attrname] = opt[attrname];
            }
            return mergedOpt;
        }

        /**
         * Cross-browser page visibility shim
         * http://stackoverflow.com/questions/12536562/detect-whether-a-window-is-visible
         */
        function isPageHidden() {
            return _doc.hidden || _doc.msHidden || _doc.webkitHidden || _doc.mozHidden;
        }

        /**
         * @namespace animation
         */
        var animation = {};
        /**
         * Animation "frame" duration
         */
        animation.duration = 40;
        /**
         * Animation types (none,fade,pop,slide)
         */
        animation.types = {};
        animation.types.fade = [{
            x: 0.4,
            y: 0.4,
            w: 0.6,
            h: 0.6,
            o: 0.0
        }, {
                x: 0.4,
                y: 0.4,
                w: 0.6,
                h: 0.6,
                o: 0.1
            }, {
                x: 0.4,
                y: 0.4,
                w: 0.6,
                h: 0.6,
                o: 0.2
            }, {
                x: 0.4,
                y: 0.4,
                w: 0.6,
                h: 0.6,
                o: 0.3
            }, {
                x: 0.4,
                y: 0.4,
                w: 0.6,
                h: 0.6,
                o: 0.4
            }, {
                x: 0.4,
                y: 0.4,
                w: 0.6,
                h: 0.6,
                o: 0.5
            }, {
                x: 0.4,
                y: 0.4,
                w: 0.6,
                h: 0.6,
                o: 0.6
            }, {
                x: 0.4,
                y: 0.4,
                w: 0.6,
                h: 0.6,
                o: 0.7
            }, {
                x: 0.4,
                y: 0.4,
                w: 0.6,
                h: 0.6,
                o: 0.8
            }, {
                x: 0.4,
                y: 0.4,
                w: 0.6,
                h: 0.6,
                o: 0.9
            }, {
                x: 0.4,
                y: 0.4,
                w: 0.6,
                h: 0.6,
                o: 1.0
            }];
        animation.types.none = [{
            x: 0.4,
            y: 0.4,
            w: 0.6,
            h: 0.6,
            o: 1
        }];
        animation.types.pop = [{
            x: 1,
            y: 1,
            w: 0,
            h: 0,
            o: 1
        }, {
                x: 0.9,
                y: 0.9,
                w: 0.1,
                h: 0.1,
                o: 1
            }, {
                x: 0.8,
                y: 0.8,
                w: 0.2,
                h: 0.2,
                o: 1
            }, {
                x: 0.7,
                y: 0.7,
                w: 0.3,
                h: 0.3,
                o: 1
            }, {
                x: 0.6,
                y: 0.6,
                w: 0.4,
                h: 0.4,
                o: 1
            }, {
                x: 0.5,
                y: 0.5,
                w: 0.5,
                h: 0.5,
                o: 1
            }, {
                x: 0.4,
                y: 0.4,
                w: 0.6,
                h: 0.6,
                o: 1
            }];
        animation.types.popFade = [{
            x: 0.75,
            y: 0.75,
            w: 0,
            h: 0,
            o: 0
        }, {
                x: 0.65,
                y: 0.65,
                w: 0.1,
                h: 0.1,
                o: 0.2
            }, {
                x: 0.6,
                y: 0.6,
                w: 0.2,
                h: 0.2,
                o: 0.4
            }, {
                x: 0.55,
                y: 0.55,
                w: 0.3,
                h: 0.3,
                o: 0.6
            }, {
                x: 0.50,
                y: 0.50,
                w: 0.4,
                h: 0.4,
                o: 0.8
            }, {
                x: 0.45,
                y: 0.45,
                w: 0.5,
                h: 0.5,
                o: 0.9
            }, {
                x: 0.4,
                y: 0.4,
                w: 0.6,
                h: 0.6,
                o: 1
            }];
        animation.types.slide = [{
            x: 0.4,
            y: 1,
            w: 0.6,
            h: 0.6,
            o: 1
        }, {
                x: 0.4,
                y: 0.9,
                w: 0.6,
                h: 0.6,
                o: 1
            }, {
                x: 0.4,
                y: 0.9,
                w: 0.6,
                h: 0.6,
                o: 1
            }, {
                x: 0.4,
                y: 0.8,
                w: 0.6,
                h: 0.6,
                o: 1
            }, {
                x: 0.4,
                y: 0.7,
                w: 0.6,
                h: 0.6,
                o: 1
            }, {
                x: 0.4,
                y: 0.6,
                w: 0.6,
                h: 0.6,
                o: 1
            }, {
                x: 0.4,
                y: 0.5,
                w: 0.6,
                h: 0.6,
                o: 1
            }, {
                x: 0.4,
                y: 0.4,
                w: 0.6,
                h: 0.6,
                o: 1
            }];
        /**
         * Run animation
         * @param {Object} opt Animation options
         * @param {Object} cb Callabak after all steps are done
         * @param {Object} revert Reverse order? true|false
         * @param {Object} step Optional step number (frame bumber)
         */
        animation.run = function (opt, cb, revert, step) {
            var animationType = animation.types[isPageHidden() ? 'none' : _opt.animation];
            if (revert === true) {
                step = (typeof step !== 'undefined') ? step : animationType.length - 1;
            } else {
                step = (typeof step !== 'undefined') ? step : 0;
            }
            cb = (cb) ? cb : function () {
            };
            if ((step < animationType.length) && (step >= 0)) {
                type[_opt.type](merge(opt, animationType[step]));
                _animTimeout = setTimeout(function () {
                    if (revert) {
                        step = step - 1;
                    } else {
                        step = step + 1;
                    }
                    animation.run(opt, cb, revert, step);
                }, animation.duration);

                link.setIcon(_canvas);
            } else {
                cb();
                return;
            }
        };
        //auto init
        init();
        return {
            badge: badge,
            video: video,
            image: image,
            rawImageSrc: rawImageSrc,
            webcam: webcam,
            setOpt: setOpt,
            reset: icon.reset,
            browser: {
                supported: _browser.supported
            }
        };
    });

    // AMD / RequireJS
    if (typeof define !== 'undefined' && define.amd) {
        define([], function () {
            return Favico;
        });
    }
    // CommonJS
    else if (typeof module !== 'undefined' && module.exports) {
        module.exports = Favico;
    }
    // included directly via <script> tag
    else {
        this.Favico = Favico;
    }

})();