// *** Global Variables ***

var PROTOTYPE_VERSION = parseFloat(Prototype.Version);

var isBrowserIE = document.all;

// *** Global Functions ***

function ABSTRACT_METHOD() {
	throw new Error('Abstract method');
}

function IGNORE_EXCEPTION(/*e*/) {}

function includeJsFile(src, params) {
	document.write('<script src="' + makeUrl(src, params).escapeHTML() + '" type="text/javascript"></script>');
}

if (PROTOTYPE_VERSION >= 1.6) {
function doOnReady(func) {
	document.observe('dom:loaded', func);
}
}

function doOnLoad(func) {
	Event.observe(window, 'load', func);
}

function doOnUnload(func) {
	Event.observe(window, 'unload', func);
}

function getFirstForm(formName) {
	var forms = $A(document.forms).select(function(form) { return (form.id == formName) || (form.name == formName); });
	return (forms.length > 0) ? $(forms[0]) : null;
}

function isArray(obj) {
	return !!Object.isArray(obj);
}

function loadPage(url) {
	window.location = url;
}

function printPage() {
	window.print();
}

function makeUrl(baseUrl, params) {
	if (params) {
		var queryString = Object.toQueryString(params);
		if (queryString.length > 0) {
			var separator = baseUrl.include('?') ? '&' : '?';
			return baseUrl + separator + queryString;
		}
	}
	return baseUrl;
}

function passFormValuesToPage(baseUrl, form, options) {
	form = $(form);
	loadPage(makeUrl(baseUrl, form.getParams(options)));
}

// *** Class: document.viewport ***

if (PROTOTYPE_VERSION >= 1.6) {
document.viewport.getBottom = function() {
	return document.viewport.getHeight() + this.getScrollOffsets().top;
};

document.viewport.getRight = function() {
	return document.viewport.getWidth() + this.getScrollOffsets().left;
};
}

// *** Class: Object ***

Object.toString = function(obj) {
	return ((obj != null) && (obj != undefined)) ? obj.toString() : '';
};

Object.extendEnumerable = function(source) {
	Object.extend(Enumerable, source);
	Object.extend(Array.prototype, source);
	if (PROTOTYPE_VERSION >= 1.6) {
		Object.extend(Hash.prototype, source);
		Object.extend(ObjectRange.prototype, source);
		Object.extend(Ajax.Responders, source);
	}
	Object.extend(Element.ClassNames.prototype, source);
};

// *** Class: Array ***

Object.extend(Array.prototype, {
	empty: function() {
		return (this.length == 0);
	},

	isValidIndex: function(index) {
		return (index >= 0) && (index < this.length);
	},

	contains: function(obj) {
		return (this.indexOf(obj) >= 0);
	},

	add: function(obj) {
		var i = this.length;
		this[i] = obj;
		return i;
	},

	insert: function(index, obj) {
		if (index >= 0) {
			for (var i = this.length; i > index; i--) {
				this[i] = this[i-1];
			}
			this[index] = obj;
		}
	},

	replace: function(index, obj) {
		if (this.isValidIndex(index)) {
			this[index] = obj;
		}
	},

	remove: function(index) {
		if (this.isValidIndex(index)) {
			for (var i = index; i < this.length; i++) {
				this[i] = this[i+1];
			}
			this.length -= 1;
		}
	},

	setLength: function(len, def) {
		if (this.length > len) {
			this.length = len;
		} else {
			while (this.length < len) {
				this.add(def);
			}
		}
	},

	getLengths: function() {
		return this.collect(function(obj) { return Object.toString(obj).length; });
	},

	joinLengths: function(sep) {
		sep = (sep != undefined) ? sep : ','; // For IE
		return this.getLengths().join(sep);
	}
});

// *** Class: Enumerable ***

Object.extendEnumerable({
	findIndex: function(iterator, context) {
		iterator = iterator.bind(context);
		var result;
		this.each(function(value, index) {
			if (iterator(value, index)) {
				result = index;
				throw $break;
			}
		});
		return result;
	},

	findAllIndexes: function(iterator, context) {
		iterator = iterator.bind(context);
		var results = [];
		this.each(function(value, index) {
			if (iterator(value, index)) {
				results.add(index);
			}
		});
		return results;
	}
});

// *** Class: Number ***

