if (window._JSON) {
	JSON.stringify = _JSON.stringify;
	JSON.parse = _JSON.parse;
}

String.implement({
	startsWith: function(s) {
		return this.substr(0,s.length)==s;
	},
	replaceAll: function(searchValue, replaceValue, regExOptions) {
		return this.replace(new RegExp(searchValue, $pick(regExOptions,"gi")), replaceValue);
	},
	gsub: function(search, replace) {
		return this.split(search).join(replace);
	},
	urlEncode: function() {
		if (this.indexOf("%") > -1) return this;
		else return escape(this);
	},
	htmlEntitiesEncode: function() {
		var enc = this.toString();
		enc = enc.gsub("&","&amp;");
		enc = enc.gsub("<","&lt;");
		enc = enc.gsub(">","&gt;");
		return enc;
	},
	injectHTML: function(inside) {
		var container = new Element("DIV", {"html": this.toString()});
		var children = container.getChildren();
		children.each(function(child) { child.inject(inside); });
		container.dispose();
		return children;
	},
	toClipboard: function() {
		try {
			document.execCommand('Copy', this.toString());
		} catch(e) {
			try {
				netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
			} catch(e) {
				if (Browser.Engine.gecko)
					alert("Browser security settings doesn't allow this action to be taken.\n\nSet the 'signed.applets.codebase_principal_support' property in the 'about:config' page to true/1 if you want to enable this feature.");
				return this;
			}
			try {
				clip = Components.classes["@mozilla.org/widget/clipboard;1"].createInstance(Components.interfaces.nsIClipboard);
			} catch(e) {
				return this;
			}
			try {
				transf = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
			} catch(e) {
				return this;
			}
			transf.addDataFlavor("text/unicode");
			suppstr = Components.classes["@mozilla.org/supports-string;1"].createInstance(Components.interfaces.nsISupportsString);
			suppstr.data = this.toString();
			transf.setTransferData("text/unicode",suppstr,this.length*2);
			try {
				clipi = Components.interfaces.nsIClipboard;
			} catch(e) {
				return this;
			}
			clip.setData(transf,null,clipi.kGlobalClipboard);
		}
		return this;
	}
});

Element.tDataRegExp = new RegExp("_\\|JS:(.*)\\|_");

