var tle = {
	Version: '1.2.0'
}

// s:tle.Class, excerpt from prototype's code
// since 1.2.0
tle.Class = {
	create: function() {
		return function() {
			this.init.apply(this, arguments);
		}
	},
	extend: function(destination, source) {
		for (property in source) {
			destination[property] = source[property];
		}
		return destination;
	}
}
// e:tle.Class

// s:tle.FormChecker
tle.FormChecker = tle.Class.create();
tle.FormChecker.prototype = {
	init: function(checkForm) {
		this.checkForm = checkForm;
		this.validatorList = new Array();
	},
	setCheckForm: function(checkForm) {
		this.checkForm = checkForm;
	},
	checkRequired: function(fieldName, errorMessage, focus) {
		this.validatorList.push(new tle.RequiredValidator(this.checkForm, fieldName, errorMessage, focus));
	},
	checkLengthEquals: function(fieldName, length, errorMessage, focus) {
		this.validatorList.push(new tle.LengthEqualsValidator(this.checkForm, fieldName, length, errorMessage, focus));
	},
	checkLengthByteEquals: function(fieldName, length, errorMessage, focus) {
		this.validatorList.push(new tle.LengthByteEqualsValidator(this.checkForm, fieldName, length, errorMessage, focus));
	},
	checkMaxLength: function(fieldName, maxLength, errorMessage, focus) {
		this.validatorList.push(new tle.MaxLengthValidator(this.checkForm, fieldName, maxLength, errorMessage, focus));
	},
	checkMaxLengthByte: function(fieldName, maxLength, errorMessage, focus) {
		this.validatorList.push(new tle.MaxLengthByteValidator(this.checkForm, fieldName, maxLength, errorMessage, focus));
	},
	checkMinLength: function(fieldName, minLength, errorMessage, focus) {
		this.validatorList.push(new tle.MinLengthValidator(this.checkForm, fieldName, minLength, errorMessage, focus));
	},
	checkMinLengthByte: function(fieldName, minLength, errorMessage, focus) {
		this.validatorList.push(new tle.MinLengthByteValidator(this.checkForm, fieldName, minLength, errorMessage, focus));
	},
	checkRegex: function(fieldName, regex, errorMessage, focus) {
		this.validatorList.push(
			new tle.RegexValidator(this.checkForm, fieldName, regex, errorMessage, focus));
	},
	checkAlphaNum: function(fieldName, errorMessage, focus) {
		this.validatorList.push(
			new tle.RegexValidator(this.checkForm, fieldName, 
				/^[a-zA-Z0-9]+$/, errorMessage, focus));
	},
	checkOnlyNumber: function(fieldName, errorMessage, focus) {
		this.validatorList.push(
			new tle.RegexValidator(this.checkForm, fieldName, 
				/^[0-9]+$/, errorMessage, focus));
	},
	checkDecimal: function(fieldName, errorMessage, focus) {
		this.validatorList.push(
			new tle.RegexValidator(this.checkForm, fieldName, 
				/^(\-)?[0-9]*(\.[0-9]*)?$/, errorMessage, focus));
	},
	checkEmail: function(fieldName, errorMessage, focus) {
		// modified regex provided by shrek
		this.validatorList.push(
			new tle.RegexValidator(this.checkForm, fieldName,
				/^([A-Za-z0-9_-]([A-Za-z0-9_-]|[\+\.])*)@(\[\d{1,3}(\.\d{1,3}){3}]|[A-Za-z0-9][A-Za-z0-9_-]*(\.[A-Za-z0-9][A-Za-z0-9_-]*)+)$/,
				errorMessage, focus) );
	},
	checkSelected: function(fieldName, firstIdx, errorMessage, focus) {
		this.validatorList.push(new tle.SelectionValidator(this.checkForm, fieldName, firstIdx, errorMessage, focus));
	},
	checkAtLeastOneChecked: function(fieldName, errorMessage, focus) {
		this.validatorList.push(new tle.AtLeastOneCheckValidator(this.checkForm, fieldName, errorMessage, focus));
	},
	checkEquals: function(fieldName, val, errorMessage, focus) {
		this.validatorList.push(new tle.EqualsValidator(this.checkForm, fieldName, val, errorMessage, focus));
	},
	checkTwoFieldEquals: function(field1Name, field2Name, errorMessage, focus, focusFieldName) {
		this.validatorList.push(
			new tle.TwoFieldEqualsValidator(this.checkForm, 
				field1Name, field2Name, 
				errorMessage, 
				focus,
				focusFieldName
				));
	},
	validate: function() {
		for (var vali = 0 ; vali < this.validatorList.length ; vali ++ ) {
			validator = this.validatorList[vali];
			if (validator.validate() == false) {
				alert(validator.getErrorMessage());
				if (validator.isFocus() == true) {
					this.checkForm[validator.getFieldName()].focus();
				}
				return false;
			}
		}
		return true;
	},
	getForm: function() {
		return this.checkForm;
	}
}