Object.extend(Number.prototype, {
	compareTo: function(other) {
		return this - other;
	},

	formatDollar: function() {
		var result = this.toFixed(2);
		if (result == '-0.00') { result = '0.00'; }

		// Add commas.
		var regex = /(-?\d+)(\d{3})/;
		while (regex.test(result)) {
			result = result.replace(regex, "$1,$2");
		}

		return result;
	},

	isDivisibleBy: function(other) {
		return ((this % other) == 0);
	}
});

// *** Class: String ***

Object.extend(String.prototype, {
	equalsIgnoreCase: function(other) {
		return String.equalsIgnoreCase(this, other);
	},

	isDigit: function() {
		if (this.length == 1) {
			var ch = this.charAt(0);
			return ((ch >= '0') && (ch <= '9'));
		}
		return false;
	},

	isNumeric: function() {
		return (this.match(/^\d*$/) != null);
	},

	isInteger: function() {
		return (this.match(/^-?\d*$/) != null);
	},

	countLeadingChars: function(ch) {
		for (var i = 0; i < this.length; i++) {
			if (this.charAt(i) != ch) {
				return i;
			}
		}
		return this.length;
	},

	countTrailingChars: function(ch) {
		for (var i = 0; i < this.length; i++) {
			if (this.charAt(this.length - 1 - i) != ch) {
				return i;
			}
		}
		return this.length;
	},

	padLeft: function(len, ch) {
		ch = ch || ' ';
		return ch.times(len - this.length) + this;
	},

	padRight: function(len, ch) {
		ch = ch || ' ';
		return this + ch.times(len - this.length);
	},

	padZero: function(len) {
		return this.padLeft(len, '0');
	},

	trimLeft: function(ch) {
		ch = ch || ' ';
		return this.substring(this.countLeadingChars(ch), this.length);
	},

	trimRight: function(ch) {
		ch = ch || ' ';
		return this.substring(0, this.length - this.countTrailingChars(ch));
	},

	trimZero: function() {
		return this.trimLeft('0');
	},

	afterLast: function(ch) {
		ch = ch || ' ';
		var i = this.lastIndexOf(ch);
		return (i >= 0) ? this.substring(i + 1) : String(this);
	},

	toInt: function() {
		var str = this.strip().trimZero();
		return !str.empty() ? parseInt(str) : 0;
	},

	splitInts: function(sep) {
		return this.split(sep).collect(function(s) { return s.toInt(); });
	},

	splitToLength: function(len, sep) {
		var results = this.split(sep);
		results.setLength(len, '');
		return results;
	},

	splitIntsToLength: function(len, sep) {
		var results = this.splitInts(sep);
		results.setLength(len, '');
		return results;
	},

	splitUsingLengths: function(lens) {
		var startpos = 0;
		return $A(lens).collect((function(len) {
			var s = this.substr(startpos, len);
			startpos += len;
			return s;
		}).bind(this));
	}
});

String.equalsIgnoreCase = function(str1, str2) {
	str1 = (str1) ? str1.toUpperCase().toLowerCase() : '';
	str2 = (str2) ? str2.toUpperCase().toLowerCase() : '';
	return (str1 == str2);
};

String.padLeft = function(str, len, ch) {
	str = Object.toString(str);
	return str.padLeft(len, ch);
};

String.padRight = function(str, len, ch) {
	str = Object.toString(str);
	return str.padRight(len, ch);
};

String.padZero = function(str, len) {
	str = Object.toString(str);
	return str.padZero(len);
};

// *** Class: Date ***