Element.implement({
	searchAttachedData: function() {
		this.getElements("[title]").each( function(elem) {
			elem.storeAttachedData();
		} );
	},

	// Cerca dati json inclusi nel parametro title e ne fa lo store in "tData". I dati json devono essere racchiusi tra "_|JS:" e "|_"
	storeAttachedData: function() {
		var t = this.get("title");
		var matches = Element.tDataRegExp.exec(t);
		try {
			var data = JSON.decode(matches[1]);
			this.store("tData", data);
			this.set("title", t.gsub(matches[0],""));
		} catch(e) {
			//dbug.log(e);
		}
	},

	isVisible: function(recursive) {
		if (recursive && this.getParent()) {
			var parent=this.getParent();
			var element=this;
			var result=true;

			while (parent && !element.isBody() && result) {
				var visible = ((element.getStyle('display') != 'none') && (element.getStyle('visibility') != 'hidden') && (element.getStyle('opacity') != 0));
				if (!visible) result=false;
				element=element.getParent();
				parent=element.getParent();
			}

			return result;
		} else {
			return this.getStyle('display') != 'none';
		}
	},

	inlineEdit: function(options) {
		return new InlineEdit(this, options);
	},

	hide: function() {
		var d;
		try {
			d = this.getStyle('display');
			if (d=='none') d = '';
		} catch(e){}
		this.store('originalDisplay', d||'');
		this.setStyle('display','none');
		return this;
	},

	show: function(display) {
		original = this.retrieve('originalDisplay')?this.retrieve('originalDisplay'):this.get('originalDisplay');
		this.setStyle('display',(display || original || ''));
		return this;
	},

	getCells: function() {
		if ($defined(this.cells))
			return this.cells;
		else
			return this.getElements("td");
	},

	getSiblings: function() {
		return this.getAllPrevious().extend(this.getAllNext());
	},

	toQueryString: function(){
		var queryString = [];
		this.getElements('input, select, textarea', true).each(function(el){
			if (!el.name || el.disabled || el.type == 'submit' || el.type == 'reset' || el.type == 'file') return;
			var value = el.value;
			if (el.tagName.toLowerCase() == 'select') {
				value = Element.getSelected(el).map(function(opt){
					return opt.value;
				});
			} else if ((el.type == 'radio' || el.type == 'checkbox') && !el.checked) {
				value = el.type == 'checkbox' ? '' : null;
			}
			$splat(value).each(function(val){
				if (typeof val != 'undefined') queryString.push(el.name + '=' + encodeURIComponent(val));
			});
		});
		return queryString.join('&');
	},

	toHash: function(){
		var hash = {};
		this.getElements('input[name][type!=submit], select[name], textarea[name]').each(function(el){
			if (el.disabled) return;
			var value = (el.tagName.toLowerCase() == 'select') ? Element.getSelected(el).map(function(opt){
				return opt.value;
			}) : ((el.type == 'radio' || el.type == 'checkbox') && !el.checked) ? null : el.value;
			hash[el.name] = value;
		});
		return hash;
	},

	cloneWithEvents: function(contents, keepid, type) {
		return this.clone(contents, keepid).cloneEvents(this, type);
	},

	isBody: function() {
		return (/^(?:body|html)$/i).test(this.tagName);
	},

	fxBlindDown: function(options) {
		options = $merge({
			direction: 'auto'
		}, options);

		if (!this.fullHeight) this.fullHeight = this.getDimensions().y;

		if (options.direction == 'auto')
			options.direction = this.getHeight() == 0 ? 'down' : 'up';

		var dim;
		if (options.direction == 'down')
			dim = [0, this.fullHeight];
		else
			dim = [this.fullHeight, 0];

		return this.setStyles({height: dim[0]+"px", overflow: 'hidden'}).show().tween('height', dim);
	},

	highlightOpacity: function() {
		var fx = this.retrieve("highlightOpacityFx");
		if (!fx) {
			fx = new Fx.Morph(this, {
				link: "cancel",
				duration:500
			});
			fx.addEvent("onChainComplete", function() {
				this.start({
					'opacity':1
				});
			}.bind(fx));
			this.store("highlightOpacityFx", fx);
		}
		fx.start({
			'opacity':0
		});
		return this;
	},
	blinkFx: function(index){
		(function(){
			if (this.hasClass('blinked')){
				this.removeClass('blinked');
			} else {
				this.addClass('blinked');
			}
			index--;
			if (index>0)
				this.blinkFx(index);
		}).delay(150,this);
	},
	getFxFadedShowHide: function() {
		var fx = this.retrieve("fxFadedShowHide");
		if (!fx) {
			fx = new Fx.Morph(this, {
				link: "cancel"
			}).addEvent("onChainComplete", function() {
				if (this.getStyle("opacity") == 0) {
					this.hide();
				} else {
					this.setStyle("height","");
				}
			}.bind(this));
			this.store("fxFadedShowHide", fx);
		}
		return fx;
	},
	fxFadedHide: function() {
		if (this.isVisible()) {
			this.store("fxFadedShowHide-Height", this.getSize().y);
			this.getFxFadedShowHide().start({
				opacity: 0,
				height: 0
			});
		}
		return this;
	},
	fxFadedShow: function() {
		if (!this.isVisible()) {
			var h = $pick( this.retrieve("fxFadedShowHide-Height"), 0 );
			this.store("fxFadedShowHide-Height", 0);
			var morph = {opacity:1};
			if (h>0)
				morph.height = h;
			this.setStyles({
				display: '',
				opacity: 0
			}).getFxFadedShowHide().start(morph);
		}
		return this;
	}
});

