// *** Global Variables ***

var PROTOTYPE_VERSION = parseFloat(Prototype.Version);

var isBrowserIE = (document.all ? true : false);

// *** Global Functions ***

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

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

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 ***

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

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

// *** Class: NumberUtils ***

var NumberUtils = {

	isDivisibleBy: function(num1, num2) {
		return ((num1 % num2) == 0);
	},

	formatDollar: function(number) {
		var result = number.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;
	}
};

// *** Class: Array ***

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

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

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

// *** Class: Date ***

Object.extend(Date.prototype, {

	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;
	},

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

	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);
	},

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

Date.getDaysInMonth = function(year, month) {
	if ((year >= 0) && (year <= 9999)) {
		if ((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;
			}
		}
	}
	return 0;
};

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

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

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.now = function() {
	return new Date();
};

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

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 null;
};

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 null;
};

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

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

// *** Class: Point ***

var Point = Class.create({

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

// *** Class: String ***

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

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

String.padZero = function(str, len) {
	str = str.toString();
	var pad = '';
	if (str.length < len) {
		for (var i=0; i < (len - str.length); i++) {
			pad += '0';
		}
	}
	return pad + str;
};

// *** Class: ImageUtils ***

var ImageUtils = {};

ImageUtils._preloadedImages = null;

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

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

ImageUtils.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));
				}
			}
		});
	}
};

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

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

ImageUtils.toggleSrcForAll = function(images, src1, src2) {
	$(images).each(function(image) {
		toggleSrc(image, src1, src2);
	});
};

// Form & Element

if (PROTOTYPE_VERSION >= 1.6) {

Object.extend(Form.Element.Methods, {
	getParamValue: function(element) {
		element = $(element);
		return (element.type == 'radio') ? $RF(element) : HelperText.getRealValue(element);
	}
});

Object.extend(Form.Methods, {
	getParamValue: function(form, elementName) {
		form = $(form);
		return Element.getParamValue(form.elements[elementName]);
	},

	getParams: function(form, options) {
		form = $(form);
		options = options || {};
		var params = new Object();
		$A(form.elements).each(
			function(e) {
				if (!params[e.name]
						&& (!options.includeClass || Element.hasClassName(e, options.includeClass))
						&& (!options.includeNames || options.includeNames.include(e.name))
						&& (!options.excludeNames || !options.excludeNames.include(e.name))) {
					params[e.name] = Form.Element.getParamValue(e);
				}
			}
		);
		return params;
	}
});

Element.addMethods();

};

// *** Class: Toggle ***

var Toggle = Class.create({

	initialize: function() {},

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

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

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

	setState: function(state) {
		state ? this.on() : this.off();
	},

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

// *** Class: ToggleImage ***

var ToggleImage = Class.create(Toggle, {

	initialize: function($super, image, onSrc, offSrc) {
		$super();
		this.image = $(image);
		this.onSrc = onSrc;
		this.offSrc = offSrc;
	},

	getState: function() {
		return (this.image.src == this.onSrc);
	},

	off: function() {
		this.image.src = this.offSrc;
	},

	on: function() {
		this.image.src = this.onSrc;
	}
});

// *** Class: ToggleClass ***

var ToggleClass = 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);
	},

	off: function() {
		this.element.removeClassName(this.onClass);
		this.element.addClassName(this.offClass);
	},

	on: function() {
		this.element.removeClassName(this.offClass);
		this.element.addClassName(this.onClass);
	}
});

// *** Class: Tab ***

var Tab = Class.create({

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

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

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

	setState: function(state) {
		state ? this.on() : this.off();
	},

	_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) {
		$super();
		this.anchor = $(anchor);

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

// *** Class: ToggleAnchorTab ***

var ToggleAnchorTab = Class.create(AnchorTab, {

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

	off: function() {
		this.toggle.off();
	},

	on: function() {
		this.toggle.on();
	}
});

// *** Class: ToggleImageTab ***

var ToggleImageTab = Class.create(ToggleAnchorTab, {

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

// *** Class: ToggleClassTab ***

var ToggleClassTab = Class.create(ToggleAnchorTab, {

	initialize: function($super, anchor, onClass, offClass) {
		anchor = $(anchor);
		$super(anchor, new ToggleClass(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;
	},

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

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

	setState: function(state) {
		state ? this.show() : 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 || false;
		this.paneSets = $A(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 = $A(panes[0]);
		}
		this.tabSet.add(tab);
		this.paneSets.each(function(paneSet, index) {
			paneSet.add(panes[index]);
		});
	},

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

// *** 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 || null;
		this.image = $(image);
		this.imageSrcs = imageSrcs;
		this.index = 0;
		this.onChange = options.onChange || null;
	},

	_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;
	},

	_setIndex: 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);
			}
		}
	},

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

	showPicture: function(index) {
		this._setIndex(index);
	}
});