Object.extend(Date.prototype, {

	equals: function(other) {
		return (other) ? (this.valueOf() == other.valueOf()) : false;
	},

	compareToDateOnly: function(other) {
		var result = this.getFullYear().compareTo(other.getFullYear());
		if (result == 0) {
			result = this.getMonth().compareTo(other.getMonth());
			if (result == 0) {
				result = this.getDate().compareTo(other.getDate());
			}
		}
		return result;
	},

	isInSameMonth: function(other) {
		return ((this.getFullYear() == other.getFullYear())
			&& (this.getMonth() == other.getMonth()));
	},

	addDays: function(days) {
		this.setDate(this.getDate() + days);
		return this;
	},

	addMonths: function(months) {
		this.setMonth(this.getMonth() + months);
		return this;
	},

	clearTime: function() {
		this.setHours(0);
		this.setMinutes(0);
		this.setSeconds(0);
		this.setMilliseconds(0);
		return this;
	},

	getCopy: function() {
		return new Date(this.getTime());
	},

	getCopyPlusDays: function(days) {
		var result = this.getCopy();
		result.addDays(days);
		return result;
	},

	getCopyPlusMonths: function(months) {
		var result = this.getCopy();
		result.addMonths(months);
		return result;
	},

	getFirstOfMonth: function() {
		var result = this.getCopy().clearTime();
		result.setDate(1);
		return result;
	},

	formatMsDsYYYY: function() {
		return (this.getMonth() + 1) + '/' + this.getDate() + '/' + this.getFullYear();
	},

	formatMMsDDsYY: function() {
		return String.padZero((this.getMonth() + 1), 2) + '/' + String.padZero(this.getDate(), 2) + '/' + this.getFullYear().toString().substr(2, 2);
	},

	formatHHsMMsSS: function() {
		return String.padZero(this.getHours(), 2) + ':' + String.padZero(this.getMinutes(), 2) + ':' + String.padZero(this.getSeconds(), 2);
	},

	formatInputDate: function() {
		return this.formatMMsDDsYY();
	}
});

Date.isLeapYear = function(year) {
	return (year.isDivisibleBy(4) && (!year.isDivisibleBy(100) || year.isDivisibleBy(400)));
};

Date.isValid = function(year, month, day) {
	return (year >= 0)
		&& (month >= 0) && (month <= 11)
		&& (day >= 1) && (day <= Date.getDaysInMonth(year, month));
};

Date.isValidMsDsYY = function(str) {
	var good = /^\d{1,2}([\/\-\.])\d{1,2}\1\d{2}$/;
	return (str.match(good) != null);
};

Date.isValidMsDsYYYY = function(str) {
	var good = /^\d{1,2}([\/\-\.])\d{1,2}\1\d{4}$/;
	return (str.match(good) != null);
};

Date.getDaysInMonth = function(year, month) {
	if ((year >= 0) && ((month >= 0) && (month <= 11))) {
		switch (month) {
			case  0: return 31;
			case  1: return Date.isLeapYear(year) ? 29 : 28;
			case  2: return 31;
			case  3: return 30;
			case  4: return 31;
			case  5: return 30;
			case  6: return 31;
			case  7: return 31;
			case  8: return 30;
			case  9: return 31;
			case 10: return 30;
			case 11: return 31;
		}
	}
};

Date.parseMsDsYY = function(str) {
	if (Date.isValidMsDsYY(str)) {
		var separator = str.substr(str.length - 3, 1);
		var index1 = str.indexOf(separator);
		var index2 = str.indexOf(separator, index1 + 1);
		var month = parseInt(str.substring(0, index1), 10) - 1;
		var day = parseInt(str.substring(index1 + 1, index2), 10);
		var year = parseInt(str.substring(index2 + 1), 10);
		year += (year < 50) ? 2000 : 1900;
		if (Date.isValid(year, month, day)) {
			return new Date(year, month, day);
		}
	}
	return undefined;
};

Date.parseMsDsYYYY = function(str) {
	if (Date.isValidMsDsYYYY(str)) {
		var separator = str.substr(str.length - 5, 1);
		var index1 = str.indexOf(separator);
		var index2 = str.indexOf(separator, index1 + 1);
		var month = parseInt(str.substring(0, index1), 10) - 1;
		var day = parseInt(str.substring(index1 + 1, index2), 10);
		var year = parseInt(str.substring(index2 + 1), 10);
		if (Date.isValid(year, month, day)) {
			return new Date(year, month, day);
		}
	}
	return undefined;
};

Date.parseInputDate = function(str) {
	if (Date.isValidMsDsYY(str)) {
		return Date.parseMsDsYY(str);
	}
	if (Date.isValidMsDsYYYY(str)) {
		return Date.parseMsDsYYYY(str);
	}
	return undefined;
};

Date.resolveInputDate = function(date) {
	if ((date) && (typeof date == 'string')) {
		return Date.parseInputDate(date);
	}
	return date;
};

Date.now = function() {
	return new Date();
};

Date.today = function() {
	return Date.now().clearTime();
};

// *** Class: Point ***

var Point = Class.create({

	initialize: function(x, y) {
		this.x = x;
		this.y = y;
	}
});

// *** Class: ImageUtils ***

