/**
 * jsslib/jsslib.js
 * Javascript Áý¼Ò¼º ¶óÀÌºê·¯¸®
 * ¿ä±¸»çÇ×
 *   prototype.js  (tested 1.6)
 *   jsslib.css
 */

/**
 * Áý¼Ò¼º ¶óÀÌºê·¯¸® ±âº» Á¤º¸
 */
var JSSLib = {
	version: '1.0',
	init: function() {
		if (typeof(Prototype) == 'undefined')
			alert('JSSLib requires prototype.js.');
		var scripts = document.getElementsByTagName('script');
		for (var i = 0; i < scripts.length; i++) {
			var s = scripts[i];
			if (s.src != null && s.src.match(/jsslib\.js$/i)) {
				JSSLib.base = s.src.replace(/\/[^\/]*$/, '');
				break;
			}
		}
	},
	a: function() {
		var a = null;
		a.merong();
	}
};
JSSLib.init();

/*
 * ÆäÀÌÁö ·Îµù½Ã¿¡ ÃÊ±âÈ­ ¼öÇà
 */
var AutoInitializer = {
	mapping: {},
	add: function(className, autoClass) {
		this.mapping[className] = autoClass;
	},
	handleLoad: function(event) {
		// TODO  DOM ½ºÄµÀ» 1È¸¸¸ ÇÏµµ·Ï
		for (var className in this.mapping) {
			var autoClass = this.mapping[className];
			if (autoClass != null && autoClass.autoInit) {
				var elements = document.getElementsByClassName(className);
				for (var i = 0; i < elements.length; i++) {
					var options = null, os = elements[i].getAttribute("jss:options");
					if (os != null && os != '') {
						try {
							options = os.evalJSON();
						} catch (e) {}
					}
					if (autoClass.doAutoInit != null)
						autoClass.doAutoInit(elements[i], options);
					else if (typeof(autoClass) == 'function')
						new autoClass(elements[i], options);
				}
			}
		}
	}
};
Event.observe(window, 'load',
	AutoInitializer.handleLoad.bind(AutoInitializer));

/**
 * ·¹ÀÌ¾î Ç¥½Ã/¼û±è °ü·Ã À¯Æ¿¸®Æ¼ ¸Þ¼Òµå ¸ðÀ½
 * TODO  onresize, onmove µî Áö¿ø
 */
var LayerUtils = {
	/**
	 * IE 6 ÀÌÇÏÀÇ °æ¿ì <select>°¡ ·¹ÀÌ¾î¸¦ ¶Õ°í
	 * ÃÖ»óÀ§¿¡ ³ª¿À´Â '¹«Àû <select>' ¹ö±×°¡ ÀÖ´Ù.
	 */
	hasSelectBug: (Prototype.Browser.IE &&
		navigator.appVersion.match(/MSIE\s(\d+)\./) &&
			Number(RegExp.$1) <= 6),
	/**
	 * ·¹ÀÌ¾î¸¦ Ç¥½ÃÇÑ´Ù.
	 */
	show: function(layer, options) {
		Element.show(layer);
		if (options != null && options.target != null)
			this.moveTo(layer, options.target, options);
		if (this.hasSelectBug)
			this.hideSelects(layer);
	},
	/**
	 * ·¹ÀÌ¾î¸¦ ¼û±ä´Ù.
	 */
	hide: function(layer) {
		Element.hide(layer);
		if (this.hasSelectBug)
			this.restoreSelects(layer);
	},
	/**
	 * ·¹ÀÌ¾î¸¦ ÁöÁ¤ÇÑ ´ë»ó ¿¤¸®¸ÕÆ® ±ÙÃ³·Î ¿Å±ä´Ù.
	 * TODO  position Áö¿ø
	 */
	moveTo: function(layer, target, options) {
		target = $(target);
		if (target == null)
			return;
		var pos = DOMUtils.cumulativeOffset(target);
		//var pos = Element.cumulativeOffset(target);
		var cWidth = Element.getWidth(layer);
		var top = pos.top + Element.getHeight(target);
		var left = pos.left;
		if (left + cWidth > document.body.offsetWidth)
			left = document.body.offsetWidth - cWidth +
				DOMUtils.getStyleNumber(document.body, 'margin-left');
		layer.style.top = top + 'px';
		layer.style.left = left + 'px';
	},
	/**
	 * ¹«Àû <select> ¹ö±×¸¦ ÇÇÇÏ±â À§ÇØ
	 * ·¹ÀÌ¾î¿Í °ãÃÄÁö´Â <select>¸¦ Ã£¾Æ visibility: hidden À¸·Î ¼³Á¤ÇÑ´Ù.
	 */
	hideSelects: function(layer) {
		if (layer.hidedSelects != null && layer.hidedSelects.length > 0)
			this.restoreSelects(layer);
		var selects = document.getElementsByTagName('select');
		layer.hidedSelects = [];
		for (var i = 0; i < selects.length; i++) {
			var select = selects[i];
			if (DOMUtils.isOverlaps(layer, select)) {
				DOMUtils.addClass(select, 'HiddenSelect');
				layer.hidedSelects[layer.hidedSelects.length] = select;
			}
		}
	},
	/**
	 * hideSelects()·Î ¼û°å´ø <select>¸¦ º¹¿øÇÑ´Ù.
	 */
	restoreSelects: function(layer) {
		if (layer.hidedSelects != null && layer.hidedSelects.length > 0) {
			for (var i = 0; i < layer.hidedSelects.length; i++)
				DOMUtils.removeClass(layer.hidedSelects[i], 'HiddenSelect');
			layer.hidedSelects = [];
		}
	}
};

/**
 * DOM Á¶ÀÛÀ» À§ÇÑ Àâ´ÙÇÑ À¯Æ¿¸®Æ¼ ¸Þ¼Òµå ¸ðÀ½
 */
var DOMUtils = {
	/**
	 * ÇöÀç ¸¶¿ì½º Æ÷ÀÎÅÍ°¡ ÁöÁ¤ÇÑ element À§¿¡ Á¸ÀçÇÏ´Â°¡?
	 */
	hasMouse: function(element) {
		if (element == null)
			return false;
		var box = this.getBox(element);
		return ((this.mouseX > box.left && this.mouseX < box.right) &&
			(this.mouseY > box.top && this.mouseY < box.bottom));
	},
	/**
	 * µÎ°³ÀÇ element °¡ ¼­·Î °ãÃÄÀÖ´Â°¡?
	 */
	isOverlaps: function(element1, element2) {
		var box1 = this.getBox(element1);
		var box2 = this.getBox(element2);
		return (
			(box1.top <= box2.bottom) &&
			(box1.left <= box2.right) &&
			(box1.bottom >= box2.top) &&
			(box1.right >= box2.left)
		);
	},
	/**
	 * ancestor °¡ elementÀÇ Á¶»ó ¿ä¼ÒÀÎ°¡?
	 */
	isAncestorOf: function(ancestor, element) {
		while (element != null) {
			if (element == ancestor)
				return true;
			element = element.parentNode;
		}
		return false;
	},
	/**
	 * descendants°¡ elementÀÇ ÀÚ¼Õ ¿ä¼ÒÀÎ°¡?
	 */
	isDescendantOf: function(element, ancestor) {
		return DOMUtils.isAncestorOf(ancestor, element);
	},
	/**
	 * element ÀÇ top, left, right, bottom ÁÂÇ¥ Á¶È¸
	 */
	getBox: function(element) {
		var box = DOMUtils.cumulativeOffset(element);
		Object.extend(box, Element.getDimensions(element));
		box.right = box.left + box.width;
		box.bottom = box.top + box.height;
		return box;
	},
	isClass: function(element, className) {
		var re = new RegExp("\\b" + className + "\\b");
		return re.test(element.className);
	},
	/**
	 * element¿¡ CSS Å¬·¡½º¸íÀ» Ãß°¡ÇÑ´Ù.
	 */
	addClass: function(element, className) {
		if (!DOMUtils.isClass(element, className))
			element.className += ' ' + className;
	},
	/**
	 * element¿¡¼­ CSS ÁöÁ¤ÇÑ Å¬·¡½º¸íÀ» Á¦°ÅÇÑ´Ù.
	 * ÇÏ³ª ÀÌ»óÀÇ Å¬·¡½º°¡ ÁöÁ¤µÇ¾î ÀÖ´Â °æ¿ì ´Ù¸¥ °ÍÀº º¸Á¸µÈ´Ù.
	 */
	removeClass: function(element, className) {
		if (DOMUtils.isClass(element, className))
			element.className =
				element.className.replace(
					new RegExp("\\s*" + className + "\\s*", "g"), '');
	},
	/**
	 * element ¸¦ »ý¼ºÇÑ´Ù.
	 * @param tagName ÅÂ±×ÀÌ¸§
	 * @param attrs ¼Ó¼º ÇÁ·ÎÅäÅ¸ÀÔ
	 * @param styles ½ºÅ¸ÀÏ ÇÁ·ÎÅä Å¸ÀÔ
	 * @param content innerHTML ¶Ç´Â ÇÏÀ§ ¿¤¸®¸ÕÆ® ¶Ç´Â ¹è¿­
	 */
	createElement: function(tagName, attrs, styles) {
		var element = document.createElement(tagName);
		var innerHTML = null;
		if (attrs != null && attrs.innerHTML) {
			innerHTML = attrs.innerHTML;
			delete attrs.innerHTML;
		}
		if (attrs != null)
			Object.extend(element, attrs);
		if (styles != null)
			Object.extend(element.style, styles);
		if (innerHTML != null) {
			if (typeof(innerHTML) == 'object' &&
					innerHTML.constructor == Array) {
				for (var i = 0; i < content.length; i++)
					Element.insert(element, innerHTML[i]);
			}
			else {
				Element.update(element, innerHTML);
			}
		}
		return element;
	},
	/**
	 * element ¸¦ »ý¼ºÇØ¼­ ÁöÁ¤ÇÑ ºÎ¸ð ¿ä¼ÒÀÇ ÇÏÀ§¿¡ Ãß°¡ÇÑ´Ù.
	 * @param parent ºÎ¸ð ¿ä¼Ò
	 * @param tagName ÅÂ±×ÀÌ¸§
	 * @param attrs ¼Ó¼º ÇÁ·ÎÅäÅ¸ÀÔ
	 * @param styles ½ºÅ¸ÀÏ ÇÁ·ÎÅä Å¸ÀÔ
	 * @param content innerHTML ¶Ç´Â ÇÏÀ§ ¿¤¸®¸ÕÆ® ¶Ç´Â ¹è¿­
	 */
	appendElement: function(parent, tagName, attrs, styles) {
		var element = DOMUtils.createElement(tagName, attrs, styles);
		Element.insert($(parent), element);
		return element;
	},
	/**
	 * opacity ¸¦ ¼³Á¤ÇÑ´Ù.
	 * Element.setOpacity()´Â CSS opacity ¸¦ ¼³Á¤ÇÏ´Âµ¥
	 * IE ÀÇ °æ¿ì´Â µ¿ÀÛÇÏÁö ¾Ê´Â´Ù.
	 * IEÀÎ °æ¿ì´Â filter¸¦ ¼³Á¤ÇÏµµ·Ï ÇÑ ¹öÀüÀÌ´Ù.
	 */
	setOpacity: function(element, value) {
		if (Prototype.Browser.IE)
			element.style.filter =
				'progid:DXImageTransform.Microsoft.Alpha(opacity=' +
					(value * 100) + ')';
		else
			Element.setOpacity(element, value);
	},
	/**
	 * Element.getStyle()Àº ½ÇÁ¦ Àû¿ëµÈ ½ºÅ¸ÀÏ °ªÀ» ¾Ë¾Æ³»´Â
	 * À¯¿ëÇÑ ¸Þ¼ÒµåÀÎµ¥, ¸®ÅÏ°ªÀÌ ¹®ÀÚ¿­ÀÌ¶ó Å©±â³ª ÁÂÇ¥ °°Àº °æ¿ì
	 * Ãß°¡ÀûÀÎ °è»êÀ» À§ÇØ ÆÄ½ÌÀ» ÇØ¾ß ÇÑ´Ù.
	 * ±×·² ¶§ ¾²´Â ¿ëµµÀÇ ¸Þ¼ÒµåÀÌ´Ù.
	 */
	getStyleNumber: function(element, name) {
		var value = Element.getStyle(element, name);
		return (value.match(/^([\d.]+)/))? parseInt(RegExp.$1): 0;
	},
	/**
	 * Element.cumulativeOffset()´Â offsetTop °ú offsetLeft ¸¦
	 * DOM Æ®¸®ÀÇ Á¶»óµéÀÇ ¸ðµç °ªÀ» ´õÇÏ´Â °ÍÀÎµ¥
	 * °á°ú°ª¿¡ <td>ÀÇ border´Â Æ÷ÇÔµÇÁö ¾Ê´Â ¹®Á¦°¡ ÀÖ´Ù.
	 * Á¤È®ÇÑ ¿øÀÎÀº ¸ð¸£°Ú´Âµ¥ ¾Æ¹«·¡µµ DTD ¼±¾ð¿Í °ü·Ã ÀÖ´Âµí ÇÏ´Ù.
	 * ¾ÏÆ°°£¿¡ ÀÌ¸¦ ÃÖ´ëÇÑ º¸¿ÏÇÑ ¹öÀüÀÌ´Ù.
	 */
	cumulativeOffset: function(element) {
		var valueT = 0, valueL = 0;
		var borderT = 0, borderL = 0;
		do {
			valueT += element.offsetTop  || 0;
			valueL += element.offsetLeft || 0;
			// CSS ·Î td¿¡ border¸¦ ÁöÁ¤ÇÑ °æ¿ì
			if (element.tagName.toUpperCase() == 'TD') {
				valueT += this.getStyleNumber(element, 'border-top-width');
				valueL += this.getStyleNumber(element, 'border-left-width');
			}
			// <table border="1"> °°Àº ½ÄÀ¸·Î ÁöÁ¤ÇÑ °æ¿ì
			else if (!Prototype.Browser.Gecko &&
						element.tagName.toUpperCase() == 'TABLE') {
				var border = (element.border)? parseInt(element.border): 0;
				valueT += border;
				valueL += border;
			}
			element = element.offsetParent;
		} while (element);
		return Element._returnOffset(valueL, valueT);
	}
};
Event.observe(document, 'mousemove', function(event) {
	DOMUtils.mouseX = Event.pointerX(event || window.event);
	DOMUtils.mouseY = Event.pointerY(event || window.event);
});

