// FX CORE

(function($) {

	$.fx = $.fx || {}; //Add the 'fx' scope

	/*
	 * Public methods (jQuery FX scope)
	 */

	$.extend($.fx, {
		relativize: function(el) {
			if(!el.css("position") || !el.css("position").match(/fixed|absolute|relative/)) el.css("position", "relative"); //Relativize
		},
		save: function(el, set) {
			for(var i=0;i<set.length;i++) {
				if(set[i] !== null) $.data(el[0], "fx.storage."+set[i], el.css(set[i]));
			}
		},
		restore: function(el, set, ret) {
			if(ret) var obj = {};
			for(var i=0;i<set.length;i++) {
				if(ret) obj[set[i]] = $.data(el[0], "fx.storage."+set[i]);
				if(set[i] !== null && !ret) el.css(set[i], $.data(el[0], "fx.storage."+set[i]));
			}
			if(ret) return obj;
		},
		findSides: function(el) { //Very nifty function (especially for IE!)
			return [ !!parseInt(el.css("left")) ? "left" : "right", !!parseInt(el.css("top")) ? "top" : "bottom" ];
		},
		animateClass: function(value, duration, easing, callback) {

			var cb = (typeof easing == "function" ? easing : (callback ? callback : null));
			var ea = (typeof easing == "object" ? easing : null);

			this.each(function() {

				var offset = {}; var that = $(this); var oldStyleAttr = that.attr("style") || '';
				if(typeof oldStyleAttr == 'object') oldStyleAttr = oldStyleAttr["cssText"]; /* Stupidly in IE, style is a object.. */
				if(value.toggle) { that.hasClass(value.toggle) ? value.remove = value.toggle : value.add = value.toggle; }

				//Let's get a style offset
				var oldStyle = $.extend({}, (document.defaultView ? document.defaultView.getComputedStyle(this,null) : this.currentStyle));
				if(value.add) that.addClass(value.add); if(value.remove) that.removeClass(value.remove);
				var newStyle = $.extend({}, (document.defaultView ? document.defaultView.getComputedStyle(this,null) : this.currentStyle));
				if(value.add) that.removeClass(value.add); if(value.remove) that.addClass(value.remove);

				// The main function to form the object for animation
				for(var n in newStyle) {
					if( typeof newStyle[n] != "function" && newStyle[n] /* No functions and null properties */
						&& n.indexOf("Moz") == -1 && n.indexOf("length") == -1 /* No mozilla spezific render properties. */
						&& newStyle[n] != oldStyle[n] /* Only values that have changed are used for the animation */
						&& (n.match(/color/i) || (!n.match(/color/i) && !isNaN(parseInt(newStyle[n])))) /* Only things that can be parsed to integers or colors */
						&& (oldStyle.position != "static" || (oldStyle.position == "static" && !n.match(/left|top|bottom|right/))) /* No need for positions when dealing with static positions */
					) offset[n] = newStyle[n];
				}

				that.animate(offset, duration, ea, function() {	// Animate the newly constructed offset object
					// Change style attribute back to original. For stupid IE, we need to clear the damn object.
					if(typeof $(this).attr("style") == 'object') { $(this).attr("style")["cssText"] = ""; $(this).attr("style")["cssText"] = oldStyleAttr; } else $(this).attr("style", oldStyleAttr);
					if(value.add) $(this).addClass(value.add); if(value.remove) $(this).removeClass(value.remove);
					if(cb) cb.apply(this, arguments);
				});

			});
		}
	});

	//Extend the methods of jQuery
	$.fn.extend({
		effect: function(fx,o) { if($.fx[fx]) this.each(function() { $.fx[fx].apply(this, [o]); }); }, //This just forwards single used effects
		_show: $.fn.show,
		_hide: $.fn.hide,
		_addClass: $.fn.addClass,
		_removeClass: $.fn.removeClass,
		_toggleClass: $.fn.toggleClass,
		show: function(obj,speed,callback){
			return typeof obj == 'string' || typeof obj == 'undefined' ? this._show(obj, speed) : $.fx[obj.method].apply(this, [{method: 'show', options: obj, duration: speed, callback: callback }]);
		},

		hide: function(obj,speed,callback){
			return typeof obj == 'string' || typeof obj == 'undefined' ? this._hide(obj, speed) : $.fx[obj.method].apply(this, [{method: 'hide', options: obj, duration: speed, callback: callback }]);
		},
		addClass: function(classNames,speed,easing,callback) {
			return speed ? $.fx.animateClass.apply(this, [{ add: classNames },speed,easing,callback]) : this._addClass(classNames);
		},
		removeClass: function(classNames,speed,easing,callback) {
			return speed ? $.fx.animateClass.apply(this, [{ remove: classNames },speed,easing,callback]) : this._removeClass(classNames);
		},
		toggleClass: function(classNames,speed,easing,callback) {
			return speed ? $.fx.animateClass.apply(this, [{ toggle: classNames },speed,easing,callback]) : this._toggleClass(classNames);
		},
		morph: function(remove,add,speed,easing,callback) {
			return $.fx.animateClass.apply(this, [{ add: add, remove: remove },speed,easing,callback]);
		},
		switchClass: function() { this.morph.apply(this, arguments); }
	});

})(jQuery);