if (typeof(StickyWin) != 'undefined') {
	//Fix per aggiungere scrolls al posizionamento delle finestre
	StickyWin.implement({
		position: function(options){
			this.positioned = true;
			this.setOptions(options);

			//FIX
			if (this.options.relativeTo != document.body) {
				if (!this.originalOffset) this.originalOffset = this.options.offset;
				var scrolls = this.options.relativeTo.getScrolls();
				this.options.offset.x = this.originalOffset.x + scrolls.x;
				this.options.offset.y = this.originalOffset.y + scrolls.y -5; // offset di 10px inspiegabile
			}

			this.win.position({
				allowNegative: $pick(this.options.allowNegative, this.options.relativeTo != document.body),
				relativeTo: this.options.relativeTo,
				position: this.options.position,
				offset: this.options.offset,
				edge: this.options.edge
			});
			if (this.shim) this.shim.position();
			return this;
		}
	});
}
Number.format_regex = /(\d+)(\d{3})/;
Number.implement({
	format: function(precision, decimal_point, thousands_sep) {
		decimal_point = $pick(decimal_point, ".");
		thousands_sep = $pick(thousands_sep, "");

		var x = this.toFloat().round(precision).toString().split("."), x1, x2;
		x1 = x[0];
		if (precision > 0) {
			if (x.length > 1) {
				if (x[1].length < precision)
					x[1] += "0".repeat( precision-x[1].length )
				x2 = x.length > 1 ? decimal_point + x[1] : "";
			} else {
				x2 = decimal_point+"0".repeat(precision);
			}
		} else {
			x2 = "";
		}
		if (thousands_sep != "") {
			while (Number.format_regex.test(x1)) {
				x1 = x1.replace(Number.format_regex, "$1" + thousands_sep + "$2");
			}
		}
		return x1 + x2;
	},

	humanize: function(precision, options) {
		options = $merge({startUnit: "", machineK: false, unitType: ""}, options);

		precision = $pick(precision, 0);
		num = this.toFloat();

		var units = ["","K","M","G","T","P"];
		var unitX = units.indexOf(options.startUnit);
		var divisor = options.machineK ? 1024 : 1000;

		while (num>=divisor && unitX<units.length) {
			unitX++;
			num /= divisor;
		}
		return num.localized()+units[unitX]+options.unitType;
	},

	localized: function (monetary, currency_span) {
		var n = "", num = this.toFloat(), sign, sign_posn, sep_by_space, cs_precedes;
		if (num>=0) {
			sign = localeconv['positive_sign'];
			sign_posn = localeconv['p_sign_posn'];
			sep_by_space = localeconv['p_sep_by_space'];
			cs_precedes = localeconv['p_cs_precedes'];
		} else {
			sign = localeconv['negative_sign'];
			sign_posn = localeconv['n_sign_posn'];
			sep_by_space = localeconv['n_sep_by_space'];
			cs_precedes = localeconv['n_cs_precedes'];
		}

		if (monetary) {
			var symbol = localeconv['currency_symbol'];
			if (currency_span)
				symbol = '<span class="currency">'+symbol+'</span>';
			//-€ -#- €-
			//012345678
			var a = new Array(9);

			switch(sign_posn) {
				case 1: a[3] = sign; break;
				case 2: a[5] = sign; break;
				case 3: symbol = sign+symbol; break;
				case 4: symbol += sign; break;
				default: n += " [error sign_posn="+sign_posn+"&nbsp;!]";
			}

			if (cs_precedes) {
				a[1] = symbol;
				if (sep_by_space) a[2] = " ";
			} else {
				if (sep_by_space) a[6] = " ";
				a[7] = symbol;
			}
			a[4] = Math.abs(num).format(localeconv['frac_digits'], localeconv['mon_decimal_point'], localeconv['mon_thousands_sep']);
			n = a.join("");
		} else {
			n = Math.abs(num).format(localeconv['frac_digits'], localeconv['decimal_point'], localeconv['thousands_sep']);
			switch(sign_posn) {
				case 0: n = "("+n+")"; break;
				case 2: case 4: n += sign; break;
				case 1: case 3: n = sign+n; break;
				default: n += " [error sign_posn="+sign_posn+"&nbsp;!]";
			}
		}

		return n;
	}
});

Window.implement({
	getMedia: function() {
		var el = new Element("DIV", {"class": "no_print", styles: {"height":"0","width":"0"}}).inject(this.document.documentElement);
		var media = (el.getStyle("display") == "none") ? "print" : "screen";
		el.dispose();
		return media;
	}
});

