/**
 * jQuery-Plugin zur Erzeugung einfacher Schatten an den Raendern eines
 * Objektes mit Hilfe von 8 Grafiken. Die Grafiken koennen automatisch
 * gefunden werden oder einzeln konfiguriert werden. Zudem kann die Groesse
 * und Sichtbarkeit des Schattens angepasst werden.
 *
 * ----- Kompatibilitaet: -----
 *
 * Uneingeschraenkt kompatibel mit Firefox 1.0 - 3.5, Opera 9.0 - 10.0,
 * Internet Explorer 7 und Safari 3.1 - 4.0
 * Eingeschraenkt kompatibel mit Opera 8.5 (keine Transparenz), Internet Explorer
 * 6 & 8 (keine transparenten PNGs)
 *
 * © 2009 - Kristian Kriehl
 *
 * ----- Verwendung: -----
 *
 * $("selektor").simpleshadow([optionen]); // Zum Erzeugen eines neuen Schattens
 *
 * oder
 *
 * $("selektor").simpleshadow("aktion", [optionen]); // Zur Aenderung des Schattens
 *
 * ----- Aktionen: -----
 *
 * create			Standardaktion: Erzeugt einen neuen Schatten.
 * update			Berechnet Position und Groesse des Schattens neu.
 * hide|show		Zeigt bzw. versteckt den Schatten.
 *
 * ----- Optionen: -----
 *
 *  opacity			Sichtbarkeit des Schattens zwischen 0.0 (unsichtbar) und
 *					1.0 (undurchsichtig). Standard: 1.0.
 *	size			Groesse des Schattenrahmens in Pixeln oder "auto" fuer die
 *					Verwendung der Groesse des ersten Eckbildes links-oben.
 *					Standard: "auto".
 *	imagePath		Pfad zu den Bilddateien. Standard: "images/".
 *	imageBasename	Basisname der Bilder vor dem Positionsspezifischen Suffix.
 *					Standard: "shadow_".
 *	imageExtension	Dateiendung der Bilddateien. Standard: "png".
 *	imageFilenames	Enthaelt ein Unterobjekt mit Dateinamen. Mit diesen koennen
 *					die automatisch erzeugten Dateinamen, die mit den obigen
 *					Optionen konfiguriert werden, ueberschrieben werden. Die
 *					zur Verfuegung stehenden Schluessel sind:
 *					topLeft, top, topRight,
 *					left, right,
 *					bottomLeft, bottom, bottomRight
 *	disable			Hier koennen optional Bilder deaktiviert werden. Die moeglichen
 *					deaktivierbaren Bereiche sind diesselben wie die Bildschluessel
 *					der Option "imageFilenames". Die Schluessel werden als
 *					Kommaseparierter Text eingetragen: "topLeft,top,topRight" wuerde
 *					z.B. den Schatten an der Oberseite deaktivieren.
 *	loggingEnabled	Aktiviert (nur auf dem Firefox mit installiertem Firebug)
 *					das Logging auf der Firebug-Konsole
 *	containerClass  Setzt den Klassennamen des Container-Divs der erzeugten
 *					Schatten.
 *
 * Die Optionen werden als JavaScript-Objekt uebergeben.
 * 
 * ----- Verwendungsbeispiel: -----
 *
 * $('#shadowed').simpleshadow({
 *     opacity: 0.2,
 *     size: "auto",
 *     imageFilenames: {
 *         topLeft    : "/bilder/schatten/oben_links.png",
 *         top        : "/bilder/schatten/oben_mitte.png",
 *         topRight   : "/bilder/schatten/oben_rechts.png",
 *         left       : "/bilder/schatten/links.png",
 *         right      : "/bilder/schatten/rechts.png",
 *         bottomLeft : "/bilder/schatten/unten_links.png",
 *         bottom     : "/bilder/schatten/unten_mitte.png",
 *         bottomRight: "/bilder/schatten/unten_rechts.png",
 *     }
 * });
 *
 * Das Beispiel fuegt an alle Elemente mit der ID "shadowed" einen Schatten hinzu
 * und setzt die Sichtbarkeit auf 0.2, die Groesse auf "auto" und ueberschreibt
 * alle Bilder um die automatisch Verwendeten Dateinamen zu umgehen.
 *
 * ----- Changelog: -----
 *
 * 08.10.2009:
 *  - neue Option : loggingEnabled
 *  - neue Option : containerClass
 *  - Standardoptionen koennen nun ueber $.fn.simpleshadow.defaults.OPTION
 *    ueberschrieben werden.
 *  - Preloader hat nun eine executionQueue, damit mehrere erzeugte
 *    Schatteninstanzen nicht parallel versuchen, die Bilder vorzuladen, was im
 *    IE Probleme macht, da das load-Ereignis nicht mehr ausgeführt wird, wenn
 *    die Bilder einmal geladen wurden, und damit ohne Queueing die Callback-
 *    Aufrufe vor dem Abschluss des Preloadings ausgefuehrt wuerden.
 *
 */
