/* SelectboxEx 090409 */

$Element.prototype.addClass = function(sClass) {
	var e = this._element;
	e.className = (e.className + ' ' + sClass).replace(/^\s+/, '');
	return this;
};
$Element.prototype.removeClass = function(sClass) {
	var e = this._element;
	e.className = e.className.replace(
			new RegExp('(^|\\s)' + sClass + '(\\s|$)'), ' ')
			.replace(/\s+$/, '');
	return this;
};
$Element.prototype.hasClass = function(sClass) {
	return (' ' + this._element.className + ' ').indexOf(' ' + sClass + ' ') > -1;
};
var Timer = function() {
	this.initialize.apply(this, arguments);
};
Timer.prototype = {
	_bInterval :false,
	_oInstance :null,
	initialize : function(sType) {
		this._bInterval = sType.toLowerCase() == "interval";
	},
	start : function(nTime, fCallback, pThis) {
		this.stop();
		if (this._bInterval) {
			this._oInstance = window.setInterval(fCallback, nTime);
		} else {
			var self = this;
			this._oInstance = window.setTimeout( function() {
				pThis ? fCallback.call(pThis) : fCallback();
				self.stop();
			}, nTime);
		}
	},
	stop : function() {
		var fFunc = window[this._bInterval ? "clearInterval" : "clearTimeout"];
		if (this._oInstance)
			fFunc(this._oInstance);
		this._oInstance = null;
	}
};
$Element._box_addon = {
	compatMode :document.compatMode || "BackCompat",
	getCSS : function(o, sPrefix, sPostfix, aWhich) {
		var size = 0;
		size += parseInt(o.css(sPrefix + aWhich[0] + sPostfix)) || 0;
		size += parseInt(o.css(sPrefix + aWhich[1] + sPostfix)) || 0;
		return size;
	},
	oldWidth :$Element.prototype.width,
	oldHeight :$Element.prototype.height,
	oldSize :$Element.prototype.size,
	adjustSize : function(o, nSize, sInclude, aWhich) {
		var addon = $Element._box_addon;
		var getCSS = addon.getCSS;
		if ($Agent().navigator().ie && addon.compatMode == "BackCompat") {
			if (sInclude == "margin")
				nSize -= getCSS(o, "margin", "", aWhich);
			if (sInclude != "margin" && sInclude != "border")
				nSize += getCSS(o, "border", "Width", aWhich);
			if (sInclude != "margin" && sInclude != "padding"
					&& sInclude != "border")
				nSize += getCSS(o, "padding", "", aWhich);
		} else {
			switch (sInclude) {
			case "margin":
				nSize -= getCSS(o, "margin", "", aWhich);
			case "border":
				nSize -= getCSS(o, "border", "Width", aWhich);
			case "padding":
				nSize -= getCSS(o, "padding", "", aWhich);
			}
		}
		return nSize;
	},
	getElementSize : function(o, sInclude, aWhich) {
		var addon = $Element._box_addon;
		var getCSS = addon.getCSS;
		var nSize = o.$value()["offset" + aWhich[2]];
		if (nSize == 0 && !o.visible())
			return 0;
		switch (sInclude) {
		case "margin":
			nSize += getCSS(o, "margin", "", aWhich);
			break;
		case "border":
			break;
		default:
			if (sInclude != "padding")
				nSize -= getCSS(o, "padding", "", aWhich);
			nSize -= getCSS(o, "border", "Width", aWhich);
		}
		return nSize;
	},
	setWidth : function(nSize, sInclude) {
		this.$value().style.width = $Element._box_addon.adjustSize(this, nSize,
				sInclude, [ "Left", "Right", "Width" ]) + 'px';
		return this;
	},
	setHeight : function(nSize, sInclude) {
		this.$value().style.height = $Element._box_addon.adjustSize(this,
				nSize, sInclude, [ "Top", "Bottom", "Height" ]) + 'px';
		return this;
	},
	getWidth : function(sInclude) {
		return $Element._box_addon.getElementSize(this, sInclude, [ "Left",
				"Right", "Width" ]);
	},
	getHeight : function(sInclude) {
		return $Element._box_addon.getElementSize(this, sInclude, [ "Top",
				"Bottom", "Height" ]);
	}
};
$Element.prototype.width = function() {
	var len = arguments.length;
	var last;
	if (len == 0 || typeof arguments[len - 1] == 'number')
		return $Element._box_addon.oldWidth.apply(this, arguments);
	if (len > 1)
		return $Element._box_addon.setWidth.apply(this, arguments);
	else
		return $Element._box_addon.getWidth.apply(this, arguments);
};
$Element.prototype.height = function() {
	var len = arguments.length;
	var last;
	if (len == 0 || typeof arguments[len - 1] == 'number')
		return $Element._box_addon.oldHeight.apply(this, arguments);
	if (len > 1)
		return $Element._box_addon.setHeight.apply(this, arguments);
	else
		return $Element._box_addon.getHeight.apply(this, arguments);
};
$Element.prototype.fireEvent = function(sType, oParams) {
	var el = this.$value();
	var orgEvent = null;
	if (typeof sType != 'string') {
		orgEvent = sType._event || sType;
		sType = sType.type;
	}
	if (document.createEventObject) {
		var e = orgEvent;
		if (!e || oParams) {
			e = document.createEventObject();
			if (orgEvent)
				for ( var k in orgEvent)
					try {
						e[k] = orgEvent[k];
					} catch (ex) {
					}
			if (oParams)
				for ( var k in oParams)
					e[k] = oParams[k];
		}
		return el.fireEvent('on' + sType, e);
	}
	var e;
	var params = {
		canBubble :true,
		cancelable :true,
		view :window,
		detail :1,
		screenX :0,
		screenY :0,
		clientX :0,
		clientY :0,
		ctrlKey :false,
		altKey :false,
		shiftKey :false,
		metaKey :false,
		button :1,
		relatedTarget :null,
		keyCode :0,
		charCode :0
	};
	if (orgEvent)
		for ( var k in orgEvent)
			try {
				params[k] = orgEvent[k];
			} catch (ex) {
			}
	if (oParams)
		for ( var k in oParams)
			params[k] = oParams[k];
	switch (sType) {
	case 'click':
	case 'mousedown':
	case 'mouseup':
	case 'mouseover':
	case 'mouseout':
	case 'mousemove':
		e = document.createEvent('MouseEvents');
		e.initEvent(sType, params.canBubble, params.cancelable, params.view,
				params.detail, params.screenX, params.screenY, params.clientX,
				params.clientY, params.ctrlKey, params.altKey, params.shiftKey,
				params.metaKey, params.button, params.relatedTarget);
		break;
	case 'keydown':
	case 'keypress':
	case 'keyup':
		if (window.KeyEvent) {
			e = document.createEvent('KeyEvents');
			e.initKeyEvent(sType, params.cancelBubble, params.cancelable,
					params.view, params.ctrlKey, params.altKey,
					params.shiftKey, params.metaKey, params.keyCode,
					params.charCode);
			break;
		} else {
			e = document.createEvent('Events');
			e.initEvent(sType, params.cancelBubble, params.cancelable);
			e.view = params.view;
			e.altKey = params.altKey;
			e.ctrlKey = params.ctrlKey;
			e.shiftKey = params.shiftKey;
			e.metaKey = params.metaKey;
			e.keyCode = params.keyCode;
			e.charCode = params.charCode;
		}
	default:
		e = document.createEvent('HTMLEvents');
		e.initEvent(sType, params.canBubble, params.cancelable);
		break;
	}
	return el.dispatchEvent(e);
};
var SimulateFocus = $Class(
		{
			_object :null,
			_dummy :null,
			_timer :null,
			_options :null,
			_focused :false,
			_handlers :null,
			$init : function(oEl, oOptions) {
				var e = $Element(oEl);
				this._options = {
					scrollable :false
				};
				for ( var k in oOptions)
					this._options[k] = oOptions[k];
				this._object = oEl;
				var self = this;
				this._object.focus = function() {
					self._dummy.disabled = false;
					return self._dummy.focus();
				};
				this._object.blur = function() {
					return self._dummy.blur();
				};
				this._handlers = {};
				this._createDummy();
				this._bindEvents();
			},
			_createDummy : function() {
				this._dummy = $(this._options.scrollable ? '<button>'
						: '<textarea>');
				with (this._dummy.style) {
					position = 'absolute';
					display = 'block';
					left = '-999999px';
					width = '50px';
				}
				;
				this._dummy.value = 'dummy';
				this._dummy.readOnly = true;
				this._rePositionDummy();
			},
			_rePositionDummy : function() {
				this._object.parentNode.insertBefore(this._dummy, this._object);
			},
			_fireFocusEvent : function(oEvent) {
				if (this._timer)
					clearTimeout(this._timer);
				this._timer = null;
				if (this._focused == true)
					return;
				this._focused = true;
				this.fireEvent('focus', oEvent);
			},
			_fireBlurEvent : function(oEvent) {
				var self = this;
				if (this._timer)
					clearTimeout(this._timer);
				var cloneEvent = document.createEventObject ? document
						.createEventObject(oEvent._event) : oEvent._event;
				this._timer = setTimeout( function() {
					if (self._focused == false)
						return;
					self._focused = false;
					self.fireEvent('blur', cloneEvent);
					delete cloneEvent;
					cloneEvent = null;
				}, 10);
			},
			_onDummyEvents : function(oEvent) {
				if (!this.fireEvent(oEvent.type, oEvent))
					oEvent.stop();
			},
			setFriend : function(oFriend) {
				var oHandlers = this._handlers;
				var textboxes = cssquery('textarea, input, button, select, a',
						oFriend);
				for ( var i = 0, textbox; textbox = textboxes[i]; i++) {
					var o = $Element(textbox);
					if (o.hasClass('-simfocus-friend'))
						return;
					o.addClass('-simfocus-friend');
					oHandlers.textboxFocus.attach(textbox, 'focus');
					oHandlers.textboxBlur.attach(textbox, 'blur');
					oHandlers.dummyToss.attach(textbox, 'keydown');
					oHandlers.dummyToss.attach(textbox, 'keypress');
					oHandlers.dummyToss.attach(textbox, 'keyup');
				}
				return this;
			},
			focus : function() {
				this._dummy.focus();
			},
			blur : function() {
				this._dummy.blur();
			},
			_bindEvents : function() {
				var oHandlers = this._handlers;
				oHandlers.objectMouseDown = $Fn( function(oEvent) {
					var el = oEvent.element;
					if (cssquery.test(el, 'input, textarea, button, a'))
						return;
					var self = this;
					setTimeout( function() {
						try {
							self._dummy.focus();
						} catch (e) {
						}
					}, 0);
				}, this).attach(this._object, 'mousedown');
				oHandlers.dummyFocus = $Fn(this._fireFocusEvent, this).attach(
						this._dummy, 'focus');
				oHandlers.dummyBlur = $Fn(this._fireBlurEvent, this).attach(
						this._dummy, 'blur');
				var oToss = oHandlers.dummyToss = $Fn(this._onDummyEvents, this);
				oToss.attach(this._dummy, 'keydown');
				oToss.attach(this._dummy, 'keypress');
				oToss.attach(this._dummy, 'keyup');
				oHandlers.textboxFocus = $Fn( function(oEvent) {
					this._dummy.disabled = true;
					this._fireFocusEvent(oEvent);
					this.fireEvent('fieldfocus', oEvent);
				}, this);
				oHandlers.textboxBlur = $Fn( function(oEvent) {
					this._dummy.disabled = false;
					this._fireBlurEvent(oEvent);
					this.fireEvent('fieldblur', oEvent);
				}, this);
				this.setFriend(this._object);
			},
			destroy : function() {
				var oHandlers = this._handlers;
				var oFriend = this._object;
				oHandlers.objectMouseDown.detach(this._object, 'mousedown');
				oHandlers.dummyFocus.detach(this._dummy, 'focus');
				oHandlers.dummyBlur.detach(this._dummy, 'blur');
				oHandlers.dummyToss.detach(this._dummy, 'keydown');
				oHandlers.dummyToss.detach(this._dummy, 'keypress');
				oHandlers.dummyToss.detach(this._dummy, 'keyup');
				var textboxes = cssquery('textarea, input, button, select, a',
						oFriend);
				for ( var i = 0, textbox; textbox = textboxes[i]; i++) {
					var o = $Element(textbox);
					if (!o.hasClass('-simfocus-friend'))
						return;
					oHandlers.textboxFocus.detach(textbox, 'focus');
					oHandlers.textboxBlur.detach(textbox, 'blur');
					oHandlers.dummyToss.detach(textbox, 'keydown');
					oHandlers.dummyToss.detach(textbox, 'keypress');
					oHandlers.dummyToss.detach(textbox, 'keyup');
				}
			}
		}).extend(nhn.Component);