Element.Events.valuechange = {
	base: "keyup",
	condition: function(event) {
		if ($pick( event.target.retrieve("last_value"), "") != event.target.value) {
			event.target.store("last_value", event.target.value);
			return true;
		} else return false;
	}
}

if (typeof(Sortables) != 'undefined') {
	Sortables.implement({
		getClone: function(element){
			if (!this.options.clone) return new Element('div').inject(document.body);
			if ($type(this.options.clone) == 'function') return this.options.clone.call(this, event, element, this.list);
			return element.clone(true).setStyles({
				'margin': '0px',
				'position': 'absolute',
				'display': 'none',
				'width': element.getStyle('width')
			}).inject(this.list).position(element.getPosition(element.offsetParent));
		},

		serialize: function() {
			var serial = [];
			this.list.getChildren().each(function(el, i){
				serial.push( "ids[]="+el.id.replace(/\D+/,"") );
			}, this);
			return serial.join("&");
		}
	});
}

Tips.implement({
	position: function(event){
		var size = window.getSize(), scroll = window.getScroll();
		var tip = {x: this.tip.offsetWidth, y: this.tip.offsetHeight};

		var alignOffsets = {x:0,y:0};
		if ($pick(this.options.alignment, 'right') == 'left')
			alignOffsets.x = -this.tip.getSize().x;
		if ($pick(this.options.verticalAlignment, 'bottom') == 'top')
			alignOffsets.y = -this.tip.getSize().y;

		var props = {x: 'left', y: 'top'};
		for (var z in props){
			var pos = event.page[z] + this.options.offsets[z] + alignOffsets[z];
			this.tip.setStyle(props[z], pos);
		}
	}
});
/*
if (!document.getElementsByClassName) {
	Native.implement([Document, Element], {
		getElementsByClassName: function(className) {
			return this.getElements("."+className);
		}
	});
}*/

// http://blog.kassens.net/outerclick-event
(function(){
	var events;
	var check = function(e){
		var target = $(e.target);
		var parents = target.getParents();
		events.each(function(item){
			var element = item.element;
			if (element != target && !parents.contains(element))
				item.fn.call(element, e);
		});
	};
	Element.Events.outerClick = {
		onAdd: function(fn){
			if(!events) {
				window.addEvent('click', check);
				events = [];
			}
			events.push({element: this, fn: fn});
		},
		onRemove: function(fn){
			events = events.filter(function(item){
				return item.element != this || item.fn != fn;
			}, this);
			if (!events.length) {
				window.removeEvent('click', check);
				events = null;
			}
		}
	};
})();


var InlineEdit = new Class({Implements: [Options, Events],
	options: {
		type: "input"
	},
	initialize: function(element,options){
		this.setOptions(options);
		if (element.get("tag") != this.options.type.toLowerCase()){
			this.element = element;
			this.oldContent = this.element.get("html");
			var content = this.oldContent.trim().replace(new RegExp("<br />", "gi"), "\n");
			this.inputBox = new Element(this.options.type, { value: content }).setStyles({
				margin: 0,
				backgroundColor: "transparent",
				//width: this.element.getSize().x,
				width: "99.5%",
				fontSize: "1em",
				borderWidth: 0
			});
			if (!this.inputBox.get("value")) this.inputBox.set("html", content);
			this.setAllStyles();
			this.element.set("html", "");
			this.inputBox.inject(this.element);
			this.inputBox.focus.delay(300, this.inputBox);
			this.inputBox.addEvent("change",this.onSave.bind(this));
			this.inputBox.addEvent("blur",this.onSave.bind(this));
			this.inputBox.addEvent("keyup",this.onKeyUp.bindWithEvent(this));
		}
	},
	onKeyUp: function(e){
		if("enter" == e.key) this.onSave();
	},
	onSave: function(){
		this.inputBox.removeEvents();
		this.newContent = this.inputBox.get("value").trim().replace(new RegExp("\n", "gi"), "<br />");
		this.element.set("html", this.newContent);
		this.fireEvent("onComplete", [this.element, this.oldContent, this.newContent]);
	},
	setAllStyles: function() {
		["text-align", "font", "font-family", "font-weight", "line-height", "letter-spacing", "color"].each(function (property) {
			if (this.element.getStyle(property)) this.inputBox.setStyle(property, this.element.getStyle(property));
		}.bind(this));
	}
});