/**
 * ÇÃ·¯±×ÀÎ Ãâ·Â/Á¶ÀÛ °ü·Ã
 */
var PluginUtils = {
	/**
	 * ÇÃ·¡½Ã HTML ÅÂ±×¸¦ document.write()·Î »ý¼ºÇÑ´Ù.(·Îµù Å¸ÀÓ)
	 */
	writeFlash: function(id, url, width, height, options) {
		document.write(this.getFlashHtml(id, url, width, height, options));
	},
	/**
	 * ÇÃ·¡½Ã HTML ÅÂ±×¸¦ Element.update()·Î (Àç)»ý¼ºÇÑ´Ù.(·±Å¸ÀÓ)
	 */
	updateFlash: function(container, id, url, width, height, options) {
		var containerObj = $(container);
		if (containerObj)
			Element.update(containerObj,
				this.getFlashHtml(id, url, width, height, options));
	},
	/**
	 * ÇÃ·¡½Ã HTML ÅÂ±×¸¦ »ý¼ºÇÏ¿© ¸®ÅÏÇÑ´Ù.
	 */
	getFlashHtml: function(id, url, width, height, options) {
		var tmpl = (Prototype.Browser.IE)?
					this.getTemplate('flashAx'):
						this.getTemplate('flash');
		var options = Object.extend({
			quality: 'high',
			wmode: 'transparent',
			bgcolor: '#ffffff',
			flashvars: ''
		}, options);
		return tmpl.evaluate(Object.extend(options, {
			id: id,
			url: url,
			width: (width || '100%'),
			height: (height || '100%')
		}));
	},
	/**
	 * ¹Ìµð¾î ÇÃ·¹ÀÌ¾î HTML ÅÂ±×¸¦ document.write()·Î »ý¼ºÇÑ´Ù.(·Îµù Å¸ÀÓ)
	 */
	writeMediaPlayer: function(id, url, width, height, options) {
		document.write(
			this.getMediaPlayerHtml(id, url, width, height, options));
	},
	/**
	 * ¹Ìµð¾î ÇÃ·¹ÀÌ¾î HTML ÅÂ±×¸¦ Element.update()·Î (Àç)»ý¼ºÇÑ´Ù.(·±Å¸ÀÓ)
	 */
	updateMediaPlayer: function(container, id, url, width, height, options) {
		var containerObj = $(container);
		if (containerObj)
			Element.update(containerObj,
				this.getMediaPlayerHtml(id, url, width, height, options));
	},
	/**
	 * ¹Ìµð¾î ÇÃ·¹ÀÌ¾î HTML ÅÂ±×¸¦ »ý¼ºÇÏ¿© ¸®ÅÏÇÑ´Ù.
	 */
	getMediaPlayerHtml: function(id, url, width, height, options) {
		var tmpl = (Prototype.Browser.IE)?
					this.getTemplate('mediaPlayerAx'):
						this.getTemplate('mediaPlayer');
		var options = Object.extend({
			animationatstart: false,
			autostart: true,
			autoresize: true,
			clicktoplay: true,
			enablecontextmenu: false,
			invokeurls: false,
			showcontrols: true,
			showstatusbar: true,
			transparentatstart: true
		}, options);
		return tmpl.evaluate(Object.extend(options, {
			id: id,
			url: url,
			width: (width || '100%'),
			height: (height || '100%')
		}));
	},
	/*
	 * private methods
	 */
	getTemplate: function(name) {
		var t = this.templates[name];
		if (t == null || t == '')
			return '';
		else if (typeof(t) == 'string') {
			t = new Template(t);
			this.templates[name] = t;
			return t;
		}
		else {
			return t;
		}
	},
	templates: {
		flash: '<embed id="#{id}" name="#{id}" src="#{url}" width="#{width}" height="#{height}" quality="#{quality}" wmode="#{wmode}" bgcolor="#{bgcolor}" flashvars="#{flashvars}" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"></embed>',
		flashAx: '<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" width="#{width}" height="#{height}" id="#{id}" name="#{id}">' +
			'<param name="movie" value="#{url}" />' +
			'<param name="quality" value="#{quality}" />' +
			'<param name="wmode" value="#{wmode}" />' +
			'<param name="bgcolor" value="#{bgcolor}" />' +
			'<param name="flashvars" value="#{flashvars}" />' +
		'</object>',
		mediaPlayer: '<embed id="#{id}" name="#{id}" src="#{url}" width="#{width}" height="#{height}" animationatstart="#{animationatstart}" autostart="#{autostart}" autoresize="#{autoresize}" clicktoplay="#{clicktoplay}" enablecontextmenu="#{enablecontextmenu}" invokeurls="#{invokeurls}" showcontrols="#{showcontrols}" showstatusbar="#{showstatusbar}" transparentatstart="#{transparentatstart}" type="application/x-mplayer2" pluginspage="http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=6,4,5,715"></embed>',
		mediaPlayerAx: '<object classid="clsid:22d6f312-b0f6-11d0-94ab-0080c74c7e95" codebase="http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=6,4,5,715" width="#{width}" height="#{height}" id="#{id}" name="#{id}">' +
			'<param name="animationatstart" value="#{animationatstart}" />' +
			'<param name="autostart" value="#{autostart}" />' +
			'<param name="autoresize" value="#{autoresize}" />' +
			'<param name="clicktoplay" value="#{clicktoplay}" />' +
			'<param name="enablecontextmenu" value="#{enablecontextmenu}" />' +
			'<param name="invokeurls" value="#{invokeurls}" />' +
			'<param name="showcontrols" value="#{showcontrols}" />' +
			'<param name="showstatusbar" value="#{showstatusbar}" />' +
			'<param name="transparentatstart" value="#{transparentatstart}" />' +
		'</object>'
	}
};

/**
 * ÀÌ¹ÌÁö °ü·Ã À¯Æ¿¸®Æ¼
 */
