Add enquire.js and favico.js as stand alone files
authorTim Düsterhus <duesterhus@woltlab.com>
Sun, 3 May 2015 21:39:37 +0000 (23:39 +0200)
committerTim Düsterhus <duesterhus@woltlab.com>
Sun, 3 May 2015 21:39:37 +0000 (23:39 +0200)
wcfsetup/install/files/js/3rdParty/enquire.js [new file with mode: 0644]
wcfsetup/install/files/js/3rdParty/favico.js [new file with mode: 0644]

diff --git a/wcfsetup/install/files/js/3rdParty/enquire.js b/wcfsetup/install/files/js/3rdParty/enquire.js
new file mode 100644 (file)
index 0000000..0a80eeb
--- /dev/null
@@ -0,0 +1,296 @@
+/*!
+ * enquire.js v2.1.2 - Awesome Media Queries in JavaScript
+ * Copyright (c) 2014 Nick Williams - http://wicky.nillia.ms/enquire.js
+ * License: MIT (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+;(function (name, context, factory) {
+       var matchMedia = window.matchMedia;
+
+       if (typeof module !== 'undefined' && module.exports) {
+               module.exports = factory(matchMedia);
+       }
+       else if (typeof define === 'function' && define.amd) {
+               define(function() {
+                       return (context[name] = factory(matchMedia));
+               });
+       }
+       else {
+               context[name] = factory(matchMedia);
+       }
+}('enquire', this, function (matchMedia) {
+
+       'use strict';
+
+    /*jshint unused:false */
+    /**
+     * Helper function for iterating over a collection
+     *
+     * @param collection
+     * @param fn
+     */
+    function each(collection, fn) {
+        var i      = 0,
+            length = collection.length,
+            cont;
+
+        for(i; i < length; i++) {
+            cont = fn(collection[i], i);
+            if(cont === false) {
+                break; //allow early exit
+            }
+        }
+    }
+
+    /**
+     * Helper function for determining whether target object is an array
+     *
+     * @param target the object under test
+     * @return {Boolean} true if array, false otherwise
+     */
+    function isArray(target) {
+        return Object.prototype.toString.apply(target) === '[object Array]';
+    }
+
+    /**
+     * Helper function for determining whether target object is a function
+     *
+     * @param target the object under test
+     * @return {Boolean} true if function, false otherwise
+     */
+    function isFunction(target) {
+        return typeof target === 'function';
+    }
+
+    /**
+     * Delegate to handle a media query being matched and unmatched.
+     *
+     * @param {object} options
+     * @param {function} options.match callback for when the media query is matched
+     * @param {function} [options.unmatch] callback for when the media query is unmatched
+     * @param {function} [options.setup] one-time callback triggered the first time a query is matched
+     * @param {boolean} [options.deferSetup=false] should the setup callback be run immediately, rather than first time query is matched?
+     * @constructor
+     */
+    function QueryHandler(options) {
+        this.options = options;
+        !options.deferSetup && this.setup();
+    }
+    QueryHandler.prototype = {
+
+        /**
+         * coordinates setup of the handler
+         *
+         * @function
+         */
+        setup : function() {
+            if(this.options.setup) {
+                this.options.setup();
+            }
+            this.initialised = true;
+        },
+
+        /**
+         * coordinates setup and triggering of the handler
+         *
+         * @function
+         */
+        on : function() {
+            !this.initialised && this.setup();
+            this.options.match && this.options.match();
+        },
+
+        /**
+         * coordinates the unmatch event for the handler
+         *
+         * @function
+         */
+        off : function() {
+            this.options.unmatch && this.options.unmatch();
+        },
+
+        /**
+         * called when a handler is to be destroyed.
+         * delegates to the destroy or unmatch callbacks, depending on availability.
+         *
+         * @function
+         */
+        destroy : function() {
+            this.options.destroy ? this.options.destroy() : this.off();
+        },
+
+        /**
+         * determines equality by reference.
+         * if object is supplied compare options, if function, compare match callback
+         *
+         * @function
+         * @param {object || function} [target] the target for comparison
+         */
+        equals : function(target) {
+            return this.options === target || this.options.match === target;
+        }
+
+    };
+    /**
+     * Represents a single media query, manages it's state and registered handlers for this query
+     *
+     * @constructor
+     * @param {string} query the media query string
+     * @param {boolean} [isUnconditional=false] whether the media query should run regardless of whether the conditions are met. Primarily for helping older browsers deal with mobile-first design
+     */
+    function MediaQuery(query, isUnconditional) {
+        this.query = query;
+        this.isUnconditional = isUnconditional;
+        this.handlers = [];
+        this.mql = matchMedia(query);
+
+        var self = this;
+        this.listener = function(mql) {
+            self.mql = mql;
+            self.assess();
+        };
+        this.mql.addListener(this.listener);
+    }
+    MediaQuery.prototype = {
+
+        /**
+         * add a handler for this query, triggering if already active
+         *
+         * @param {object} handler
+         * @param {function} handler.match callback for when query is activated
+         * @param {function} [handler.unmatch] callback for when query is deactivated
+         * @param {function} [handler.setup] callback for immediate execution when a query handler is registered
+         * @param {boolean} [handler.deferSetup=false] should the setup callback be deferred until the first time the handler is matched?
+         */
+        addHandler : function(handler) {
+            var qh = new QueryHandler(handler);
+            this.handlers.push(qh);
+
+            this.matches() && qh.on();
+        },
+
+        /**
+         * removes the given handler from the collection, and calls it's destroy methods
+         * 
+         * @param {object || function} handler the handler to remove
+         */
+        removeHandler : function(handler) {
+            var handlers = this.handlers;
+            each(handlers, function(h, i) {
+                if(h.equals(handler)) {
+                    h.destroy();
+                    return !handlers.splice(i,1); //remove from array and exit each early
+                }
+            });
+        },
+
+        /**
+         * Determine whether the media query should be considered a match
+         * 
+         * @return {Boolean} true if media query can be considered a match, false otherwise
+         */
+        matches : function() {
+            return this.mql.matches || this.isUnconditional;
+        },
+
+        /**
+         * Clears all handlers and unbinds events
+         */
+        clear : function() {
+            each(this.handlers, function(handler) {
+                handler.destroy();
+            });
+            this.mql.removeListener(this.listener);
+            this.handlers.length = 0; //clear array
+        },
+
+        /*
+         * Assesses the query, turning on all handlers if it matches, turning them off if it doesn't match
+         */
+        assess : function() {
+            var action = this.matches() ? 'on' : 'off';
+
+            each(this.handlers, function(handler) {
+                handler[action]();
+            });
+        }
+    };
+    /**
+     * Allows for registration of query handlers.
+     * Manages the query handler's state and is responsible for wiring up browser events
+     *
+     * @constructor
+     */
+    function MediaQueryDispatch () {
+        if(!matchMedia) {
+            throw new Error('matchMedia not present, legacy browsers require a polyfill');
+        }
+
+        this.queries = {};
+        this.browserIsIncapable = !matchMedia('only all').matches;
+    }
+
+    MediaQueryDispatch.prototype = {
+
+        /**
+         * Registers a handler for the given media query
+         *
+         * @param {string} q the media query
+         * @param {object || Array || Function} options either a single query handler object, a function, or an array of query handlers
+         * @param {function} options.match fired when query matched
+         * @param {function} [options.unmatch] fired when a query is no longer matched
+         * @param {function} [options.setup] fired when handler first triggered
+         * @param {boolean} [options.deferSetup=false] whether setup should be run immediately or deferred until query is first matched
+         * @param {boolean} [shouldDegrade=false] whether this particular media query should always run on incapable browsers
+         */
+        register : function(q, options, shouldDegrade) {
+            var queries         = this.queries,
+                isUnconditional = shouldDegrade && this.browserIsIncapable;
+
+            if(!queries[q]) {
+                queries[q] = new MediaQuery(q, isUnconditional);
+            }
+
+            //normalise to object in an array
+            if(isFunction(options)) {
+                options = { match : options };
+            }
+            if(!isArray(options)) {
+                options = [options];
+            }
+            each(options, function(handler) {
+                if (isFunction(handler)) {
+                    handler = { match : handler };
+                }
+                queries[q].addHandler(handler);
+            });
+
+            return this;
+        },
+
+        /**
+         * unregisters a query and all it's handlers, or a specific handler for a query
+         *
+         * @param {string} q the media query to target
+         * @param {object || function} [handler] specific handler to unregister
+         */
+        unregister : function(q, handler) {
+            var query = this.queries[q];
+
+            if(query) {
+                if(handler) {
+                    query.removeHandler(handler);
+                }
+                else {
+                    query.clear();
+                    delete this.queries[q];
+                }
+            }
+
+            return this;
+        }
+    };
+
+       return new MediaQueryDispatch();
+
+}));
\ No newline at end of file
diff --git a/wcfsetup/install/files/js/3rdParty/favico.js b/wcfsetup/install/files/js/3rdParty/favico.js
new file mode 100644 (file)
index 0000000..e876151
--- /dev/null
@@ -0,0 +1,843 @@
+/**
+ * @license MIT
+ * @fileOverview Favico animations
+ * @author Miroslav Magda, http://blog.ejci.net
+ * @version 0.3.6
+ */
+
+/**
+ * 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',
+ *    position : 'down',
+ *    type : 'circle',
+ *    animation : 'slide',
+ *    dataUrl: function(url){}
+ * });
+ */
+(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,
+                       dataUrl : false
+               };
+               var _opt, _orig, _h, _w, _canvas, _context, _img, _ready, _lastBadge, _running, _readyCb, _stop, _browser, _animTimeout, _drawTimeout;
+
+               _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;
+
+                       var isUp = _opt.position.indexOf('up') > -1;
+                       var isLeft = _opt.position.indexOf('left') > -1;
+
+                       //transform animation
+                       if (isUp || isLeft) {
+                               for (var i = 0; i < animation.types['' + _opt.animation].length; i++) {
+                                       var step = animation.types['' + _opt.animation][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['' + _opt.animation][i] = step;
+                               }
+                       }
+                       _opt.type = (type['' + _opt.type]) ? _opt.type : _def.type;
+
+                       _orig = link.getIcon();
+                       //create temp canvas
+                       _canvas = document.createElement('canvas');
+                       //create temp image
+                       _img = document.createElement('img');
+                       if (_orig.hasAttribute('href')) {
+                               _img.setAttribute('src', _orig.getAttribute('href'));
+                               //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();
+                               };
+                       } else {
+                               _img.setAttribute('src', '');
+                               _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;
+                       }
+                       _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 'Too many badges requests in queue.';
+                                               }
+                                               icon.start();
+                                       } else {
+                                               icon.reset();
+                                       }
+                               } catch(e) {
+                                       throw '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('src', imageElement.getAttribute('src'));
+                                       newImg.height = (h / ratio);
+                                       newImg.width = (w / ratio);
+                                       _context.clearRect(0, 0, _w, _h);
+                                       _context.drawImage(newImg, 0, 0, _w, _h);
+                                       link.setIcon(_canvas);
+                               } catch(e) {
+                                       throw 'Error setting image. Message: ' + e.message;
+                               }
+                       };
+                       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 '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 'Error setting webcam. Message: ' + e.message;
+                                       }
+                               };
+                               if (_ready) {
+                                       _readyCb();
+                               }
+                       }
+
+               };
+
+               /**
+                * 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(drawVideo, animation.duration, video);
+                       link.setIcon(_canvas);
+               }
+
+               var link = {};
+               /**
+                * Get icon from HEAD tag or create a new <link> element
+                */
+               link.getIcon = function() {
+                       var elm = false;
+                       //get link element
+                       var getLink = function() {
+                               var link = document.getElementsByTagName('head')[0].getElementsByTagName('link');
+                               for (var l = link.length, i = (l - 1); i >= 0; i--) {
+                                       if ((/(^|\s)icon(\s|$)/i).test(link[i].getAttribute('rel'))) {
+                                               return link[i];
+                                       }
+                               }
+                               return false;
+                       };
+                       if (_opt.element) {
+                               elm = _opt.element;
+                       } else if (_opt.elementId) {
+                               //if img element identified by elementId
+                               elm = document.getElementById(_opt.elementId);
+                               elm.setAttribute('href', elm.getAttribute('src'));
+                       } else {
+                               //if link element
+                               elm = getLink();
+                               if (elm === false) {
+                                       elm = document.createElement('link');
+                                       elm.setAttribute('rel', 'icon');
+                                       document.getElementsByTagName('head')[0].appendChild(elm);
+                               }
+                       }
+                       elm.setAttribute('type', 'image/png');
+                       return elm;
+               };
+               link.setIcon = function(canvas) {
+                       var url = canvas.toDataURL('image/png');
+                       if (_opt.dataUrl) {
+                               //if using custom exporter
+                               _opt.dataUrl(url);
+                       }
+                       if (_opt.element) {
+                               _opt.element.setAttribute('src', url);
+                       } else if (_opt.elementId) {
+                               //if is attached to element (image)
+                               document.getElementById(_opt.elementId).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 = document.createElement('link');
+                                       //_orig.setAttribute('rel', originalType);
+                                       if (_browser.opera) {
+                                               _orig.setAttribute('rel', 'icon');
+                                       }
+                                       _orig.setAttribute('rel', 'icon');
+                                       _orig.setAttribute('type', 'image/png');
+                                       document.getElementsByTagName('head')[0].appendChild(_orig);
+                                       _orig.setAttribute('href', url);
+                                       if (old.parentNode) {
+                                               old.parentNode.removeChild(old);
+                                       }
+                               } else {
+                                       _orig.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 document.hidden || document.msHidden || document.webkitHidden || document.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,
+                       webcam : webcam,
+                       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;
+       }
+
+})();
+