var InlineSelect = new Class({Implements: [Options, Events],
	value: 0,

	initialize: function(options) {
		this.setOptions(options);
		if (!this.options.element || !this.options.url) return null;
		this.build();
	},

	build: function() {
		this.element = $(this.options.element).addEvent("click", this.onClick.bind(this));
		document.documentElement.addEvent("click", this.documentClick.bindWithEvent(this));
		this.request = new Request({
			url: this.options.url,
			onSuccess: this.fillOptions.bind(this),
			onFailure: function() {
				onAjaxFailure();
				this.waiter.stop();
			}.bind(this)
		});

		this.optionsContainer = new Element("DIV", {
			"class": "inlineSelect"
		}).hide().inject(this.element, "after");
		if (this.options.className)
			this.optionsContainer.addClass(this.options.className);
		this.waiter = new Waiter(this.element);
	},

	onClick: function() {
		if (this.optionsContainer.isVisible())
			this.hideOptions();
		else
			this.showOptions();
	},

	documentClick: function(e) {
		e = new Event(e);
		if (e.target != this.element && !e.target.getParents().contains(this.element))
			this.hideOptions();
	},

	showOptions: function() {
		this.optionsContainer.set("html","").show();
		this.waiter.start();
		this.request.send(this.options.requestOptions);
	},

	hideOptions: function() {
		this.optionsContainer.hide();
		this.waiter.stop();
		this.request.cancel();
	},

	fillOptions: function(response) {
		var options = JSON.decode(response);
		options.each(function(option) {
			var optionDiv = new Element("DIV", {
				"class": "option",
				"html": option.text
			}).store("value", option.value).inject(this.optionsContainer);

			optionDiv.addEvent("click", this.onOptionClick.bind(this, optionDiv));
		}, this);
		if (tooltips) tooltips.init(this.optionsContainer);
		this.waiter.stop();
	},

	onOptionClick: function(option) {
		this.value = option.retrieve("value");
		this.fireEvent("change", [option]);
	}
});

var InlineSelectStatic = new Class({Implements: [Options, Events],
	value: 0,

	initialize: function(options) {
		this.setOptions(options);
		if (!this.options.element) return null;
		this.build();
	},

	build: function() {
		this.element = $(this.options.element).addEvent("click", this.elementClick.bind(this));
		document.documentElement.addEvent("click", this.documentClick.bindWithEvent(this));

		this.optionsContainer = new Element("DIV", {
			"class": "inlineSelect"
		}).hide().inject(this.element, "after");
		if (this.options.className)
			this.optionsContainer.addClass(this.options.className);
	},

	elementClick: function() {
		if (this.optionsContainer.isVisible())
			this.hideOptions();
		else
			this.showOptions();
	},

	documentClick: function(e) {
		e = new Event(e);
		if (e.target != this.element && !e.target.getParents().contains(this.element))
			this.hideOptions();
	},

	showOptions: function() {
		this.optionsContainer.set("html","").show();
		this.fillOptions( this.element.retrieve("inlineSelectData") );
	},

	hideOptions: function() {
		this.optionsContainer.hide();
	},

	fillOptions: function(options) {
		options = $pick(options, []);
		options.each(function(option) {
			var optionDiv = new Element("DIV", {
				"class": "option",
				"html": option
			}).inject(this.optionsContainer);

			optionDiv.addEvent("click", this.onOptionClick.bindWithEvent(this, optionDiv));
		}, this);
		if (tooltips) tooltips.init(this.optionsContainer);
	},

	onOptionClick: function(e, option) {
		e = new Event(e);
		e.stop();
		this.hideOptions();
		this.value = option.get("html");
		this.fireEvent("change", [option]);
	}
});