var ImageUtils = {
	/**
	 * ÇöÀç ÇÁ¸®·Îµù ÁßÀÎ ÀÌ¹ÌÁö ¸ñ·Ï
	 */
	preloading: [],
	/**
	 * ÀÌ¹ÌÁö ÇÁ¸®·Îµù
	 * url Àº ´ÜÀÏ urlÀ» ÁöÁ¤ÇØµµ µÇ°í, urlÀÇ ¹è¿­À» ÁöÁ¤ÇØµµ µÈ´Ù.
	 * callback À» ÁöÁ¤ÇÏ¸é ÇÁ¸®·ÎµùÀÌ ³¡³µÀ»¶§ ½ÇÇàµÈ´Ù.
	 * ImageUtils.preload(['http://.../a.jpg',
	 *			'http://.../b.jpg', ...], function() {...});
	 */
	preload: function(url, callback, args) {
		if (typeof(url) == 'object' && url.constructor == Array) {
			for (var i = 0; i < url.length; i++) {
				this.preload(url[i], callback, args);
			}
		}
		else {
			var imageObj = new Image();
			this.preloading[this.preloading.length] = imageObj;
			imageObj.onload = this.preloadComplete;
			if (callback != null) {
				imageObj.onpreload = callback;
				imageObj.args = args;
			}
			imageObj.src = url;
		}
	},
	/**
	 * ÀÌ¹ÌÁö°¡ ·ÎµåµÈ ÀÌÈÄ Å©±â¸¦ Á¶Á¤ÇÑ´Ù.
	 * ÀÌÁö ¿øº»ÀÇ ºñÀ²À» À¯ÁöÇÑ Ã¤ ÁöÁ¤ÇÑ Å©±âÀÇ »ç°¢Çü ¿µ¿ª¿¡
	 * µé¾î°¥ ¼ö ÀÖµµ·Ï Å©±â°¡ Á¶Á¤µÈ´Ù.
	 * <img src="..." onload="ImageUtils.resize(this, 100, 100)" />
	 */
	resize: function(imageSpec, width, height, options) {
		var image = this.getImage(imageSpec);
		var size = this.calcResize({
				width: (options != null && options.realWidth)?
						options.realWidth: image.width,
				height: (options != null && options.realHeight)?
						options.realHeight: image.height
			}, {width: width, height: height});
		image.width = size.width;
		image.height = size.height;
		if (DOMUtils.isClass(image, 'ImageLoading'))
			DOMUtils.removeClass(image, 'ImageLoading');
	},
	calcResize: function(image, box) {
		var boxRate = box.width/box.height;
		var imageRate = image.width/image.height;
		var result = {width: image.width, height: image.height};

		if (boxRate < imageRate) {	// °¡·Î
			if (image.width > box.width) {
				result.width = box.width;
				result.height = Math.floor(image.height / image.width * box.width);
			}
			else if (image.height > box.height) {
				result.height = box.height;
				result.width = Math.floor(image.width / image.height * box.height);
			}
		}
		else {
			if (image.height > box.height) {
				result.height = box.height;
				result.width = Math.floor(image.width / image.height * box.height);
			}
			else if (image.width > box.width) {
				result.width = box.width;
				result.height = Math.floor(image.height / image.width * box.width);
			}
		}
		return result;
	},
	/**
	 * ÀÌ¹ÌÁö°¡ ·ÎµåµÈ ÀÌÈÄ Å©±â¸¦ Á¶Á¤ÇÑ´Ù.
	 * ÀÌÁö ¿øº»ÀÇ ºñÀ²À» À¯ÁöÇÑ Ã¤ ÁöÁ¤ÇÑ Å©±âÀÇ Æø ÀÌ³»¿¡
	 * µé¾î°¥ ¼ö ÀÖµµ·Ï Å©±â°¡ Á¶Á¤µÈ´Ù.
	 * <img src="..." onload="ImageUtils.resizeWidth(this, 100)" />
	 */
	resizeWidth: function(imageSpec, width, options) {
		var image = this.getImage(imageSpec);
		var size = this.calcResizeWidth({
				width: (options != null && options.realWidth)?
						options.realWidth: image.width,
				height: (options != null && options.realHeight)?
						options.realHeight: image.height
			}, width);
		image.width = size.width;
		image.height = size.height;
		if (DOMUtils.isClass(image, 'ImageLoading'))
			DOMUtils.removeClass(image, 'ImageLoading');
	},
	calcResizeWidth: function(image, boxWidth) {
		var result = {width: image.width, height: image.height};
		if (image.width > boxWidth) {
			result.width = boxWidth;
			result.height = Math.floor(image.height / image.width * boxWidth);
		}
		return result;
	},
	/**
	 * ÀÌ¹ÌÁö°¡ ·ÎµåµÈ ÀÌÈÄ Å©±â¸¦ Á¶Á¤ÇÑ´Ù.
	 * ÀÌÁö ¿øº»ÀÇ ºñÀ²À» À¯ÁöÇÑ Ã¤ ÁöÁ¤ÇÑ Å©±âÀÇ ³ôÀÌ ÀÌ³»¿¡
	 * µé¾î°¥ ¼ö ÀÖµµ·Ï Å©±â°¡ Á¶Á¤µÈ´Ù.
	 * <img src="..." onload="ImageUtils.resizeHeight(this, 100)" />
	 */
	resizeHeight: function(imageSpec, height, options) {
		var image = this.getImage(imageSpec);
		var size = this.calcResizeHeight({
				width: (options != null && options.realWidth)?
						options.realWidth: image.width,
				height: (options != null && options.realHeight)?
						options.realHeight: image.height
			}, width);
		image.width = size.width;
		image.height = size.height;
		if (DOMUtils.isClass(image, 'ImageLoading'))
			DOMUtils.removeClass(image, 'ImageLoading');
	},
	calcResizeHeight: function(image, boxHeight) {
		var result = {width: image.width, height: image.height};
		if (image.height > boxHeight) {
			result.height = boxHeight;
			result.width = Math.floor(image.width / image.height * boxHeight);
		}
		return result;
	},
	/**
	 * ÀÌ¹ÌÁö ¿¤¸®¸ÕÆ®¸¦ ÁöÁ¤ÇÑ URLÀ» ÀÌ¿ëÇØ¼­ »õ·Î »ý¼ºµÈ ÀÌ¹ÌÁö·Î ±³Ã¼ÇÑ´Ù.
	 * ÀÌ ¸Þ¼Òµå´Â ÀÌ¹ÌÁöÀÇ Å©±â¸¦ °­Á¦·Î º¯°æÇÑ °æ¿ì
	 * »õ·Î ·ÎµùµÈ ÀÌ¹ÌÁöÀÇ ¿ø·¡ Å©±â¸¦ ¾Ë¾Æ³¾ ¼ö ¾ø±â ¶§¹®¿¡
	 * resize(), resizeWidth() µîÀÌ Á¦´ë·Î µ¿ÀÛÇÏÁö ¾Ê±â ¶§¹®¿¡
	 * ÀÌ¹ÌÁö ¿¤¸®¸ÕÆ® ÀÚÃ¼¸¦ »õ·Î »ý¼ºÇÏ´Â ¹æ¹ýÀ» »ç¿ëÇØ¾ß ÇÒ¶§ ÇÊ¿äÇÏ´Ù.
	 */
	replace: function(image, url, styles) {
		var oldImageObj = $(image);
		var newImageObj = new Image();

		var parent = oldImageObj.parentNode;
		var nextSibling = oldImageObj.nextSibling;
		parent.removeChild(oldImageObj);

		var properties = ['id', 'className', 'onload', 'onclick'];
		for (var i = 0; i < properties.length; i++) {
			var property = properties[i];
			if (oldImageObj[property] != null)
				newImageObj[property] = oldImageObj[property];
		}
		if (styles != null) {
			for (var i = 0; i < styles.length; i++) {
				var style = styles[i];
				if (oldImageObj.style[style])
					newImageObj.style[style] = oldImageObj.style[style];
			}
		}

		newImageObj.src = url;
		if (nextSibling != null)
			Element.insert(nextSibling, {before: newImageObj});
		else
			Element.insert(parent, newImageObj);
		return newImageObj;
	},
	/**
	 * Àü´ÞµÈ ÀÎÀÚ·Î ÀÌ¹ÌÁö ÀÎ½ºÅÏ½º¸¦ Ã£¾Æ³½´Ù.
	 * Àü´Þ °¡´ÉÇÑ ÀÎÀÚÀÇ Á¾·ù´Â <img> °´Ã¼,
	 * id, name, ÀÌº¥Æ® °´Ã¼ Áß ÇÏ³ª´Ù.
	 */
	getImage: function(imageSpec) {
		if (imageSpec == null)
			return null;
		else if (typeof(imageSpec) == 'object' &&
				imageSpec.tagName != null &&
				imageSpec.tagName.toLowerCase() == 'img')
			return imageSpec;
		else if (typeof(imageSpec) == 'string')
			return ($(imageSpec) || document.images[imageSpec]);
		else {
			var element = Event.element(imageSpec);
			return (element != null &&
				element.tagName != null &&
					element.tagName.toLowerCase() == 'img')?
						element: null;
		}
	},
	/**
	 * ÀÌ¹ÌÁö ÇÁ¸®·ÎµùÀÌ ³¡³µÀ» ¶§ È£ÃâµÇ´Â ÀÌº¥Æ® ÇÚµé·¯
	 */
	preloadComplete: function() {
		var pos = ImageUtils.preloading.indexOf(this);
		if (pos >= 0)
			ImageUtils.preloading.splice(pos, 1);
		if (typeof(this.onpreload) == 'function')
			this.onpreload(this, this.args);
	}
};

/**
 * form input °´Ã¼ Á¶ÀÛ À¯Æ¿¸®Æ¼ ¸ðÀ½.
 * input °´Ã¼¶ó ÇÔÀº <input>, <select>, <textarea>,
 * ¹è¿­(radio, checkbox) µî¸¦ ¸»ÇÑ´Ù.
 */