// Validator is base class of all validators
tle.Vaildator = function() {}
tle.Vaildator.prototype = {
	getFieldName: function() {
		return this.fieldName;
	},
	getErrorMessage: function() {
		return this.errorMessage;
	},
	isFocus: function() {
		return this.focus;
	}
}
// required validator
tle.RequiredValidator = tle.Class.create();
tle.RequiredValidator.prototype = tle.Class.extend(new tle.Vaildator,
{
	init: function(form, fieldName, errorMessage, focus) {
		this.form = form;
		this.fieldName = fieldName;
		this.errorMessage = errorMessage;
		this.focus = focus;
	},
	validate: function() {
		return this.form[this.fieldName].value != '';
	}
});
// length equals validator
tle.LengthEqualsValidator = tle.Class.create();
tle.LengthEqualsValidator.prototype = tle.Class.extend(new tle.Vaildator,
{
	init: function(form, fieldName, length, errorMessage, focus) {
		this.form = form;
		this.fieldName = fieldName;
		this.errorMessage = errorMessage;
		this.focus = focus;
		this.length = length;
	},
	validate: function() {
		return this.form[this.fieldName].value.length == this.length;
	}
});
// length byte equals validator
tle.LengthByteEqualsValidator = tle.Class.create();
tle.LengthByteEqualsValidator.prototype = tle.Class.extend(new tle.Vaildator,
{
	init: function(form, fieldName, length, errorMessage, focus) {
		this.form = form;
		this.fieldName = fieldName;
		this.errorMessage = errorMessage;
		this.focus = focus;
		this.length = length;
	},
	validate: function() {
		var str = this.form[this.fieldName].value;
		return (str.length+(escape(str)+"%u").match(/%u/g).length-1) == this.length;
	}
});
// max length validator
tle.MaxLengthValidator = tle.Class.create();
tle.MaxLengthValidator.prototype = tle.Class.extend(new tle.Vaildator,
{
	init: function(form, fieldName, maxLength, errorMessage, focus) {
		this.form = form;
		this.fieldName = fieldName;
		this.errorMessage = errorMessage;
		this.focus = focus;
		this.maxLength = maxLength;
	},
	validate: function() {
		return this.form[this.fieldName].value.length <= this.maxLength;
	}
});

// max length(byte) validator
tle.MaxLengthByteValidator = tle.Class.create();
tle.MaxLengthByteValidator.prototype = tle.Class.extend(new tle.Vaildator,
{
	init: function(form, fieldName, maxLength, errorMessage, focus) {
		this.form = form;
		this.fieldName = fieldName;
		this.errorMessage = errorMessage;
		this.focus = focus;
		this.maxLength = maxLength;
	},
	validate: function() {
		var str = this.form[this.fieldName].value;
		return(str.length+(escape(str)+"%u").match(/%u/g).length-1) <= this.maxLength;
	}
});

// min length validator
tle.MinLengthValidator = tle.Class.create();
tle.MinLengthValidator.prototype = tle.Class.extend(new tle.Vaildator,
{
	init: function(form, fieldName, minLength, errorMessage, focus) {
		this.form = form;
		this.fieldName = fieldName;
		this.errorMessage = errorMessage;
		this.focus = focus;
		this.minLength = minLength;
	},
	validate: function() {
		return this.form[this.fieldName].value.length >= this.minLength;
	}
});

// min length(byte) validator
tle.MinLengthByteValidator = tle.Class.create();
tle.MinLengthByteValidator.prototype = tle.Class.extend(new tle.Vaildator,
{
	init: function(form, fieldName, minLength, errorMessage, focus) {
		this.form = form;
		this.fieldName = fieldName;
		this.errorMessage = errorMessage;
		this.focus = focus;
		this.minLength = minLength;
	},
	validate: function() {
		var str = this.form[this.fieldName].value;
		return(str.length+(escape(str)+"%u").match(/%u/g).length-1) >= this.minLength;
	}
});