/*FormValidator.implement({
	watchFields: function(){
		this.getFields().each(function(el){
				el.addEvent('focus', function(){
					el.addClass('validation-passed').removeClass('validation-failed');
					if ($defined(el.getParent().getElement('.validation-advice')))
						this.hideAdvice('required',el);
				}.bind(this));
				el.addEvent('blur', this.validateField.pass([el, false], this));
			if (this.options.evaluateFieldsOnChange)
				el.addEvent('change', this.validateField.pass([el, true], this));
		}, this);
	}
});*/
FormValidator.implement({
	validate: function(event){
		var result = this.getFields().map(function(field){
			return field.isVisible(true) ? this.validateField(field, true) : true;
		}, this).every(function(v){ return v;});
		this.fireEvent('formValidate', [result, this.element, event]);
		if (this.options.stopOnFailure && !result && event) event.preventDefault();
		return result;
	}
});

Fx.Accordion.implement({
	display: function(index, useFx){
		useFx = $pick(useFx, true);
		index = ($type(index) == 'element') ? this.elements.indexOf(index) : index;

		if ((this.timer && this.options.wait)) return this;//|| (index === this.previous &&  !this.options.alwaysHide)
		var leaveOpen=false;
		var previousInline=this.previous;
		var objToOpen;
		this.previous = index;
		var obj = {};
		this.elements.each(function(el, i){
			obj[i] = {};
			var hide = (i != index) || (this.options.alwaysHide && (el.offsetHeight > 0));
			this.fireEvent(hide ? 'background' : 'active', [this.togglers[i], el]);

			for (var fx in this.effects) {
				if ((i==index)&&(el.getStyle('width').toInt())) {
					hide=true;
					leaveOpen=true;
				} else if (leaveOpen){
					hide=false;
					leaveOpen=false;
				}
				if (!hide) objToOpen=el;
				obj[i][fx] = hide ? 0 : el[this.effects[fx]];
			}
		}, this);
		if ($defined(objToOpen)) {
			checkImageMagnifier(objToOpen.getElement('img').get('rel'),objToOpen.getElement('.expo-box-inside').get('id'));
		};
		return useFx ? this.start(obj) : this.set(obj);
	}
});

//dbug.enable(true);