var FormUtils = {
	/**
	 * input °´Ã¼ÀÇ °ªÀ» Á¶È¸ÇÑ´Ù.
	 *
	 * <input type="checkbox">ÀÇ °æ¿ì
	 * Ã¼Å©°¡ µÇ¾îÀÖÀ¸¸é value °ªÀ» ¸®ÅÏÇÏ°í,
	 * Ã¼Å©°¡ ¾ÈµÇ¾î ÀÖÀ¸¸é nullÀ» ¸®ÅÏÇÑ´Ù.
	 * ±× ¹Û¿¡µµ nullÀÌ ¸®ÅÏµÇ´Â °æ¿ì°¡ ¸î°¡Áö ÀÖ´Ù.
	 *
	 * ½ºÅ©¸³Æ® ÀÛ¾÷½Ã nullÀÌ ¸®ÅÏµÇ¸é °ï¶õÇÑ °æ¿ì¿¡´Â
	 * defaultValue¸¦ »ç¿ëÇÏ¸é µÈ´Ù.
	 * FormUtils.get(form.someFlag, 'N')
	 */
	get: function(input, defaultValue) {
		// Firefox¿¡¼­ DOM tree¿¡¼­ Á¦°ÅµÈ checkbox °¡ <form>ÀÇ ÂüÁ¶´Â ³²¾ÆÀÖ´Â ¹®Á¦°¡ ÀÖ´ÂµíÇÔ 2009-03-26
		if (input == null || (input.nodeType == 1 && input.parentNode == null))
			return defaultValue;
		else if (input.tagName == null && input.length != null) { // radio
			for (var i = 0; i < input.length; i++) {
				if (input[i].checked)
					return input[i].value;
			}
			return defaultValue;
		}
		else {
			var tagName = input.tagName.toLowerCase();
			if (tagName == 'select')
				return (input.selectedIndex < 0)?
					defaultValue: input.options[input.selectedIndex].value;
			else if (tagName == 'input' &&
						(input.type == 'checkbox' || input.type == 'radio'))
				return (input.checked)? input.value: defaultValue;
			else
				return input.value;
		}
	},
	/**
	 * input °´Ã¼ÀÇ °ªÀ» ¼³Á¤ÇÑ´Ù.
	 *
	 * ´ÙÁß checkbox, radio(¹è¿­ÀÎ °æ¿ì)ÀÇ °æ¿ì input.value ¿Í value°¡ °°Àº°æ¿ì
	 * input.checked¸¦ true·Î ¼³Á¤ÇÑ´Ù.
	 *
	 * ´ÜÀÏ checkbox, radio ÀÇ °æ¿ì
	 * value°¡ ÂüÀÌ¸é input.checked¸¦ true·Î ¼³Á¤ÇÑ´Ù.
	 */
	set: function(input, value) {
		if (input == null)
			return;
		else if (input.tagName == null && input.length != null) { // radio
			for (var i = 0; i < input.length; i++) {
				input[i].checked = (input[i].value == value);
			}
		}
		else {
			var tagName = input.tagName.toLowerCase();
			if (tagName == 'select') {
				input.selectedIndex = -1;
				for (var i = 0; i < input.options.length; i++) {
					if (input.options[i].value == value) {
						input.selectedIndex = i;
						break;
					}
				}
			}
			else if (tagName == 'input' &&
						(input.type == 'checkbox' || input.type == 'radio'))
				input.checked = (value)? true: false;
			else
				input.value = value;
		}
	},
	/**
	 * input °´Ã¼ÀÇ °ªÀ» Á¦°ÅÇÑ´Ù.
	 */
	clear: function(input) {
		if (input == null)
			return;
		else if (input.tagName == null && input.length != null) { // radio
			for (var i = 0; i < input.length; i++)
				input[i].checked = false;
		}
		else {
			var tagName = input.tagName.toLowerCase();
			if (tagName == 'select')
				input.selectedIndex = 0;
			else if (tagName == 'input' &&
						(input.type == 'checkbox' || input.type == 'radio'))
				input.checked = false;
			else
				input.value = '';
		}
	},
	/**
	 * input °´Ã¼¸¦ ¼öÁ¤ÇÒ ¼ö ¾øµµ·Ï ÇÏ°Å³ª
	 * ´Ù½Ã ¼öÁ¤ÇÒ ¼ö ÀÖµµ·Ï ÇØÁ¦ÇÑ´Ù.
	 * À¯Çü¿¡ µû¶ó¼­ readOnly ¶Ç´Â diabled ¸¦ »ç¿ë ÇÑ´Ù.
	 */
	setReadOnly: function(input, readOnly) {
		if (input == null)
			return;
		else if (input.tagName == null && input.length != null) { // radio
			for (var i = 0; i < input.length; i++)
				input[i].disabled = readOnly;
		}
		else {
			var tagName = input.tagName.toLowerCase();
			if (tagName == 'select')
				input.disabled = readOnly;
			else if (tagName == 'input' &&
						(input.type == 'checkbox' || input.type == 'radio'))
				input.disabled = readOnly;
			else
				input.readOnly = readOnly;
		}
	},
	/**
	 * checkbox ÀÇ ¹è¿­¿¡¼­ ¸ðµç Ã¼Å©µÈ °ªÀ» ¹è¿­·Î ¸®ÅÏÇÑ´Ù.
	 */
	getAll: function(checkList) {
		if (checkList == null)
			return [];
		else if (checkList.tagName == null &&
					checkList.length != null) {	// radio
			var a = [];
			for (var i = 0; i < checkList.length; i++) {
				if (checkList[i].checked)
					a[a.length] = checkList[i].value;
			}
			return a;
		}
		else {
			var value = this.get(checkList, null);
			return (value != null)? [value]: [];
		}
	},
	/**
	 * checkbox ÀÇ ¹è¿­À» ÀüÃ¼ ¼±ÅÃ ÇÏ°Å³ª ÀüÃ¼ ÇØÁ¦ÇÑ´Ù.
	 * <input type="checkbox" value="1"
	 *	onclick="FormUtils.checkAll(this.form.checkboxFields, this)" />
	 */
	checkAll: function(checkList, master) {
		var checked = (typeof(master) == 'object')? master.checked: master;
		if (checkList == null)
			return;
		else if (checkList.length == null)
			checkList.checked = checked;
		else {
			for (var i = 0; i < checkList.length; i++)
				checkList[i].checked = checked;
		}
	},
	/**
	 * select ¹Ú½º ¼±ÅÃ½Ã option¿¡ ¼³Á¤µÈ
	 * ¹è°æ»ö°ú ±ÛÀÚ»öÀ» µ¿±âÈ­
	 * example:
	 *   <select onchange="FormUtils.syncSelectColors(event)">
	 *   OR
	 *   Event.observe(selectObj, 'change', FormUtils.syncSelectColors);
	 */
	syncSelectColors: function(selectOrEvent) {
		var select = (event.tagName != null &&
						event.tagName.toLowerCase() == 'select')?
				selectOrEvent: Event.element(selectOrEvent);
		var selectedIndex = select.selectedIndex;
		if (selectedIndex == -1) {
			select.style.backgroundColor = '';
			select.style.color = '';
		}
		else {
			var option = select.options[selectedIndex];
			select.style.backgroundColor = option.style.backgroundColor;
			select.style.color = option.style.color;
		}
	}
};

/**
 * ÄíÅ° Á¶ÀÛ À¯Æ¿¸®Æ¼
 */
var CookieUtils = {
	/**
	 * ÄíÅ°¸¦ ¼³Á¤ÇÑ´Ù.
	 * CookieUtils.set('name', 'value', '24h');
	 * CookieUtils.set('name', 'value', {
	 *		domain: 'merong.com',
	 *		path: '/',
	 *		expires: '24h'
	 * });
	 */
	set: function(name, value, options) {
		if (options == null)
			options = {};
		else if (typeof(options) == 'string' ||
				typeof(options) == 'number' ||
			(typeof(options) == 'object' && options.constructor == Date)) {
			options = { expires: options };
		}
		var domain = options.domain;
		var path = (options.path || '/');
		var expires = (options.expires != null)?
						this.parseExpires(options.expires): null;
		var cookie = name + "=" + encodeURIComponent(value);
		if (domain != null)
			cookie += "; domain=" + domain;
		cookie += "; path=" + path;
		if (expires != null)
			cookie += "; expires=" + expires.toGMTString();
		document.cookie =  cookie;
	},
	/**
	 * Æ¯Á¤ ÀÌ¸§ÀÇ ÄíÅ°¸¦ Á¶È¸ÇÑ´Ù.
	 * var value = CookieUtils.get('name');
	 */
	get: function(name) {
		var cookies = document.cookie.split(/\s*;\s*/);
		var pattern = new RegExp("^" + name + "=");
		for (var i = 0; i < cookies.length; i++) {
			if (pattern.test(cookies[i])) {
				var pair = cookies[i].split(/=/);
				return decodeURIComponent(pair[1]);
			}
		}
	},
	/**
	 * ¸ðµç ÄíÅ°¸¦ Á¶È¸ÇÑ´Ù.
	 * var cookies = CookieUtils.getAll();
	 * var value = cookies['name'];
	 */
	getAll: function(name) {
		var cookies = document.cookie.split(/\s*;\s*/);
		var map = {};
		for (var i = 0; i < cookies.length; i++) {
			var pair = cookies[i].split(/=/);
			map[pair[0]] = decodeURIComponent(pair[1]);
		}
		return map;
	},
	/*
	 * private methods
	 */
	parseExpires: function(expires) {
		var t = typeof(expires);
		if (t == 'null' || t == 'undefined')
			return null;
		else if (t == 'object' && expires.constructor == Date)
			return expires;

		var date = new Date();
		if (t == 'number') {	// ¼ýÀÚÀÎ°æ¿ì ½Ã°£À¸·Î °£ÁÖ
			date.setHours(date.getHours() + t);
			return date;
		}
		else if (expires.toLowerCase() == 'forever') {
			// ¿µ¿øÈ÷, ÃÖ´ëÇÑ ¿À·¡ À¯Áö
			date.setYear(9999);
			return date;
		}
		else if (expires.match(/^(\d+)\s*([ymwdh])$/i)) {
			var value = Number(RegExp.$1);
			var unit = RegExp.$2.toLowerCase();
			if (unit == 'y')
				date.setYear(date.getYear() + value);
			else if (unit == 'm')
				date.setMonth(date.getMonth() + value);
			else if (unit == 'w')
				date.setDate(date.getDate() + value * 7);
			else if (unit == 'd')
				date.setDate(date.getDate() + value);
			else if (unit == 'h')
				date.setHours(date.getHours() + value);
			else {
				return null;
			}
			return date;
		}
	}
};

/**
 * Ajax.Request ¸¦ »ç¿ëÇÏ±â ÆíÇÏµµ·Ï ÀçÁ¤ÀÇÇÔ.
 * °á°ú JSONÀº Ç×»ó ´ÙÀ½°ú °°Àº Çü½ÄÀÌ¾î¾ß ÇÑ´Ù.
 * {
 *    code: -1,     // 0ÀÏ °æ¿ì ¼º°ø ±× ÀÌ¿Ü¿¡´Â ¿À·ù
 *    message: '¿À·ù°¡ ¹ß»ýÇß½À´Ï´Ù.',
 *                  // ÃÖÁ¾ »ç¿ëÀÚ¿¡°Ô º¸¿©ÁÙ ¿À·ù
 *    debugMessage: 'ÆÄÀÏÀÌ ¾ø´Ù´ÂµÕ...',
 *                  // °³¹ßÀÚ¿¡°Ô º¸¿©ÁÙ ¿À·ù
 *                  // (debug ¸ðµå¿¡¸¸ º¸¿©ÁÜ)
 *    result: ...   // ¼­¹ö¿¡¼­ ¸®ÅÏµÈ Ã³¸® °á°ú °´Ã¼
 * }
 */