var Xml = {

	getTag: function(xml, tagName) {
		if (xml) {
			var tags = xml.getElementsByTagName(tagName);
			if (tags && (tags.length > 0)) {
				return tags[0];
			}
		}
		return null;
	},

	getTagContent: function(tag, stripScripts) {
		stripScripts = stripScripts || false;
		if (tag) {
			var firstChild = tag.firstChild;
			if (firstChild) {
				var data = firstChild.data;
				if (data) {
					if (stripScripts) {
						data = data.stripScripts();
					}
					return data;
				}
			}
		}
		return null;
	}
};

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 blocksTag = Xml.getTag(response.responseXML, Ajax.MultiUpdater._BLOCKS_TAG_NAME);
			if (blocksTag) {
				var blockTags = $A(blocksTag.getElementsByTagName(Ajax.MultiUpdater._BLOCK_TAG_NAME));
				if (blockTags) {
					blockTags.each((function(blockTag, index) {
						var blockName = blockTag.getAttribute(Ajax.MultiUpdater._BLOCK_NAME_ATTR_NAME) || index;
						var content = Xml.getTagContent(blockTag, !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);
			}
		}
	}
});

Ajax.MultiUpdater._BLOCKS_TAG_NAME      = 'blocks';
Ajax.MultiUpdater._BLOCK_TAG_NAME       = 'block';
Ajax.MultiUpdater._BLOCK_NAME_ATTR_NAME = 'name';

// *** Class: String ***

String.compare = function(str1, str2) {
	str1 = (str1) ? str1 : '';
	str2 = (str2) ? str2 : '';
	return (str1 > str2) ? 1 : ((str1 < str2) ? -1 : 0);
};

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

// *** Class: Object ***

Object.getProperty = function(obj, property) {
	if (Object.isArray(property)) {
		if (property.size() > 1) {
			return Object.getProperty(Object.getProperty(obj, property[0]), property.slice(1));
		} else {
			return Object.getProperty(obj, property[0]);
		}
	} else {
		return obj[property];
	}
};

// *** Class: Transformer ***

var Transformer = {
	getMapLookup: function(map) {
		return function(input) { return map[input]; };
	},

	getProperty: function(property) {
		return function(input) { return Object.getProperty(input, property); };
	}
};

// *** Class: Comparator ***

var Comparator = {
	NATURAL: function(v1, v2) {
		return (v1 > v2) ? 1 : ((v1 < v2) ? -1 : 0);
	},

	getTransformed: function(transformer, comparator) {
		comparator = comparator || Comparator.NATURAL;
		return function(o1, o2) {
			return comparator.call(null, transformer.call(null, o1), transformer.call(null, o2));
		};
	},

	getMapLookup: function(map, comparator) {
		return Comparator.getTransformed(Transformer.getMapLookup(map), comparator);
	},

	getProperty: function(property, comparator) {
		return Comparator.getTransformed(Transformer.getProperty(property), comparator);
	},

	getNull: function(nullPos, comparator) {
		return function(v1, v2) {
			var isNull1 = ((v1 === null) || Object.isUndefined(v1));
			var isNull2 = ((v2 === null) || Object.isUndefined(v2));
			if (!isNull1 && !isNull2) {
				return comparator.call(null, v1, v2);
			} else if (isNull1 && !isNull2) {
				return (nullPos == 'high') ? 1 : -1;
			} else if (!isNull1 && isNull2) {
				return (nullPos == 'high') ? -1 : 1;
			} else {
				return 0;
			}
		};
	},

	getReverse: function(comparator) {
		return function(v1, v2) {
			return -1 * comparator.call(null, v1, v2);
		};
	},

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

		var result = (options.comparator) ? options.comparator : Comparator.NATURAL;

		if (options.nulls) {
			result = Comparator.getNull(options.nulls, result);
		}

		if (options.map) {
			result = Comparator.getMapLookup(options.map, result);
		}

		if (options.property) {
			result = Comparator.getProperty(options.property, result);
		}

		if (options.reverse) {
			result = Comparator.getReverse(result);
		}

		return result;
	},

	getChain: function() {
		var comparators = $A(arguments);
		return function(v1, v2) {
			var result = 0;
			comparators.each(function(comparator) {
				result = comparator.call(null, v1, v2);
				if (result != 0) {
					throw $break;
				}
			});
			return result;
		};
	}
};

// *** Class: Element ***

Element.addMethods({
	showOrHide: function(element, show) {
		element = $(element);
		if (show) {
			element.show();
		} else {
			element.hide();
		}
	},

	sortChildElements: function(element, comparator) {
		element = $(element);
		var childElements = element.childElements();
		childElements.sort(comparator);
		childElements.each(function(childElement) {
			element.appendChild(childElement);
		});
	}
});