// regex pattern validator
tle.RegexValidator = tle.Class.create();
tle.RegexValidator.prototype = tle.Class.extend(new tle.Vaildator,
{
	init: function(form, fieldName, regex, errorMessage, focus) {
		this.form = form;
		this.fieldName = fieldName;
		this.regex = regex;
		this.errorMessage = errorMessage;
		this.focus = focus;
	},
	validate: function() {
		var str = this.form[this.fieldName].value;
		if (str.length == 0) return true;
		return str.search(this.regex) != -1;
	}
});

// check selected
tle.SelectionValidator = tle.Class.create();
tle.SelectionValidator.prototype = tle.Class.extend(new tle.Vaildator,
{
	init: function(form, fieldName, firstIdx, errorMessage, focus) {
		this.form = form;
		this.fieldName = fieldName;
		this.firstIdx = firstIdx;
		this.errorMessage = errorMessage;
		this.focus = focus;
	},
	validate: function() {
		var idx = this.form[this.fieldName].selectedIndex;
		return idx >= this.firstIdx;
	}
});

// check checkbox checked
tle.AtLeastOneCheckValidator = tle.Class.create();
tle.AtLeastOneCheckValidator.prototype = tle.Class.extend(new tle.Vaildator,
{
	init: function(form, fieldName, errorMessage, focus) {
		this.form = form;
		this.fieldName = fieldName;
		this.errorMessage = errorMessage;
		this.focus = focus;
	},
	validate: function() {
		var ele = this.form[this.fieldName];
		if (typeof(ele[0]) != "undefined") {
			// 2~
			for (var idxe = 0 ; idxe < ele.length ; idxe++) {
				if (ele[idxe].checked == true) {
					return true;
				}
			}
			return false;
		} else {
			// only 1
			return ele.checked == true;
		}
	}
});

// equals validator
tle.EqualsValidator = tle.Class.create();
tle.EqualsValidator.prototype = tle.Class.extend(new tle.Vaildator,
{
	init: function(form, fieldName, val, errorMessage, focus) {
		this.form = form;
		this.fieldName = fieldName;
		this.val = val;
		this.errorMessage = errorMessage;
		this.focus = focus;
	},
	validate: function() {
		var str = this.form[this.fieldName].value;
		if (str.length == 0) return true;
		return str == this.val;
	}
});

// tow field equality validator
tle.TwoFieldEqualsValidator = tle.Class.create();
tle.TwoFieldEqualsValidator.prototype = tle.Class.extend(new tle.Vaildator,
{
	init: function(form, field1Name, field2Name, errorMessage, focus, focusFieldName) {
		this.form = form;
		this.field1Name = field1Name;
		this.field2Name = field2Name;
		this.fieldName = focusFieldName;
		this.errorMessage = errorMessage;
		this.focus = focus;
	},
	validate: function() {
		var str1 = this.form[this.field1Name].value;
		var str2 = this.form[this.field2Name].value;
		return str1 == str2;
	}
});

// compatibility for 1.0 version
FormChecker = tle.Class.create();
FormChecker.prototype = tle.Class.extend(new tle.FormChecker(),
{
	init: function(checkForm) {
		this.checkForm = checkForm;
		this.validatorList = new Array();
	}
});
// e:tle.FormChecker

// s:tle.CookieUtil object
tle.CookieUtil = {
	getCookie: function(name) {
		var search = name + "=";
		if (document.cookie.length > 0) {
			// if there are any cookies
			var offset = document.cookie.indexOf(search);
			if (offset != -1) {
				// if cookie exists
				offset += search.length;
				// set index of beginning of value
				var end = document.cookie.indexOf(";", offset);
				// set index of end of cookie value
				if (end == -1)
					end = document.cookie.length;
				return unescape(document.cookie.substring(offset, end));
			}
		}
		return null;
	},
	setCookie: function(name, value, domain, path, expireDate, secure) {
		var pathValue = path == null ? null : path;
		var domainValue = domain == null ? null : domain;
		
		document.cookie = 
			name + "=" + escape(value) + 
			((domain == null) ? "" : ("; domain="+domain) ) +
			((path == null) ? "" : ("; path="+path) ) +
			((expireDate == null) ? "" : ("; expires=" + expireDate.toGMTString())) +
			(secure ? "; secure" : "");
	}
}
tle.CookieUtil.deleteCookie = function(name, domain, path) {
	if (tle.CookieUtil.getCookie(name) != null) {
		var currentDate = new Date();
		name + "=" + 
		((domain == null) ? "" : ("; domain="+domain) ) +
		((path == null) ? "" : ("; path="+path) ) +
		"; expires="+currentDate.toGMTString();
	}
}
// e:tle.CookieUtil object

