/*!
 * Tinycon - A small library for manipulating the Favicon
 * Tom Moor, http://tommoor.com
 * Copyright (c) 2012 Tom Moor
 * MIT Licensed
 * @version 0.2.5
*/

(function(){
	
	var Tinycon = {};
	var currentFavicon = null;
	var originalFavicon = null;
	var originalTitle = document.title;
	var faviconImage = null;
	var canvas = null;
	var options = {};
	var defaults = {
		width: 7,
		height: 9,
		font: '10px arial',
		colour: '#ffffff',
		background: '#F03D25',
		fallback: true
	};
	
	var ua = (function () {
		var agent = navigator.userAgent.toLowerCase();
		// New function has access to 'agent' via closure
		return function (browser) {
			return agent.indexOf(browser) !== -1;
		};
	}());

	var browser = {
		chrome: ua('chrome'),
		webkit: ua('chrome') || ua('safari'),
		safari: ua('safari') && !ua('chrome'),
		mozilla: ua('mozilla') && !ua('chrome') && !ua('safari')
	};
	
	// private methods
	var getFaviconTag = function(){
		
		var links = document.getElementsByTagName('link');
		
		for(var i=0, len=links.length; i < len; i++) {
			if ((links[i].getAttribute('rel') || '').match(/\bicon\b/)) {
				return links[i];
			}
		}
		
		return false;
	};
	
	var removeFaviconTag = function(){
	
		var links = document.getElementsByTagName('link');
		var head = document.getElementsByTagName('head')[0];
		
		for(var i=0, len=links.length; i < len; i++) {
			var exists = (typeof(links[i]) !== 'undefined');
			if (exists && links[i].getAttribute('rel') === 'icon') {
				head.removeChild(links[i]);
			}
		}
	};
	
	var getCurrentFavicon = function(){
		
		if (!originalFavicon || !currentFavicon) {
			var tag = getFaviconTag();
			originalFavicon = currentFavicon = tag ? tag.getAttribute('href') : '/favicon.ico';
		}

		return currentFavicon;
	};
	
	var getCanvas = function (){
		
		if (!canvas) {
			canvas = document.createElement("canvas");
			canvas.width = 16;
			canvas.height = 16;
		}
		
		return canvas;
	};
	
	var setFaviconTag = function(url){
		removeFaviconTag();
		
		var link = document.createElement('link');
		link.type = 'image/x-icon';
		link.rel = 'icon';
		link.href = url;
		document.getElementsByTagName('head')[0].appendChild(link);
	};
	
	var log = function(message){
		if (window.console) window.console.log(message);
	};
	
	var drawFavicon = function(num, colour) {

		// fallback to updating the browser title if unsupported
		if (!getCanvas().getContext || browser.safari || options.fallback === 'force') {
			return updateTitle(num);
		}
		
		var context = getCanvas().getContext("2d");
		var colour = colour || '#000000';
		var num = num || 0;
		var src = getCurrentFavicon();
		
		faviconImage = new Image();
		faviconImage.onload = function() {
			
			// clear canvas  
			context.clearRect(0, 0, 16, 16);

			// draw original favicon
			context.drawImage(faviconImage, 0, 0, faviconImage.width, faviconImage.height, 0, 0, 16, 16);
			
			// draw bubble over the top
			if (num > 0) drawBubble(context, num, colour);
			
			// refresh tag in page
			refreshFavicon();
		};
		
		// allow cross origin resource requests if the image is not a data:uri
		// as detailed here: https://github.com/mrdoob/three.js/issues/1305
		if (!src.match(/^data/)) {
			faviconImage.crossOrigin = 'anonymous';
		}
		
		faviconImage.src = src;
	};
	
	var updateTitle = function(num) {
		
		if (options.fallback) {
			if (num > 0) {
				document.title = '('+num+') ' + originalTitle;
			} else {
				document.title = originalTitle;
			}
		}
	};
	
	var drawBubble = function(context, num, colour) {
		
		// bubble needs to be larger for double digits
		var len = (num+"").length-1;
		var width = options.width + (6*len);
		var w = 16-width;
		var h = 16-options.height;

		// webkit seems to render fonts lighter than firefox
		context.font = (browser.webkit ? 'bold ' : '') + options.font;
		context.fillStyle = options.background;
		context.strokeStyle = options.background;
		context.lineWidth = 1;
		
		// bubble
		context.fillRect(w,h,width-1,options.height);
		
		// rounded left
		context.beginPath();
		context.moveTo(w-0.5,h+1);
		context.lineTo(w-0.5,15);
		context.stroke();
		
		// rounded right
		context.beginPath();
		context.moveTo(15.5,h+1);
		context.lineTo(15.5,15);
		context.stroke();
		
		// bottom shadow
		context.beginPath();
		context.strokeStyle = "rgba(0,0,0,0.3)";
		context.moveTo(w,16);
		context.lineTo(15,16);
		context.stroke();
		
		// number
		context.fillStyle = options.colour;
		context.textAlign = "right";
		context.textBaseline = "top";
		
		// unfortunately webkit/mozilla are a pixel different in text positioning
		context.fillText(num, 15, browser.mozilla ? 7 : 6);  
	};
	
	var refreshFavicon = function(){
		// check support
		if (!getCanvas().getContext) return;
		
		setFaviconTag(getCanvas().toDataURL());
	};
	
	
	// public methods
	Tinycon.setOptions = function(custom){
		options = {};
		
		for(var i in defaults){
			options[i] = custom[i] ? custom[i] : defaults[i];
		}
		return this;
	};
	
	Tinycon.setImage = function(url){
		currentFavicon = url;
		refreshFavicon();
		return this;
	};
	
	Tinycon.setBubble = function(num, colour){
		
		// validate
		if(isNaN(parseFloat(num)) || !isFinite(num)) return log('Bubble must be a number');
		
		drawFavicon(num, colour);
		return this;
	};
	
	Tinycon.reset = function(){
		Tinycon.setImage(originalFavicon);
	};
	
	Tinycon.setOptions(defaults);
	window.Tinycon = Tinycon;
})();

@@ -4872,17 +4872,19 @@
 for(var 
-i
+key
  in defa
@@ -4901,17 +4901,19 @@
 options%5B
-i
+key
 %5D = cust
@@ -4914,19 +4914,36 @@
 = custom
-%5Bi%5D
+.hasOwnProperty(key)
  ? custo
@@ -4944,17 +4944,19 @@
  custom%5B
-i
+key
 %5D : defa
@@ -4960,17 +4960,19 @@
 efaults%5B
-i
+key
 %5D;%0A%09%09%7D%0A%09

Fix bug where setOptions method ignored falsey properties on provided options object.