(function($){
	// jQuery erlaubt Transparenz im Opera erst ab Version 9.5, aber bereits
	// Version 9.0 erlaubt Transparenz per CSS, deshalb erlauben wir allen
	// Opera-Versionen, es wenigstens zu versuchen
	if ($.browser.opera) {
		$.support.opacity = true;
	}

	// Umschließende Funktion des Plugins
	$.fn.simpleshadow = function(action_or_options, overwrite_options) {
		var action = "create"; // Die Standardaktion ist "create"
		var options = null; // Optionen leer initialisieren
		var loadedImagesCount = 0;
		
		// ueberpruefen, ob der erste Parameter gesetzt ist
		if(action_or_options) {
			// Wenn wir kein Objekt haben, kann von einer Aktion ausgegangen werden
			if(typeof action_or_options != "object") {
				action = action_or_options;

				if(overwrite_options != null && typeof overwrite_options == "object") {
					options = overwrite_options;
				}
			} else {
				options = action_or_options;
			}
		}
		
		return this.each(function () {
			var obj = this;

			if(action == "create") {
				if(isNaN($.data(obj,"internalID"))) {
					$.data(obj,"internalID",$.fn.simpleshadow.internalID + 1);
					$.fn.simpleshadow.internalID++;
					createDefault(obj);
				} else {
					action = "update";
				}
			}

			if(options && typeof options == "object") {
				extendOptions(obj, options);
			}
			
			if(!$.fn.simpleshadow.imagesLoaded) {
				if(!$.fn.simpleshadow.imagesLoading) {
					preloadImages(obj);
				}
				if($.fn.simpleshadow.imagesLoading) {
					log("image loading in progress, queueing action");
					$.fn.simpleshadow.queue[$.fn.simpleshadow.queue.length] = {
						func : function() {
							executeAction(obj, action);
						},
						obj : obj
					}
				}
			} else {
				executeAction(obj, action);
			}
		});

		function createDefault(obj) {
			log("creating default data for " + $.data(obj,"internalID"));

			$.data(obj,"images",{
				topLeft     : null, top         : null, topRight    : null,
				left        : null,                     right       : null,
				bottomLeft  : null,	bottom      : null,	bottomRight : null
			});

			$.data(obj,"options", $.fn.simpleshadow.defaults);
			
			var container = $('<div>').css({ position : "absolute", zIndex : $(obj).css("z-index") });

			if($.fn.simpleshadow.defaults.containerClass != "") {
				container.attr("class",$.fn.simpleshadow.defaults.containerClass);
			}

			$("body").append(container);

			$.data(obj,"container",container);
		}

		function extendOptions(obj, options) {
			log("extending options for " + $.data(obj,"internalID"));

			var old = $.data(obj,"options");
			var merged = $.extend(true, {}, old, options);

			if(merged.imagePath.substr(-1) != "/") {
				merged.imagePath += "/";
			}

			$.data(obj,"options",merged);
		}

		function getData(obj) {
			return {
				options   : $.data(obj,"options"),
				images    : $.data(obj,"images"),
				ID        : $.data(obj,"ID"),
				container : $.data(obj,"container")
			};
		}

		function executeAction(obj, action) {
			log("executing action " + action + " for " + $.data(obj,"internalID"));

			if(action == "create") {
				createImages(obj);
				decorate(obj);
			} else if(action == "update") {
				updateShadow(obj);
			} else if(action == "hide") {
				$.data(obj,"container").hide();
			} else if(action == "show") {
				$.data(obj,"container").show();
			}
		}

		function mapImageKey(obj,key,fileName) {
			var imageFiles = $.data(obj,"imageFiles");
			if(!imageFiles) imageFiles = {};
			imageFiles[fileName] = key;
			$.data(obj,"imageFiles",imageFiles);
		}

		function setImage(obj,fileName,image) {
			var images = $.data(obj,"images");
			var imageFiles = $.data(obj,"imageFiles");

			images[imageFiles[fileName]] = image;
			$.data(obj,"images",images);
			
			loadedImagesCount++;
		}

		function getImage(obj,fileName) {
			var images = $.data(obj,"images");
			var imageFiles = $.data(obj,"imageFiles");

			return images[imageFiles[fileName]];
		}

		function preloadImages(obj) {
			log("preloading images for " + $.data(obj,"internalID"));
			var images = $.data(obj,"images");
			var options = $.data(obj,"options");
			var imageCount = 0;
			var key = null;

			for(key in images) { imageCount++; }
			for(key in images) {
				log("creating temp image " + key);

				var imageSrc = options.imagePath + options.imageBasename +
					key.toLowerCase() + "." + options.imageExtension;
				var fileName = imageSrc.substr(imageSrc.lastIndexOf("/"));
				mapImageKey(obj, key, fileName);

				if(options.imageFilenames[key]) {
					imageSrc = options.imagePath + options.imageFilenames[key];
				}

				var tempImage = new Image();
				tempImage.src = imageSrc;


				$.fn.simpleshadow.queue = [];

				$.fn.simpleshadow.imagesLoading = true;
				loadedImagesCount = 0;

				$(tempImage).load(function() {
					log("image " + loadedImagesCount + "/" + imageCount + " loaded: " + $(this).attr("src"));
					if(loadedImagesCount == imageCount - 1) {
						log("image loading completed");
						$.fn.simpleshadow.imagesLoaded = true;
						$.fn.simpleshadow.imagesLoading = false;
						executeQueue();
					} else loadedImagesCount++;
				}).error(function() {
					log("image error: " + $(this).attr("src"));
					if(loadedImagesCount == imageCount - 1) {
						log("image loading completed");
						$.fn.simpleshadow.imagesLoaded = true;
						$.fn.simpleshadow.imagesLoading = false;
						executeQueue();
					} else loadedImagesCount++;
				});
			}
		}

		function createImages(obj) {
			log("creating instance images for " + $.data(obj,"internalID"));
			
			var images = $.data(obj,"images");
			var options = $.data(obj,"options");

			for(var key in images) {
				log("creating image " + key);

				var imageSrc = options.imagePath + options.imageBasename +
					key.toLowerCase() + "." + options.imageExtension;

				var fileName = imageSrc.substr(imageSrc.lastIndexOf("/"));

				mapImageKey(obj, key, fileName);

				if(options.imageFilenames[key]) {
					imageSrc = options.imagePath + options.imageFilenames[key];
				}

				var $tempImage = $("<img>").attr("src", imageSrc);

				setImage(obj, fileName, $tempImage);
			}
		}

		function decorate(obj) {
			log("decorating " + $.data(obj,"internalID"));
			var data = getData(obj);

			var imageKey = null;
			var disabled = data.options.disable.split(",");
			var newImage = null;

			for(imageKey in data.images) {
				data.container.append(
					newImage = $(data.images[imageKey]).css("position","absolute")
				);

				if($.inArray(imageKey.toLowerCase(), disabled) >= 0) {
					newImage.css("display","none");
				}

				// Der IE 8 hat einen sehr nervigen Bug: Er unterstuetzt zwar nativ
				// PNG-Transparenz, aber nicht gleichzeitig mit CSS-Transparenz. Dann
				// wird die PNG-Transparenz deaktiviert. Umgehen kann man dieses Verhalten
				// nur mit dem IE6-PNG-Fix durch den AlphaImageLoaeder-Filter bei
				// CSS-Backgrounds, welcher nicht durch die CSS-Transparenz
				// beeintraechtigt wird.
				// Ich mache es mir hier erstmal leichter und deaktiviere im
				// IE 8.0 die Transparenz des Schattens. Diese kann auch durch
				// Anpassung der Grafiken geaendert werden.
				if(!$.browser.msie || ($.browser.version != "8.0" && $.browser.version != "7.0" )) {
					newImage.css("opacity", data.options.opacity);
				}
			}

			updateShadow(obj);
		}

		function updateShadow(obj) {
			log("updating shadow for " + $.data(obj,"internalID"));

			var $obj = $(obj);
			var data = getData(obj);
			var offset = $obj.offset();
			var disabled = data.options.disable.split(",");

			var width = $obj.innerWidth();
			var height = $obj.innerHeight();

			// Top Left
			var imageKey = null;
			var $image = null;
			var lcKey = "";

			var imgWidth = $(data.images.topLeft).innerWidth();
			var imgHeight = $(data.images.topLeft).innerHeight();

			if(data.options.size && data.options.size != "auto") {
				imgWidth = data.options.size;
				imgHeight = data.options.size;
			}

			data.container.css({
				top    : offset.top - imgHeight,
				left   : offset.left - imgWidth,
				width  : width + (2 * imgWidth),
				height : height + (2 * imgHeight)
			});
			
			if(data.options.zIndex && !isNaN(data.options.zIndex)) {
				data.container.css("z-index", data.options.zIndex);
			}

			for(imageKey in data.images) {
				if($.inArray(imageKey.toLowerCase(), disabled) >= 0) {
					continue;
				}
				
				$image = $(data.images[imageKey]);
				lcKey = imageKey.toLowerCase();

				$image.css({
					width  : width,
					height : height,
					top    : imgHeight,
					left   : imgWidth
				});

				if(lcKey.indexOf("bottom") == 0 || lcKey.indexOf("top") == 0) {
					$image.css("height", imgHeight);
				} else {
					$image.css("height", height);
				}

				if(lcKey.indexOf("left") >= 0) {
					$image.css("left", 0);
					$image.css("width", imgWidth);
				} else if(lcKey.indexOf("right") >= 0) {
					$image.css("left", width + imgWidth);
					$image.css("width", imgWidth);
				}

				if(lcKey.indexOf("top") >= 0) {
					$image.css("top", 0);
				} else if(lcKey.indexOf("bottom") >= 0) {
					$image.css("top", height + imgHeight);
				}
			}
		}

		function log(message) {
			if($.fn.simpleshadow.defaults.loggingEnabled) {
				if($.browser.mozilla && console != undefined && typeof console.log == "function") {
					console.log(message);
				}
			}
		}

		function executeQueue() {
			log("executing queue");
			var func, obj = null;

			for(var i in $.fn.simpleshadow.queue) {
				func = $.fn.simpleshadow.queue[i].func;
				obj = $.fn.simpleshadow.queue[i].obj;

				log("executing callback: " + func);

				func.call(obj);
			}
		}
	};
	
	$.fn.simpleshadow.imagesLoading = false;
	$.fn.simpleshadow.imagesLoaded = false;
	$.fn.simpleshadow.internalID = 0;
	$.fn.simpleshadow.queue = [];

	$.fn.simpleshadow.defaults = {
		loggingEnabled : false,
		imagePath      : "images/",
		opacity        : 1.0,
		size           : "auto",
		imageBasename  : "shadow_",
		imageExtension : "png",
		containerClass : "simpleshadow",
		imageFilenames : {
			topLeft     : null,
			top         : null,
			topRight    : null,
			left        : null,
			right       : null,
			bottomLeft  : null,
			bottom      : null,
			bottomRight : null
		},
		disable : ""
	};
})(jQuery)