var ImageUtils = {
	_preloadedImages: null,

	create: function(src) {
		var image = new Image();
		image.src = src;
		return image;
	},

	getPosition: function(image) {
		image = $(image);
		if (isBrowserIE) {
			var offset = image.cumulativeOffset();
			return new Point(offset.left, offset.top);
		} else  {
			return new Point(image.x, image.y);
		}
	},

	preload: function(/* [0..N] or an array of image srcs */) {
		if (document.images) {
			$A(arguments).each(function(arg) {
				if (Object.isArray(arg)) {
					$(arg).each(function(obj) { ImageUtils.preload(obj); });
				} else {
					if (arg.indexOf("#") != 0) {
						ImageUtils._preloadedImages = ImageUtils._preloadedImages || [];
						ImageUtils._preloadedImages.add(ImageUtils.create(arg));
					}
				}
			});
		}
	},

	setSrcForAll: function(images, src) {
		$A(images).each(function(image) { $(image).src = src; });
	},

	toggleSrc: function(image, src1, src2) {
		image = $(image);
		image.src = image.src.match(src1) ? src2 : src1;
	},

	toggleSrcForAll: function(images, src1, src2) {
		$(images).each(function(image) { this.toggleSrc(image, src1, src2); }.bind(this));
	}
};

// *** Class: Xml ***

var Xml = {
	getElementContent: function(element, stripScripts) {
		if (element) {
			var firstChild = element.firstChild;
			if (firstChild) {
				var data = firstChild.data;
				if (data) {
					if (stripScripts) {
						data = data.stripScripts();
					}
					return data;
				}
			}
			return '';
		}
	}
};

// *** Element ***

Object.extend(Element.Methods, {
	clearBody: function(element) {
		element = $(element);
		element.innerHTML = '';
	},

	clearFields: function(element) {
		element = $(element);

		$A(element.getElementsByTagName('input')).each(function(input) {
			switch (input.type.toLowerCase()) {
				case 'checkbox':
					input.checked = false;
					break;
				case 'radio':
					new RadioGroup(input).setSelectedIndex(0);
					break;
				case 'hidden':
				case 'text':
					input.setValue('');
					break;
			}
		});

		$A(element.getElementsByTagName('select')).each(function(select) {
			if (select.type.toLowerCase() == 'select-multiple') {
				$A(select.options).each(function(option) {
					option.selected = false;
				});
			} else {
				select.selectedIndex = 0;
			}
		});

		$A(element.getElementsByTagName('textarea')).each(function(textarea) {
			textarea.setValue('');
		});
	}
});

// *** Form ***

if (PROTOTYPE_VERSION >= 1.6) {
Object.extend(Form.Methods, {
	getField: function(form, elementName) {
		form = $(form);
		return $(form.elements[elementName]);
	},

	getParams: function(form, options) {
		form = $(form);
		options = options || {};
		var params = {};
		$A(form.elements).each(function(e) {
			e = $(e);
			if ((!options.includeClass || Element.hasClassName(e, options.includeClass))
					&& (!options.includeNames || options.includeNames.include(e.name))
					&& (!options.excludeNames || !options.excludeNames.include(e.name))) {
				var value = e.getValue();
				if ((value == undefined) && !e.isCheckbox() && !e.isRadio()) { value = ''; }
				if (value != null) {
					if (params[e.name] != undefined) {
						if (!Object.isArray(params[e.name])) {
							params[e.name] = [params[e.name], value];
						} else {
							params[e.name].add(value);
						}
					} else {
						params[e.name] = value;
					}
				}
			}
		});
		return params;
	}
});
Element.addMethods();
};

// *** Form Element ***

if (PROTOTYPE_VERSION >= 1.6) {
Object.extend(Form.Element.Methods, {
	isCheckbox: function(element) {
		element = $(element);
		return ((element.tagName.toLowerCase() == 'input') && (element.type.toLowerCase() == 'checkbox'));
	},

	isRadio: function(element) {
		element = $(element);
		return ((element.tagName.toLowerCase() == 'input') && (element.type.toLowerCase() == 'radio'));
	},

	setEnabledState: function(element, state) {
		return (state) ? element.enable() : element.disable();
	}
});
Element.addMethods();
};

// *** Select Element ***