// s:tle.Console
tle.Console = tle.Class.create();
tle.Console.prototype = {
	init: function(consoleId) {
		var consoleIdValue = consoleId;
		if (consoleIdValue == null) {
			consoleIdValue = 'console';
		}
		this.consoleElement = document.getElementById(consoleIdValue);
	},
	append: function(str) {
		if (this.consoleElement) {
			var logDiv = document.createElement("div");
			logDiv.innerHTML = str;
			this.consoleElement.appendChild(logDiv);
		}
	},
	clear: function() {
		if (this.consoleElement) this.consoleElement.innerHTML = "";
	}
}
// e:tle.Console

// s:tle.Element
tle.Element = {
	isDefine: function(e) {
		if (typeof(e) == 'undefined') {
			return false;
		}
		return true;
	},
	removeAllChild: function(element) {
		if (element.childNodes.length > 0) {
			var childNodes = element.childNodes;
			for (var i = childNodes.length - 1 ; i >= 0 ; i--) {
				element.removeChild(childNodes.item(i));
			}
		}
	},
	getPageX: function(element) {
		var x = 0;
		var e = element;
		while (e) {
			if (tle.Element.isDefine(e.offsetLeft)) {
				x += e.offsetLeft;
			}
			e = tle.Element.isDefine(e.offsetParent) ? e.offsetParent : null;
		}
		return x;
	},
	getPageY: function(element) {
		var y = 0;
		var e = element;
		while (e) {
			if (tle.Element.isDefine(e.offsetTop)) {
				y += e.offsetTop;
			}
			e = tle.Element.isDefine(e.offsetParent) ? e.offsetParent : null;
		}
		return y;
	},
	getPagePoint: function(element) {
		return {x: tle.Element.getPageX(element), y: tle.Element.getPageY(element) };
	},
	getPageRect: function(element) {
		var p = tle.Element.getPagePoint(element);
		return {
				left: p.x, top: p.y,
		        right: p.x + element.offsetWidth, bottom: p.y + element.offsetHeight
		};
	},
	getOffsetLeft: function(element) {
		if (tle.Element.isDefine(element.offsetLeft)) return element.offsetLeft
		return 0;
	},
	getOffsetTop: function(element) {
		if (tle.Element.isDefine(element.offsetTop)) return element.offsetTop
		return 0;
	},
	getOffsetPoint: function(element) {
		return {x: tle.Element.getOffsetLeft(element), y: tle.Element.getOffsetTop(element)};
	},
	getOffsetRect: function(element) {
		var p = tle.Element.getOffsetPoint(element);
		
		return {
				left: p.x, top: p.y,
		        right: p.x + element.offsetWidth, bottom: p.y + element.offsetHeight
		};
	}
}
// e:tle.Element

tle.GUI = {
	setOpacity: function(elementId, opacity) {
		if (window.opera) return;
		var ua = navigator.userAgent;
		element = document.getElementById(elementId);
		if (ua.indexOf("Safari") != -1 || ua.indexOf("KHTML") != -1) {
			element.style.opacity = opacity;
		} else if (document.all) {
			element.style.filter = "alpha(opacity=0)";
			element.filters.alpha.Opacity = (opacity * 100);
		} else if (ua.indexOf("Gecko") != -1) {
			element.style.MozOpacity = opacity;
		}
	}
}