var AjaxUtils = {
	/**
	 * ÀÚ¼¼ÇÑ ¿À·ù ¸Þ½ÃÁö¸¦ Ç¥½ÃÇÒ °ÍÀÎÁö ¿©ºÎ
	 */
	debug: false,
	/**
	 * ÀÌº¥Æ® ÇÚµé·¯ - »õ·Î¿î ¿äÃ»ÀÌ ½ÇÇàµÇ¾úÀ½
	 * Ajax ¿äÃ»ÀÌ ÁøÇàµÇ´Â µ¿¾È ¹«¾ð°¡ È­¸é¿¡ Ç¥½ÃÇÒ ¶§ »ç¿ë °¡´ÉÇÔ
	 */
	requestStarted: null,
	/**
	 * ÀÌº¥Æ® ÇÚµé·¯ - ¸ðµç ¿äÃ»ÀÌ ³¡³µÀ½
	 * Ajax ¿äÃ»ÀÌ ÁøÇàµÇ´Â µ¿¾È ¹«¾ð°¡ È­¸é¿¡ Ç¥½ÃÇÒ ¶§ »ç¿ë °¡´ÉÇÔ
	 */
	allRequestCompleted: null,
	/** ¸¶Áö¸·À¸·Î ¹ÞÀº JSON */
	lastJSON: null,
	/** ¸¶Áö¸·À¸·Î ¹ÞÀº ¿À·ù */
	lastError: null,
	/** ¸¶Áö¸·À¸·Î ¹ß»ýÇÑ ¿¹¿Ü(callback ½ºÅ©¸³Æ® Ã³¸®Áß) */
	lastException: null,
	/** ÁøÇàÁßÀÎ ¿äÃ»µé */
	activeRequests: [],
	/**
	 * AJAX ¿äÃ»À» ¼öÇàÇÑ´Ù.
	 */
	requestAjax: function(url, options) {
		var method = (options.method || 'post');
		var parameters = this.serializeParamters(options.parameters);
		if (this.debug && typeof(DebugUtils) == 'object')
			DebugUtils.log("requestAjax(" + url + ")\n\tmethod: " + method + "\n\tparameters: " + parameters);
		var uid = ++this.requestUid;
		var request = new Ajax.Request(url, {
			method : method,
			parameters : parameters,
			onSuccess: AjaxUtils.requestOnSuccess,
			onException: AjaxUtils.requestOnException,
			onFailure: AjaxUtils.requestOnFailure,
			onComplete: AjaxUtils.requestOnComplete
		});
		request.customOnSuccess =
				(options.onSuccess || this.defaultOnSuccess);
		request.customOnError = (options.onError || this.defaultOnError);
		this.activeRequests[this.activeRequests.length] = request;
		if (typeof(AjaxUtils.requestStarted) == 'function')
			AjaxUtils.requestStarted();
	},
	serializeParamters: function(parameters) {
		if (parameters == null || parameters == '')
			return "";
		else if (typeof(parameters) == 'string')
			return parameters;
		var arr = [];
		for (var name in parameters) {
			if (typeof(parameters[name]) == 'object' && parameters[name].constructor == Array) {
				var name2 = encodeURIComponent(name) +
						((JSSLib.Server != null && JSSLib.Server.arrayParamSuffix)?
							JSSLib.Server.arrayParamSuffix: '');
				for (var i = 0; i < parameters[name].length; i++)
					arr[arr.length] = name2 + "=" + encodeURIComponent(parameters[name][i])
			}
			else {
				arr[arr.length] = encodeURIComponent(name) + '=' + encodeURIComponent(parameters[name]);
			}
		}
		return arr.join('&');
	},
	/**
	 * Ajax ¿äÃ»ÀÌ ¼º°ø(HTTP 2xx)ÇßÀ» °æ¿ì ½ÇÇà
	 */
	requestOnSuccess: function(response) {
		var result = response.responseText.evalJSON();
		var request = response.request;
		if (result.code == 0) {	// ¼º°ø
			AjaxUtils.lastJSON = result.result;
			request.customOnSuccess(result.result);
		}
		else {	// ½ÇÆÐ
			AjaxUtils.lastError = result;
			request.customOnError(result);
		}
	},
	/**
	 * Ajax ¿äÃ»ÀÌ ½ÇÆÐ(!HTTP 2xx)ÇßÀ» °æ¿ì ½ÇÇà
	 */
	requestOnFailure: function(transport) {
		alert('HTTP ¿äÃ» ½ÇÆÐ: ' + transport.status);
	},
	/**
	 * ¿äÃ» Ã³¸® °úÁ¤¿¡¼­ ½ºÅ©¸³Æ® ¿¹¿Ü°¡ ¹ß»ýÇÒ °æ¿ì ½ÇÇà
	 */
	requestOnException: function(request, e) {
		if (AjaxUtils.debug) {
			alert("Exception:\n\n" + e.message +
					((e.stack != null)? "\n\n" + e.stack: ""));
		}
		AjaxUtils.lastException = e;
	},
	/**
	 * ¿äÃ» Ã³¸®°¡ ¿Ï·áµÈ °æ¿ì ½ÇÇà
	 */
	requestOnComplete: function(transport) {
		var pos = AjaxUtils.activeRequests.indexOf(transport.request);
		if (pos >= 0)
			AjaxUtils.activeRequests.splice(pos, 1);
		if (AjaxUtils.activeRequests.length == 0 &&
				typeof(AjaxUtils.allRequestCompleted) == 'function')
			AjaxUtils.allRequestCompleted();
	},
	/**
	 * µðÆúÆ® ¼º°ø ÇÚµé·¯
	 */
	defaultOnSuccess: function(result) {
		alert(result);
	},
	/**
	 * µðÆúÆ® ½ÇÆÐ ÇÚµé·¯
	 */
	defaultOnError: function(result) {
		if (result.debugMessage != null && AjaxUtils.debug)
			alert(result.debugMessage);
		else
			alert(result.message);
	}
};

var DateUtils = {
	timezoneOffset: (new Date()).getTimezoneOffset() * 60000,
	trunc: function(date) {
		var t = date.valueOf() + this.timezoneOffset;
		return new Date(Math.floor(t / 86400000) * 86400000 +
										this.timezoneOffset);
	},
	format: function(date) {
		return DateFormat.format(date);
	},
	getToday: function() {
		return this.trunc(new Date());
	},
	/**
	 * ÁöÁ¤ÇÑ ¿¬µµ°¡ À±³â(¾ç·Â)ÀÎ°¡?
	 * @param year ¿¬µµ
	 * @return À±³â ¿©ºÎ
	 */
	isLeapYear: function(year) {
		return (
			((year % 400) == 0) ||
			((year % 4) == 0 && (year % 100) != 0)
		);
	},
	locale: {
		'en': {
			monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
			monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
			wdayNames: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
			wdayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
		},
		'ko': {
			monthNames: ['1¿ù', '2¿ù', '3¿ù', '4¿ù', '5¿ù', '6¿ù', '7¿ù', '8¿ù', '9¿ù', '10¿ù', '11¿ù', '12¿ù'],
			monthNamesShort: ['1¿ù', '2¿ù', '3¿ù', '4¿ù', '5¿ù', '6¿ù', '7¿ù', '8¿ù', '9¿ù', '10¿ù', '11¿ù', '12¿ù'],
			wdayNames: ['ÀÏ¿äÀÏ', '¿ù¿äÀÏ', 'È­¿äÀÏ', '¼ö¿äÀÏ', '¸ñ¿äÀÏ', '±Ý¿äÀÏ', 'Åä¿äÀÏ'],
			wdayNamesShort: ['ÀÏ', '¿ù', 'È­', '¼ö', '¸ñ', '±Ý', 'Åä']
		}
	}
};

var NumberUtils = {
	pad: function(number, length, padChar) {
		if (length == null)
			return String(number);
		else {
			var s = String(number);
			return (padChar || '0').times(length - s.length) + s;
		}
	}
};

/**
 * Æ÷¸Ë °´Ã¼ ÇÁ·ÎÅäÅ¸ÀÔ
 */
var Format = function(name, options) {
	this.name = name;
	if (options != null)
		Object.extend(this, options);
};
Object.extend(Format.prototype, {
	format: function(value) {
		return value;
	},
	parse: function(str) {
		return str;
	}
});
Format.Collection = function(options) {
	if (options != null)
		Object.extend(this, options);
};
Object.extend(Format.Collection.prototype, {
	format: function(value, format) {
		var formatObj = (format == null || format == 'auto')?
							null: this[format];
		if (formatObj == null)
			formatObj = this[this.auto[0]];
		var args = [value];
		for (var i = 2; i < arguments.length; i++)
			args[args.length] = arguments[i];
		return formatObj.format.apply(formatObj, args);
	},
	parse: function(str, format) {
		var formatObj = (format == null || format == 'auto')?
							null: this[format];
		var args = [str];
		for (var i = 2; i < arguments.length; i++)
			args[args.length] = arguments[i];
		if (formatObj != null)
			return formatObj.parse.apply(formatObj, args);
		else {
			for (var i = 0; i < this.auto.length; i++) {
				formatObj = this[this.auto[i]];
				var date = formatObj.parse.apply(formatObj, args);
				if (date != null)
					return date;
			}
			return null;
		}
	},
	add: function(formatObj) {
		this[formatObj.name] = formatObj;
	}
});

var DateFormat = new Format.Collection({
	auto: ['simpleDateTime', 'raw'],
	/**
	 * ¿¬, ¿ù(1~12), ÀÏ, ½Ã(0~23), ºÐ, ÃÊ, ¹Ð¸®ÃÊ ¼ø¼­·Î µÇ¾îÀÖ´Â
	 * ³¯Â¥ Çü½ÄÀ» ¸¸µé°Å³ª ÆÄ½ÌÇÑ´Ù.
	 *
	 * ÆÄ½ÌÀº °¢ ÇÊµåÀÇ ¼ø¼­¿Í ÀÚ¸®¼ö°¡ Á¦ÀÏ Áß¿äÇÏ´Ù.
	 * ¼ýÀÚ¸¦ ¿¬, ¿ù, ÀÏ, ½Ã, ºÐ, ÃÊ, ¹Ð¸®ÃÊ ¼øÀ¸·Î ÀÐÀ¸¸ç °¢ ÇÊµå¸¦
	 * ¼³Á¤ÇÏ¹Ç·Î ¼ø¼­°¡ Á¤È®ÇØ¾ß ÇÑ´Ù.
	 *
	 * ÇÊµå Áß°£¿¡ ±¸ºÐÀÚ°¡ ¾ø´Â °æ¿ì ¿¬µµ´Â 4ÀÚ¸®, ¹Ð¸®ÃÊ´Â 3ÀÚ¸®,
	 * ±×¹ÛÀÇ ÇÊµåµéÀº 2ÀÚ¸®·Î ÆÐµùµÇ¾î¾ß ÇÑ´Ù.
	 * Áß°£¿¡ ¼ýÀÚ°¡ ¾Æ´Ñ ¹®ÀÚ°¡ ÀÖ´Â°æ¿ì ±×³É ¹«½ÃµÈ´Ù.
	 * simpleDateTime, simpleDate, simpleTime ¸ðµÎ ÆÄ½Ì ±â´ÉÀº °°À¸¸ç
	 * ¹Ð¸®ÃÊ ´ÜÀ§±îÁö Áö¿øÇÑ´Ù.
	 *
	 * Æ÷¸ÅÆÃ ±â´ÉÀº simpleDateTime Àº ÃÊ´ÜÀ§±îÁö, simpleDate ´Â ³¯Â¥¸¸,
	 * simpleTimeÀº ½Ã°£¸¸ Ãâ·ÂÇÑ´Ù.
	 */
	simpleDateTime: new Format('simpleDateTime', {
		datePattern: /^(\d{4})\D*(\d{1,2})\D*(\d{1,2})\D*/,
		timePattern: /^(\d{1,2})\D*(\d{2})\D*(\d{2})?\D*(\d{1,3})?/,
		parse: function(str) {
			var date = new Date(1970, 0, 1, 0, 0, 0, 0);
			var matched = false;
			var m = this.datePattern.exec(str);
			if (m != null && m.length > 0) {
				date.setFullYear(Number(m[1]));
				date.setMonth(Number(m[2]) - 1);
				date.setDate(Number(m[3]));
				str = RegExp.rightContext;
				matched = true;
			}
			m = this.timePattern.exec(str);
			if (m != null && m.length > 0) {
				date.setHours(Number(m[1]));
				date.setMinutes(Number(m[2]));
				if (m[3])
					date.setSeconds(Number(m[3]));
				if (m[4])
					date.setMilliseconds(Number(m[4]));
				matched = true;
			}
			return (matched)? date: null;
		},
		format: function(date, delim, level) {
			return DateFormat.simpleDate.format(date, delim) + " " +
								DateFormat.simpleTime.format(date, level);
		}
	}),
	simpleDate: new Format('simpleDate', {
		format: function(date, delim) {
			delim = (delim || '-');
			var y = date.getFullYear();
			var m = date.getMonth() + 1;
			var d = date.getDate();
			return y + delim + NumberUtils.pad(m, 2) +
								delim + NumberUtils.pad(d, 2);
		},
		parse: function(str) {
			return DateFormat.simpleDateTime.parse(str);
		}
	}),
	simpleTime: new Format('simpleTime', {
		/**
		 * @param level Ç¥½ÃÇÒ ÇÊµå(minute | second | millisecond)
		 */
		format: function(date, level) {
			level = (level || 'second');
			var s = NumberUtils.pad(date.getHours(), 2) +
					':' + NumberUtils.pad(date.getMinutes(), 2);
			if (level == 'minute')
				return s;
			s += ':' + NumberUtils.pad(date.getSeconds(), 2);
			if (level != 'millisecond')
				return s;
			s += '.' + NumberUtils.pad(date.getMilliseconds(), 3);
			return s;
		},
		parse: function(str) {
			return DateFormat.simpleDateTime.parse(str);
		}
	}),
	/**
	 * ÀÚ¹Ù½ºÅ©¸³Æ® ÀÚÃ¼ÀÇ ³¯Â¥ Æ÷¸ÅÆÃ/ÆÄ½Ì ±â´É
	 */
	raw: new Format('raw', {
		format: function(date) {return date.toString();},
		parse: function(str) {return new Date(str);}
	})
});