if (PROTOTYPE_VERSION >= 1.6) {
Element.addMethods('SELECT', {
	isValidIndex: function(select, index) {
		select = $(select);
		return (index >= 0) && (index < select.length);
	},

	getIndexOfValue: function(select, value) {
		select = $(select);
		for (var i = 0; i < select.length; i++) {
			var optionValue = Form.Element.Serializers.optionValue(select.options[i]);
			if (optionValue == value) {
				return i;
			}
		}
		return -1;
	},

	selectOptionByValue: function(select, value) {
		select = $(select);
		select.selectedIndex = select.getIndexOfValue(value);
		return select.selectedIndex;
	},

	appendOption: function(select, value, text) {
		select = $(select);
		var option = new Option(text, value);
		try {
			select.add(option, null);
		} catch(e) {
			IGNORE_EXCEPTION(e);
			select.add(option); // IE
		}
	},

	insertOptionAt: function(select, index, value, text) {
		select = $(select);
		if (index >= 0) {
			if (index < select.length) {
				var option = new Option(text, value);
				try {
					select.add(option, select.options[index]);
				} catch(e) {
					IGNORE_EXCEPTION(e);
					select.add(option, index); // IE
				}
			} else {
				select.appendOption(value, text);
			}
		}
	},

	replaceOptionAt: function(select, index, value, text) {
		select = $(select);
		if (index >= 0) {
			select.removeOptionAt(index);
			select.insertOptionAt(index, value, text);
		}
	},

	removeOptionAt: function(select, index) {
		select = $(select);
		if (select.isValidIndex(index)) {
			select.remove(index);
		}
	},

	removeAllOptions: function(select) {
		select = $(select);
		select.removeAllOptionsExcept(0);
	},

	removeAllOptionsExcept: function(select, count) {
		select = $(select);
		if (count < 0) {
			count = 0;
		}
		for (var i = select.length - 1; i >= count; i--) {
			select.remove(i);
		}
	},

	submit: function(select) {
		select = $(select);
		if (select.value && !select.value.empty()) {
			select.form.submit();
			return true;
		}
		return false;
	}
});
}

// *** Class: RadioGroup ***

var RadioGroup = Class.create({
	initialize: function(element, name) {
		element = $(element);
		if (element.type && (element.type.toLowerCase() == 'radio')) {
			this.form = $(element.form);
			this.name = element.name;
		} else if (element.tagName.toLowerCase() == 'form') {
			this.form = element;
			this.name = name;
		} else {
			throw 'Invalid arguments to RadioGroup constructor';
		}
	},

	getButtons: function() {
		return $A(this.form.getInputs('radio', this.name));
	},

	getButton: function(value) {
		return this.getButtons().find(function(e) { return (e.value == value); });
	},

	getButtonAt: function(index) {
		return this.getButtons()[index];
	},

	getCheckedButton: function() {
		return this.getButtons().find(function(e) { return e.checked; });
	},

	getValue: function() {
		var checkedButton = this.getCheckedButton();
		return (checkedButton) ? $F(checkedButton) : undefined;
	},

	getSelectedIndex: function() {
		var index = this.getButtons().findIndex(function(button) { return button.checked; });
		return (index != undefined) ? index : -1;
	},

	clear: function() {
		this.getButtons().each(function(e) { e.checked = false; });
		return this;
	},

	setCheckedButton: function(button) {
		if (button) {
			button.checked = true;
		} else {
			this.clear();
		}
		return this;
	},

	setValue: function(value) {
		this.setCheckedButton(this.getButton(value));
		return this;
	},

	setSelectedIndex: function(index) {
		this.setCheckedButton(this.getButtonAt(index));
		return this;
	},

	enable: function() {
		this.getButtons().each(function(e) { e.enable(); });
		return this;
	},

	disable: function() {
		this.getButtons().each(function(e) { e.disable(); });
		return this;
	}
});

function $RF(element, name) {
	return (new RadioGroup(element, name)).getValue();
}

// *** Class: MultiBox ***