// s:tle.Event
tle.Event = {
	getEvent: function(e) {
		return window.event ? window.event : e;
	},
	getMouseX: function(e) {
		if (e.clientX) {
			return e.clientX;
		} else {
			return e.pageX;
		}
	},
	getMouseY: function(e) {
		if (e.clientY) {
			return e.clientY;
		} else {
			return e.pageY;
		}
	},
	getMousePoint: function(e) {
		return {x: tle.Event.getMouseX(e), y: tle.Event.getMouseY(e)};
	},
	isLeftClick: function(e) {
		return (((e.which) && (e.which == 1)) ||
		        ((e.button) && (e.button == 1)));
	},
	getTarget: function(e) {
		if(e.target) return e.target;
		else if(e.srcElement) return e.srcElement;
		return null;
	},
	addListener: function(element, name, observer, useCapture) {
	    useCapture = useCapture || false;
	
		if (name == 'keypress' &&
		    (navigator.appVersion.match(/Konqueror|Safari|KHTML/) ||
		    element.attachEvent) ) {
			name = 'keydown';
		}
		if (element.addEventListener) {
			element.addEventListener(name, observer, useCapture);
		} else if (element.attachEvent) {
			element.attachEvent('on' + name, observer);
		}
	},
	removeListener: function(element, name, observer, useCapture) {
		useCapture = useCapture || false;
		
		if (name == 'keypress' &&
		    (navigator.appVersion.match(/Konqueror|Safari|KHTML/) ||
		    element.detachEvent)) {
			name = 'keydown';
		}
		if (element.removeEventListener) {
			element.removeEventListener(name, observer, useCapture);
		} else if (element.detachEvent) {
			element.detachEvent('on' + name, observer);
		}
	},
	cancelEvent: function(e) {
		if ( e.stopPropagation != undefined )
			e.stopPropagation();
		else if ( e.cancelBubble != undefined )
			e.cancelBubble = true;
		
		if ( e.preventDefault != undefined )
			e.preventDefault();
		else
			e.returnValue = false;
	},
	// excerpt from prototype's code
	bindAsListener: function(listener, object) {
		var __method = listener;
		return function(event) {
			return __method.call(object, tle.Event.getEvent(event));
		}
	}
}
// e:tle.Event

tle.dnd = {}

