/*
	FadableObject: A flexible, powerful, object-oriented way
	to fade HTML elements in and out, cross-browser.
	Written by Twey.
	Use, copying, and modification allowed, so long as credit
	remains intact.

	Notes:
		In order to function in IE, the objects the effects are
		applied to need to have a defined width and background
		colour, except for images.  To this purpose, the script
		will define a background colour of "white" and a width of
		"100%" in IE to all elements to which it is applied, except
		images.  This behaviour will not occur if you apply your
		own background colour and width to the elements.

	Functions defined:
		FadableObject(HTMLElement element, int stepAmount, int speed, int minOpacity, int maxOpacity, bool startMin, bool pulsingTakesPrecedence):
			Construct a new FadableObject around the given HTML element.
			startMin defines how it should start: faded in (false) or faded out (true).
			pulsingTakesPrecedence specifies whether, if shown/hidden whilst pulsing,
			to ignore the requested operation.
			Also creates element.fadeThread, a reference to the FadableObject
			constructed around element.

		float FadableObject.getOpacity():
			Get current opacity.  Returns in int form (from 0 to 100).

		float FadableObject.setOpacity(float val):
			Set opacity.  Note that val is in IE opacity (0-to-100)
			format; it is converted for use with others later.
			Returns the new value.

		bool FadableObject.fadeIn(callback):
			Fade from current to maximum opacity.  If specified, callback will be called
			when finished.

		bool FadableObject.fadeOut(callback):
			Fade from current to minimum opacity.  If specified, callback will be called
			when finished.

		bool FadableObject.toggleFade():
			Toggle faded state.  Returns true if fading in, false if fading out.

		bool FadableObject.startPulsing():
			Start fading back and forth.  Returns true on success, false on failure.

		bool FadableObject.stopPulsing():
			Stop pulsing.  Returns true on success, false on failure.

		bool FadableObject.togglePulsing():
			Toggle pulsing.  Returns true if pulsing started, false if pulsing stopped
			(or couldn't be started).

		static FadeObject.getFadableObjectByElement(HTMLElement el):
			Get a fadable object by its HTML element.

		static FadableObject.init():
			Set up the FadableObject constructor function.  Used internally.

	Important static variables:
		FadeObject.fadeThreads:
			An array of all the fadable objects.

				********************
				***** WARNING: *****
				********************
				IF THE OBJECTS AREN'T ALL IN HERE, OR AREN'T IN THE CORRECT
				POSITIONS ACCORDING TO THEIR IDs, THE SCRIPT WILL NOT WORK.
*/

	FadableObject = function(element, stepAmount, speed, minOpacity, maxOpacity, startMin, pulsingTakesPrecedence) {
		if(!this.inited) FadableObject.init();
		this.pulsingTakesPrecedence = pulsingTakesPrecedence;
		this.pulsing = false;
		this.step = stepAmount;
		this.id = FadableObject.currentId++;
		this.thread = null;
		this.element = element;
		this.speed = speed;
		this.defaultOn = !startMin;
		this.minOpacity = minOpacity < 0 ? 0 : minOpacity;
		this.maxOpacity = maxOpacity >= 100 ? 99 : maxOpacity;
		this.pulsingUp = false;
		this.pulsingDown = false;
		this.faded = this.defaultOn;

		if(typeof this.element.filters != "undefined") {
			if(typeof this.element.filters['DXImageTransform.Microsoft.Alpha'] == "undefined") this.element.style.filter = "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
			if(this.element.tagName.toLowerCase() != "img") {
				if(this.element.style.backgroundColor == "") this.element.style.backgroundColor = "white";
				if(this.element.style.width == "") this.element.style.width = "100%";
			}
		}

		if(startMin) this.setOpacity(this.minOpacity);
		else this.setOpacity(this.maxOpacity);

		FadableObject.fadeThreads.push(this);
		this.element.fadeThread = this;
	}
	FadableObject.inited = false;
	FadableObject.currentId = 0;
	FadableObject.fadeThreads = new Array();
	FadableObject.getFadableObjectByElement = function(el) {
		for(var ij = 0; ij < FadableObject.fadeThreads.length; ij++) if(el == FadableObject.fadeThreads[ij].element) return FadableObject.fadeThreads[ij];
		return null;
	}
	FadableObject.opacityProperty = "";
	FadableObject.filterName = "DXImageTransform.Microsoft.Alpha";