var MultiBox = Class.create({
	initialize: function(element, name) {
		element = $(element);
		if (element.type && (element.type.toLowerCase() == 'checkbox')) {
			this.form = $(element.form);
			this.name = element.name;
		} else if (element.tagName.toLowerCase() == 'form') {
			this.form = element;
			this.name = name;
		} else {
			throw 'Invalid arguments to MultiBox constructor';
		}
	},

	getCheckboxes: function() {
		return $A(this.form.getInputs('checkbox', this.name));
	},

	getCheckbox: function(value) {
		return this.getCheckboxes().find(function(e) { return (e.value == value); });
	},

	getCheckboxAt: function(index) {
		return this.getCheckboxes()[index];
	},

	empty: function() {
		return this.getChecked().empty();
	},

	getChecked: function() {
		return this.getCheckboxes().select(function(e) { return e.checked; });
	},

	getValues: function() {
		return this.getChecked().pluck('value');
	},

	getIndexes: function() {
		return this.getCheckboxes().findAllIndexes(function(checkbox) { return checkbox.checked; });
	},

	clear: function() {
		this.getCheckboxes().each(function(e) { e.checked = false; });
		return this;
	},

	setChecked: function(checkboxes) {
		checkboxes = checkboxes || [];
		this.setValues(checkboxes.pluck('value'));
		return this;
	},

	setValues: function(values) {
		values = values || [];
		this.getCheckboxes().each(function(e) { e.checked = values.include(e.value); });
		return this;
	},

	setIndexes: function(indexes) {
		indexes = indexes || [];
		this.getCheckboxes().each(function(e, i) { e.checked = indexes.include(i); });
		return this;
	},

	enable: function() {
		this.getCheckboxes().each(function(e) { e.enable(); });
		return this;
	},

	disable: function() {
		this.getCheckboxes().each(function(e) { e.disable(); });
		return this;
	}
});

function $MF(element, name) {
	return (new MultiBox(element, name)).getValues();
}

// *** Class: Toggle ***

var Toggle = Class.create({

	initialize: function() {},

	getState: function() { ABSTRACT_METHOD(); },

	setState: function(/*state*/) { ABSTRACT_METHOD(); },

	on: function() {
		this.setState(true);
	},

	off: function() {
		this.setState(false);
	},

	toggle: function() {
		this.setState(!this.getState());
	}
});

// *** Class: ImageToggle ***

var ImageToggle = Class.create(Toggle, {

	initialize: function($super, image, onSrc, offSrc) {
		$super();
		this.image = $(image);
		this.onSrc = onSrc;
		this.offSrc = offSrc;
		this._onImageName = onSrc.afterLast('/').afterLast('\\');
	},

	getState: function() {
		return this.image.src.endsWith(this._onImageName);
	},

	setState: function(state) {
		this.image.src = state ? this.onSrc : this.offSrc;
	}
});

// *** Class: ClassToggle ***

var ClassToggle = Class.create(Toggle, {

	initialize: function($super, element, onClass, offClass) {
		$super();
		this.element = $(element);
		this.onClass = onClass;
		this.offClass = offClass;
	},

	getState: function() {
		return this.element.hasClassName(this.onClass);
	},

	setState: function(state) {
		if (state) {
			this.element.removeClassName(this.offClass);
			this.element.addClassName(this.onClass);
		} else {
			this.element.removeClassName(this.onClass);
			this.element.addClassName(this.offClass);
		}
	}
});

// *** Class: Tab ***

var Tab = Class.create(Toggle, {

	initialize: function($super, toggle) {
		$super();
		this._toggle = toggle;
		this.index = null;
		this.onClick = null;
	},

	getState: function() {
		return this._toggle.getState();
	},

	setState: function(state) {
		this._toggle.setState(state);
	},

	_callOnClick: function() {
		if (this.onClick) {
			this.onClick.call(this, this.index);
			return true;
		}
		return false;
	}
});

// *** Class: AnchorTab ***

var AnchorTab = Class.create(Tab, {

	initialize: function($super, anchor, toggle) {
		$super(toggle);
		this.anchor = $(anchor);

		Event.observe(
			this.anchor,
			'click',
			(function(event) {
				if (this._callOnClick()) {
					Event.stop(event);
				}
			}).bindAsEventListener(this)
		);
	}
});

// *** Class: ImageTab ***

var ImageTab = Class.create(AnchorTab, {

	initialize: function($super, anchor, onSrc, offSrc) {
		anchor = $(anchor);
		$super(anchor, new ImageToggle(anchor.firstDescendant(), onSrc, offSrc));
	}
});

// *** Class: ClassTab ***

var ClassTab = Class.create(AnchorTab, {

	initialize: function($super, anchor, onClass, offClass) {
		anchor = $(anchor);
		$super(anchor, new ClassToggle(anchor, onClass, offClass));
	}
});

// *** Class: TabSet ***