// s:tle.dnd.DndManager
tle.dnd.DndManager = tle.Class.create();
tle.dnd.DndManager.prototype = {
	init: function() {
		this.dragSourceList = new Array();
		this.dropTargetList = new Array();
		
		this.selectedDragSource = null;
		this.startDragFlag = false;
		
		this._mouseDown = tle.Event.bindAsListener(this._mousedownListener, this);
		this._mouseMove = tle.Event.bindAsListener(this._mousemoveListener, this);
		this._mouseUp = tle.Event.bindAsListener(this._mouseupListener, this);
		this._mouseOver = tle.Event.bindAsListener(this._mouseoverListener, this);
		this._mouseOut = tle.Event.bindAsListener(this._mouseoutListener, this);
	},
	addDragSource: function(dragSource) {
		this.dragSourceList[ this.dragSourceList.length ] = dragSource;
		var mouseDownElement = dragSource.getMouseDownElement();
		if (mouseDownElement != null) {
			tle.Event.addListener(mouseDownElement, 'mousedown', this._mouseDown);
			tle.Event.addListener(mouseDownElement, 'mouseover', this._mouseOver);
			tle.Event.addListener(mouseDownElement, 'mouseout', this._mouseOut);
			mouseDownElement.dragSource = dragSource;
		}
	},
	addDropTarget: function(dropTarget) {
		this.dropTargetList[ this.dropTargetList.length ] = dropTarget;
	},
	removeDragSource: function(dragSource) {
		var temp = new Array();
		for (var i = 0 ; i < this.dragSourceList.length ; i ++) {
			if (this.dragSourceList[i] != dragSource) {
				temp[ temp.length ] = this.dragSourceList[i];
			} else {
				var mouseDownElement = dragSource.getMouseDownElement();
				if (mouseDownElement != null) {
					tle.Event.removeListener(mouseDownElement, 'mousedown', this._mouseDown);
					mouseDownElement.dragSource = null;
				}
			}
		}
		this.dragSourceList = temp;
	},
	removeDropTarget: function(dropTarget) {
		var temp = new Array();
		for (var i = 0 ; i < this.dropTargetList.length ; i ++) {
			if (this.dropTargetList[i] != dropTarget) {
				temp[ temp.length ] = this.dropTargetList[i];
			}
		}
		this.dropTargetList = temp;
	},
	_mousedownListener: function(event) {
		if (!tle.Event.isLeftClick(event)) return;
		
		var target = tle.Event.getTarget(event);
		var dragSource = this._getDragSourceFromTarget(target);;
		if (dragSource == null) return;
		
		this._selectDragSource(dragSource, event);
		
		tle.Event.addListener(document, 'mousemove', this._mouseMove);
		tle.Event.addListener(document, 'mouseup', this._mouseUp);
		tle.Event.cancelEvent(event);
	},
	_selectDragSource: function(dragSource, event) {
		var dsElement = dragSource.getDragSourceElement();
		var dsElementPoint = tle.Element.getPagePoint(dsElement);
		var mousePoint = tle.Event.getMousePoint(event);
		dragSource.select();
		
		this.selectedDragSource = dragSource;
		this.selectedDiffX = mousePoint.x - dsElementPoint.x;
		this.selectedDiffY = mousePoint.y - dsElementPoint.y;
	},
	_deselectDragSource: function() {
		if (this.selectedDragSource != null) {
			this.selectedDragSource.deselect();
		}
		this.selectedDragSource = null;
		this.selectedDiffX = -1;
		this.selectedDiffY = -1;
	},
	getSelectedDragSource: function() {
		return this.selectedDragSource;
	},
	isSelectedDragSource: function() {
		return this.selectedDragSource != null;
	},
	isStartDrag: function() {
		return this.startDragFlag;
	},
	_mousemoveListener: function(event) {
		if (!this.isSelectedDragSource()) return;
		
		if (!this.isStartDrag()) {
			this._startDrag(event);
		}
		this._dragElement(event);
		tle.Event.cancelEvent(event);
	},
	_startDrag: function(event) {
		this.startDragFlag = true;
		var element = this.selectedDragSource.getDragSourceElement();
		element.style.position = 'absolute';
		
		this.selectedDragSource.startDrag();
		
		for (var i = 0 ; i < this.dropTargetList.length ; i++) {
			if (this.dropTargetList[i].canAccept(this.selectedDragSource)) {
				this.dropTargetList[i].activate();
			}
		}
	},
	_endDrag: function() {
		var element = this.selectedDragSource.getDragSourceElement();
		element.style.position = '';
		element.style.left = '';
		element.style.top = '';
		this.selectedDragSource.endDrag();
	},
	_stopDrag: function() {
		this.startDragFlag = false;
	},
	_dragElement: function(event) {
		var dragSource = this.selectedDragSource;
		var dsElement = dragSource.getDragSourceElement();
		var mousePoint = tle.Event.getMousePoint(event);
		this._moveDragElement(mousePoint.x - this.selectedDiffX, mousePoint.y - this.selectedDiffY);
	},
	_moveDragElement: function(x, y) {
		var dsElement = this.selectedDragSource.getDragSourceElement();
		dsElement.style.left = x + "px";
		dsElement.style.top = y + "px";
	},
	_mouseupListener: function(event) {
		if (!this.isStartDrag()) return;
		
		var dropTarget = null;
		var mousePoint = tle.Event.getMousePoint(event);
		for (var i = 0 ; i < this.dropTargetList.length ; i++) {
			if (this.dropTargetList[i].checkInDropTarget(this.selectedDragSource, event)) {
				dropTarget = this.dropTargetList[i];
				break;
			}
		}
		
		if (dropTarget == null) {
			this._cancelDrag();
		} else {
			this._endDrag();
			dropTarget.accept(this.selectedDragSource);
		}
		this._deselectDragSource();
		this._stopDrag();
		this._deactivateDropTarget();
		tle.Event.cancelEvent(event);
		
		tle.Event.removeListener(document, 'mousemove', this._mouseMove);
		tle.Event.removeListener(document, 'mouseup', this._mouseUp);
	},
	_cancelDrag: function() {
		var dsElement = this.selectedDragSource.getDragSourceElement();
		dsElement.style.position = '';
		dsElement.style.left = '';
		dsElement.style.top = '';
		this.selectedDragSource.cancelDrag();
	},
	_deactivateDropTarget: function() {
		for (var i = 0 ; i < this.dropTargetList.length ; i++) {
			this.dropTargetList[i].deactivate();
		}
	},
	_mouseoverListener: function(event) {
		var target = tle.Event.getTarget(event);
		var dragSource = this._getDragSourceFromTarget(target);;
		if (dragSource == null) return;
		dragSource.hover();
	},
	_mouseoutListener: function(event) {
		var target = tle.Event.getTarget(event);
		var dragSource = this._getDragSourceFromTarget(target);;
		if (dragSource == null) return;
		dragSource.unhover();
	},
	_getDragSourceFromTarget: function(target) {
		var dragSource = target.dragSource;
		var candidate = target;
		while (dragSource == null && candidate.parentNode) {
			candidate = candidate.parentNode;
			dragSource = candidate.dragSource;
		}
		return dragSource
	}
}