var Selectbox = $Class(
		{
			tagName :'select',
			_agent :$Agent().navigator(),
			_object :null,
			_options :null,
			_focusTimer :null,
			_comboTimer :null,
			_customTimer :null,
			_simfocus :null,
			_items :null,
			_customs :null,
			_title :'',
			_selectedIndex :-1,
			_hoveredIndex :-1,
			_heightFitted :false,
			_heightOverflowed :false,
			_comboChanged :false,
			_needle :'',
			_handlers : {
				box : {
					focus : function(oEvent) {
						var o = this._object;
						if (o.source.disabled)
							return;
						var sPrefix = this.option('classPrefix');
						this._focusTimer.stop();
						var selectbox = $Element(o.selectbox);
						if (selectbox.hasClass(sPrefix + 'focused'))
							return;
						selectbox.addClass(sPrefix + 'focused');
						this.fireEvent('focus');
					},
					blur : function(oEvent) {
						var o = this._object;
						var sPrefix = this.option('classPrefix');
						this._focusTimer.start(50, function() {
							$Element(o.selectbox).removeClass(
									sPrefix + 'focused');
							this._showLayer(false);
							this.fireEvent('blur');
						}, this);
					},
					keydown : function(oEvent) {
						var o = this._object;
						var keyCode = oEvent.key().keyCode;
						if (o.source.disabled)
							return;
						var el = oEvent.element;
						if (this.fireEvent('keydown', oEvent)) {
							switch (keyCode) {
							case 38:
								this.selectAbove(1);
								this._paintLabel();
								oEvent.stop();
								break;
							case 40:
								this.selectBelow(1);
								this._paintLabel();
								oEvent.stop();
								break;
							case 33:
								this.selectAbove(Infinity);
								this._paintLabel();
								oEvent.stop();
								break;
							case 34:
								this.selectBelow(Infinity);
								this._paintLabel();
								oEvent.stop();
								break;
							case 13:
								if (this._isVisibleLayer())
									this._showLayer(false);
								oEvent.stop();
								break;
							}
							var item = this._items[this._selectedIndex];
							if (el == o.combobox) {
								var orgValue = o.combobox.value;
								this._comboTimer.start(0, function() {
									var newValue = o.combobox.value;
									if (orgValue == newValue)
										return;
									this._comboChanged = true;
									o.source.selectedIndex = this._pseudoIndex;
									this._pseudo.value = newValue;
									this._paintItem();
								}, this);
							} else if (item && !item.custom) {
								switch (keyCode) {
								case 37:
									this.selectAbove(1);
									this._paintLabel();
									oEvent.stop();
									break;
								case 39:
									this.selectBelow(1);
									this._paintLabel();
									oEvent.stop();
									break;
								default:
									var sChar = String.fromCharCode(keyCode);
									if (/[a-z0-9 ]/i.test(sChar)) {
										this._needle += sChar.toUpperCase();
										var idx = this._findMatchItem();
										if (idx != -1)
											this._setSelectedIndex(idx);
										oEvent.stop();
									}
									this._paintLabel();
									break;
								}
							} else if (this.option('customValue')) {
								var orgValue = this.option('customValue').call(
										this, item.custom, item.object);
								this._comboTimer.start(0, function() {
									item = this._setCustomValue(item);
									if (orgValue == item.source.value)
										return;
									this._comboChanged = true;
									this._paintLabel();
								}, this);
							}
						}
						this._paintItem();
					},
					mousedown : function(oEvent) {
						var o = this._object;
						if (o.source.disabled)
							return;
						if (oEvent.element == o.combobox) {
							this._showLayer(false);
							return;
						}
						this._showLayer();
						if (o.combobox)
							setTimeout( function() {
								o.combobox.focus();
							}, 10);
					}
				},
				combobox : {
					keyup : function(oEvent) {
					}
				},
				layer : {
					mousedown : function(oEvent) {
						var o = this._object;
						if (o.source.disabled)
							return;
						var isForm = cssquery.test(oEvent.element,
								'input, textarea, button, a');
						var idx = this._getItemIndexFromEvent(oEvent);
						if (idx != -1 && this._items[idx].custom && isForm) {
						} else {
							this._delayCall(0, function() {
								try {
									o.box.focus();
								} catch (e) {
								}
							});
						}
						oEvent.stop();
					}
				},
				list : {
					mouseover : function(oEvent) {
						var idx = this._getItemIndexFromEvent(oEvent);
						if (idx == -1)
							return;
						var item = this._items[idx];
						if (item.disable)
							return;
						this._addHoveredClass(idx);
					},
					mousedown : function(oEvent) {
						var idx = this._getItemIndexFromEvent(oEvent);
						if (idx == -1)
							return;
						var item = this._items[idx];
						if (!this.fireEvent('itemclick', {
							itemIndex :idx,
							itemObject :item
						}))
							return;
						if (item.disable)
							return;
						this._setSelectedIndex(idx);
						this._paintLabel();
						this._paintItem();
						if (this.option('closeOnClickItem') && !item.custom)
							this._showLayer(false);
					}
				},
				field : {
					focus : function(oEvent) {
						var el = oEvent.element;
						var sPrefix = this.option('classPrefix');
						this._customTimer.start(10, function() {
							for (; el && el.tagName; el = el.parentNode) {
								var e = $Element(el);
								if (e.hasClass(sPrefix + 'item-selected'))
									return;
								if (e.hasClass(sPrefix + 'list'))
									return this._showLayer(false);
							}
						}, this);
					}
				}
			},
			$init : function(vEl, oOptions) {
				var o = this._object = {};
				var uniq = Math.round(new Date().getTime() * 10000)
						+ Math.random() * 10000;
				var uniqid = 'selectbox(' + uniq + ')';
				this.option( {
					useHtml :false,
					closeOnClickItem :true,
					classPrefix :'selectbox-'
				});
				this.option(oOptions || {});
				var sPrefix = this.option('classPrefix');
				o.selectbox = $(vEl);
				if (cssquery.test(o.selectbox, 'select'))
					o.selectbox = cssquery('! .' + sPrefix + 'naked',
							o.selectbox)[0];
				o.source = cssquery('select.' + sPrefix + 'source', o.selectbox)[0];
				o.box = cssquery('.' + sPrefix + 'box', o.selectbox)[0];
				o.label = cssquery('.' + sPrefix + 'label', o.selectbox)[0];
				o.combobox = cssquery('input.' + sPrefix + 'combobox',
						o.selectbox)[0];
				var sLayer = this.option('layer');
				if (sLayer) {
					o.layer = $(sLayer);
					document.body.insertBefore(o.layer,
							document.body.firstChild);
				} else {
					o.layer = cssquery('.' + sPrefix + 'layer', o.selectbox)[0];
				}
				o.list = cssquery.test(o.layer, 'ul.' + sPrefix + 'list') ? o.layer
						: cssquery('ul.' + sPrefix + 'list', o.layer)[0];
				if (o.combobox)
					o.label.appendChild(o.combobox);
				$Element(o.selectbox).removeClass(sPrefix + 'naked');
				$Element(o.selectbox).addClass(sPrefix + 'applied');
				$Element(o.source).css( {
					position :'absolute',
					visibility :'hidden',
					display :'block'
				});
				this._processCustoms();
				this._showLayer(false);
				this._focusTimer = new Timer('timeout');
				this._comboTimer = new Timer('timeout');
				this._customTimer = new Timer('timeout');
				this._simfocus = new SimulateFocus(o.box);
				this._bindEvents();
				this.paint();
				$Element(o.selectbox).addClass(uniqid);
				$Element(o.layer).addClass(uniqid);
				Selectbox._instanceKeys[uniq] = this;
			},
			_toRegExp : function(sStr) {
				return sStr.replace(/\-/g, '\\-');
			},
			_getCustom : function(oEl) {
				var sPrefix = this.option('classPrefix');
				if (new RegExp(
						'\\b' + this._toRegExp(sPrefix) + 'item\\-custom\\(([^\\)]+)\\)')
						.test(oEl.className))
					return RegExp.$1;
			},
			_processCustoms : function() {
				var o = this._object;
				var lis = cssquery('> li', o.list);
				this._custom = [];
				var custom;
				for ( var i = 0, li; li = lis[i]; i++) {
					if (custom = this._getCustom(li)) {
						var q = cssquery('input', li)[0];
						this._custom[custom] = li.cloneNode(true);
					}
				}
			},
			_paintPseudoItem : function() {
				var sPrefix = this.option('classPrefix');
				var o = this._object;
				if (!o.combobox)
					return;
				var pseudo = cssquery('option.' + sPrefix + 'item-pseudo',
						o.source)[0];
				if (pseudo) {
					this._pseudo = pseudo;
					this._pseudoIndex = pseudo.index;
					return;
				}
				var pseudo = this._pseudo = $('<option>');
				this._pseudoIndex = o.source.options.length;
				pseudo.className = sPrefix + 'item-pseudo ' + sPrefix
						+ 'invisible';
				pseudo.innerHTML = '콤보박스 항목';
				o.source.appendChild(pseudo);
			},
			_findMatchItem : function(nGap) {
				var len = this._items.length;
				var start = this._object.source.selectedIndex;
				var needle = this._needle;
				var offset = -1;
				if (typeof nGap == 'undefined')
					nGap = 0;
				for ( var i = start + nGap; i < start + len; i++) {
					var idx = i % len;
					var found = this._items[idx].found;
					if (found.indexOf(needle) == 0) {
						offset = idx;
						break;
					}
				}
				if (offset == -1 && needle.length > 1) {
					this._needle = needle = needle.substr(needle.length - 1);
					offset = this._findMatchItem(1);
				}
				return offset;
			},
			_delayCall : function(nDelay, fpFunc) {
				setTimeout($Fn(fpFunc, this).bind(), nDelay);
			},
			_text : function(option) {
				var oOptions = this._options;
				return oOptions.useHtml ? (option.textContent || option.innerText)
						: option.innerHTML;
			},
			_value : function(option) {
				return option.getAttribute('value') || '';
			},
			_isVisibleLayer : function() {
				var layer = this._object.layer;
				return $Element(layer).visible();
			},
			_showLayer : function(bFlag) {
				var sPrefix = this.option('classPrefix');
				var layer = $Element(this._object.layer);
				var visible = layer.visible();
				if (typeof bFlag == 'undefined')
					bFlag = !visible;
				$Element(this._object.selectbox)[bFlag ? 'addClass'
						: 'removeClass'](sPrefix + 'expanded');
				if (bFlag != visible
						&& !this.fireEvent(bFlag ? 'open' : 'close'))
					return;
				layer[bFlag ? 'show' : 'hide']();
				if (bFlag) {
					this._paintHeight();
					this._paintItem();
					this._paintLayer();
				} else {
					this._fireChangeEvent();
				}
			},
			_fireChangeEvent : function() {
				var o = this._object;
				if (this._comboChanged) {
					this._comboChanged = false;
					this._onCustomSelect();
					var nSelectedIndex = o.source.selectedIndex;
					$Element(o.source).fireEvent('change');
					this.fireEvent('change', {
						custom :this._items[nSelectedIndex].custom
					});
				}
			},
			_onCustomSelect : function() {
				var o = this._object;
				var sPrefix = this.option('classPrefix');
				if (!$Element(o.selectbox).hasClass(sPrefix + 'focused'))
					return;
				var item = this._setCustomValue();
				if (!item)
					return;
				if (!this._isVisibleLayer())
					this._showLayer(true);
				var self = this;
				if (this.option('customSelect'))
					setTimeout( function() {
						self.option('customSelect').call(self, item.custom,
								item.object);
					}, 0);
			},
			_setCustomValue : function(item) {
				var o = this._object;
				if (!item)
					item = this._items[o.source.selectedIndex];
				if (!item.custom)
					return;
				var value = this.option('customValue').call(this, item.custom,
						item.object);
				item.source.value = value;
				return item;
			},
			_getItemIndexFromEvent : function(oEvent) {
				var el = oEvent.element;
				if (el.tagName.toLowerCase() != 'li')
					el = cssquery('! li', el)[0];
				return this._getItemIndex(el);
			},
			_getItemIndex : function(item) {
				var sPrefix = this.option('classPrefix');
				if (item
						&& new RegExp(
								'\\b' + this._toRegExp(sPrefix) + 'item\\(([0-9]+)\\)')
								.test(item.className)) {
					var r = parseInt(RegExp.$1);
					return isNaN(r) ? -1 : r;
				}
				return -1;
			},
			selectAbove : function(nOffset) {
				return this._addSelectedIndex(-nOffset);
			},
			selectBelow : function(nOffset) {
				return this._addSelectedIndex(nOffset);
			},
			_addSelectedIndex : function(nOffset) {
				var o = this._object;
				var tarIndex = -1;
				var orgIndex = this._selectedIndex;
				var newIndex = orgIndex + nOffset;
				var len = this._items.length;
				if (newIndex < 0)
					newIndex = 0;
				if (newIndex > len - 1)
					newIndex = len - 1;
				if (orgIndex < newIndex) {
					for (tarIndex = newIndex; tarIndex < len
							&& this._items[tarIndex].disable; tarIndex++) {
					}
					if (tarIndex >= len)
						for (tarIndex = newIndex; tarIndex >= orgIndex
								&& this._items[tarIndex].disable; tarIndex--) {
						}
				} else {
					for (tarIndex = newIndex; tarIndex >= 0
							&& this._items[tarIndex].disable; tarIndex--) {
					}
					if (tarIndex < 0)
						for (tarIndex = newIndex; tarIndex <= orgIndex
								&& this._items[tarIndex].disable; tarIndex++) {
						}
				}
				this._setSelectedIndex(tarIndex);
			},
			_setSelectedIndex : function(nIndex) {
				var o = this._object;
				if (o.source.selectedIndex == nIndex)
					return;
				o.source.selectedIndex = nIndex;
				this._comboChanged = true;
				this._fireChangeEvent();
			},
			_bindEvents : function() {
				var o = this._object;
				this._fnHandlers = {};
				for ( var key in this._handlers) {
					for ( var evt in this._handlers[key]) {
						if (typeof this._fnHandlers[key] == 'undefined')
							this._fnHandlers[key] = {};
						this._fnHandlers[key][evt] = $Fn(
								this._handlers[key][evt], this);
					}
				}
				this._simfocus.attach( {
					focus :this._fnHandlers.box.focus.bind(),
					blur :this._fnHandlers.box.blur.bind(),
					fieldfocus :this._fnHandlers.field.focus.bind()
				});
				this._simfocus.attach(this._agent.opera ? 'keypress'
						: 'keydown', this._fnHandlers.box.keydown.bind());
				this._fnHandlers.box.mousedown.attach(o.box, 'mousedown');
				this._fnHandlers.combobox.keyup.attach(o.box, 'keyup');
				this._fnHandlers.layer.mousedown.attach(o.layer, 'mousedown');
				this._fnHandlers.list.mouseover.attach(o.list, 'mouseover');
				this._fnHandlers.list.mousedown.attach(o.list, 'mousedown');
			},
			destroy : function() {
				var o = this._object;
				this._simfocus.destroy();
				this._fnHandlers.box.mousedown.detach(o.box, 'mousedown');
				this._fnHandlers.combobox.keyup.detach(o.box, 'keyup');
				this._fnHandlers.layer.mousedown.detach(o.layer, 'mousedown');
				this._fnHandlers.list.mouseover.detach(o.list, 'mouseover');
				this._fnHandlers.list.mousedown.detach(o.list, 'mousedown');
			},
			_removeSelectedClass : function() {
				var sPrefix = this.option('classPrefix');
				var item;
				var nIndex = this._selectedIndex;
				if (nIndex == -1)
					return;
				if ((item = this._items[nIndex]) && item.object) {
					$Element(item.object)
							.removeClass(sPrefix + 'item-selected');
				}
				this._selectedIndex = -1;
			},
			_addSelectedClass : function(nIndex) {
				var sPrefix = this.option('classPrefix');
				this._removeSelectedClass();
				var item;
				if (nIndex == -1)
					return;
				if ((item = this._items[nIndex]) && item.object) {
					$Element(item.object).addClass(sPrefix + 'item-selected');
					this._selectedIndex = nIndex;
				}
			},
			_removeHoveredClass : function() {
				var sPrefix = this.option('classPrefix');
				var item;
				var nIndex = this._hoveredIndex;
				if (nIndex == -1)
					return;
				if ((item = this._items[nIndex]) && item.object) {
					$Element(item.object).removeClass(sPrefix + 'item-hovered');
				}
				this._hoveredIndex = -1;
			},
			_addHoveredClass : function(nIndex) {
				var sPrefix = this.option('classPrefix');
				this._removeHoveredClass();
				var item;
				if (nIndex == -1)
					return;
				if ((item = this._items[nIndex]) && item.object) {
					$Element(item.object).addClass(sPrefix + 'item-hovered');
					this._hoveredIndex = nIndex;
				}
			},
			paint : function() {
				var o = this._object;
				var sPrefix = this.option('classPrefix');
				this._paintWidth();
				this._paintPseudoItem();
				this._paintItems();
				this._paintLabel();
				this._paintItem();
				var bDisabled = o.source.disabled;
				this._simfocus._object.disabled = bDisabled;
				$Element(o.selectbox)[bDisabled ? 'addClass' : 'removeClass']
						(sPrefix + 'disabled');
				if (bDisabled) {
					this._showLayer(false);
					this._handlers.box.blur.call(this);
				}
			},
			paintValue : function() {
				this._setCustomValue();
				this._paintLabel();
			},
			_paintWidth : function() {
				var o = this._object;
				var oOptions = this._options;
				var toPx = function(width) {
					if (typeof width == 'string') {
						if (/^[0-9]+%$/.test(width))
							width = Math.round(parseInt(width)
									* o.source.offsetWidth / 100);
						else
							width = parseInt(width);
					}
					return width;
				};
				$Element(o.source).show();
				var boxWidth = toPx(oOptions.boxWidth || oOptions.width);
				var layerWidth = toPx(oOptions.layerWidth || oOptions.width);
				$Element(o.source).hide();
				if (!isNaN(boxWidth))
					$Element(o.box).css( {
						width :boxWidth + 'px'
					});
				if (!isNaN(layerWidth))
					$Element(o.layer).css( {
						width :layerWidth + 'px'
					});
			},
			_paintHeight : function() {
				var height = this.option('height');
				if (!height || this._heightFitted)
					return;
				var o = this._object;
				if (o.list.offsetHeight > height) {
					o.list.style.height = height + 'px';
					if (this._agent.safari) {
						o.list.style.overflow = 'scroll';
						o.list.style.overflowX = 'hidden';
					} else {
						o.list.style.overflow = 'auto';
					}
					this._heightOverflowed = true;
				}
				this._heightFitted = true;
			},
			_paintLayer : function() {
				var o = this._object;
				var sPrefix = this.option('classPrefix');
				var layer = $Element(o.layer);
				var box = $Element(o.box);
				var pos = box.offset();
				var layerHeight = layer.height('margin');
				var boxHeight = box.height('margin');
				var layerBottom = pos.top + o.box.offsetHeight + layerHeight;
				var scrBottom = (document.documentElement.scrollTop || document.body.scrollTop)
						+ (window.innerHeight
								|| document.documentElement.clientHeight || document.body.clientHeight);
				var overflowed = layerBottom > scrBottom;
				layer[overflowed ? 'addClass' : 'removeClass']
						(sPrefix + 'layer-above');
				if (this.option('layer')) {
					if (overflowed) {
						o.layer.style.top = pos.top - layerHeight + 'px';
						o.layer.style.left = pos.left + 'px';
					} else {
						o.layer.style.top = pos.top + boxHeight + 'px';
						o.layer.style.left = pos.left + 'px';
					}
				}
			},
			_paintItems : function() {
				var o = this._object;
				var sPrefix = this.option('classPrefix');
				var aOptions = cssquery('option', o.source);
				var aTmpItem = [];
				var aItems = [];
				for ( var i = 0, eOption; eOption = aOptions[i]; i++) {
					var oOption = $Element(eOption);
					var invisible = oOption.hasClass(sPrefix + 'invisible');
					var disable = invisible
							|| oOption.hasClass(sPrefix + 'item-pseudo')
							|| eOption.disabled;
					var custom = this._getCustom(eOption);
					var text = this._text(eOption) || '';
					aItems[i] = {
						'source' :eOption,
						'text' :text,
						'invisible' :invisible,
						'disable' :disable,
						'custom' :custom
					};
					aTmpItem.push(text + '%%' + invisible + '%%' + disable
							+ '%%' + custom);
				}
				var sTmpItem = aTmpItem.join('##');
				if (this._oldItem == sTmpItem)
					return;
				this._oldItem = sTmpItem;
				this._items = [];
				o.list.innerHTML = '';
				o.list.style.height = 'auto';
				for ( var i = 0, oItem; oItem = aItems[i]; i++) {
					var li = null;
					if (!oItem.invisible) {
						if (oItem.custom && this._custom[oItem.custom]) {
							li = this._custom[oItem.custom].cloneNode(true);
						} else {
							li = $('<li>');
							li.style.cssText = oItem.source.style.cssText;
							li.innerHTML = oItem.text;
						}
						li.className = (oItem.source.className || '') + ' '
								+ sPrefix + 'item(' + i + ')';
						o.list.appendChild(li);
					}
					this._items[i] = {
						'index' :i,
						'source' :oItem.source,
						'object' :li,
						'text' :oItem.text,
						'found' :oItem.text.toUpperCase(),
						'invisible' :oItem.invisible,
						'disable' :oItem.disable,
						'custom' :oItem.custom
					};
				}
				this._simfocus.setFriend(o.list);
				this._heightFitted = false;
				this._heightOverflowed = false;
				if ($Element(this._object.layer).visible())
					this._paintHeight();
			},
			_paintLabel : function() {
				var o = this._object;
				var idx = o.source.selectedIndex;
				if (idx == -1)
					return;
				var item = this._items[idx];
				if (o.combobox) {
					if (item.custom) {
						o.combobox.value = this._value(item.source);
					} else {
						o.combobox.value = this._value(item.source);
						o.combobox.select();
					}
					return;
				}
				var label = o.source.title;
				if (!label)
					label = item.text;
				$Element(o.label).html(label);
			},
			_paintItem : function() {
				var o = this._object;
				var nIndex = o.source.selectedIndex;
				this._customTimer.stop();
				this._addSelectedClass(nIndex);
				this._addHoveredClass(nIndex);
				var item = this._items[nIndex];
				if (!item)
					return;
				var li = item.object;
				this._heightOverflowed = true;
				if (li && this._isVisibleLayer() && this._heightOverflowed) {
					var wrapper = li.offsetParent;
					var top = (this._agent.ie && this._agent.version == 8) ? li.offsetTop
							+ o.list.scrollTop
							: li.offsetTop - li.parentNode.offsetTop;
					var range = [ o.list.scrollTop,
							o.list.scrollTop + o.list.clientHeight ];
					var itemrgn = [ top, top + li.offsetHeight ];
					if (range[0] > itemrgn[0]) {
						o.list.scrollTop = itemrgn[0];
					}
					if (range[1] < itemrgn[1]) {
						o.list.scrollTop += itemrgn[1] - range[1];
					}
				}
			},
			focus : function() {
				this._simfocus.focus();
			},
			blur : function() {
				this._simfocus.blur();
			}
		}).extend(nhn.HTMLComponent);
Selectbox._instanceKeys = {};
Selectbox.getInstance = function(o) {
	var regexp = /\bselectbox\(([0-9]+)\)/;
	for ( var className; o && (className = o.className); o = o.parentNode)
		if (regexp.test(className))
			return Selectbox._instanceKeys[RegExp.$1];
	return null;
};