var TabSet = Class.create({

	initialize: function() {
		this.tabs = [];
		this.onChange = null;
	},

	add: function(tab) {
		tab.index = this.tabs.add(tab);
		tab.onClick = (function(index) {
			this.showTab(index);
			this._callOnChange(index);
		}).bind(this);
	},

	showTab: function(index) {
		this.tabs.each(function(tab) { tab.setState(tab.index == index); });
	},

	_callOnChange: function(index) {
		if (this.onChange) {
			this.onChange.call(this, index);
			return true;
		}
		return false;
	}
});

// *** Class: Pane ***

var Pane = Class.create({

	initialize: function(element) {
		this.element = $(element);
		this.index = null;
	},

	show: function() {
		this.element.show();
	},

	hide: function() {
		this.element.hide();
	},

	setState: function(state) {
		if (state) {
			this.show();
		} else {
			this.hide();
		}
	}
});

// *** Class: PaneSet ***

var PaneSet = Class.create({

	initialize: function() {
		this.panes = [];
	},

	add: function(pane) {
		pane.index = this.panes.add(pane);
	},

	showPane: function(index) {
		this.panes.each(function(pane) { pane.setState(pane.index == index); });
	}
});

// *** Class: TabbedPaneSet ***

var TabbedPaneSet = Class.create({

	initialize: function(options) {
		options = options || {};

		this.autoHistory = !!options.autoHistory;
		this.paneSets = new Array(options.paneSetCount || 1);
		for (var i = 0; i < this.paneSets.length; i++) {
			this.paneSets[i] = new PaneSet();
		}
		this.tabSet = new TabSet();

		this.tabSet.onChange = (function(index) {
			this.paneSets.each(function(paneSet) { paneSet.showPane(index); });
			if (this.autoHistory) {
				dhtmlHistory.add(index);
			}
		}).bind(this);

		if (this.autoHistory) {
			dhtmlHistory.addListener((function(location) {
				this._showPane(location);
			}).bind(this));
		}
	},

	add: function(tab /* [0..N] or an array of panes */) {
		var panes = $A(arguments).slice(1);
		if ((panes.length == 1) && Object.isArray(panes[0])) {
			panes = panes[0];
		}
		this.tabSet.add(tab);
		this.paneSets.each(function(paneSet, index) {
			var pane = panes[index];
			if (!(pane instanceof Pane)) {
				pane = new Pane(pane);
			}
			paneSet.add(pane);
		});
	},

	showTab: function(index) {
		this.tabSet.showTab(index);
		this.paneSets.each(function(paneSet) { paneSet.showPane(index); });
	}
});

// *** Class: HelperText ***

var HelperText = {
	CSS_CLASS_NAME: 'helpertext',

	_hide: function(element) {
		element = $(element);
		if (element.helperText) {
			if (element.hasClassName(HelperText.CSS_CLASS_NAME)) {
				element.removeClassName(HelperText.CSS_CLASS_NAME);
				if (element.value == element.helperText) {
					element.value = '';
				}
			}
		}
	},

	_show: function(element) {
		element = $(element);
		if (element.helperText) {
			if (element.value.empty()) {
				element.addClassName(HelperText.CSS_CLASS_NAME);
				element.value = element.helperText;
			}
		}
	},

	getValue: function(element) {
		element = $(element);
		var result = element.value;
		if ((element.helperText) && element.hasClassName(HelperText.CSS_CLASS_NAME)) {
			if (result == element.helperText) {
				result = '';
			}
		}
		return result;
	},

	setValue: function(element, value) {
		element = $(element);
		element.value = value || '';
		if (element.helperText) {
			if (!element.value.empty()) {
				element.removeClassName(HelperText.CSS_CLASS_NAME);
			} else if (!element.hasFocus) {
				element.addClassName(HelperText.CSS_CLASS_NAME);
				element.value = element.helperText;
			}
		}
	},

	decorate: function(element, helperText) {
		element = $(element);

		element.blur();

		element.helperText = helperText;
		element.hasFocus   = false;

		Event.observe(
			element,
			'focus',
			(function() { this.hasFocus = true; HelperText._hide(this); }).bindAsEventListener(element)
		);

		Event.observe(
			element,
			'blur',
			(function() { this.hasFocus = false; HelperText._show(this); }).bindAsEventListener(element)
		);

		if (element.form) {
			Event.observe(
				element.form,
				'submit',
				(function() { HelperText._hide(this); }).bindAsEventListener(element)
			);
		};

		HelperText._show(element);
	},

	decorateForm: function(form, infos) {
		form = $(form);
		infos.each(function(info) { HelperText.decorate(form.getField(info.element), info.text); });
	}
};