var NumberFormat = new Format.Collection({
	auto: ['comma', 'fileSize', 'raw'],
	/**
	 * ¼¼ÀÚ¸®¸¶´Ù ','¸¦ Ãß°¡ÇÑ ¼ýÀÚ Çü½ÄÀ» ¸¸µé°Å³ª ÆÄ½ÌÇÑ´Ù.
	 */
	comma: new Format('comma', {
		format: function(number, fraction) {
			number = (fraction == null)? number:
						((fraction == 0)? Math.round(number):
							Math.round(number * Math.pow(10, fraction)) /
											Math.pow(10, fraction));
			if (Math.abs(number) < 1000)
				return String(number);
			var a = String(number).split(/\./);
			var i = a[0], f = (a[1] || ''), sign = '';
			if (i.match(/^([+-])/)) {
				sign = RegExp.$1;
				i = i.substring(1);
			}
			var s = '', s0 = String(i);
			var p = s0.length % 3;
			if (p > 0)
				s += s0.substring(0, p) + ",";
			while (p < s0.length) {
				s += s0.substring(p, p + 3);
				if (p < s0.length - 3)
					s += ",";
				p += 3;
			}
			return sign + s + ((f != '')? "." + f: '');
		},
		parse: function(str) {
			var s = str.replace(/,/g, '');
			var n = Number(s);
			return (isNaN(n))? null: n;
		}
	}),
	/**
	 * ÆÄÀÏ Å©±â ´ÜÀ§¸¦ Æ÷ÇÔÇÑ Çü½ÄÀ» ¸¸µé°Å³ª ÆÄ½ÌÇÑ´Ù.
	 */
	fileSize: new Format('fileSize', {
		format: function(size) {
			if (size == null)
				return "0";
			else if (size < 1024)
				return NumberFormat.comma.format(size, 0);
			else if (size < 1048576)
				return NumberFormat.comma.format(size/1024, 1) + "kB";
			else if (size < 1073741824)
				return NumberFormat.comma.format(size/1048576, 1) + "MB";
			else
				return NumberFormat.comma.format(size/1073741824, 1) + "GB";
		},
		sizePattern: /^([\d,\.-]+)([kmgb])?/i,
		parse: function(str) {
			var m = this.sizePattern.exec(str);
			if (m) {
				var n = NumberFormat.comma.parse(m[1]);
				if (!m[2])
					return n;
				var unit = m[2].toLowerCase();
				if (unit == 'b')
					return n;
				else if (unit == 'k')
					return n * 1024;
				else if (unit == 'm')
					return n * 1048576;
				else if (unit == 'g')
					return n * 1073741824;
			}
			return null;
		}
	}),
	/**
	 * ÀÚ¹Ù½ºÅ©¸³Æ® ÀÚÃ¼ÀÇ ¼ýÀÚ Æ÷¸ÅÆÃ/ÆÄ½Ì ±â´É
	 */
	raw: new Format('raw', {
		format: function(number) {return number.toString();},
		parse: function(str) {return new Number(str);}
	})
});

/**
 * ÆË¾÷ °øÁö Å¬·¡½º
 */
var PopupNotice = function(name, url, width, height, options) {
	this.name	= (name == null)? "popup": name;
	this.url	= (url == null)? "about:blank": url;
	this.width	= (width == null)? 300: width;
	this.height	= (height == null)? 400: height;
	this.options = Object.extend({}, options);
	PopupNotice.registry[PopupNotice.registry.length] = this;
};
// static members
Object.extend(PopupNotice, {
	registry: [],
	showAll: function() {
		for (var i = 0; i < this.registry.length; i++) {
			if (this.registry[i] != null)
				this.registry[i].show();
		}
	},
	register: function() {
		var name, url, width, height, options;
		if (arguments.length == 1) {
			options = arguments[0];
			name = options.name; delete options.name;
			url = options.url; delete options.url;
			width = options.width; delete options.width;
			height = options.height; delete options.height;
		}
		else {
			name = arguments[0];
			url = arguments[1];
			width = arguments[2];
			height = arguments[3];
			options = arguments[4];
		}
		new PopupNotice(name, url, width, height, options);
	},
	registerAll: function(array) {
		if (array == null)
			return;
		for (var i = 0; i < array.length; i++)
			this.register(array[i]);
	}
});
// instance members
Object.extend(PopupNotice.prototype, {
	open: function() {
		var features = "width=" + this.width + ",height=" + this.height;
		if (this.options.top)
			features += ",top=" + this.options.top;
		if (this.options.left)
			features += ",left=" + this.options.left;
		if (this.options.scrollbars)
			features += ",scrollbars=" + this.options.scrollbars;
		if (this.options.resizable)
			features += ",resizable=" + this.options.resizable;
		return window.open(this.url, this.name, features);
	},
	show: function() {
		// °Ô½Ã ±â°£ÀÌ ¼³Á¤µÇ¾ú´Ù¸é
		if (this.options.showFrom != null && this.options.showTo != null) {
			var showFrom = DateFormat.parse(this.options.showFrom);
			var showTo = DateFormat.parse(this.options.showTo);
			var now = new Date();
			if (showFrom.valueOf() > now.valueOf() ||
					showTo.valueOf() < now.valueOf())
				return false;
		}
		// ÄíÅ°°¡ ¼³Á¤µÇ¾ú´Ù¸é
		var cookie = CookieUtils.get(this.name);
		if (cookie != null && cookie != '')
			return false;
		else
			return this.open();
	},
	nothanks: function(hours) {
		CookieUtils.set(this.name, 'nothanks', (hours || '24h'));
	},
	close: function() {
		this.nothanks();
		window.close();
	},
	gotoEvent: function() {
		var url = this.options.linkUrl;
		var target = (this.options.linkUrl || '_self');
		opener.open(url, target);
		window.close();
	}
});

/**
 * input °´Ã¼(<input type="text">, <textarea>) ¶Ç´Â
 * ¹öÆ°(<a>, <button>, <img>)¿¡ tool ·¹ÀÌ¾î¸¦ ¿¬°á½ÃÄÑ ÀÔ·ÂÀ» º¸Á¶ÇÏ´Â
 * ¹æ½ÄÀÇ UI Á¦ÀÛÀ» À§ÇÑ Ãß»ó Å¬·¡½º.
 *
 * input °´Ã¼ÀÎ °æ¿ì toolÀÇ ÀÔ·ÂÀÌ ³¡³ª¸é onchange ÀÌº¥Æ®¸¦ ½ÇÇàÇÑ´Ù.
 * onchange ÀÌº¥Æ® ÇÚµé·¯¿¡¼­´Â this.value ·Î ÀÔ·ÂµÈ °ªÀ» È®ÀÎÇÒ ¼ö ÀÖ´Ù.
 *
 * ¹öÆ°ÀÇ °æ¿ì´Â onclick ÀÌº¥Æ® ÇÚµé·¯°¡ toolÀ» È£ÃâÇÏ´Â ÄÚµå·Î ±³Ã¼µÇ¸ç
 * toolÀÇ ÀÔ·ÂÀÌ ³¡³ª¸é ¿ø·¡ onclick ÀÌº¥Æ® ÇÚµé·¯°¡ È£ÃâµÈ´Ù.
 * onclick ÀÌº¥Æ® ÇÚµé·¯¿¡¼­µµ this.value ·Î ÀÔ·ÂµÈ °ªÀ» È®ÀÎÇÒ ¼ö ÀÖ´Ù.
 * (´Ü, onclick ÀÌ È£ÃâµÇ´õ¶óµµ ¸¶¿ì½º ÀÌº¥Æ® Á¤º¸´Â Àü´ÞµÇÁö ¾Ê´Â´Ù.
 * ½ÇÁ¦·Î´Â Å¬¸¯ ÀÌº¥Æ®°¡ ¾Æ´Ï±â ¶§¹®ÀÌ´Ù.)
 */
