text: (function () {
  
  	// Scroll Variables (tweakable)
  	var defaultOptions = {
  
  		// Scrolling Core
  		frameRate: 300, // [Hz]
  		animationTime: 600, // [ms]
  		stepSize: 100, // [px]
  
  		// Pulse (less tweakable)
  		// ratio of "tail" to "acceleration"
  		pulseAlgorithm: true,
  		pulseScale: 4,
  		pulseNormalize: 1,
  
  		// Acceleration
  		accelerationDelta: 50, // 50
  		accelerationMax: 3, // 3
  
  		// Keyboard Settings
  		keyboardSupport: true, // option
  		arrowScroll: 50, // [px]
  
  		// Other
  		touchpadSupport: false, // ignore touchpad by default
  		fixedBackground: true,
  		excluded: ''
  	};
  
  	var options = defaultOptions;
  
  
  
  	// Other Variables
  	var isExcluded = false;
  	var isFrame = false;
  	var direction = {
  		x: 0,
  		y: 0
  	};
  	var initDone = false;
  	var root = document.documentElement;
  	var activeElement;
  	var observer;
  	var deltaBuffer = [120, 120, 120];
  
  	var key = {
  		left: 37,
  		up: 38,
  		right: 39,
  		down: 40,
  		spacebar: 32,
  		pageup: 33,
  		pagedown: 34,
  		end: 35,
  		home: 36
  	};
  
  
  	/***********************************************
  	 * SETTINGS
  	 ***********************************************/
  
  	var options = defaultOptions;
  
  
  	/***********************************************
  	 * INITIALIZE
  	 ***********************************************/
  
  	/**
  	 * Tests if smooth scrolling is allowed. Shuts down everything if not.
  	 */
  	function initTest() {
  
  		var disableKeyboard = false;
  
  		// disable keyboard support if anything above requested it
  		if (disableKeyboard) {
  			removeEvent("keydown", keydown);
  		}
  
  		if (options.keyboardSupport && !disableKeyboard) {
  			addEvent("keydown", keydown);
  		}
  	}
  
  	/**
  	 * Sets up scrolls array, determines if frames are involved.
  	 */
  	function init() {
  
  		if (!document.body) return;
  
  		var body = document.body;
  		var html = document.documentElement;
  		var windowHeight = window.innerHeight;
  		var scrollHeight = body.scrollHeight;
  
  		// check compat mode for root element
  		root = (document.compatMode.indexOf('CSS') >= 0) ? html : body;
  		activeElement = body;
  
  		initTest();
  		initDone = true;
  
  		// Checks if this script is running in a frame
  		if (top != self) {
  			isFrame = true;
  		}
  
  		/**
  		 * This fixes a bug where the areas left and right to
  		 * the content does not trigger the onmousewheel event
  		 * on some pages. e.g.: html, body { height: 100% }
  		 */
  		else if (scrollHeight > windowHeight &&
  			(body.offsetHeight <= windowHeight ||
  				html.offsetHeight <= windowHeight)) {
  
  			// DOMChange (throttle): fix height
  			var pending = false;
  			var refresh = function () {
  				if (!pending && html.scrollHeight != document.height) {
  					pending = true; // add a new pending action
  					setTimeout(function () {
  						html.style.height = document.height + 'px';
  						pending = false;
  					}, 500); // act rarely to stay fast
  				}
  			};
  			html.style.height = 'auto';
  			setTimeout(refresh, 10);
  
  			// clearfix
  			if (root.offsetHeight <= windowHeight) {
  				var underlay = document.createElement("div");
  				underlay.style.clear = "both";
  				body.appendChild(underlay);
  			}
  		}
  
  		// disable fixed background
  		if (!options.fixedBackground && !isExcluded) {
  			body.style.backgroundAttachment = "scroll";
  			html.style.backgroundAttachment = "scroll";
  		}
  	}
  
  
  	/************************************************
  	 * SCROLLING
  	 ************************************************/
  
  	var que = [];
  	var pending = false;
  	var lastScroll = +new Date;
  
  	/**
  	 * Pushes scroll actions to the scrolling queue.
  	 */
  	function scrollArray(elem, left, top, delay) {
  
  		delay || (delay = 1000);
  		directionCheck(left, top);
  
  		if (options.accelerationMax != 1) {
  			var now = +new Date;
  			var elapsed = now - lastScroll;
  			if (elapsed < options.accelerationDelta) {
  				var factor = (1 + (30 / elapsed)) / 2;
  				if (factor > 1) {
  					factor = Math.min(factor, options.accelerationMax);
  					left *= factor;
  					top *= factor;
  				}
  			}
  			lastScroll = +new Date;
  		}
  
  		// push a scroll command
  		que.push({
  			x: left,
  			y: top,
  			lastX: (left < 0) ? 0.99 : -0.99,
  			lastY: (top < 0) ? 0.99 : -0.99,
  			start: +new Date
  		});
  
  		// don't act if there's a pending queue
  		if (pending) {
  			return;
  		}
  
  		var scrollWindow = (elem === document.body);
  
  		var step = function (time) {
  
  			var now = +new Date;
  			var scrollX = 0;
  			var scrollY = 0;
  
  			for (var i = 0; i < que.length; i++) {
  
  				var item = que[i];
  				var elapsed = now - item.start;
  				var finished = (elapsed >= options.animationTime);
  
  				// scroll position: [0, 1]
  				var position = (finished) ? 1 : elapsed / options.animationTime;
  
  				// easing [optional]
  				if (options.pulseAlgorithm) {
  					position = pulse(position);
  				}
  
  				// only need the difference
  				var x = (item.x * position - item.lastX) >> 0;
  				var y = (item.y * position - item.lastY) >> 0;
  
  				// add this to the total scrolling
  				scrollX += x;
  				scrollY += y;
  
  				// update last values
  				item.lastX += x;
  				item.lastY += y;
  
  				// delete and step back if it's over
  				if (finished) {
  					que.splice(i, 1);
  					i--;
  				}
  			}
  
  			// scroll left and top
  			if (scrollWindow) {
  				window.scrollBy(scrollX, scrollY);
  			} else {
  				if (scrollX) elem.scrollLeft += scrollX;
  				if (scrollY) elem.scrollTop += scrollY;
  			}
  
  			// clean up if there's nothing left to do
  			if (!left && !top) {
  				que = [];
  			}
  
  			if (que.length) {
  				requestFrame(step, elem, (delay / options.frameRate + 1));
  			} else {
  				pending = false;
  			}
  		};
  
  		// start a new queue of actions
  		requestFrame(step, elem, 0);
  		pending = true;
  	}
  
  
  	/***********************************************
  	 * EVENTS
  	 ***********************************************/
  
  	/**
  	 * Mouse wheel handler.
  	 * @param {Object} event
  	 */
  	function wheel(event) {
  
  		if (!initDone) {
  			init();
  		}
  
  		var target = event.target;
  		var overflowing = overflowingAncestor(target);
  
  		// use default if there's no overflowing
  		// element or default action is prevented    
  		if (!overflowing || event.defaultPrevented ||
  			isNodeName(activeElement, "embed") ||
  			(isNodeName(target, "embed") && /\.pdf/i.test(target.src))) {
  			return true;
  		}
  
  		var deltaX = event.wheelDeltaX || 0;
  		var deltaY = event.wheelDeltaY || 0;
  
  		// use wheelDelta if deltaX/Y is not available
  		if (!deltaX && !deltaY) {
  			deltaY = event.wheelDelta || 0;
  		}
  
  		// check if it's a touchpad scroll that should be ignored
  		if (!options.touchpadSupport && isTouchpad(deltaY)) {
  			return true;
  		}
  
  		// scale by step size
  		// delta is 120 most of the time
  		// synaptics seems to send 1 sometimes
  		if (Math.abs(deltaX) > 1.2) {
  			deltaX *= options.stepSize / 120;
  		}
  		if (Math.abs(deltaY) > 1.2) {
  			deltaY *= options.stepSize / 120;
  		}
  
  		scrollArray(overflowing, -deltaX, -deltaY);
  		event.preventDefault();
  	}
  
  	/**
  	 * Keydown event handler.
  	 * @param {Object} event
  	 */
  	function keydown(event) {
  
  		var target = event.target;
  		var modifier = event.ctrlKey || event.altKey || event.metaKey ||
  			(event.shiftKey && event.keyCode !== key.spacebar);
  
  		// do nothing if user is editing text
  		// or using a modifier key (except shift)
  		// or in a dropdown
  		if (/input|textarea|select|embed/i.test(target.nodeName) ||
  			target.isContentEditable ||
  			event.defaultPrevented ||
  			modifier) {
  			return true;
  		}
  		// spacebar should trigger button press
  		if (isNodeName(target, "button") &&
  			event.keyCode === key.spacebar) {
  			return true;
  		}
  
  		var shift, x = 0,
  			y = 0;
  		var elem = overflowingAncestor(activeElement);
  		var clientHeight = elem.clientHeight;
  
  		if (elem == document.body) {
  			clientHeight = window.innerHeight;
  		}
  
  		switch (event.keyCode) {
  		case key.up:
  			y = -options.arrowScroll;
  			break;
  		case key.down:
  			y = options.arrowScroll;
  			break;
  		case key.spacebar: // (+ shift)
  			shift = event.shiftKey ? 1 : -1;
  			y = -shift * clientHeight * 0.9;
  			break;
  		case key.pageup:
  			y = -clientHeight * 0.9;
  			break;
  		case key.pagedown:
  			y = clientHeight * 0.9;
  			break;
  		case key.home:
  			y = -elem.scrollTop;
  			break;
  		case key.end:
  			var damt = elem.scrollHeight - elem.scrollTop - clientHeight;
  			y = (damt > 0) ? damt + 10 : 0;
  			break;
  		case key.left:
  			x = -options.arrowScroll;
  			break;
  		case key.right:
  			x = options.arrowScroll;
  			break;
  		default:
  			return true; // a key we don't care about
  		}
  
  		scrollArray(elem, x, y);
  		event.preventDefault();
  	}
  
  	/**
  	 * Mousedown event only for updating activeElement
  	 */
  	function mousedown(event) {
  		activeElement = event.target;
  	}
  
  
  	/***********************************************
  	 * OVERFLOW
  	 ***********************************************/
  
  	var cache = {}; // cleared out every once in while
  	setInterval(function () {
  		cache = {};
  	}, 10 * 1000);
  
  	var uniqueID = (function () {
  		var i = 0;
  		return function (el) {
  			return el.uniqueID || (el.uniqueID = i++);
  		};
  	})();
  
  	function setCache(elems, overflowing) {
  		for (var i = elems.length; i--;)
  			cache[uniqueID(elems[i])] = overflowing;
  		return overflowing;
  	}
  
  	function overflowingAncestor(el) {
  		var elems = [];
  		var rootScrollHeight = root.scrollHeight;
  		do {
  			var cached = cache[uniqueID(el)];
  			if (cached) {
  				return setCache(elems, cached);
  			}
  			elems.push(el);
  			if (rootScrollHeight === el.scrollHeight) {
  				if (!isFrame || root.clientHeight + 10 < rootScrollHeight) {
  					return setCache(elems, document.body); // scrolling root in WebKit
  				}
  			} else if (el.clientHeight + 10 < el.scrollHeight) {
  				overflow = getComputedStyle(el, "").getPropertyValue("overflow-y");
  				if (overflow === "scroll" || overflow === "auto") {
  					return setCache(elems, el);
  				}
  			}
  		} while (el = el.parentNode);
  	}
  
  
  	/***********************************************
  	 * HELPERS
  	 ***********************************************/
  
  	function addEvent(type, fn, bubble) {
  		window.addEventListener(type, fn, (bubble || false));
  	}
  
  	function removeEvent(type, fn, bubble) {
  		window.removeEventListener(type, fn, (bubble || false));
  	}
  
  	function isNodeName(el, tag) {
  		return (el.nodeName || "").toLowerCase() === tag.toLowerCase();
  	}
  
  	function directionCheck(x, y) {
  		x = (x > 0) ? 1 : -1;
  		y = (y > 0) ? 1 : -1;
  		if (direction.x !== x || direction.y !== y) {
  			direction.x = x;
  			direction.y = y;
  			que = [];
  			lastScroll = 0;
  		}
  	}
  
  	var deltaBufferTimer;
  
  	function isTouchpad(deltaY) {
  		if (!deltaY) return;
  		deltaY = Math.abs(deltaY)
  		deltaBuffer.push(deltaY);
  		deltaBuffer.shift();
  		clearTimeout(deltaBufferTimer);
  		var allDivisable = (isDivisible(deltaBuffer[0], 120) &&
  			isDivisible(deltaBuffer[1], 120) &&
  			isDivisible(deltaBuffer[2], 120));
  		return !allDivisable;
  	}
  
  	function isDivisible(n, divisor) {
  		return (Math.floor(n / divisor) == n / divisor);
  	}
  
  	var requestFrame = (function () {
  		return window.requestAnimationFrame ||
  			window.webkitRequestAnimationFrame ||
  			function (callback, element, delay) {
  				window.setTimeout(callback, delay || (1000 / 60));
  		};
  	})();
  
  
  	/***********************************************
  	 * PULSE
  	 ***********************************************/
  
  	/**
  	 * Viscous fluid with a pulse for part and decay for the rest.
  	 * - Applies a fixed force over an interval (a damped acceleration), and
  	 * - Lets the exponential bleed away the velocity over a longer interval
  	 * - Michael Herf, http://stereopsis.com/stopping/
  	 */
  	function pulse_(x) {
  		var val, start, expx;
  		// test
  		x = x * options.pulseScale;
  		if (x < 1) { // acceleartion
  			val = x - (1 - Math.exp(-x));
  		} else { // tail
  			// the previous animation ended here:
  			start = Math.exp(-1);
  			// simple viscous drag
  			x -= 1;
  			expx = 1 - Math.exp(-x);
  			val = start + (expx * (1 - start));
  		}
  		return val * options.pulseNormalize;
  	}
  
  	function pulse(x) {
  		if (x >= 1) return 1;
  		if (x <= 0) return 0;
  
  		if (options.pulseNormalize == 1) {
  			options.pulseNormalize /= pulse_(1);
  		}
  		return pulse_(x);
  	}
  
  	var isSupportedBrowser = /chrome/i.test(window.navigator.userAgent) ? true : /safari/i.test(window.navigator.userAgent);
  	var wheelEvent = null;
  	if ("onwheel" in document.createElement("div"))
  		wheelEvent = "wheel";
  	else if ("onmousewheel" in document.createElement("div"))
  		wheelEvent = "mousewheel";
  
  	if (wheelEvent && isSupportedBrowser) {
  		addEvent(wheelEvent, wheel);
  		addEvent("mousedown", mousedown);
  		addEvent("load", init);
  	}
  
  })();