FadableObject.init = function() {
	FadableObject.inited = true;
	FadableObject.prototype.getOpacity = (function() { // Only perform the browser check once, and generate optimized code.
		var noOpacity = function() { return -1; }
		var useFilter = function() {
			if(isNaN(parseInt(this.element.filters[FadableObject.filterName].opacity))) this.element.filters[FadableObject.filterName].opacity = 100;
			return parseInt(this.element.filters[FadableObject.filterName].opacity);
		}
		var useStyle = function() {
			if(isNaN(parseFloat(this.element.style[FadableObject.opacityProperty]))) this.element.style[FadableObject.opacityProperty] = 1.0;
			return parseInt(parseFloat(this.element.style[FadableObject.opacityProperty]) * 100);
		}

		if(typeof window.document.body.style.opacity != "undefined") {
			FadableObject.opacityProperty = "opacity";
			return useStyle;
		} else if(typeof window.document.body.style.MozOpacity != "undefined") {
			FadableObject.opacityProperty = "MozOpacity";
			return useStyle;
		} else if(typeof window.document.body.style.KhtmlOpacity != "undefined") {
			FadableObject.opacityProperty = "KhtmlOpacity";
			return useStyle;
		} else if(typeof window.document.body.filters != "undefined") {
			return useFilter;
		} else return noOpacity;
	})();
	FadableObject.prototype.setOpacity = (function() { // Well, maybe twice.
		var noOpacity = function(val) { return -1; }
		var useFilter = function(val) {
			return parseInt(this.element.filters[FadableObject.filterName].opacity = parseInt(val));
		}
		var useStyle = function(val) {
			return parseInt(parseFloat(this.element.style[FadableObject.opacityProperty] = (parseInt(val) / 100)) * 100);
		}

		if(typeof window.document.body.style.opacity != "undefined") {
		FadableObject.opacityProperty = "opacity";
			return useStyle;
		} else if(typeof window.document.body.style.MozOpacity != "undefined") {
			FadableObject.opacityProperty = "MozOpacity";
			return useStyle;
		} else if(typeof window.document.body.style.KhtmlOpacity != "undefined") {
			FadableObject.opacityProperty = "KhtmlOpacity";
			return useStyle;
		} else if(typeof window.document.body.filters != "undefined") {
			return useFilter;
		} else return noOpacity;
	})();
	FadableObject.prototype.fadeIn = function(callback) {
		this.faded = false;
		var lastOpacity;
		if((lastOpacity = this.getOpacity()) == -1) return false;
		if(this.pulsing && !arguments[1])
			if(this.pulsingTakesPrecedence) return false;
			else this.stopPulsing();
		if(this.getOpacity() >= this.maxOpacity) {
			if(this.pulsing) {
				this.pulsingUp = false;
				this.pulsingDown = true;
				this.fadeOut(callback, true);
			}
			if(callback) callback.apply(this);
			return false;
		}
		this.setOpacity((this.getOpacity() + this.step > 100) ? 99 : this.getOpacity() + this.step);
		if(lastOpacity == this.getOpacity()) // We ain't goin' anywhere.  Assume stuck because of floating-point inaccuracies.
						     // Bit of a hack, but apparently necessary.
			this.setOpacity(this.getOpacity() + this.step + 1);
		clearTimeout(this.thread);
		var me = this;
		this.thread = setTimeout(function(){me.fadeIn(callback, me.pulsing);}, this.speed);
		return true;
	};
	FadableObject.prototype.fadeOut = function(callback) {
		this.faded = true;
		var lastOpacity;
		if((lastOpacity = this.getOpacity()) == -1) return false;
		if(this.pulsing && !arguments[1])
			if(this.pulsingTakesPrecedence) return false;
			else this.stopPulsing();
		if(this.getOpacity() <= this.minOpacity) {
			if(this.pulsing) {
				this.pulsingDown = false;
				this.pulsingUp = true;
				this.fadeIn(callback, true);
			}
			if(callback) callback.apply(this);
			return false;
		}
		this.setOpacity((this.getOpacity() - this.step < 0) ? 0 : this.getOpacity() - this.step);
		if(lastOpacity == this.getOpacity()) // We ain't goin' anywhere.  Assume stuck because of floating-point inaccuracies.
						     // Bit of a hack, but apparently necessary.
			this.setOpacity(this.getOpacity() - this.step - 1);
		clearTimeout(this.thread);
		var me = this;
		this.thread = setTimeout(function(){me.fadeOut(callback, me.pulsing);}, this.speed);
		return true;
	};
	FadableObject.prototype.toggleFade = function() {
		this.faded ? this.fadeIn() : this.fadeOut();
		this.faded = !this.faded;
		return !this.faded;
	};
	FadableObject.prototype.startPulsing = function() {
		if(this.pulsing) return false;
		this.pulsing = true;
		if(this.getOpacity() <= this.minOpacity) {
			this.pulsingUp = true;
			this.fadeIn(null, true);
		} else {
			this.pulsingDown = true;
			this.fadeOut(null, true);
		}
		return true;
	};
	FadableObject.prototype.stopPulsing = function() {
		if(!this.pulsing) return false;
		this.pulsing = false;
		this.pulsingUp = false;
		this.pulsingDown = false;
		if(this.defaultOn) this.fadeIn(null, true);
		else this.fadeOut(null, true);
		return true;
	};
	FadableObject.prototype.togglePulsing = function() {
		if(this.pulsing) {
			this.stopPulsing();
			return false;
		} else {
			if(!this.startPulsing()) return false;
			return true;
		}
	};
}