if (PROTOTYPE_VERSION >= 1.6) {
Form.Element.Serializers['textarea'] = function(element, value) {
    if (value === undefined) return HelperText.getValue(element);
    else HelperText.setValue(element, value);
};
}

// *** Class: Marquee ***

var Marquee = Class.create({

	initialize: function(element, messages, interval) {
		this.element = $(element);
		this.messages = messages;
		this.interval = interval;

		this._timeoutId = null;
	},

	start: function() {
		this._messageIndex = 0;
		this._showMessage();
	},

	stop: function() {
		if (this._timeoutId) {
			clearTimeout(this._timeoutId);
		}
	},

	_showMessage: function() {
		this.element.update(this.messages[this._messageIndex]);
		this._messageIndex++;
		if (this._messageIndex == this.messages.length) {
			this._messageIndex = 0;
		}
		this._timeoutId = setTimeout((function() { this._showMessage(); }).bind(this), this.interval);
	}
});

// *** Class: SlideShow ***

var SlideShow = Class.create({

	initialize: function(image, imageSrcs, options) {
		options = options || {};

		this.basePath = options.basePath;
		this.image = $(image);
		this.imageSrcs = imageSrcs;
		this.index = 0;
		this.onChange = options.onChange;
	},

	_getNextIndex: function(direction) {
		var result = this.index + direction;
		if (result < 0) { result = this.imageSrcs.length - 1; }
		if (result >= this.imageSrcs.length) { result = 0; }
		return result;
	},

	nextPicture: function(direction) {
		this.showPicture(this._getNextIndex(direction));
	},

	showPicture: function(index) {
		if (this.index != index) {
			this.index = index;

			if (isBrowserIE) {
				this.image.style.filter = 'blendTrans(duration=1)';
				this.image.filters.blendTrans.Apply();
			}
			this.image.src = (this.basePath ? this.basePath : '') + this.imageSrcs[this.index];
			if (isBrowserIE) {
				this.image.filters.blendTrans.Play();
			}

			if (this.onChange) {
				this.onChange.call(null, this.index);
			}
		}
	}
});

// *** Class: Ajax.MultiUpdater ***

if (PROTOTYPE_VERSION >= 1.6) {
Ajax.MultiUpdater = Class.create(Ajax.Request, {

	initialize: function($super, containers, url, options) {
		var successContainerMap = this._createContainerMap(containers.success || containers);
		this.containers = {
			success: successContainerMap,
			failure: containers.failure
		};

		options = options || {};
		var onComplete = options.onComplete;
		options.onComplete = (function(response, param) {
			this.updateContent(response);
			if (Object.isFunction(onComplete)) onComplete(response, param);
		}).bind(this);

		$super(url, options);
	},

	updateContent: function(response) {
		if (this.success() || !this.containers.failure) {
			var root = response.responseXML.documentElement;
			if (root) {
				var blocks = $A(root.getElementsByTagName('block'));
				if (blocks) {
					blocks.each((function(block, index) {
						var blockName = block.getAttribute('name') || index;
						var content = Xml.getElementContent(block, !this.options.evalScripts);
						var receiver = this._getContainer(blockName);
						this._updateReceiver(receiver, content, this.options.insertion);
					}).bind(this));
				}
			}
		} else {
			var receiver = this.containers.failure;
			var content = response.responseText;
			if (!this.options.evalScripts) {
				content = content.stripScripts();
			}
			this._updateReceiver(receiver, content, this.options.insertion);
		}

		if (this.success()) {
			if (this.onComplete) {
				this.onComplete.bind(this).defer();
			}
		}
	},

	_createContainerMap: function(containers) {
		var map = {};
		containers.each(function(container, index) {
			if (container.name) {
				map[container.name] = container.element;
			} else {
				map[index] = container;
			}
		});
		return map;
	},

	_getContainer: function(blockName) {
		var container = this.containers.success[blockName];
		if (container) {
			return $(container);
		}
		return null;
	},

	_updateReceiver: function(receiver, content, insertion) {
		if (receiver) {
			if (insertion) {
				if (Object.isString(insertion)) {
					var insertions = {};
					insertions[insertion] = content;
					receiver.insert(insertions);
				} else {
					insertion(receiver, content);
				}
			} else {
				receiver.update(content);
			}
		}
	}
});
};