var ToolBinder = {
	/**
	 * [»ç¿ëÀÚ ¼³Á¤ Ç×¸ñ]
	 * ÆäÀÌÁö ·Îµù½Ã¿¡ ÁöÁ¤ÇÑ Å¬·¡½º¸íÀ» Ã£¾Æ ÀÚµ¿À¸·Î ¹ÙÀÎ´õ¸¦ »ý¼ºÇÒ °ÍÀÎ°¡?
	 */
	autoInit: true,
	/**
	 * [»ç¿ëÀÚ ¼³Á¤ Ç×¸ñ]
	 * input °´Ã¼¿¡ focus ÀÌº¥Æ® ¹ß»ý½Ã toolÀ» º¸¿©ÁÙ °ÍÀÎ°¡?
	 */
	observeFocus: true,
	/**
	 * [»ç¿ëÀÚ ¼³Á¤ Ç×¸ñ]
	 * input °´Ã¼¿¡ blur ÀÌº¥Æ® ¹ß»ý½Ã toolÀ» ´ÝÀ» °ÍÀÎ°¡?
	 */
	observeBlur: true,
	/**
	 * [»ç¿ëÀÚ ¼³Á¤ Ç×¸ñ]
	 * input °´Ã¼¿¡ tool Åä±Û ¹öÆ°À» Ãß°¡ÇÒ °ÍÀÎ°¡?
	 */
	makeButton: true,
	/**
	 * [»ç¿ëÀÚ ¼³Á¤ Ç×¸ñ]
	 * ¹öÆ° °´Ã¼¿¡ click ÀÌº¥Æ® ¹ß»ý½Ã Åä±ÛÇÏµµ·Ï ÇÒ°ÍÀÎ°¡?
	 */
	observeClick: true,
	/**
	 * [»ó¼Ó½Ã ¼³Á¤ Ç×¸ñ (ÇÊ¼ö)]
	 * °ü·ÃµÈ tool ·¹ÀÌ¾î °´Ã¼
	 */
	tool: null,
	/**
	 * [»ó¼Ó½Ã ¼³Á¤ Ç×¸ñ]
	 * Åä±Û¹öÆ° »ý¼º½Ã ÀÌ¹ÌÁö URL
	 */
	buttonImage: '',
	/**
	 * ¹ÙÀÎ´õ ÀÎ½ºÅÏ½º¸¦ »ý¼ºÇÑ´Ù.
	 */
	initInstance: function(instance, target, options) {
		instance.target = target;
		target.binder = instance;
		var tagName = target.tagName.toLowerCase();
		options = Object.extend({
				observeFocus: this.observeFocus,
				observeBlur: this.observeBlur,
				makeButton: this.makeButton,
				observeClick: this.observeClick
			}, options);

		// onchange ÀÌº¥Æ®¸¦ °¡Áø input °´Ã¼
		if ((tagName == 'input' && target.type == 'text') ||
										tagName == 'textarea') {
			if (options.observeFocus)
				Event.observe(target, 'focus', this.handleFocus.bind(this));
			if (options.observeBlur)
				Event.observe(target, 'blur', this.handleBlur.bind(this));
			if (options.makeButton && this.buttonImage != null &&
											this.buttonImage != '') {
				var button = document.createElement('a');
				Element.insert(target, {after: button});
				button.href = 'javascript:void(0)';
				button.className = 'BinderButton';
				button.tabIndex = -1;
				button.innerHTML = '<img src="' + this.buttonImage + '" />';
				Event.observe(button, 'click',
									this.handleButtonClick.bind(this));
				instance.binderButton = button;
			}
		}
		// onchange ÀÌº¥Æ®°¡ ¾ø´Â ¹öÆ° °´Ã¼
		else if (tagName == 'a' || tagName == 'button' || tagName == 'img') {
			if (options.observeBlur)
				Event.observe(document, 'click',
								this.handleFocusOther.bind(target));
			if (options.observeClick) {
				if (typeof(target.onclick) == 'function') {	// onclickÀ» ¹é¾÷
					instance.onchange = target.onclick.bind(target);
					target.onclick = null;
				}
				Event.observe(target, 'click', this.handleClick.bind(this));
			}
		}
		if (options.onchange)
			instance.onchange = options.onchange;
		if (options.onshow)
			instance.onshow = options.onshow;
		if (options.onhide)
			instance.onhide = options.onhide;
		// Ãß°¡ÀûÀÎ ÃÊ±âÈ­ ÀýÂ÷°¡ ÀÖÀ» °æ¿ì¸¦ À§ÇØ
		if (instance.init != null)
			instance.init();
	},
	/**
	 * input °´Ã¼ ¶Ç´Â ¹öÆ°ÀÌ ¹ÙÀÎ´õ¸¦ °¡Áö°í ÀÖ´ÂÁö °Ë»ç
	 */
	hasBinder: function(target) {
		return (typeof(target.binder) == 'object' &&
						target.binder.constructor == this);
	},
	/**
	 * input °´Ã¼ÀÇ Åä±Û¹öÆ°ÀÇ click ÀÌº¥Æ® ÇÚµé·¯
	 */
	handleButtonClick: function(event) {
		var button = Event.element(event || window.event);
		while (button != null && button.tagName.toLowerCase != 'a' &&
									button.className != 'BinderButton')
			button = button.parentNode;
		var input = Element.previous(button);
		if (this.hasBinder(input))
			input.binder.toggle();
		button.blur();
	},
	/**
	 * ¹öÆ° °´Ã¼ÀÇ click ÀÌº¥Æ® ÇÚµé·¯
	 */
	handleClick: function(event) {
		var button = Event.element(event || window.event);
		if (this.hasBinder(button))
			button.binder.toggle();
	},
	/**
	 * input °´Ã¼ÀÇ focus ÀÌº¥Æ® ÇÚµé·¯
	 */
	handleFocus: function(event) {
		var input = Event.element(event || window.event);
		if (this.hasBinder(input))
			input.binder.show();
	},
	/**
	 * input °´Ã¼ÀÇ blur ÀÌº¥Æ® ÇÚµé·¯
	 */
	handleBlur: function(event) {
		var input = Event.element(event || window.event);
		if (this.hasBinder(input)) {
			// Æ÷Ä¿½º°¡ ¾ø¾îÁø °æ¿ì, tool ·¹ÀÌ¾î¿¡ ¸¶¿ì½º°¡ ÀÖ´ÂÁö È®ÀÎ
			if (this.tool != null && this.tool.hasMouse())
				return;
			// ¾Æ´Ï¸é ¹ÙÀÎ´õ ¹öÆ°¿¡ ¸¶¿ì½º°¡ ÀÖ´ÂÁö
			if (input.binder.binderButton != null &&
					DOMUtils.hasMouse(input.binder.binderButton))
				return;
			input.binder.hide();
		}
	},
	/**
	 * <img>µî¿¡ ¹ÙÀÎ´õ¸¦ ¸¸µé¾úÀ» °æ¿ì blur ÀÌº¥Æ®¸¦
	 * ¹ÞÀ» ¼ö ¾øÀ¸¹Ç·Î ´Ù¸¥ °´Ã¼ÀÇ click À» ¸ð´ÏÅÍÇÑ´Ù.
	 * ÀÌ ÀÌº¥Æ® ÇÚµé·¯´Â ´ë»ó <img>°´Ã¼¿¡ bind()µÈ´Ù.
	 */
	handleFocusOther: function(event) {
		var focused = Event.element(event || window.event);
		//alert((focused == null)? 'null': focused.tagName);
		if (focused == null || focused != this) {
			var tool = this.binder.getTool();
			if (tool != null && !tool.hasMouse() && !DOMUtils.isAncestorOf(tool.layer, focused))
				this.binder.hide();
		}
	}
};

/**
 * ToolBinder ÀÎ½ºÅÏ½º¿ë ÇÁ·ÎÅäÅ¸ÀÔ
 */
var ToolBinderPrototype = {
	/**
	 * [»ó¼Ó½Ã ¼³Á¤ Ç×¸ñ]
	 * ÇöÀç ´ë»ó °´Ã¼ÀÇ °ªÀ» Á¶È¸ÇÑ´Ù.
	 * input °´Ã¼¿¡¼­ °ª(¹®ÀÚ¿­)À» ÀÐ¾î¿Í¼­
	 * tool °´Ã¼¿¡ ³Ñ±æ¶§ º¯È¯ÀÌ ÇÊ¿äÇÒ °æ¿ì
	 * ÀçÁ¤ÀÇ ÇÒ ¼ö ÀÖ´Ù.
	 */
	getValue: function() {
		return this.target.value;
	},
	/**
	 * [»ó¼Ó½Ã ¼³Á¤ Ç×¸ñ]
	 * ÇöÀç ´ë»ó °´Ã¼ÀÇ °ªÀ» ¼³Á¤ÇÑ´Ù.
	 * tool °´Ã¼¿¡¼­ ÆíÁýÀÌ ³¡³­ °ªÀ» input °´Ã¼¿¡ ÀúÀåÇÒ¶§
	 * (¹®ÀÚ¿­·Î) º¯È¯ÀÌ ÇÊ¿äÇÒ °æ¿ì ÀçÁ¤ÀÇ ÇÒ ¼ö ÀÖ´Ù.
	 */
	setValue: function(value) {
		this.target.value = value;
	},
	/**
	 * ¹ÙÀÎ´õ°¡ ÇöÀç tool ·¹ÀÌ¾î¸¦ »ç¿ëÇÏ°í ÀÖ´ÂÁö
	 */
	hasTool: function() {
		return (this.constructor.tool.binder == this);
	},
	/**
	 * ¹ÙÀÎ´õ°¡ ÇöÀç »ç¿ëÇÏ°í ÀÖ´Â tool layer
	 */
	getTool: function() {
		return this.constructor.tool;
	},
	/**
	 * tool ·¹ÀÌ¾î¸¦ ÃÊ±âÈ­ÇÏ°í
	 * ´ë»ó input ¶Ç´Â ¹öÆ° °´Ã¼ ±ÙÃ³¿¡ º¸¿©ÁØ´Ù.
	 */
	show: function() {
		var tool = this.getTool();
		if (!tool.initialized)
			tool.init();
		if (tool.binder != null && tool.binder != this)
			tool.binder.hide();
		tool.binder = this;
		tool.show(this.target);
		tool.setInitialValue(this.getValue());
		if (typeof(this.onshow) == 'function')
			this.onshow();
	},
	/**
	 * tool ·¹ÀÌ¾î¸¦ °¨Ãá´Ù.
	 */
	hide: function() {
		var tool = this.getTool();
		if (tool.binder != this)
			return;
		tool.binder = null;
		tool.hide();
		if (typeof(this.onhide) == 'function')
			this.onhide();
	},
	/**
	 * tool ·¹ÀÌ¾î¸¦ º¸¿©ÁÖ°Å³ª °¨Ãá´Ù.
	 */
	toggle: function() {
		var tool = this.getTool();
		if (tool.binder == this && tool.active)
			this.hide();
		else
			this.show();
	},
	/**
	 * tool ·¹ÀÌ¾î¿¡¼­ ÀÔ·ÂÀ» ¸¶ÃÆÀ» °æ¿ì
	 * ÃÖÁ¾ °á°ú¸¦ ´ë»ó °´Ã¼¿¡ ¼³Á¤ÇÏ±â À§ÇØ È£ÃâµÈ´Ù.
	 */
	toolCompleted: function(value) {
		// ÀÔ·Â°ªÀ» ¼³Á¤ÇÏ°í
		this.setValue(value);
		if (this.onchange != null) {// ¿ø·¡ onclickÀ» È£Ãâ
			this.onchange();
		}
		else { // ½ÇÁ¦ onchange ÀÌº¥Æ® ¹ß»ý½ÃÅ´
			if (this.target.fireEvent != null) {	// MSIE
				this.target.fireEvent('onchange');
			}
			else if (this.target.dispatchEvent != null) { // other browsers
				var evt = document.createEvent('HTMLEvents');
				evt.initEvent('change', true, false);
				this.target.dispatchEvent(evt);
			}
		}
	}
};

/**
 * tool ·¹ÀÌ¾î Å¬·¡½º
 */
