(function ($) {
	/*----------------------------------------------------------------------------------
	Class: FloatObject
	-------------------------------------------------------------------------------------*/
	function FloatObject(jqObj, params) {
		this.jqObj = jqObj;

		switch (params.speed) {
			case 'fast': this.steps = 5; break;
			case 'normal': this.steps = 10; break;
			case 'slow': this.steps = 20; break;
			default: this.steps = 10;
		};

		var offset = this.jqObj.position();
		this.currentX = offset.left;
		this.currentY = offset.top;
		this.width = this.jqObj.width();
		this.height = this.jqObj.height();
		this.alwaysVisible = params.alwaysVisible;
		this.alwaysTop = params.alwaysTop;


		this.origX = typeof (params.x) == "string" ? this.currentX : params.x;
		this.origY = typeof (params.y) == "string" ? this.currentY : params.y;


		//now we make sure the object is in absolute positions.
		this.jqObj.css({ 'position': 'absolute', 'top': this.currentY, 'left': this.currentX });
	}

	FloatObject.prototype.updateLocation = function () {
		this.updatedX = $(window).scrollLeft() + this.origX;

		if (this.alwaysTop == false) {
			this.updatedY = $(window).scrollTop() + this.origY;

			if (this.alwaysVisible) {
				if (this.origX + this.width > this.windowWidth())
					this.updatedX = this.windowWidth() - this.width + $(window).scrollLeft();
				if (this.origY + this.height > this.windowHeight()) {
					this.updatedY = $(window).scrollTop() + this.windowHeight() - this.height;
					if (this.updatedY < this.origY) this.updatedY = this.origY;
				}
			}
		}
		else {

			if ($(window).scrollTop() > this.origY) {
				this.updatedY = $(window).scrollTop() + 5;
			}
			else {
				this.updatedY = this.origY + 5;
			}
		}
		this.dx = Math.abs(this.updatedX - this.currentX);
		this.dy = Math.abs(this.updatedY - this.currentY);

		return this.dx || this.dy;
	}

	FloatObject.prototype.windowHeight = function () {
		var de = document.documentElement;

		return self.innerHeight ||
			(de && de.clientHeight) ||
			document.body.clientHeight;
	}

	FloatObject.prototype.windowWidth = function () {
		var de = document.documentElement;

		return self.innerWidth ||
			(de && de.clientWidth) ||
			document.body.clientWidth;
	}


	FloatObject.prototype.move = function () {
		if (this.jqObj.css("position") != "absolute") return;
		var cx = 0;
		var cy = 0;

		if (this.dx > 0) {
			if (this.dx < this.steps / 2)
				cx = (this.dx >= 1) ? 1 : 0;
			else
				cx = Math.round(this.dx / this.steps);

			if (this.currentX < this.updatedX)
				this.currentX += cx;
			else
				this.currentX -= cx;
		}

		if (this.dy > 0) {
			if (this.dy < this.steps / 2)
				cy = (this.dy >= 1) ? 1 : 0;
			else
				cy = Math.round(this.dy / this.steps);

			if (this.currentY < this.updatedY)
				this.currentY += cy;
			else
				this.currentY -= cy;
		}

		this.jqObj.css({ 'left': this.currentX, 'top': this.currentY });
	}



	/*----------------------------------------------------------------------------------
	Object: floatMgr
	-------------------------------------------------------------------------------------*/
	$.floatMgr = {

		FOArray: new Array(),

		timer: null,

		initializeFO: function (jqObj, params) {
			var settings = $.extend({
				x: 0,
				y: 0,
				speed: 'normal',
				alwaysVisible: false,
				alwaysTop: false
			}, params || {});
			var newFO = new FloatObject(jqObj, settings);

			$.floatMgr.FOArray.push(newFO);

			if (!$.floatMgr.timer) $.floatMgr.adjustFO();

			//now making sure we are registered to all required window events
			if (!$.floatMgr.registeredEvents) {
				$(window).bind("resize", $.floatMgr.onChange);
				$(window).bind("scroll", $.floatMgr.onChange);
				$.floatMgr.registeredEvents = true;
			}
		},

		adjustFO: function () {
			$.floatMgr.timer = null;

			var moveFO = false;

			for (var i = 0; i < $.floatMgr.FOArray.length; i++) {
				FO = $.floatMgr.FOArray[i];
				if (FO.updateLocation()) moveFO = true;
			}

			if (moveFO) {
				for (var i = 0; i < $.floatMgr.FOArray.length; i++) {
					FO = $.floatMgr.FOArray[i];
					FO.move();
				}

				if (!$.floatMgr.timer) $.floatMgr.timer = setTimeout($.floatMgr.adjustFO, 50);
			}
		},

		stopFloatChk: false,

		onChange: function () {
			if (!$.floatMgr.timer && !$.floatMgr.stopFloatChk) $.floatMgr.adjustFO();
		}
	};

	/*----------------------------------------------------------------------------------
	Function: makeFloat
	-------------------------------------------------------------------------------------*/
	$.fn.makeFloat = function (params) {
		var obj = this.eq(0); //we only operate on the first selected object;
		$.floatMgr.initializeFO(obj, params);
		if ($.floatMgr.timer == null) $.floatMgr.adjustFO();
		return obj;
	};
	$.fn.stopFloat = function (params) {
		$.floatMgr.stopFloatChk = true;
	};

	$.fn.restartFloat = function (params) {
		$.floatMgr.stopFloatChk = false;
	};
})(jQuery);