MooTools.lang.set('it-IT', 'Date', {

	months: ['Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'Giugno', 'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre', 'Dicembre'],
	days: ['Domenica', 'Lunedì', 'Martedì', 'Mercoledì', 'Giovedì', 'Venerdì', 'Sabato'],
	dateOrder: ['date', 'month', 'year'],
	shortDate: '%d/%m/%Y',
	shortTime: '%H:%M',
	AM: 'AM',
	PM: 'PM',
	ordinal: $lambda(''),

	lessThanMinuteAgo: 'meno di un minuto fa',
	minuteAgo: 'circa un minuto fa',
	minutesAgo: '{delta} minuti fa',
	hourAgo: 'circa un\'ora fa',
	hoursAgo: 'circa {delta} ore fa',
	dayAgo: '1 giorno fa',
	daysAgo: '{delta} giorni fa',
	lessThanMinuteUntil: 'menu di un minuto da ora',
	minuteUntil: 'circa un minuto da ora',
	minutesUntil: '{delta} minuti da ora',
	hourUntil: 'circa un\'ora da ora',
	hoursUntil: 'circa {delta} ore da ora',
	dayUntil: '1 giorno da ora',
	daysUntil: '{delta} giorni da ora'

});
MooTools.lang.setLanguage("it-IT");

//Clientcide.setAssetLocation("/images/clientcide");


/*
Script: Waiter.js

Adds a semi-transparent overlay over a dom element with a spinnin ajax icon.

License:
	http://www.clientcide.com/wiki/cnet-libraries#license
*/
var Waiter = new Class({
	Implements: [Options, Events, Chain, Class.Occlude],
	options: {
		baseHref: 'http://www.cnet.com/html/rb/assets/global/waiter/',
		containerProps: {
			styles: {
				position: 'absolute',
				'text-align': 'center'
			},
			'class':'waiterContainer'
		},
		containerPosition: {},
		msg: false,
		msgProps: {
			styles: {
				'text-align': 'center',
				fontWeight: 'bold'
			},
			'class':'waiterMsg'
		},
		img: {
			src: 'waiter.gif',
			styles: {
				width: 24,
				height: 24
			},
			'class':'waiterImg'
		},
		layer:{
			styles: {
				width: 0,
				height: 0,
				position: 'absolute',
				zIndex: 999,
				display: 'none',
				opacity: 0.9,
				background: '#fff'
			},
			'class': 'waitingDiv'
		},
		useIframeShim: true,
		fxOptions: {},
		injectWhere: null
//	iframeShimOptions: {},
//	onShow: $empty
//	onHide: $empty
	},
	property: 'Waiter',
	initialize: function(target, options){
		this.element = document.id(target)||document.id(document.body);
		if (this.occlude()) return this.occluded;
		this.setOptions(options);
		this.build();
		this.place(target);
	},
	build: function(){
		this.waiterContainer = new Element('div', this.options.containerProps);
		if (this.options.msg) {
			this.msgContainer = new Element('div', this.options.msgProps);
			this.waiterContainer.adopt(this.msgContainer);
			if (!document.id(this.options.msg)) this.msg = new Element('p').appendText(this.options.msg);
			else this.msg = document.id(this.options.msg);
			this.msgContainer.adopt(this.msg);
		}
		if (this.options.img) this.waiterImg = document.id(this.options.img.id) || new Element('img', $merge(this.options.img, {
			src: this.options.baseHref + this.options.img.src
		})).inject(this.waiterContainer);
		this.waiterOverlay = document.id(this.options.layer.id) || new Element('div').adopt(this.waiterContainer);
		this.waiterOverlay.set(this.options.layer);
		try {
			if (this.options.useIframeShim) this.shim = new IframeShim(this.waiterOverlay, this.options.iframeShimOptions);
		} catch(e) {
			//dbug.log("Waiter attempting to use IframeShim but failed; did you include IframeShim? Error: ", e);
			this.options.useIframeShim = false;
		}
		this.waiterFx = this.waiterFx || new Fx.Elements($$(this.waiterContainer, this.waiterOverlay), this.options.fxOptions);
	},
	place: function(target, where){
		var where = where || this.options.injectWhere || target == document.body ? 'inside' : 'after';
		this.waiterOverlay.inject(target, where);
	},
	toggle: function(element, show) {
		//the element or the default
		element = document.id(element) || document.id(this.active) || document.id(this.element);
		this.place(element);
		if (!document.id(element)) return this;
		if (this.active && element != this.active) return this.stop(this.start.bind(this, element));
		//if it's not active or show is explicit
		//or show is not explicitly set to false
		//start the effect
		if ((!this.active || show) && show !== false) this.start(element);
		//else if it's active and show isn't explicitly set to true
		//stop the effect
		else if (this.active && !show) this.stop();
		return this;
	},
	reset: function(){
		this.waiterFx.cancel().set({
			0: { opacity:[0]},
			1: { opacity:[0]}
		});
	},
	start: function(element){
		this.reset();
		element = document.id(element) || document.id(this.element);
		this.place(element);
		var start = function() {
			var dim = element.getComputedSize();
			this.active = element;
			this.waiterOverlay.setStyles({
				width: this.options.layer.width||dim.totalWidth,
				height: this.options.layer.height||dim.totalHeight,
				display: 'block'
			}).position({
				relativeTo: element,
				position: 'upperLeft'
			});
			this.waiterContainer.position($merge({
				relativeTo: this.waiterOverlay
			}, this.options.containerPosition));
			if (this.options.useIframeShim) this.shim.show();
			this.waiterFx.start({
				0: { opacity:[1] },
				1: { opacity:[this.options.layer.styles.opacity]}
			}).chain(function(){
				if (this.active == element) this.fireEvent('onShow', element);
				this.callChain();
			}.bind(this));
		}.bind(this);

		if (this.active && this.active != element) this.stop(start);
		else start();

		return this;
	},
	stop: function(callback){
		if (!this.active) {
			if ($type(callback) == "function") callback.attempt();
			return this;
		}
		this.waiterFx.cancel();
		this.waiterFx.clearChain();
		//fade the waiter out
		this.waiterFx.start({
			0: { opacity:[0]},
			1: { opacity:[0]}
		}).chain(function(){
			this.active = null;
			this.waiterOverlay.hide();
			if (this.options.useIframeShim) this.shim.hide();
			this.fireEvent('onHide', this.active);
			this.callChain();
			this.clearChain();
			if ($type(callback) == "function") callback.attempt();
		}.bind(this));
		return this;
	}
});