var ToolLayer = function(id, className) {
	this.id = id;
	this.className = className;
	ToolLayer.instances[ToolLayer.instances.length] = this;
};
Object.extend(ToolLayer, {
	/**
	 * [»ç¿ëÀÚ ¼³Á¤ Ç×¸ñ]
	 * ¿©·¯°³ÀÇ tool ·¹ÀÌ¾î°¡ »ç¿ëµÇ°í ÀÖ´Â °æ¿ì
	 * ÇÑ ¼ø°£¿¡ ÇÏ³ª¸¸ Ç¥½Ã ÇÏµµ·Ï ÇÒ °ÍÀÎ°¡?
	 * tool ·¹ÀÌ¾î°¡ ¿­¸®¸é ´Ù¸¥ toolÀº ¸ðµÎ ´Ý±è
	 */
	showOnlyOne: true,
	/**
	 * »ý¼ºµÇ¾îÀÖ´Â ToolLayer ÀÎ½ºÅÏ½ºµé
	 */
	instances: [],
	getContainer: function() {
		var container = $('ToolLayers');
		if (container == null) {
			Element.insert(document.body, '<div id="ToolLayers"></div>');
			container = $('ToolLayers');
		}
		return container;
	},
	hideAll: function() {
		for (var i = 0; i < this.instances.length; i++)
			this.instances[i].hide();
	},
	hideOthers: function(toolLayer) {
		for (var i = 0; i < this.instances.length; i++) {
			if (toolLayer != this.instances[i])
				this.instances[i].hide();
		}
	}
});
Object.extend(ToolLayer.prototype, {
	/**
	 * [»ó¼Ó½Ã ¼³Á¤ Ç×¸ñ (ÇÊ¼ö)]
	 * »ý¼ºµÉ ·¹ÀÌ¾îÀÇ ID ¼Ó¼º.
	 * ´Ù¸¥ ·¹ÀÌ¾î³ª ¿¤¸®¸ÕÆ®µé°ú Áßº¹µÇÁö ¾Ê¾Æ¾ß ÇÑ´Ù.
	 */
	id: null,
	/**
	 * [»ó¼Ó½Ã ¼³Á¤ Ç×¸ñ (ÇÊ¼ö)]
	 * »ý¼ºµÉ ·¹ÀÌ¾îÀÇ CSS Å¬·¡½º¸í.
	 * <div>´Â µðÆúÆ®·Î ÆøÀÌ ¼³Á¤µÇÁö ¾ÊÀ¸¹Ç·Î Á¤»óÀûÀ¸·Î »ç¿ëÇÏ·Á¸é
	 * CSS Å¬·¡½º¸¦ ÀÌ¿ëÇØ¼­ ÆøÀ» ¼³Á¤ÇØ Áà¾ß ÇÑ´Ù.
	 */
	className: null,
	/**
	 * [»ó¼Ó½Ã ¼³Á¤ Ç×¸ñ (ÇÊ¼ö)]
	 * init()¿¡ ÀÇÇØ ·¹ÀÌ¾î °´Ã¼(ToolLayer)°¡ ÃÊ±âÈ­ µÈ ÈÄ
	 * Ãß°¡ÀûÀÎ tool ±â´É ÃÊ±âÈ­¸¦ À§ÇØ È£ÃâµÈ´Ù.
	 * ÀÌ ¸Þ¼Òµå¿¡¼­ ·¹ÀÌ¾î¿¡ µé¾î°¥ HTMLÀ» »ý¼ºÇØ¼­
	 * Element.update(this.layer, content) ¸¦ »ç¿ëÇØ¼­ Àû¿ë½ÃÅ²´Ù.
	 */
	initTool: function() {},
	/**
	 * [»ó¼Ó½Ã ¼³Á¤ Ç×¸ñ]
	 * tool ·¹ÀÌ¾î¸¦ ´ë»ó °´Ã¼ÀÇ ¾î´ÀÂÊ¿¡ Ç¥½ÃÇÒ °ÍÀÎ°¡?
	 */
	position: 'bottom',
	/**
	 * [»ó¼Ó½Ã ¼³Á¤ Ç×¸ñ]
	 * ¹ÙÀÎ´õ°¡ show¸¦ È£ÃâÇÒ ¶§ ÃÊ±â°ªÀ» ¼³Á¤ ÇÏ±â À§ÇØ È£ÃâÇÏ´Â ¸Þ½îµå
	 */
	setInitialValue: function(value) {},
	/**
	 * tool¿¡¼­ ÀÔ·ÂÀ» ¸¶Ä£ÈÄ ¹ÙÀÎ´õ¿¡°Ô °ªÀ» ¼³Á¤ÇÏ±â À§ÇØ¼­´Â
	 * ÀÌ ¸Þ¼Òµå¸¦ È£ÃâÇÏ¸é µÈ´Ù.
	 */
	setReturn: function(value) {
		var binder = this.binder;
		if (binder != null) {
			binder.toolCompleted(value);
			binder.hide();
		}
		else {
			this.hide();
		}
	},
	/**
	 * tool ·¹ÀÌ¾î°¡ ÃÊ±âÈ­ µÇ¾ú´ÂÁö ¿©ºÎ
	 */
	initialized: false,
	/**
	 * ·¹ÀÌ¾î °´Ã¼(element)
	 */
	layer: null,
	/**
	 * ¹ÙÀÎ´õ¿Í ¿¬°áµÇ¾î¼­ È­¸é¿¡ Ç¥½ÃµÇ°í ÀÖ´Â°¡?
	 */
	active: false,
	/**
	 * ÇöÀç toolÀ» »ç¿ëÇÏ°í ÀÖ´Â ¹ÙÀÎ´õ
	 */
	binder: null,
	/**
	 * ¸¶¿ì½º°¡ tool ·¹ÀÌ¾î À§¿¡ ÀÖ´ÂÁö È®ÀÎ
	 */
	hasMouse: function() {
		return DOMUtils.hasMouse(this.layer);
	},
	/**
	 * tool ·¹ÀÌ¾î¸¦ ´Ý´Â´Ù.
	 */
	hide: function() {
		if (this.binder != null)
			this.binder.hide();
		else {
			if (this.layer != null)
				LayerUtils.hide(this.layer);
			this.active = false;
		}
	},
	/**
	 * tool ·¹ÀÌ¾î¸¦ ´ë»ó°´Ã¼ ±ÙÃ³¿¡ Ç¥½ÃÇÑ´Ù.
	 */
	show: function(target) {
		if (ToolLayer.showOnlyOne)
			ToolLayer.hideOthers(this);
		LayerUtils.show(this.layer,
						{target: target, position: this.position});
		this.active = true;
	},
	/**
	 * ·¹ÀÌ¾î¸¦ »ý¼ºÇÏ°í ÃÊ±âÈ­ÇÑ´Ù.
	 * ÀÌ ¸Þ¼Òµå´Â ¹ÙÀÎ´õ°¡ ÃÖÃÊ·Î È£ÃâµÉ¶§ ½ÇÇàµÈ´Ù.
	 */
	init: function() {
		Element.insert(ToolLayer.getContainer(),
			'<div id="' + this.id + '" class="ToolLayer' +
				((this.className != null)? ' ' + this.className: '') +
					'" style="display:none"></div>');
		this.layer = $(this.id);
		this.initTool();
		this.initialized = true;
	}
});

var ToolBinderGroup = function() {
	this.binders = [];
};
Object.extend(ToolBinderGroup.prototype, {
	binders: null,
	addBinder: function(binder) {
		this.binders[this.binders.length] = binder;
	},
	hide: function() {
		for (var i = 0; i < this.binders.length; i++)
			this.binders[i].hide();
	}
});

/**
 * <a>¿ä¼Ò¸¦ ÀÌ¿ëÇÑ ¹öÆ° UI
 * <a href="..." class="abu"><b><u>¹öÆ°</u></b></a>
 * <a href="..." class="abuTiny"><b><u>ÀÛÀº¹öÆ°</u></b></a>
 */
var AbuButton = {
	autoInit: true,
	doAutoInit: function(element) {
		if (element.tagName.toLowerCase() == 'a')
			Event.observe(element, 'click', this.handleFocus.bind(element));
	},
	handleFocus: function() {
		this.blur();
	}
};
AutoInitializer.add('abu', AbuButton);
AutoInitializer.add('abuTiny', AbuButton);

/**
 * <tt>¿ä¼Ò¸¦ ÀÌ¿ëÇÑ ÅÇ UI
 * <dl class="ttab">
 *	<dt>
 *		<tt><a href="..."><b>1¹øÂ° ÅÇ</b></a></tt>
 *		<tt><a href="..."><b>2¹øÂ° ÅÇ</b></a></tt>
 *		<tt><a href="..."><b>3¹øÂ° ÅÇ</b></a></tt>
 *	</dt>
 *	<dd>1¹øÂ° ÅÇ¿¡ ´ëÇÑ ÄÁÅÙÃ÷...</dd>
 *	<dd>2¹øÂ° ÅÇ¿¡ ´ëÇÑ ÄÁÅÙÃ÷...</dd>
 *	<dd>3¹øÂ° ÅÇ¿¡ ´ëÇÑ ÄÁÅÙÃ÷...</dd>
 * </dl>
 */
var TTab = function(target, options) {
	this.target = target;
	this.target.ttab = this;
	this.tabs = [];
	this.contents = [];
	this.options = Object.extend({}, options);
	this.init();
};
Object.extend(TTab, {
	autoInit: true
});
Object.extend(TTab.prototype, {
	selectedIndex: 0,
	init: function() {
		if (this.options.selectedIndex != null)
			this.selectedIndex = this.options.selectedIndex;
		this.tabContainer = Element.down(this.target, 'dt');
		if (this.tabContainer == null) {
			this.tabContainer = document.createElement('dt');
			Element.insert(this.target, {top: this.tabContainer});
		}
		Event.observe(this.tabContainer, 'click', this.handleTabClick.bind(this));
		var ttList = this.tabContainer.getElementsByTagName('tt');
		for (var i = 0; i < ttList.length; i++) {
			if (ttList[i].parentNode == this.tabContainer) {
				var index = this.tabs.length;
				this.tabs[index] = ttList[i];
				if (DOMUtils.isClass(ttList[i], 'selected'))
					this.selectedIndex = i;
			}
		}
		var ddList = this.target.getElementsByTagName('dd');
		for (var i = 0; i < ddList.length; i++) {
			if (ddList[i].parentNode == this.target) {
				ddList[i].style.display = 'none';
				this.contents[this.contents.length] = ddList[i];
			}
		}
		this.setTab();
	},
	setTab: function(index) {
		if (index != null)
			this.selectedIndex = index;
		for (var i = 0; i < this.tabs.length; i++) {
			if (i == this.selectedIndex) {
				DOMUtils.addClass(this.tabs[i], 'selected');
				this.contents[i].style.display = '';
			}
			else {
				DOMUtils.removeClass(this.tabs[i], 'selected');
				this.contents[i].style.display = 'none';
			}
		}
	},
	addTab: function(label, content) {
		this.tabs[this.tabs.length] = DOMUtils.appendElement(this.tabContainer, 'tt', {innerHTML: '<a><b>' + label + '</b></a>'});
		this.contents[this.contents.length] = DOMUtils.appendElement(this.target, 'dd', {innerHTML: content}, {display:'none'});
	},
	handleTabClick: function(event) {
		var element = Event.element(event || window.event);
		var index = -1, tab = Element.up(element, 'tt');
		if (tab != null && (index = this.tabs.indexOf(tab)) >= 0 && !DOMUtils.isClass(this.tabContainer, 'locked'))
			this.setTab(index);
	}
});
AutoInitializer.add('ttab', TTab);
AutoInitializer.add('ttabSmall', TTab);
AutoInitializer.add('ttabTiny', TTab);

// ÀÎÅÍÆäÀÌ½º Á¤¸®°¡ ÇÊ¿äÇÑ ...
var TODO = {
	//
	// ÇÑ±Û ¿©ºÎ È®ÀÎ
	//
	isHangul: function(str) {
		var len = str.length;
		for (var i = 0; i < len; i++) {
			if (str.charCodeAt(i) != 32 &&
				(str.charCodeAt(i) < 44032 || str.charCodeAt(i) > 55191)) {
					return true;
			}
		}
		return false;
	}
};