tle.dnd.DragSource = tle.Class.create();
tle.dnd.DragSource.prototype = {
	init: function(dragSourceId) {
		var element = document.getElementById(dragSourceId);
		this.dragSourceElement = element;
		this.mouseDownElement = element;
		this.selected = false;
	},
	getDragSourceElement: function() {
		return this.dragSourceElement;
	},
	getMouseDownElement: function() {
		return this.mouseDownElement;
	},
	hover: function() {
	},
	unhover: function() {
	},
	select: function() {
		this.selected = true;
	},
	deselect: function() {
		this.selected = false;
	},
	isSelected: function() {
		return this.selected;
	},
	startDrag: function() {
	},
	endDrag: function() {
	},
	cancelDrag: function() {
	}
}

tle.dnd.DropTarget = tle.Class.create();
tle.dnd.DropTarget.prototype = {
	init: function(dropTargetId) {
		this._init(dropTargetId);
	},
	_init: function(dropTargetId) {
		var element = document.getElementById(dropTargetId);
		this.dropTargetElement = element;
	},
	getDropTargetElement: function() {
		return this.dropTargetElement;
	},
	canAccept: function(dragSource) {
		return true;
	},
	checkInDropTarget: function(dragSource, event) {
		var tRect = tle.Element.getPageRect(this.dropTargetElement);
		var sRect = tle.Element.getPageRect(dragSource.getDragSourceElement());
		var mp = tle.Event.getMousePoint(event);
		
		var mouseIn = tRect.left <= mp.x && tRect.right >= mp.x && tRect.top <= mp.y && tRect.bottom >= mp.y;
		return mouseIn;
	},
	accept: function(dragSource) {
		var sElement = dragSource.getDragSourceElement();
		if (sElement.parentNode != null) {
			sElement.parentNode.removeChild(sElement);
		}
		this.dropTargetElement.appendChild(sElement);
	},
	activate: function() {
	},
	deactivate: function() {
	}
}
// e:tle.dnd.DnDManager

tle.tree = {};
tle.tree.TreeNode = tle.Class.create();
tle.tree.TreeNode.prototype = {
	init: function(value) {
		this.children = new Array();
		this.parent = null;
		this.value = value;
	},
	children: function() {
		return this.children;
	},
	addChild: function(child) {
		this.children[this.children.length] = child;
		child.setParent(this);
	},
	removeChild: function(child) {
		if (child == null) return;
		var temp = new Array();
		for (i = 0 ; i < this.children.length ; i++) {
			if (child != this.children[i]) {
				temp[temp.length] = this.children[i];
			}
		}
		if (temp.length != this.children.length) {
			child.setParent(null);
		}
		this.children = temp;
	},
	hasChild: function() {
		return this.getChildCount() > 0;
	},
	getChildCount: function() {
		return this.children.length;
	},
	getChild: function(idx) {
		return this.children[idx];
	},
	isLeaf: function() {
		return this.children.length == 0;
	},
	setParent: function(parent) {
		this.parent = parent;
	},
	getParent: function() {
		return this.parent;
	},
	getValue: function() {
		return this.value;
	}
}

tle.tree.Tree = tle.Class.create();
tle.tree.Tree.prototype = {
	init: function(topNode, treeRenderer) {
		this.uiElement = null;
		this.topNode = topNode;
		if (treeRenderer != null) {
			this.treeRenderer = treeRenderer;
		} else {
			this.treeRenderer = new tle.tree.TreeRenderer();
		}
		this.treeRenderer.setTreeCellRenderer(new tle.tree.TreeCellRenderer());
	},
	setTreeRenderer: function(treeRenderer) {
		this.treeRenderer = treeRenderer;
	},
	getTreeRenderer: function() {
		return this.treeRenderer;
	},
	insertInto: function(elementId) {
		var element = document.getElementById(elementId);
		this.uiElement = element;
		this.uiElement.appendChild(this.treeRenderer.makeElement(this));
	}
}