// FX.SHADOW

(function($) {

	//Make nodes selectable by expression
	$.extend($.expr[':'], { shadowed: "(' '+a.className+' ').indexOf(' fx-shadowed ')" });

	$.fn.shadowEnable  = function() { return $(this).find("+ .fx-shadow").show().end();   };
	$.fn.shadowDisable = function() { return $(this).find("+ .fx-shadow").hide().end();   };
	$.fn.shadowDestroy = function() { return $(this).find("+ .fx-shadow").remove().end(); };

	$.fn.shadow = function(options) {
		options = $.extend({
			offset:  1,
			opacity: 0.2,
			color:   "#000",
			monitor: false
		}, options || {});
		options.offset -= 1;

		return this.each(function() {

			// Remove an existing shadow if it exists
			var $element = $(this).shadowDestroy(),

			// Create a shadow element
			$shadow = $("<div class='fx-shadow' style='position: relative;'></div>").insertAfter($element);

			// Figure the base height and width
			baseWidth  = $element.outerWidth(),
			baseHeight = $element.outerHeight(),

			// Get the offset
			position = $element.position(),

			// Get z-index
			zIndex = parseInt($element.css("zIndex")) || 0;

			// Append smooth corners
			$('<div class="fx-shadow-color fx-shadow-layer-1"></div>').css({ position: 'absolute', opacity: options.opacity - 0.05,  left: options.offset,     top: options.offset,     width: baseWidth + 1, height: baseHeight + 1 }).appendTo($shadow);
			$('<div class="fx-shadow-color fx-shadow-layer-2"></div>').css({ position: 'absolute', opacity: options.opacity - 0.10,  left: options.offset + 2, top: options.offset + 2, width: baseWidth,     height: baseHeight - 3 }).appendTo($shadow);
			$('<div class="fx-shadow-color fx-shadow-layer-3"></div>').css({ position: 'absolute', opacity: options.opacity - 0.10,  left: options.offset + 2, top: options.offset + 2, width: baseWidth - 3, height: baseHeight     }).appendTo($shadow);
			$('<div class="fx-shadow-color fx-shadow-layer-4"></div>').css({ position: 'absolute', opacity: options.opacity,         left: options.offset + 1, top: options.offset + 1, width: baseWidth - 1, height: baseHeight - 1 }).appendTo($shadow);

			// Add color
			$("div.fx-shadow-color", $shadow).css("background-color", options.color);

			// Set zIndex +1 and make sure position is at least relative
			// Attention: the zIndex will get one higher!
			$element
				.css({
					zIndex: zIndex + 1,
					position: ($element.css("position") != "absolute" ? "relative" : "")
				});

			// Copy the original z-index and position to the clone
			// alert(shadow); If you insert this alert, opera will time correctly!!
			$shadow.css({
				position:     "absolute",
				zIndex:       zIndex,
				top:          position.top+"px",
				left:         position.left+"px",
				width:        baseWidth,
				height:       baseHeight,
				marginLeft:   $element.css("marginLeft"),
				marginRight:  $element.css("marginRight"),
				marginBottom: $element.css("marginBottom"),
				marginTop:    $element.css("marginTop")
			});


			if ( options.monitor ) {
				function rearrangeShadow() {
					var $element = $(this), $shadow = $element.next();
					// $shadow.css( $element.position() );
					$shadow.css({
						top:  parseInt($element.css("top"))  +"px",
						left: parseInt($element.css("left")) +"px"
					})
					$(">*", $shadow).css({ height: this.offsetHeight+"px", width: this.offsetWidth+"px" });
				}

				// Attempt to use DOMAttrModified event
				$element.bind("DOMAttrModified", rearrangeShadow);

				// Use expressions if they exist (IE)
				if( $shadow[0].style.setExpression ) {
					$shadow[0].style.setExpression("top" , "parseInt(this.previousSibling.currentStyle.top ) + 'px'");
					$shadow[0].style.setExpression("left", "parseInt(this.previousSibling.currentStyle.left) + 'px'");
				}
			}

		});
	};

})(jQuery);