tle.tree.TreeRenderer = tle.Class.create();
tle.tree.TreeRenderer.prototype = {
	init: function() {
		this._mouseDown = tle.Event.bindAsListener(this._mousedownListener, this);
	},
	setTreeCellRenderer: function(treeCellRenderer) {
		this.treeCellRenderer = treeCellRenderer;
	},
	makeElement: function(tree) {
		var topNode = tree.topNode;
		var topULElement = document.createElement("ul");
		var topNodeLI = this._makeCellElement(tree, topNode, 0);
		topULElement.appendChild(topNodeLI);
		
		if (topNode.hasChild()) {
			var topNodeDIV = document.createElement("div");
			var topNodeUL = document.createElement("ul");
			topNodeDIV.style.display = 'none';
			
			topNodeDIV.appendChild(topNodeUL);
			topNodeLI.appendChild(topNodeDIV);
			
			var iStack = new Array();
			var nodeStack = new Array();
			var nodeULStack = new Array();
			
			var parentNode = topNode;
			var parentNodeLI = topNodeLI;
			var parentNodeUL = topNodeUL;
			
			var level = 1;
			topNodeUL.className = 'tree_node_ul_'+level;
			
			for (var i = 0 ; i < parentNode.getChildCount() ; i++) {
				var childNode = parentNode.getChild(i);
				var childNodeLI = this._makeCellElement(tree, childNode, level);
				parentNodeUL.appendChild(childNodeLI);
				
				if (childNode.hasChild()) {
					var tempNodeDIV = document.createElement("div");
					var tempNodeUL = document.createElement("ul");
					tempNodeDIV.appendChild(tempNodeUL);
					childNodeLI.appendChild(tempNodeDIV);
					tempNodeDIV.style.display = 'none';
					
					nodeStack.push(parentNode);
					nodeULStack.push(parentNodeUL);
					iStack.push(i);
					
					parentNode = childNode;
					parentNodeUL = tempNodeUL;

					i = -1;
					level++;
					
					tempNodeUL.className = 'tree_node_ul_'+level;
					
					continue;
				}
				while (i == parentNode.getChildCount() - 1 && nodeStack.length > 0) {
					parentNode = nodeStack.pop();
					parentNodeUL = nodeULStack.pop();
					i = iStack.pop();
					level--;
				}
			}
		}
		return topULElement;
	},
	_makeCellElement: function(tree, node, level) {
		var element = document.createElement("li");
		var div = document.createElement("div");
		var cellInfo = this.treeCellRenderer.makeCellElement(tree, node.getValue(), false, node.isLeaf());
		div.appendChild(cellInfo.getUIElement());
		
		element.appendChild(div);
		
		if (node.hasChild()) tle.Event.addListener(cellInfo.getClickElement(), "mousedown", this._mouseDown);
		
		element.className = 'tree_node_li_'+level;
		element.treeNode = node;
		element.expanded = false;
		element.tree = tree;
		return element;
	},
	_mousedownListener: function(event) {
		var spanElement = tle.Event.getTarget(event);
		while (spanElement.parentNode.treeNode == null) {
			spanElement = spanElement.parentNode;
		}
		var liElement = spanElement.parentNode;
		
		if (liElement.childNodes.length > 1) {
			this._toggleExpanded(liElement);
		}
		tle.Event.cancelEvent(event);
	},
	_toggleExpanded: function(liElement) {
		var node = liElement.treeNode;
		
		if (liElement.lastChild.style.display == '') {
			liElement.expanded = false;
			liElement.lastChild.style.display = 'none';
		} else if (liElement.lastChild.style.display == 'none') {
			liElement.expanded = true;
			liElement.lastChild.style.display = '';
		}
		var span = liElement.firstChild;
		tle.Element.removeAllChild(span);

		var cellInfo = this.treeCellRenderer.makeCellElement(liElement.tree, node.getValue(), liElement.expanded, node.isLeaf());
		span.appendChild(cellInfo.getUIElement());
		if (node.hasChild()) tle.Event.addListener(cellInfo.getClickElement(), "mousedown", this._mouseDown);
	}
}

tle.tree.TreeCellRenderer = tle.Class.create();
tle.tree.TreeCellRenderer.prototype = {
	init: function() {
	},
	makeCellElement: function(tree, value, expanded, isLeaf) {
		var span = document.createElement("span");
		var html = "";
		if (!isLeaf) {
			if (expanded) {
				html += '<span>[-]</span>';
			} else {
				html += '<span>[+]</span>';
			}
		}
		html += value;
		span.innerHTML = html;
		
		return new tle.tree.TreeCellRendererInfo(span, isLeaf ? null : span);
	}
}

tle.tree.TreeCellRendererInfo = tle.Class.create();
tle.tree.TreeCellRendererInfo.prototype = {
	init: function(uiElement, clickElement) {
		this.uiElement = uiElement;
		this.clickElement = clickElement;
	},
	getUIElement: function() {
		return this.uiElement;
	},
	getClickElement: function() {
		return this.clickElement;
	}
}