/* version : 0.3.1 */
var curOver = curOut = time = null;


/**
 * Core object
 *
 */
//Core object
if (typeof window != "undefined" && typeof window.nhn == "undefined") {
	window.nhn = new Object;
}


nhn.DOMLoad = (function() {
	var DOMLoad = {
		_handler : [],
		done : false,

		attach : function(fpHandler) {
			this._handler.push(fpHandler);
		},

		_fire : function() {
			this.done = true;
			var aHandlers = this._handler;

			setTimeout(function() {
				for (var i = 0, fpHandler; fpHandler = aHandlers[i]; i++)
					fpHandler();
			}, 0);
		}
	};

	if (document.addEventListener) {
		document.addEventListener("DOMContentLoaded", function() { DOMLoad._fire(); }, false);
    }
	else if (document.attachEvent) {
		var oDummy = document.createElement('document:ready');
		var oTimer = setInterval(function() {
			try {
				oDummy.doScroll('left');
				oDummy = null;

				clearInterval(oTimer);
				DOMLoad._fire();
			} catch(e) { }
		}, 10);
	}
	return DOMLoad;
})();


/**
 * $Jindo 객체를 반환한다. $Jindo 객체는 프레임웍에 대한 정보와 유틸리티 함수를 제공한다.
 * @constructor
 * @class $Jindo 객체는 프레임웍에 대한 정보와 유틸리티 함수를 제공한다.
 * @author Kim, Taegon
 */
function $Jindo() {
	var cl=arguments.callee;
	var cc=cl._cached;

	if (cc) return cc;
	if (!(this instanceof cl)) return new cl();
	if (!cc) cl._cached = this;

	// information
	this.version = "1.2.7";
}

/**
 * @function
 * $ 함수는 다음의 두 가지 역할을 한다.
 * <ul><li/>ID를 사용하여 DOM 엘리먼트를 가져온다. 인수를 두 개 이상 지정하면 각각의 DOM 엘리먼트를 배열로 리턴한다.
 * <li>또한 "<tagName>" 과 같은 형식의 문자열을 입력하면 tagName을 가지는 객체를 생성한다.</li></ul>
 * @param {String...} sID 찾을 DOM 엘리먼트의 ID. ID는 하나 이상 지정할 수 있다.
 * @return {Element|Array} DOM 엘리먼트를 리턴한다. 만약 ID에 해당하는 DOM 엘리먼트가 없다면 null을 리턴한다.
 */
function $(sID/*, id1, id2*/) {
	var ret = new Array;
	var el  = null;
	var reg = /^<([a-z]+|h[1-5])>$/i;
	var reg2 = /^<([a-z]+|h[1-5])(\s+[^>]+)?>/i;

	for(var i=0; i < arguments.length; i++) {
		el = arguments[i];
		if (typeof el == "string") {

			if (reg.test(el)) {
				el = document.createElement(RegExp.$1);
			} else if (reg2.test(el)) {

				var p = { thead:'table', tbody:'table', tr:'tbody', td:'tr', dt:'dl', dd:'dl', li:'ul', legend:'fieldset' };
				var tag = RegExp.$1.toLowerCase();

				var parents = [];

 				for (var j = 0; tag = p[tag]; j++) {

 					var o = document.createElement(tag);
 					if (j) o.appendChild(parents[j - 1]);

 					parents.push(o);

 				}

 				if (!parents[0]) parents[0] = document.createElement('div');

 				var first = parents[0];
				$Element(first).html(el);

				for (el = first.firstChild; el; el = el.nextSibling)
					if (el.nodeType == 1) ret[ret.length] = el;

			} else {
				el = document.getElementById(el);
			}
		}
		if (el) ret[ret.length] = el;
	}

	return ret.length>1?ret:(ret[0] || null);
}

/**
 * 클래스 타입을 정의한다.
 * @extends core
 * @class $Class는 Jindo에서 객체 지향 프로그래밍(OOP)를 구현하는 객체이다. #Class의 생성자는 $init 으로 정의한다. 모든 인스턴스는 같은 속성을 공유한다. 각 인스턴스가 독립적인 속성값을 가지려면 해당 속성을 $init에서 초기화한다. 자세한 것은 예제 코드를 참조한다.
 * @param {Object} oDef 클래스를 정의하는 객체. 메서드, 프로퍼티와 생성자를 정의한다.
 * @return {$Class} 클래스 객체
 * @example
var CClass = $Class({
    prop : null,
    $init : function() {
         this.prop = $Ajax();
         ...
    }
});

var c1 = new CClass();
var c2 = new CClass();
// c1과 c2는 서로 다른 $Ajax 객체를 각각 가진다.

 */
 function $Class(oDef) {
	function typeClass() {
		var t = this;
		var a = [];

		while(typeof t.$super != "undefined") {
			t.$super.$this = this;
			if (typeof t.$super.$init == "function") a[a.length] = t;
			t = t.$super;
		}

		for(var i=a.length-1; i > -1; i--) a[i].$super.$init.apply(a[i].$super, arguments);

		if (typeof this.$init == "function") this.$init.apply(this,arguments);
	}

	if (typeof oDef.$static != "undefined") {
		var i=0, x;
		for(x in oDef) x=="$static"||i++;
		for(x in oDef.$static) typeClass[x] = oDef.$static[x];

		if (!i) return oDef.$static;
		delete oDef.$static;
	}

	typeClass.prototype = oDef;
	typeClass.prototype.constructor = typeClass;
	typeClass.extend = $Class.extend;

	return typeClass;
 }

/**
 * 클래스를 상속한다.
 * 하위 클래스는 this.$super.method 로 상위 클래스의 메서드에 접근할 수 있으나, this.$super.$super.method 와 같이 한 단계 이상의 상위 클래스는 접근할 수 없다.
 * @function
 * @param {$Class} superClass 수퍼 클래스 객체
 * @return {$Class} 확장된 클래스
 * @example
var ClassExt = $Class(classDefinition);
ClassExt.extend(superClass);
// ClassExt는 SuperClass를 상속받는다.

 */
 $Class.extend = function(superClass) {
 	/**
	* 부모 클래스의 메서드에 접근할 때 사용한다.
	* @memberOf $Class
	* @name $super
	* @remark $super를 사용하여 부모 클래스의 메서드에 접근할 때 부모 클래스의 매서드가 자식 클래스와 동일한 이름의 속성을 사용하면 자식 클래스의 속성을 사용한다.
	* @example
var Parent = $Class ({
	a: 100,
	b: 200,
	c: 300,
	sum2: function () {
		var init = this.sum();
		return init;
	},
	sum: function () {
		return this.a + this.b
	}
});

var Child = $Class ({
	a: 10,
	b: 20,
	sum2 : function () {
		var init = this.sum();
		return init;
	},
	sum: function () {
		return this.b;
	}
}).extend (Parent);

var oChild = new Child();
var oParent = new Parent();

oChild.sum();           // 20
oChild.sum2();          // 20
oChild.$super.sum();    // 30 -> 부모 클래스의 100과 200대신 자식 클래스의 10과 20을 더한다.
oChild.$super.sum2();   // 20 -> 부모 클래스의 sum()대신 자식 클래스의 sum()을 호출한다.
	*/
	this.prototype.$super = new Object;

	var superFunc = function(m, func) {
		return function() {
			var f = this.$this[m];
			var t = this.$this;
			var r = (t[m] = func).apply(t, arguments);
			t[m] = f;

			return r;
		};
	};

	for(var x in superClass.prototype) {
		if (typeof this.prototype[x] == "undefined" && x !="$init") this.prototype[x] = superClass.prototype[x];
		if (typeof superClass.prototype[x] == "function") {
			this.prototype.$super[x] = superFunc(x, superClass.prototype[x]);
		} else {
			this.prototype.$super[x] = superClass.prototype[x];
		}
	}

	// inherit static methods of parent
	for(var x in superClass) {
		if (x == "prototype") continue;
		this[x] = superClass[x];
	}

	return this;
};
/////
/**
 * 지정된 CSS 셀렉터에 해당하는 객체의 배열을 반환한다.
 * 해당하는 객체가 존재하지 않으면 빈 배열을 반환한다.
 * @id core.$$
 * @param {string} CSS 셀렉터
 * @return {array of element} 객체의 배열
 */
function $$(sSelector, oRoot) {
	if (typeof $$._compile == "undefined") {
		var statics = {
			_cache: [],
			_regex: {
				bparse : /([^>|\+|\s]+)\s*([>|\+]?)\s*/g,
				fparse : /\s*([>|\+]?)\s*([^>|\+|\s]+)/g,

				tag : /^([\w-]+)/,
				id : /#([\w-]+)/g,
				cls : /\.([\w-]+)/g,

				pseudo : /:([\w-]+(\(.*\))?)/g,
				pseudoparse : /([\w-]+)(\((.*)\))?/,

				attr : /\[([^\]]+)]/g,
				attrparse : /^(\[)([\w-]+)([!|\^|\$|\*]?=)(.*)(\])$/
			},
			_push : function(arr, el) {
				arr[arr.length] = el;
				return arr;
			},
			_next : function(o) {
				while (o && (o = o.nextSibling) && o.nodeType != 1);
				return o;
			},
			_prev : function(o) {
				while (o && (o = o.previousSibling) && o.nodeType != 1);
				return o;
			},
			_index : function(o) {
				var idx = 1;
				for (var child = o.parentNode.firstChild; child && child != o; child = $$._next(child), idx++);
				return idx;
			},
			_pseudo : {
				first_child : function(o) {
					return !($$._prev(o));
				},
				last_child : function(o) {
					return !($$._next(o));
				},
				empty : function(o) {
					return !(o.firstChild);
				},
				contains : function(o, arg) {
					return o.innerHTML.indexOf(arg) > -1;
				}
			},
			_filter : function(selector, backward) {
				var filter = {};
				var declare = {}, cond = [ 'true' ];
				var varname, func = '';
				var id, tag, classes, pseudoes, attres;

				if (id = selector.match($$._regex.id)) {
					if (id[1]) return false;
					filter.id = id[0].substr(1);
				}

				tag = selector.match($$._regex.tag);
				filter.tag = (tag && tag[1]) || '*';

				if (classes = selector.match($$._regex.cls)) {
					for (var cls, i = 0; cls = classes[i]; i++)
						cond.push('/\\b'+cls.substr(1)+'\\b/.test(o.className)');
				}

				if (pseudoes = selector.match($$._regex.pseudo)) {
					for (var pseudo, i = 0; pseudo = pseudoes[i]; i++) {
						pseudo = pseudo.substr(1).match($$._regex.pseudoparse);
						cond.push('$$._pseudo.' + pseudo[1].replace('-', '_') + '(o, "' + pseudo[3] + '")');
					}
				}

				if (attres = selector.match($$._regex.attr)) {
					for (var attr, i = 0; attr = attres[i]; i++) {

						attr = attr.match($$._regex.attrparse); // 2, 3, 4
						varname = 'v_' + attr[2];

						if (!declare[attr[2]]) {
							func += 'var ' + varname + ' = o.getAttribute("' + attr[2] + '") || "";\n';
							declare[attr[2]] = true;
						}

						switch (attr[3]) {
						case '=':
							cond.push(varname + ' == "' + attr[4] + '"');
							break;
						case '!=':
							cond.push(varname + ' != "' + attr[4] + '"');
							break;
						case '^=':
							cond.push(varname + '.indexOf("' + attr[4] + '") == 0');
							break;
						case '$=':
							cond.push(varname + '.substr(' + varname + '.length - "' + attr[4] + '".length) == "' + attr[4] + '"');
							break;
						case '*=':
							cond.push(varname + '.indexOf("' + attr[4] + '") > -1');
							break;
						default:
							cond.push(varname + ' != null');
						}

					}
				}

				if (backward) {
					if (filter.tag && filter.tag != '*')
						cond.push('(casei ? o.tagName.toLowerCase() == "' + filter.tag.toLowerCase() + '" : o.tagName == "' + filter.tag + '")');

					if (filter.id) cond.push('o.id == "' + filter.id + '"');
				}

				filter.func = new Function('o', 'casei', func + '\nreturn (' + cond.join(" && ") + ')');

				return filter;
			},
			_trace : function(root, o, selectors, idx, casei) {
				if (idx == -1) return true;

				var selector = selectors[idx];

				switch (selector.type) {
				case '':
					for (o = o.parentNode; o != root; o = o.parentNode)
						if (selector.filter.func(o, casei))
							if ($$._trace(root, o, selectors, idx - 1, casei))
								return true;

					break;
				case '>':
					o = o.parentNode;
					if (o && o != root && selector.filter.func(o, casei))
						if ($$._trace(root, o, selectors, idx - 1, casei))
							return true;

					break;
				case '+':
					o = this._prev(o);

					if (o && selector.filter.func(o, casei))
						if ($$._trace(root, o, selectors, idx - 1, casei))
							return true;

					break;
				}

				return false;

			},
			_compile : function(query, bquery, fquery) {
				if (!$$._cache[query]) {
					var cache = { backward : [], forward : [] };

					bquery.replace($$._regex.bparse, function(all, selector, type) {
						$$._push(cache.backward, {
							'type'   : type,
							'filter' : $$._filter(selector, true)
						});
					});

					var i = 0, method;
					var code = [];

					fquery.replace($$._regex.fparse, function(all, type, selector) {
						cache.forward.push({
							'type'   : type,
							'filter' : $$._filter(selector)
						});

						switch (type) {
						case '>':
							method = "_getChildren";
							break;
						case '':
							method = "_getOffspring";
							break;
						case '+':
							method = "_getBrother";
							break;
						}

						$$._push(code,'result = $$.' + method + '(result, forward[' + (i++) + '].filter, casei);');

					});

					$$._push(code,'return result;');
					cache.filter = new Function('result', 'forward', 'casei', code.join('\n'));

					$$._cache[query] = cache;
				}

				return $$._cache[query];
			},
			_getChildren : function(objs, filter, casei) {
				var ret = [];
				var child;

				for (var i = 0, obj; obj = objs[i]; i++) {

					for (child = obj.firstChild; child; child = $$._next(child))
						if (filter.func(child, casei)) $$._push(ret, child);

				}

				return ret;

			},
			_getOffspring : function(objs, filter, casei) {
				var ret = [];
				var childs;

				for (var i = 0, obj; obj = objs[i]; i++) {

					childs = obj.getElementsByTagName(filter.tag);
					for (var j = 0, child; child = childs[j]; j++)
						if (filter.func(child, casei)) $$._push(ret, child);

				}

				return ret;

			},

			_getBrother : function(objs, filter, casei) {
				var ret = [];
				var child;

				for (var i = 0, obj; obj = objs[i]; i++) {
					if (child = $$._next(obj))
						if (filter.func(child, casei)) $$._push(ret, child);

				}
				return ret;
			}
		};

		for(var x in statics) {
			$$[x] = statics[x];
		}
	}

	oRoot = $(oRoot || document);

	var parts  = sSelector.match(/(.*#[\w]+[^>|\+|\s]*)([>|\+|\s].*)/) || [];
	var cache  = $$._compile(sSelector, parts[1] || sSelector, parts[2] || '');
	var idx    = cache.backward.length - 1;
	var filter = cache.backward[idx].filter;
	var sands  = [], result = [];
	var casei  = (oRoot == document || (oRoot.ownerDocument || oRoot.document) == document);

	if (filter.id) {
		$$._push(sands, document.getElementById(filter.id));
	} else if (filter.tag == "*") {
		sands = oRoot.all;
	} else {
		sands = oRoot.getElementsByTagName(casei ? filter.tag.toLowerCase() : filter.tag);
	}

	for (var i = 0, sand; sand = sands[i]; i++) {
		if (filter.func(sand, casei))
			if ($$._trace(oRoot, sand, cache.backward, idx - 1, casei))
				$$._push(result, sand);
	}

	return cache.filter(result, cache.forward, casei);
}

/**
 * @fileOverview $A의 생성자 및 메서드를 정의한 파일
 * @name array.js
 */

/**
 * 배열, 혹은 컬랙션을 내부 배열로 변환하거나 새로운 $A 객체를 생성한다.
 *
 * @extends core
 * @class	$A는 배열이나 배열과 비슷한 컬렉션(Collection)을 배열로 변환한다.
 * @param 	{Array} array 배열 혹은 배열과 비슷한 컬렉션. 만약 array를 생략하면 빈 배열을 가진 새로운 $A 객체를 리턴한다.
 * @constructor
 * @author Kim, Taegon
 */
function $A(array) {
	var cl = arguments.callee;

	if (typeof array == "undefined") array = [];
	if (array instanceof cl) return array;
	if (!(this instanceof cl)) return new cl(array);

	this._array = [];
	for(var i=0; i < array.length; i++) {
		this._array[this._array.length] = array[i];
	}
};

/**
 * toString 매서드는 내부 배열을 문자열로 리턴한다. 자바스크립트의 Array.toString을 사용한다.
 * @return {String} 내부 배열을 변환한 문자열.
 */
$A.prototype.toString = function() {
	return this._array.toString();
};

/**
 * length 매서드는 내부 배열의 크기를 지정하거나 반환한다.
 *
 * @return 	Number 배열의 크기
 * @param 	{Number} [len]	새로 리턴할 배열의 크기. len이 기존의 배열보다 크면 elem으로 초기화한 원소를 마지막에 덧붙인다. len이 기존 배열보다 작으면 len번째 이후의 원소는 제거한다.
 * @param 	{Value} [elem]	새로운 원소를 추가할 때 사용할 초기값
 * @example
var zoo = ["zebra", "giraffe", "bear", "monkey"];
var birds = [];

$A(zoo).length();
// 4

$A(zoo).length(2);
// zoo.$value = ["zebra", "giraffe"]

$A(zoo).length(6, "(Empty)");
//  zoo.$value = ["zebra", "giraffe", "bear", "monkey", "(Empty)", "(Empty)"]

$A(zoo).length(5, birds);
//  zoo.$value = ["zebra", "giraffe", "bear", "monkey", []]
 */
$A.prototype.length = function(len, elem) {
	if (typeof len == "number") {
		var l = this._array.length;
		this._array.length = len;

		if (typeof elem != "undefined") {
			for(var i=l; i < len; i++) {
				this._array[i] = elem;
			}
		}

		return this;
	} else {
		return this._array.length;
	}
};

/**
 * has 메서드는 주어진 원소가 배열에 존재하는지 검색한다.
 * @return {Boolean} 배열의 원소중 인수 값과 동일한 원소를 검색했다면 true를, 그렇지 않으면 false를 리턴한다.
 * @param {Value} any 검색할 값
 * @see $A.indexOf
 * @example
var arr = $A([1,2,3]);

arr.has(3); // true
arr.has(4); // false
 */
$A.prototype.has = function(any) {
	return (this.indexOf(any) > -1);
};

/**
 * indexOf 메서드는 내부 배열의 원소를 검색하고, 찾은 원소의 인덱스를 리턴한다.
 * @param {Value} any 검색할 값
 * @return {Number} 찾은 원소의 인덱스. 원소의 인덱스는 0부터 시작한다. 인수와 동일한 원소를 찾지 못하면 -1 을 리턴한다.
 * @example
var zoo = ["zebra", "giraffe", "bear"];

$A(zoo).indexOf("giraffe");
// return 1
 * @see $A.has
 */
$A.prototype.indexOf = function(any) {
	if (typeof this._array.indexOf != 'undefined') return this._array.indexOf(any);

	for(var i=0; i < this._array.length; i++) {
		if (this._array[i] == any) return i;
	}
	return -1;
};

/**
 * $value 메서드는 원본 배열을 리턴한다.
 * @return {Array} 배열.
 */
$A.prototype.$value = function() {
	return this._array;
};

/**
 * push 메서드는 내부 배열에 하나 이상의 원소를 추가한다.
 * @param {value1, ..., valueN} elementN 추가할 N 개의 값
 * @return {Number} 하나 이상의 원소를 추가한 내부 배열의 크기.
 * @example
var arr = $A([1,2,3]);

arr.push(4); // arr => [1,2,3,4]
arr.push(5,6); // arr => [1,2,3,4,5,6]
 */
$A.prototype.push = function(element1/*, ...*/) {
	return this._array.push.apply(this._array, $A(arguments).$value());
};

/**
 * pop 메서드는 내부 배열의 마지막 원소를 삭제한다.
 * @return {Value} 삭제한 원소
 * @example
var arr = $A([1,2,3,4,5]);
var elem = arr.pop();

document.write(elem); // 5
document.write(arr); // [1,2,3,4]
 */
$A.prototype.pop = function() {
	return this._array.pop();
};

/**
 * shift 메서드는 내부 배열의 모든 원소를 한 칸씩 앞으로 이동한다. 내부 배열의 첫 원소는 삭제된다.
 * @return {Value} 삭제한 첫 원소.
 * @see $A#pop
 * @see $A#unshift
 * @example
var arr  = $A(['Melon','Grape','Apple','Kiwi']);
var elem = arr.shift();

document.write(elem); // Melon
document.write(arr);  // [Grape, Apple, Kiwi]
 */
$A.prototype.shift = function() {
	return this._array.shift();
};

/**
 * unshift 메서드는 내부 배열 맨 앞에 하나 이상의 원소를 삽입한다.
 * @param {value1, ..., valueN} elementN 추가할 하나 이상의 값
 * @return {Number} 엘리먼트를 추가한 후의 배열 객체 크기
 * @example
var arr = $A([4,5]);

arr.unshift('c');
document.write(arr); // [c, 4, 5]

arr.unshift('a', 'b');
document.write(arr); // [a, b, c, 4, 5]
 */
$A.prototype.unshift = function(element1/*, ...*/) {
	this._array.unshift.apply(this._array, $A(arguments).$value());

	return this._array.length;
};

/**
 * forEach 메서드는 내부 배열의 모든 원소를 순회하면서 콜백 함수를 실행한다.
 * @param {Function}	callback	순회 실행할 콜백 함수. 콜백 함수는 callback(value, index, array)의 형식을 가진다.
 * @param {Object}	[thisObject]	콜백 함수가 메서드일 때 콜백 함수의 this
 * @return {Object}	$A 객체
 * @import core.$A[Break, Continue]
 * @see $A#map
 * @example
$A(["zebra", "giraffe", "bear", "monkey"]).forEach(function(v,i,o) {
	document.writeln((i+1)+". " + v);
});
// 결과 : (줄 앞의 주석표시 //는 제외)
// 1. zebra
// 2. giraffe
// 3. bear
// 4. monkey
 */
$A.prototype.forEach = function(callback, thisObject) {
	var arr         = this._array;
	var errBreak    = this.constructor.Break;
	var errContinue = this.constructor.Continue;

	function f(v,i,a) {
		try {
			callback.call(thisObject, v, i, a);
		} catch(e) {
			if (!(e instanceof errContinue)) throw e;
		}
	};

	if (typeof this._array.forEach == "function") {
		try {
			this._array.forEach(f);
		} catch(e) {
			if (!(e instanceof errBreak)) throw e;
		}
		return this;
	}

	for(var i=0; i < arr.length; i++) {
		try {
			f(arr[i], i, arr);
		} catch(e) {
			if (e instanceof errBreak) break;
			throw e;

		}
	}

	return this;
};

/**
 * map 메서드는 내부 배열의 모든 원소를 순회하면서 콜백 함수를 실행한다.
 * @param {Function} callback	순회 실행할 콜백 함수. 콜백 함수는 callback(value, index, array)의 형식을 가진다.
 * @param {Object} [thisObject]	콜백 함수가 메서드일 때 콜백 함수의 this
 * @return {$A} 콜백 함수 수행 결과를 반영한 $A 객체
 * @see $A#forEach
 * @example
var animalList = $A(["zebra", "giraffe", "bear", "monkey"]).map(function(v,i,o) {
	return (i+1)+". " + v;
});

document.write (animalList.$value());
// 결과 : [1. zebra, 2. giraffe, 3. bear, 4. moneky]
 */
$A.prototype.map = function(callback, thisObject) {
	var arr         = this._array;
	var errBreak    = this.constructor.Break;
	var errContinue = this.constructor.Continue;

	function f(v,i,a) {
		try {
			return callback.call(thisObject, v, i, a);
		} catch(e) {
			if (e instanceof errContinue) return v;
			else throw e;
		}
	};

	if (typeof this._array.map == "function") {
		try {
			this._array = this._array.map(f);
		} catch(e) {
			if(!(e instanceof errBreak)) throw e;
		}
		return this;
	}

	for(var i=0; i < this._array.length; i++) {
		try {
			arr[i] = f(arr[i], i, arr);
		} catch(e) {
			if (e instanceof errBreak) break;
			throw e;
		}
	}

	return this;
};

/**
 * filter 메서드는 내부 배열의 모든 원소를 순회하면서 콜백 함수를 실행한다. 콜백 함수를 만족하는 원소는 새로운 $A 객체의 내부 배열에 추가된다.
 * @param {Function} callback	순회 실행할 콜백 함수. 콜백 함수는 Boolean 값을 리턴해야 한다.
 * @param {Object} thisObject	콜백 함수가 메서드일 경우 콜백 함수의 this
 * @returns {$A}	콜백 함수를 만족하는 원소만 포함하는 $A 객체.
 * @example
var arr = $A([1,2,3,4,5]);

// 필터링 함수
function filterFunc(element, index, array) {
	if (element > 2) {
		return true;
	} else {
		return false;
	}
}

var newArr = arr.filter(filterFunc);

document.write(arr); // [1,2,3,4,5]
document.write(newArr); // [3,4,5]
 */
$A.prototype.filter = function(callback, thisObject) {
	var ar = new Array;

	this.forEach(function(v,i,a) {
		if (callback.call(thisObject, v, i, a) === true) {
			ar[ar.length] = v;
		}
	});

	return $A(ar);
};

/**
 * every 메서드는 내부 배열의 모든 원소가 콜백 함수를 만족하는지 검사한다.
 * @param {Function} callback	순회 실행할 콜백 함수. 콜백 함수는 반드시 boolean 값을 리턴해야 한다.
 * @param {Object} [thisObject]	콜백 함수가 메서드일 경우 콜백 함수의 this
 * @returns {Boolean} 내부 배열의 모든 원소가 콜백 함수를 만족하면 true, 그렇지 않으면 false를 리턴한다.
 * @example
function isBigEnough(element, index, array) {
		return (element >= 10);
	}

var try1 = $A([12, 5, 8, 130, 44]).every(isBigEnough);
// false

var try2 = $A([12, 54, 18, 130, 44]).every(isBigEnough);
// true
 */
$A.prototype.every = function(callback, thisObject) {
	if (typeof this._array.every != "undefined") return this._array.every(callback, thisObject);

	var result = true;
	this.forEach(function(v, i, a) {
		if (callback.call(thisObject, v, i, a) === false) {
			result = false;
			$A.Break();
		}
	});
	return result;
};

/**
 * some 메서드는 내부 배열에 콜백 함수를 만족시키는 원소가 있는지 검사한다.
 * @param {Function} callback	순회 실행할 콜백 함수. 콜백 함수는 반드시 Boolean 값을 리턴해야 한다.
 * @param {Object} [thisObject]	콜백 함수가 메서드일 경우 콜백 함수의 this
 * @returns {Boolean} 내부 배열에 콜백 함수를 만족시키는 원소가 있으면 true, 콜백 함수를 만족시키는 원소가 하나도 없다면 false를 리턴한다.
 * @example
function twoDigitNumber(element, index, array) {
	return (element >= 10 && element < 100);
}

var try1 = $A([12, 5, 8, 130, 44]).some(twoDigitNumber);
// true

var try2 = $A([1, 5, 8, 130, 4]).some(twoDigitNumber);
// false
 */
$A.prototype.some = function(callback, thisObject) {
	if (typeof this._array.some != "undefined") return this._array.some(callback, thisObject);

	var result = false;
	this.forEach(function(v, i, a) {
		if (callback.call(thisObject, v, i, a) === true) {
			result = true;
			$A.Break();
		}
	});
	return result;
};

/**
 * refuse 메서드는 특정 값을 제외한 새로운 $A 객체를 리턴한다.
 * @param {Value} value 내부 배열에서 제외할 값
 * @returns {$A} 내부 배열에서 특정 값을 제외한 새로운 $A 객체
 * @example
var arr = $A([12, 5, 8, 130, 44]);

var newArr1 = arr.refuse(12);

document.write(arr); // [12, 5, 8, 130, 44]
document.write(newArr1); // [5, 8, 130, 44]

var newArr2 = newArr1.refuse(8, 44, 130);

document.write(newArr1); // [5, 8, 130, 44]
document.write(newArr2); // [5]
 */
$A.prototype.refuse = function(value) {
	var a = $A(arguments);
	return this.filter(function(v,i) { return !a.has(v) });
};

/**
 * slice 메서드는 내부 배열의 일부를 추출한다.
 * @param {Number} start 잘라낼 부분의 시작 인덱스
 * @param {Number} end 잘라낼 부분의 바로 뒤 인덱스
 * @returns {$A} 내부 배열의 일부를 추출한 새로운 $A 객체. start가 end보다 작거나 같을 경우, 혹은 start가 0보다 작을 경우는 빈 배열을 가지는 $A를 리턴한다.
 * @example
var arr = $A([12, 5, 8, 130, 44]);
var newArr = arr.slice(1,3);

document.write(arr); // [12, 5, 8, 130, 44]
document.write(newArr); // [5, 8]
 */
$A.prototype.slice = function(start, end) {
	var a = this._array.slice.call(this._array, start, end);
	return $A(a);
};

/**
 * splice 메서드는 내부 배열의 일부를 삭제한다.
 * @param {Number} index	삭제할 원소의 시작 인덱스
 * @param {Number} howMany	삭제할 원소의 갯수. 이 값을 생략하면 index 번째 원소부터 마지막 원소까지 삭제한다. ??
 * @param {value, ..., valueN} [elementN] 삭제한 배열에 추가할 하나 이상의 값. 삽입은 index로 지정한 시작 인덱스에서 부터 시작한다. ??
 * @returns {$A} 삭제한 원소를 포함하는 새로운 $A 객체.
var arr = $A(["angel", "clown", "mandarin", "surgeon"]);
var removed = arr.splice(2, 0, "drum");

document.write(arr); // [angel, clown, drum, mandarin, surgeon]
document.write(removed); // []

removed = arr.splice(3, 1);

document.write(arr); // [angel, clown, drum, surgeon]
document.write(removed); // [mandarin]

removed = a.splice(2, 1, "trumpet", "parrot");

document.write(arr); // [angel, clown, trumpet, parrot, surgeon]
document.write(removed); // [drum]
 */
$A.prototype.splice = function(index, howMany/*, element*/) {
	var a = this._array.splice.apply(this._array, arguments);

	return $A(a);
};

/**
 * shuffle 메서드는 내부 배열의 원소를 무작위로 섞는다.
 * @returns {$A} 정렬한 $A 객체.
 * @example
var dice = $A([1,2,3,4,5,6]);

dice.shuffle();

document.write("You get the number " + dice[0]);
// 결과 : 1부터 6까지의 숫자 중 랜덤한 숫자
 */
$A.prototype.shuffle = function() {
	this._array.sort(function(a,b){ return Math.random()>Math.random()?1:-1 });

	return this;
};

/**
 * unique 메서드는 내부 배열에서 중복되는 원소를 삭제한다.
 * @returns {$A} 중복되는 원소를 제거한 $A 객체.
 * @example
var arr = $A([10, 3, 76, 5, 4, 3]);

arr.unique();
document.write(arr); // [10, 3, 76, 5, 4]
 */
$A.prototype.unique = function() {
	var a = this._array, b = [], l = a.length;
	var i, j;

	// 중복되는 원소 제거
	for(i = 0; i < l; i++) {
		for(j = 0; j < b.length; j++) {
			if (a[i] == b[j]) break;
		}

		if (j >= b.length) b[j] = a[i];
	}

	this._array = b;

	return this;
};

/**
 * reverse 메서드는 내부 배열의 원소 순서를 거꾸로 뒤집는다.
 * @returns {$A} 원소 순서를 뒤집은 $A 객체.
 * @example
var arr = $A([1, 2, 3, 4, 5]);

arr.reverse();
document.write(arr); // [5, 4, 3, 2, 1]
 */
$A.prototype.reverse = function() {
	this._array.reverse();

	return this;
};

/**
 * empty 메서드는 배열의 모든 원소를 제거하고, 빈 배열로 만든다.
 * @returns {$A} 빈 배열 $A 객체
 * @example
var arr = $A([1, 2, 3]);

arr.empty();
document.write(arr); // []
 */
$A.prototype.empty = function() {
	return this.length(0);
};

/**
 * Break 메서드는 each, filter, map 메서드의 순회 루프를 중단한다.
 */
$A.Break = function() {
	if (!(this instanceof arguments.callee)) throw new arguments.callee;
};

/**
 * Continue 메서드는 each, filter, map 메서드의 순회 루프에서 나머지 명령을 실행하지 않고 다음 루프로 건너뛴다.
 */
$A.Continue = function() {
	if (!(this instanceof arguments.callee)) throw new arguments.callee;
};


/**
 * @fileOverview $H의 생성자 및 메서드를 정의한 파일
 * @name hash.js
 */

/**
 * $H 해시 객체를 리턴한다
 * @class $H 클래스는 키와 값을 원소로 가지는 열거형 배열인 해시를 구현하고, 해시를 다루기 위한 여러 가지 위한 메서드를 제공한다.
 * @param {Object} hashObject 해시로 만들 객체.
 * @return {$H} 해시 객체
 * @constructor
 * @example
var h = $H({one:"first", two:"second", three:"third"})
 * @author Kim, Taegon
 */
function $H(hashObject) {
	var cl = arguments.callee;
	if (typeof hashObject == "undefined") hashObject = new Object;
	if (hashObject instanceof cl) return hashObject;
	if (!(this instanceof cl)) return new cl(hashObject);

	this._table = {};
	for(var k in hashObject) {
		if (this._table[k] == hashObject[k]) continue;
		this._table[k] = hashObject[k];
	}
};

/**
 * $value 메서드는 해싱 대상인 객체를 반환한다.
 * @return {Object} 해싱 대상 객체
 */
$H.prototype.$value = function() {
	return this._table;
};

/**
 * $ 메서드는 키와 값을 설정하거나 키에 해당하는 값을 반환한다.
 * @param {String} key 키
 * @param {void} [value] 값
 * @return {void|$H} 키에 해당하는 값 혹은 $H 객체
 * @example
 * var hash = $H({one:"first", two:"second"});
 *
 * // 값을 설정할 때
 * hash.$("three", "third");
 *
 * // hash => {one:"first", two:"second", three:"third"}
 *
 * // 값을 반환할 때
 * var three = hash.$("three");
 *
 * // three => "third"
 */
$H.prototype.$ = function(key, value) {
	if (typeof value == "undefined") {
		return this._table[key];
	}

	this._table[key] = value;
	return this;
};

/**
 * length 메서드는 해시 객체의 크기를 반환한다.
 * @return {Number} 해시의 크기
 */
$H.prototype.length = function() {
	var i = 0;
	for(var k in this._table) {
		if (typeof Object.prototype[k] != "undeifned" && Object.prototype[k] === this._table[k]) continue;
		i++;
	}

	return i;
};

/**
 * forEach 메서드는 해시 객체의 키와 값을 인수로 지정한 콜백 함수를 실행한다.
 * @param {Function} callback 실행할 콜백 함수
 * @param {Object} thisObject 콜백 함수의 this
 * @example
function printIt(value, key) {
   document.write(key+" => "+value+" <br>");
}
$H({one:"first", two:"second", three:"third"}).forEach(printIt);
 */
$H.prototype.forEach = function(callback, thisObject) {
	var t = this._table;
	var h = this.constructor;

	for(var k in t) {
		if (!t.propertyIsEnumerable(k)) continue;
		try {
			callback.call(thisObject, t[k], k, t);
		} catch(e) {
			if (e instanceof h.Break) break;
			if (e instanceof h.Continue) continue;
		}
	}
	return this;
};

/**
 * filter 메서드는 해시 객체에서 필터 콜백 함수를 만족하는 원소를 수집한다. 수집한 원소는 새로운 $H 객체의 원소가 된다.
 * 콜백함수는 Boolean 값을 반환해야 한다.
 * @param {Function} callback 필터 콜백 함수
 * @param {Object} thisObject 콜백 함수의 this
 * @return {$H} 수집한 원소로 새로 만든 해시 객체
 * @remark 필터 콜백 함수의 결과가 true인 원소만 수집한다. 콜백 함수는 형식은 예제를 참고한다.
 * @example
function callback(value, key, object) {
   // value    해시의 값
   // key      해시의 고유한 키 혹은 이름
   // object   JavaScript Core Object 객체
}
 */
$H.prototype.filter = function(callback, thisObject) {
	var h = $H();
	this.forEach(function(v,k,o) {
		if(callback.call(thisObject, v, k, o) === true) {
			h.add(k,v);
		}
	});
	return h;
};

/**
 * map 메서드는 해시 객체의 원소를 인수로 콜백 함수를 실행하고, 함수의 리턴 값을 해당 원소의 값으로 지정한다.
 * @param {Function} callback 콜백 함수
 * @param {Object} thisObject 콜백 함수의 this
 * @return {$H} 값을 변경한 해시 객체
 * @remark 콜백 함수는 형식은 예제를 참고한다.
 * @example
function callback(value, key, object ) {
   // value    해시의 값
   // key      해시의 고유한 키 혹은 이름
   // object   JavaScript Core Object 객체

   var r = key+"_"+value;
   document.writeln (r + "<br />");
   return r;
}
$H({one:"first", two:"second", three:"third"}).map(callback);
 */
$H.prototype.map = function(callback, thisObject) {
	var t = this._table;
	this.forEach(function(v,k,o) {
		t[k] = callback.call(thisObject, v, k, o);
	});
	return this;
};

/**
 * 해시 테이블에 값을 추가한다.
 * @param {String} key 추가한 값을 위한 키
 * @param {String} value 해시 테이블에 추가할 값
 * @return {$H} 값을 추가한 해시 객체
 */
$H.prototype.add = function(key, value) {
	if (this.hasKey(key)) return null;
	this._table[key] = value;

	return this;
};

/**
 * remove 메서드는 해시 테이블의 원소를 제거한다.
 * @param {String} key 제거할 원소의 키
 * @return {void} 제거한 키 값
 * @example
var h = $H({one:"first", two:"second", three:"third"});
h.remove ("two");
// h의 해시 테이블은 {one:"first", three:"third"}
 */
$H.prototype.remove = function(key) {
	if (typeof this._table[key] == "undefined") return null;
	var val = this._table[key];
	delete this._table[key];

	return val;
};

/**
 * search 메서드는 해시 테이블에서 인수로 지정한 값을 찾는다.
 * @param {String} value 검색할 값
 * @returns {String | Boolean} 값을 찾았다면 값에 대한 키. 값을 찾지 못했다면 false.
 * @example
var h = $H({one:"first", two:"second", three:"third"});
h.search ("second");
// two

h.search ("fist");
// false
 */
$H.prototype.search = function(value) {
	var result = false;
	this.forEach(function(v,k,o) {
		if (v === value) {
			result = k;
			$H.Break();
		}
	});
	return result;
};

/**
 * hasKey 메서드는 해시 테이블에 인수로 지정한 키가 있는지 찾는다.
 * @param {String} key 해시 테이블에서 검색할 키
 * @return {Boolean} 키의 존재 여부
 * @example
var h = $H({one:"first", two:"second", three:"third"});
h.hasKey("four"); // false
h.hasKey("one"); // true
 */
$H.prototype.hasKey = function(key) {
	var result = false;

	return (typeof this._table[key] != "undefined");
};

/**
 * hasValue 메서드는 해시 테이블에 인수로 지정한 값이 있는지 확인한다.
 * @param {String} value 해시 테이블에서 검색할 값
 * @return {Boolean} 값의 존재 여부
 */
$H.prototype.hasValue = function(value) {
	return (this.search(value) !== false);
};

/**
 * sort 메서드는 값을 기준으로 원소를 오름차순 정렬한다.
 * @return {$H} 원소를 정렬한 해시 객체.
 * @see $H#ksort
 * @example
var h = $H({one:"하나", two:"둘", three:"셋"});
h.sort ();
// {two:"둘", three:"셋", one:"하나"}
 */
$H.prototype.sort = function() {
	var o = new Object;
	var a = this.values();
	var k = false;

	a.sort();

	for(var i=0; i < a.length; i++) {
		k = this.search(a[i]);

		o[k] = a[i];
		delete this._table[k];
	}

	this._table = o;

	return this;
};

/**
 * ksort 메서드는 키를 기준으로 원소를 오름차순 정렬한다.
 * @return {$H} 원소를 정렬한 해시 객체
 * @see $H#sort
 * @example
var h = $H({one:"하나", two:"둘", three:"셋"});
h.sort ();
// h => {one:"하나", three:"셋", two:"둘"}
 */
$H.prototype.ksort = function() {
	var o = new Object;
	var a = this.keys();

	a.sort();

	for(var i=0; i < a.length; i++) {
		o[a[i]] = this._table[a[i]];
	}

	this._table = o;

	return this;
};

/**
 * keys 메서드는 해시 키의 배열을 반환한다.
 * @return {Array} 해시 키의 배열
 * @example
var h = $H({one:"first", two:"second", three:"third"});
h.keys ();
// ["one", "two", "three"]
 * @see $H#values
 */
$H.prototype.keys = function() {
	var keys = new Array;
	for(var k in this._table) {
		keys.push(k);
	}

	return keys;
};

/**
 * values 메서드는 해시 값의 배열을 반환한다.
 * @return {Array} 해시 값의 배열
 * @example
var h = $H({one:"first", two:"second", three:"third"});
h.values();
// ["first", "second", "third"]
 * @see $H#keys
 */
$H.prototype.values = function() {
	var values = [];
	for(var k in this._table) {
		values[values.length] = this._table[k];
	}

	return values;
};

/**
 * toQueryString은 해시 객체를 쿼리 스트링 형태로 만든다.
 * @return {String}
 * @example
var h = $H({one:"first", two:"second", three:"third"});
h.toQueryString();
// "one=first&two=second&three=third"
 */
$H.prototype.toQueryString = function() {
	var buf = [], val = null, idx = 0;
	for(var k in this._table) {
		if (typeof(val = this._table[k]) == "object" && val.constructor == Array) {
			for(i=0; i < val.length; i++) {
				buf[buf.length] = encodeURIComponent(k)+"[]="+encodeURIComponent(val[i]+"");
			}
		} else {
			buf[buf.length] = encodeURIComponent(k)+"="+encodeURIComponent(this._table[k]+"");
		}
	}

	return buf.join("&");
};

/**
 * empty는 해시 객체를 빈 객체로 만든다.
 * @return {$H} 비워진 해시 객체
 * @example
var hash = $H({a:1, b:2, c:3});
// hash => {a:1, b:2, c:3}

hash.empty();
// hash => {}
 */
$H.prototype.empty = function() {
	var keys = this.keys();

	for(var i=0; i < keys.length; i++) {
		delete this._table[keys[i]];
	}

	return this;
};

/**
 * Break 메서드는 반복문의 실행을 중단할 때 사용한다.
 * @remark forEach, filter, map와 같은 루프를 중단한다. 강제로 exception을 발생시키므로 try ~ catch 영역에서 이 메소드를 실행하면 정상적으로 동작하지 않을 수 있다.
 * @example
$H({a:1, b:2, c:3}).forEach(function(v,k,o) {
   ...
   if (k == "b") $H.Break();
   ...
});
 * @see $H.Continue
 */
$H.Break = function() {
	if (!(this instanceof arguments.callee)) throw new arguments.callee;
};

/**
 * Continue 메서드는 루프를 실행하다 다음 단계로 넘어갈 때 사용한다.
 * @remark forEach, filter, map와 같은 루프 실행 도중에 현재 루프를 중단하고 다음으로 넘어간다. 강제로 exception을 발생시키므로 try ~ catch 영역에서 이 메소드를 실행하면 정상적으로 동작하지 않을 수 있다.
 * @example
$H({a:1, b:2, c:3}).forEach(function(v,k,o) {
   ...
   if (v % 2 == 0) $H.Continue();
   ...
});
 * @see $H.Break
 */
$H.Continue = function() {
	if (!(this instanceof arguments.callee)) throw new arguments.callee;
};


/**
 * @fileOverview $Element의 생성자 및 메서드를 정의한 파일
 * @name element.js
 */

/**
 * $Element 객체를 생성 및 리턴한다.
 * @class $Elemetn 클래스는 DOM 엘리먼트를 다루기 위한 여러 가지 메서드를 제공한다.
 * @param {Element | String} el	$Element를 사용할 DOM 엘리먼트, 혹은 DOM 엘리먼트의 고유한 id. 만약 고유하지 않은 id를 지정하면 가장 먼저 나오는 엘리먼트를 반환한다.
 * @constructor
 * @author Kim, Taegon
 */
function $Element(el) {
	var cl = arguments.callee;
	if (el instanceof cl) return el;
	if (!(this instanceof cl)) return new cl(el);

	this._element = $(el);

	// tagname
	this.tag = this._element?this._element.tagName.toLowerCase():'';

	this._queue = new Array;
}

/**
 * $value 메서드는 래핑된 원래의 DOM 엘리먼트를 리턴한다.
 * @return {HTMLElement} DOM 엘리먼트
 */
$Element.prototype.$value = function() {
	return this._element;
};

/**
 * visible 메서드는 DOM 엘리먼트의 CSS의 display 속성을 조사해서 엘리먼트가 보이는 상태인지 확인한다.
 * @param {Boolean} bVisible 엘리먼트를 보이거나 안 보이게 설정한다. true면 show() 메서드를, false면 hide() 메서드를 실행하는 것과 동일한 결과를 얻는다.
 * @return {Boolean|$Element} display 속성이 none이면 false 를, 그 외의 값이면 true를 리턴한다. 설정하게 되면 현재의 this 객체를 반환한다.
 * @since 설정 기능은 1.1.2부터
 */
$Element.prototype.visible = function(bVisible) {
	if (typeof bVisible != "undefined") {
		this[bVisible?"show":"hide"]();
		return this;
	}

	return (this.css("display") != "none");
};

/**
 * show 메서드는 DOM 엘리먼트가 화면에 보이도록 CSS의 display 속성을 변경한다.
 * @return {$Element} display 속성을 변경한 $Element 객체.
 * @see $Element#hide
 */
$Element.prototype.show = function() {
	var s = this._element.style;
	var b = "block";
	var c = {p:b,div:b,form:b,h1:b,h2:b,h3:b,h4:b,ol:b,ul:b,fieldset:b,td:"table-cell",th:"table-cell",li:"list-item",table:"table",thead:"table-header-group",tbody:"table-row-group",tfoot:"table-footer-group",tr:"table-row",col:"table-column",colgroup:"table-column-group",caption:"table-caption",dl:b,dt:b,dd:b};

	try {
		if(typeof c[this.tag] == "string") {
			s.display = c[this.tag];
		} else {
			s.display = "inline";
		}
	} catch(e) {
		s.display = "block";
	}

	return this;
};

/**
 * hide 메서드는 DOM 엘리먼트가 화면에 보이지 않도록 CSS의 display 속성을 none으로 변경한다.
 * @returns {Object} this	display 속성을 변경한 $Element 객체.
 * @see $Element#show
 */
$Element.prototype.hide = function() {
	this._element.style.display = "none";

	return this;
};

/**
 * toggle 메서드는 CSS의 Display 속성을 변경하여 DOM 엘리먼트를 화면에 보이거나 보이지 않게 한다.
 * @returns {Object} this display 속성을 변경한 $Element 객체.
 * @see $Element#show
 * @see $Element#hide
 * @example

var isOpen 	= e.toggle().visible();
var bullet	= (isOpen) ? '-' : '+';

 */
$Element.prototype.toggle = function() {
	this[this.visible()?"hide":"show"]();

	return this;
};

/**
 * opacity 메서드는 DOM 엘리먼트의 투명도 값을 리턴하거나 설정한다.
 * @param {Number} value	설정할 투명도 값. 투명도 값은 0 ~ 1 사이의 실수값으로 정한다. 값이 0보다 작으면 0을, 1보다 크면 1을 설정한다.
 * @return {Number} DOM 엘리먼트의 투명도 값.
 */
$Element.prototype.opacity = function(value) {
	var v,e = this._element,b=this.visible();

	value = parseFloat(value);

	if (!isNaN(value)) {
		value = Math.max(Math.min(value,1),0);

		if (typeof e.filters != "undefined") {
			value = Math.ceil(value*100);
			if (typeof e.filters.alpha != "undefined") {
				e.filters.alpha.opacity = value;
			} else {
				e.style.filter = (e.style.filter + " alpha(opacity=" + value + ")");
			}
		} else {
			e.style.opacity = value;
		}

		return value;
	}

	if (typeof e.filters != "undefined") {
		v = (typeof e.filters.alpha == "undefined")?(b?100:0):e.filters.alpha.opacity;
		v = v / 100;
	} else {
		v = parseFloat(e.style.opacity);
		if (isNaN(v)) v = b?1:0;
	}

	return v;
};

/**
 * appear 메서드는 DOM 엘리먼트를 서서히 나타나게 한다. (Fade-in 효과)
 * @param {Number} duration DOM 엘리먼트의 투명도가 현재의 값에서 시작해서 1이 될때 까지 걸리는 시간. 단위는 초를 사용한다.
 * @param {Function} callback DOM 엘리먼트가 완전히 나타난 후, 즉 투명도가 1이 된 후에 실행할 콜백 함수.
 * @returns {Object} $Element 객체
 * @see $Element#show
 * @see $Element#disappear
 */
$Element.prototype.appear = function(duration, callback) {
	var self = this;
	var op   = this.opacity();

	if (op == 1) return this;
	try { clearTimeout(this._fade_timer); } catch(e){};

	callback = callback || new Function;

	var step = (1-op) / ((duration||0.3)*100);
	var func = function(){
		op += step;
		self.opacity(op);

		if (op >= 1) {
			callback(self);
		} else {
			self._fade_timer = setTimeout(func, 10);
		}
	};

	this.show();
	func();

	return this;
};

/**
 * disappear 메서드는 DOM 엘리먼트를 서서히 사라지게 한다. (Fade-out 효과) DOM 엘리먼트가 완전히 사라지면 엘리먼트의 CSS 중 display 속성은 none으로 변한다.
 * @param {Number} duration DOM 엘리먼트의 투명도가 현재의 값에서 0이 될 때까지 걸리는 시간. 단위는 초를 사용한다.
 * @param {Function} callback DOM 엘리먼트가 완전히 사라지고 난 후, 즉 투명도가 0이 된 후에 실행할 콜백 함수
 * @returns {Object} $Element 객체
 * @see $Element#hide
 * @see $Element#appear
 */
$Element.prototype.disappear = function(duration, callback) {
	var self = this;
	var op   = this.opacity();

	if (op == 0) return this;
	try { clearTimeout(this._fade_timer); } catch(e){};

	callback = callback || new Function;

	var step = op / ((duration||0.3)*100);
	var func = function(){
		op -= step;
		self.opacity(op);

		if (op <= 0) {
			self.hide();
			callback(self);
		} else {
			self._fade_timer = setTimeout(func, 10);
		}
	};

	func();

	return this;
};

/**
 * css 메서드는 DOM 엘리먼트의 CSS 속성값을 가져오거나 설정한다. 속성값의
 * 인수 하나를 사용하면 CSSS에서 String에 해당하는 CSS 속성의 값을 가져온다.
 * 인수 두 개를 사용하면 첫번째 인수에 해당하는 CSS 속성을 두번째 인수의 값으로 설정한다.
 * Object 혹은 $Hash 타입의 객체를 사용하면 두 개 이상의 CSS 속성을 한꺼번에 정의할 수 있다.
 * @remark CSS 속성은 Camel 표기법을 사용한다. 따라서 border-width-bottom은 borderWidthBottom으로 정의한다.
 * @remark float 속성은 Javascript의 예약어로 사용하므로 css 메서드에서는 float 대신 cssFloat를 사용한다. (Internet Explorer에서는 styleFloat를, 그 외의 브라우저에서는 cssFloat를 사용한다.)
 * @param {String | Object | $H} sName CSS 속성 | 하나 이상의 CSS 속성과 값을 가지는 객체.
 * @param {String | Number} [sValue] CSS 속성에 설정할 값. 단위가 필요한 값은 Number, 혹은 단위를 포함하는 String으로 정의한다.
 * @return {String | $Element} 값을 가져올 때는 String 설정값을, 값을 설정할 때는 값을 설정한 현재의 $Element를 리턴한다. 값을 설정할 때 CSS 속성이 존제하지 않으면 null을 리턴한다.
 * @example

<style>
	#btn {
		width: 120px;
		height: 30px;
		background-color: blue;
	}
</style>

...

<script type="text/javascript" charset="utf-8">
	window.onload = function () {
		$Element('btn').css('backgroundColor');
		// rgb (0, 0, 256)

		$Element('btn').css('backgroundColor', 'red');
		// $Element('btn').css('backgroundColor') -> reb (255, 0, 0)

		$Element('btn').css({
			width: "200px",
			height: "80px"
		});
		// $Element('btn').css('width') -> "200px"
		// $Element('btn').css('height') -> "80px"
 	}
</script>
 */
$Element.prototype.css = function(sName, sValue) {
	var e = this._element;

	if (sName == 'opacity') return typeof sValue == 'undefined' ? this.opacity() : this.opacity(sValue);

	if (typeof sName == "string") {
		var view;

		if (typeof sValue == "string" || typeof sValue == "number") {
			var obj = new Object;
			obj[sName] = sValue;
			sName = obj;
		} else {
			if (e.currentStyle) {
				if (sName == "cssFloat") sName = "styleFloat";
				return e.currentStyle[sName]||e.style[sName];
			} else if (window.getComputedStyle) {
				if (sName == "cssFloat") sName = "float";
				var d = e.ownerDocument || e.document || document;
				return d.defaultView.getComputedStyle(e,null).getPropertyValue(sName.replace(/([A-Z])/g,"-$1").toLowerCase())||e.style[sName];
			} else {
				if (sName == "cssFloat" && /MSIE/.test(window.navigator.userAgent)) sName = "styleFloat";
				return e.style[sName];
			}

			return null;
		}
	}

	if (typeof $H != "undefined" && sName instanceof $H) {
		sName = sName.$value();
	}

	if (typeof sName == "object") {
		var v, type;

		for(var k in sName) {
			v    = sName[k];
			type = (typeof v);
			if (type != "string" && type != "number") continue;
			if (k == "cssFloat" && navigator.userAgent.indexOf("MSIE") > -1) k = "styleFloat";
			try {
				e.style[k] = v;
			} catch(err) {
				if (k == "cursor" && v == "pointer") {
					e.style.cursor = "hand";
				} else if (("#top#left#right#bottom#").indexOf(k+"#") > 0 && (type == "number" || !isNaN(parseInt(v)))) {
					e.style[k] = parseInt(v)+"px";
				}
			}
		}
	}

	return this;
};

/**
 * attr 메서드는 DOM 엘리먼트의 HTML 속성을 가져오거나 설정한다.
 * 하나의 인수만 사용하면 해당 HTML 속성의 속성값을 가져온다.
 * 두 개의 인수를 사용하면 첫번째 인수에 해당하는 HTML 속성의 속성값을 설정한다.
 * Object 혹은 $Hash 타입의 객체를 사용하면 두 개 이상의 HTML 속성을 한꺼번에 정의할 수 있다.
 * @param {String | Object | $H} sName HTML 속성 이름 혹은 설정값 객체
 * @param {String | Number} [sValue] 설정값. 설정값을 null로 지정하면 HTML 속성을 지운다.
 * @return {String | $Element} 값을 가져올 때는 String 설정값을, 값을 설정할 때는 값을 설정한 현재의 $Element를 리턴한다.
 */
$Element.prototype.attr = function(sName, sValue) {
	var e = this._element;

	if (typeof sName == "string") {
		if (typeof sValue != "undefined") {
			var obj = new Object;
			obj[sName] = sValue;
			sName = obj;
		} else {
			if (sName == "class" || sName == "className") return e.className;
			return e.getAttribute(sName);
		}
	}

	if (typeof $H != "undefined" && sName instanceof $H) {
		sName = sName.$value();
	}

	if (typeof sName == "object") {
		for(var k in sName) {
			if (typeof(sValue) != "undefined" && sValue === null) e.removeAttribute(k);
			else e.setAttribute(k, sName[k]);
		}
	}

	return this;
};

/**
 * offset 메서드는 DOM 엘리먼트의 위치를 가져오거나 설정한다. 기준점은 브라우저 문서의 왼쪽 상단이다. offset 메서드의 인수로 top, left 값을 지정하면 DOM 엘리먼트의 위치를 설정한다.
 * @remark 일부 브라우저와 일부 상황에서 inline 엘리먼트에 대한 위치가 올바르게 얻어지지 않는 문제가 있으며 이 경우 해당 엘리먼트를 position:relative; 로 바꿔주는 식으로 해결할 수 있다.
 * @author Hooriza
 * @param {Number} [nTop] 문서의 맨 위에서 부터 엘리먼트 맨 위까지의 거리. 단위는 px.
 * @param {Number} [nLeft] 문서의 왼쪽 가장자리에서 엘리먼트의 왼쪽 가장자리까지의 거리. 단위는 px.
 * @return {$Element | Object} 엘리먼트의 위치를 설정하면 기존 엘리먼트를, 엘리먼트의 위치를 가져오면 엘리먼트의 x, y 좌표를 속성으로 가지는 객체.
 */
$Element.prototype.offset = function(nTop, nLeft) {

	var oEl = this._element;
	var oPhantom = null;

	// setter
	if (typeof nTop == 'number' && typeof nLeft == 'number') {

		if (isNaN(parseInt(this.css('top')))) this.css('top', 0);
		if (isNaN(parseInt(this.css('left')))) this.css('left', 0);

		var oPos = this.offset();
		var oGap = { top : nTop - oPos.top, left : nLeft - oPos.left };

		oEl.style.top = parseInt(this.css('top')) + oGap.top + 'px';
		oEl.style.left = parseInt(this.css('left')) + oGap.left + 'px';

		return this;

	}

	// getter
	var bSafari = /Safari/.test(navigator.userAgent);
	var bIE = /MSIE/.test(navigator.userAgent);

	var fpSafari = function(oEl) {

		var oPos = { left : 0, top : 0 };

		for (var oParent = oEl, oOffsetParent = oParent.offsetParent; oParent = oParent.parentNode; ) {

			if (oParent.offsetParent) {

				oPos.left -= oParent.scrollLeft;
				oPos.top -= oParent.scrollTop;

			}

			if (oParent == oOffsetParent) {

				oPos.left += oEl.offsetLeft + oParent.clientLeft;
				oPos.top += oEl.offsetTop + oParent.clientTop ;

				if (!oParent.offsetParent) {

					oPos.left += oParent.offsetLeft;
					oPos.top += oParent.offsetTop;

				}

				oOffsetParent = oParent.offsetParent;
				oEl = oParent;
			}
		}

		return oPos;

	};

	var fpOthers = function(oEl) {

		var oPos = { left : 0, top : 0 };

		var oDoc = oEl.ownerDocument || oEl.document || document;
		var oHtml = oDoc.documentElement;
		var oBody = oDoc.body;

		if (oEl.getBoundingClientRect) { // has getBoundingClientRect

			if (!oPhantom) {

				if (bIE && window.external) {

					oPhantom = { left : 2, top : 2 };

					/*
					var oBase = oDoc.createElement('div');
					oBase.style.cssText = 'position:absolute !important; left:0 !important; top:0 !important; margin:0 !important; padding:0 !important;';
					oDoc.body.insertBefore(oBase, oDoc.body.firstChild);

					oPhantom = oBase.getBoundingClientRect();
					oPhantom.left += oHtml.scrollLeft || oBody.scrollLeft;
					oPhantom.top += oHtml.scrollTop || oBody.scrollTop;

					oDoc.body.removeChild(oBase);
					*/

					oBase = null;

				} else {

					oPhantom = { left : 0, top : 0 };

				}

			}

			var box = oEl.getBoundingClientRect();
			if (oEl !== oHtml && oEl !== oBody) {

				oPos.left = box.left - oPhantom.left;
				oPos.top = box.top - oPhantom.top;

				oPos.left += oHtml.scrollLeft || oBody.scrollLeft;
				oPos.top += oHtml.scrollTop || oBody.scrollTop;

			}

		} else if (oDoc.getBoxObjectFor) { // has getBoxObjectFor

			var box = oDoc.getBoxObjectFor(oEl);
			var vpBox = oDoc.getBoxObjectFor(oHtml || oBody);

			oPos.left = box.screenX - vpBox.screenX;
			oPos.top = box.screenY - vpBox.screenY;

		} else {

			for (var o = oEl; o; o = o.offsetParent) {

				oPos.left += o.offsetLeft;
				oPos.top += o.offsetTop;

			}

			for (var o = oEl.parentNode; o; o = o.parentNode) {

				if (o.tagName == 'BODY') break;
				if (o.tagName == 'TR') oPos.top += 2;

				oPos.left -= o.scrollLeft;
				oPos.top -= o.scrollTop;

			}

		}

		return oPos;

	};

	return (bSafari ? fpSafari : fpOthers)(oEl);
};

/**
 * width 메서드는 DOM 엘리먼트의 너비를 가져오거나 설정한다.
 * @remark width 메서드는 엘리먼트의 실제 너비를 가져온다. 브라우저마다 Box 모델의 크기 계산 방법이 다르므로 CSS의 width 속성값과 width 메서드의 리턴 값은 서로 다를 수 있다.
 * @param {Number} [width]	설정할 너비. 단위는 px.
 * @return {Number|$Element} DOM 엘리먼트의 실제 너비. 너비를 설정하면 this를 반환한다.
 */
$Element.prototype.width = function(width) {
	if (typeof width == "number") {
		var e = this._element;

		e.style.width = width+"px";
		if (e.offsetWidth != width) {
			e.style.width = (width*2 - e.offsetWidth) + "px";
		}

		return this;
	}

	return this._element.offsetWidth;
};

/**
 * height 메서드는 DOM 엘리먼트의 높이를 가져오거나 설정한다.
 * @remark height 메서드는 엘리먼트의 실제 높이를 가져온다. 브라우저마다 Box 모델의 크기 계산 방법이 다르므로 CSS의 height 속성값과 height 메서드의 리턴 값은 서로 다를 수 있다.
 * @param {Number} height	설정할 높이. 단위는 px.
 * @return {Number|$Element} DOM 엘리먼트의 실제 높이. 높이를 설정하면 this를 반환한다.
 */
$Element.prototype.height = function(height) {
	if (typeof height == "number") {
		var e = this._element;

		e.style.height = height+"px";
		if (e.offsetHeight != height) {
			e.style.height = (height*2 - e.offsetHeight) + "px";
		}

		return this;
	}

	return this._element.offsetHeight;
};

/**
 * className 메서드는 DOM 엘리먼트에 클래스 이름을 설정하거나 반환한다.
 * @param {String} sClass 	클래스 이름. 두 개 이상의 클래스를 설정했다면 공백을 포함한 문자열을 반환한다.
 * @example

<div id="zoo" class="animal rest">

..

<script type="text/javascript">

var el = $Element("zoo");
el.className();
// "animal rest"

el.className("entertainment");
el.className();
// "entertainment"

</script>
 */
$Element.prototype.className = function(sClass) {
	var e = this._element;

	if (typeof sClass == "undefined") return e.className;
	e.className = sClass;

	return this;
};

/**
 * hasClass 메서드는 DOM 엘리먼트에서 특정한 클래스를 사용하고 있는지 확인한다.
 * @param {String} sClass 확인할 클래스 셀랙터
 * @return {Boolean} 클래스의 사용 여부.
 */
$Element.prototype.hasClass = function(sClass) {
	try{
		return (new RegExp("\\b"+sClass+"\\b")).test(this._element.className);
	}catch(e){ }
};

/**
 * addClass 메서드는 DOM 엘리먼트에 클래스를 추가한다.
 * @param {String} sClass 추가할 클래스 셀랙터
 * @return {$Element} 현재의 DOM 엘리먼트
 */
$Element.prototype.addClass = function(sClass) {
	try{
		var e = this._element;
		if (this.hasClass(sClass)) return this;
		e.className = (e.className+" "+sClass).replace(/^\s+/, "");
		return this;
	}catch(e){ }
};

/**
 * removeClass 메서드는 DOM 엘리먼트에서 특정 클래스를 제거한다.
 * @param {String} sClass 제거할 클래스 이름
 * @return {$Element} 현재의 DOM 엘리먼트
 */
$Element.prototype.removeClass = function(sClass) {
	var e = this._element;
	e.className = (e.className+" ").replace(sClass+" ", "").replace(/\s+$/, "");

	return this;
};

/**
 * toggle 메서드는 클래스 셀렉터을 토글한다. 하나의 클래스 셀렉터를 인수로 사용하면 해당 클래스만 토글하고, 두 개의 셀렉터를 인수로 사용하면 사용하고 있는 셀렉터는 지우고, 나머지 셀렉터를 추가한다.
 * @param {Object} sClass	클래스 셀렉터 이름. 하나의 클래스 셀렉터만 인수로 사용하면, DOM 엘리먼트에서 해당 클래스 셀렉터를 사용하는지 확인한다. 만약 셀렉터를 사용하고 있다면 해당 셀렉터를 지운다. 사용하고 있지 않다면 해당 셀렉터를 추가한다.
 * @param {Object} [sClass2]	클래스 셀렉터 이름.  두 개의 클래스 셀렉터를 인수로 사용하면, 두 셀렉터 중 사용하고 있는 셀랙터를 지우고 나머지 셀렉터를 추가한다.
 * @import core.$Element[hasClass,addClass,removeClass]
 * @example

<div id="naver" class="search highlight active">Naver</div>

<script>
var r = $Element("naver")
r.toggleClass("highlight");
// <div id="naver" class="search active">Naver</div>

r.toggleClass("highlight");
// <div id="naver" class="search highlight active">Naver</div>

r.toggleClass("active", "deactive");
// <div id="naver" class="search highlight deactive">Naver</div>

r.toggleClass("active", "deactive");
// <div id="naver" class="search highlight active">Naver</div>
</script>

 */
$Element.prototype.toggleClass = function(sClass, sClass2) {
	sClass2 = sClass2 || "";
	if (this.hasClass(sClass)) {
		this.removeClass(sClass);
		if (sClass2) this.addClass(sClass2);
	} else {
		this.addClass(sClass);
		if (sClass2) this.removeClass(sClass2);
	}

	return this;
};

/**
 * text 메서드는 DOM 엘리먼트의 텍스트 노드값을 가져오거나 설정한다. String 타입의 인수를 지정하면 인수의 값으로 텍스트 노드를 변경하고 변경한 결과를 리턴한다.
 * @param {String} sText 설정할 텍스트
 * @returns {String} 설정한 텍스트 값
 */
$Element.prototype.text = function(sText) {
	try{
		var prop = (typeof this._element.innerText != "undefined")?"innerText":"textContent";

		if (this.tag == "textarea" || this.tag == "input") prop = "value";

		if (typeof sText == "string") {
			try { this._element[prop] = sText; } catch(e) {
				return this.html(sText.replace(/&/g, '&amp;').replace(/</g, '&lt;'));
			}
			return this;
		}

		return this._element[prop];
	}catch(e){ }
};

/**
 * html 메서드는 DOM 엘리먼트의 내부 HTML(innerHTML)을 가져오거나 설정한다. String 타입의 인수를 지정하면 인수의 값으로 내부 HTML을 변경하고 변경한 결과를 리턴한다.
 * @param {String} sHTML 설정할 HTML 문자열
 * @return {String} 내부 HTML
 */
$Element.prototype.html = function(sHTML) {
	if (typeof sHTML == "string") {

		var oEl = this._element;
		var bBugAgent = $Agent().navigator().ie || ($Agent().navigator().firefox && !oEl.parentNode);

		if (bBugAgent) {

			/*
				IE 나 FireFox 의 일부 상황에서 SELECT 태그나 TABLE, TR, THEAD, TBODY 태그에 innerHTML 을 셋팅해도
				문제가 생기지 않도록 보완 - hooriza
			*/
			var sId = 'R' + new Date().getTime() + parseInt(Math.random() * 100000);
			var oDoc = oEl.ownerDocument || oEl.document || document;

			var oDummy;
			var sTag = oEl.tagName.toLowerCase();

			switch (sTag) {
			case 'select':
			case 'table':
				oDummy = $('<div>');
				oDummy.innerHTML = '<' + sTag + ' class="' + sId + '">' + sHTML + '</' + sTag + '>';
				break;

			case 'tr':
			case 'thead':
			case 'tbody':
				oDummy = $('<div>');
				oDummy.innerHTML = '<table><' + sTag + ' class="' + sId + '">' + sHTML + '</' + sTag + '></table>';
				break;

			default:
				oEl.innerHTML = sHTML;
				break;
			}

			if (oDummy) {

				var oFound;
				for (oFound = oDummy.firstChild; oFound; oFound = oFound.firstChild)
					if (oFound.className == sId) break;

				if (oFound) {

					for (var oChild; oChild = oEl.firstChild;) oChild.removeNode(true); // innerHTML = '';

					for (var oChild = oFound.firstChild; oChild; oChild = oFound.firstChild)
						oEl.appendChild(oChild);

					oDummy.removeNode && oDummy.removeNode(true);

				}

				oDummy = null;

			}

		} else {

			oEl.innerHTML = sHTML;

		}

		return this;

	}

	return this._element.innerHTML;
};

/**
 * evalScripts 메서드는 인자로 들어온 문자열 중에서 <script> 태그 안에 있는 내용을 파싱해서 eval 로 수행한다.
 * @param {String} sHTML <script> 태그가 포함된 HTML 문자열
 * @return {$Element} 현재의 DOM 엘리먼트
 */
$Element.prototype.evalScripts = function(sHTML) {

	var aJS = [];
    sHTML = sHTML.replace(new RegExp('<script(\\s[^>]+)*>(.*?)</'+'script>', 'gi'), function(_, _, sPart) { aJS.push(sPart); return ''; });
    eval(aJS.join('\n'));

    return this;

};

/**
 * outerHTML 메서드는 DOM 엘리먼트의 외부 HTML(outerHTML)을 반환한다.
 * @return {String} 외부 HTML
 */
$Element.prototype.outerHTML = function() {
	var e = this._element;
	if (typeof e.outerHTML != "undefined") return e.outerHTML;

	var div = $("<div>");
	var par = e.parentNode;

	par.insertBefore(div, e);
	div.style.display = "none";
	div.appendChild(e);

	var s = div.innerHTML;
	par.insertBefore(e, div);
	par.removeChild(div);

	return s;
};

/**
 * toString 메서드는 DOM 엘리먼트를 HTML 문자열로 변환하여 리턴한다. 리턴값은 outerHTML과 동일하다.
 * @return {String} 외부 HTML
 */
$Element.prototype.toString = $Element.prototype.outerHTML;

/**
 * append 메서드는 인수로 지정한 DOM 엘리먼트를 복사하여 $Element 객체의 마지막 자식 노드로 추가한다.
 * @param {$Element | HTMLElement | String} oElement	추가할 DOM 엘리먼트
 * @returns {Object} 새로운 자식노드를 추가한 $Element 객체
 */
$Element.prototype.append = function(oElement) {
	var o = $Element(oElement).$value();

	this._element.appendChild(o);

	return $Element(o);
};

/**
 * prepend 메서드는 인수로 지정한 DOM 엘리먼트를 복사하여 $Element 객체의 첫번째 자식 노드로 추가한다.
 * @param {$Element | HTMLElement | String} oElement	추가할 DOM 엘리먼트, 혹은 엘리먼트의 id
 * @returns {$Element} 새로운 자식 노드를 추가한 $Element 객체
 */
$Element.prototype.prepend = function(oElement) {
	var e = this._element;
	var o = $Element(oElement).$value();

	if (e.childNodes.length > 0) {
		e.insertBefore(o, e.childNodes[0]);
	} else {
		e.appendChild(o);
	}

	return $Element(o);
};

/**
 * replace 메서드는 $Element 객체의 노드를 인수로 지정한 DOM 엘리먼트로 대체한다.
 * @param {$Element | HTMLElement | String} oElement	대체할 DOM 엘리먼트, 혹은 엘리먼트의 id
 * @returns {$Element} DOM 엘리먼트가 대체된 $Element 객체
 */
$Element.prototype.replace = function(oElement) {
	var e = this._element;
	var o = $Element(oElement).$value();

	e.parentNode.insertBefore(o, e);
	e.parentNode.removeChild(e);

	return $Element(o);
};

/**
 * appendTo 메서드는 인수로 지정한 엘리먼트를 $Element 객체의 마지막 자식 노드로 추가한다.
 * @param {$Element | HTMLElement | String} oElement 추가할 DOM 엘리먼트, 혹은 엘리먼트의 id
 * @returns {$Element} 자식 노드를 추가한 $Element 객체
 */
$Element.prototype.appendTo = function(oElement) {
	var o = $Element(oElement).$value();

	o.appendChild(this._element);

	return this;
};

/**
 * prependTo 메서드는 인수로 지정한 엘리먼트를 $Element 객체의 첫번째 자식 노드로 추가한다.
 * @param {$Element | HTMLElement | String} oElement 추가할 DOM 엘리먼트, 혹은 엘리먼트의 id
 * @returns {$Element} 자식 노드를 추가한 $Element 객체
 */
$Element.prototype.prependTo = function(oElement) {
	var o = $Element(oElement).$value();

	if (o.childNodes.length > 0) {
		o.insertBefore(this._element, o.childNodes[0]);
	} else {
		o.appendChild(this._element);
	}

	return this;
};

/**
 * before 메서드는 인수로 지정한 엘리먼트를 $Element 객체 바로 앞에 삽입한다.
 * @param {$Element | HTMLElement | String} oElement 삽입할 DOM 엘리먼트, 혹은 엘리먼트의 id
 * @returns {Object} 삽입한 DOM 엘리먼트
 */
$Element.prototype.before = function(oElement) {
	var o = $Element(oElement).$value();

	this._element.parentNode.insertBefore(o, this._element);

	return $Element(o);
};

/**
 * after 메서드는 인수로 지정한 엘리먼트를 $Element 객체의 바로 뒤에 지정한 DOM 엘리먼트를 삽입한다.
 * @param {$Element | HTMLElement | String} oElement 삽입할 DOM 엘리먼트, 혹은 엘리먼트의 id
 * @returns {Object} 삽입한 DOM 엘리먼트
 */
$Element.prototype.after = function(oElement) {
	var o = this.before(oElement);
	o.before(this);

	return o;
};

/**
 * parent 메서드는 특정 노드의 부모 노드 (컨테이너 노드)를 검색한다.
 * @param {String} [pFunc] 상위 노드의 검색 조건을 지정한 콜백 함수. 여러 상위 노드를 조건 없이 검색하려면 pFunc를 null로 설정하고 limit에 상위 노드의 개수를 설정한다.
 * @param {Number} [limit] 탐색할 상위 노드의 개수.
 * @returns {$Element | Array} 부모 노드, 혹은 상위 노드의 배열. 부모 노드만 리턴할 경우 $Element 타입을, 상위 노드의 배열을 리턴할 경우 $Element의 배열을 리턴한다.
 */
$Element.prototype.parent = function(pFunc, limit) {
	var e = this._element;
	var a = [], p = null;

	if (typeof pFunc == "undefined") return $Element(e.parentNode);
	if (typeof limit == "undefined" || limit == 0) limit = -1;

	while (e.parentNode && limit-- != 0) {
		p = $Element(e.parentNode);
		if (e.parentNode == document.documentElement) break;
		if (!pFunc || (pFunc && pFunc(p))) a[a.length] = p;

		e = e.parentNode;
	}

	return a;
};

/**
 * child 메서드는 자식 노드를 검색한다.
 * @param {Function} [pFunc]  자식 노드의 검색 조건을 지정한 콜백 함수. 여러 하위 노드를 조건 없이 검색하려면 pFunc를 null로 설정하고 limit에 하위 노드의 개수를 설정한다.
 * @param {Number} [limit] 탐색할 하위 노드 개수
 * @returns {$Element | Array} 자식 노드, 혹은 조건에 맞는 자식 노드의 배열. 자식 노드 하나만 리턴할 경우 $Element 타입을, 자식 노드의 배열을 리턴할 경우 $Element의 배열을 리턴한다.
 */
$Element.prototype.child = function(pFunc, limit) {
	var e = this._element;
	var a = [], c = null, f = null;

	if (typeof pFunc == "undefined") return $A(e.childNodes).filter(function(v){ return v.nodeType == 1}).map(function(v){ return $Element(v) }).$value();
	if (typeof limit == "undefined" || limit == 0) limit = -1;

	(f = function(el, lim){
		var ch = null, o = null;

		for(var i=0; i < el.childNodes.length; i++) {
			ch = el.childNodes[i];
			if (ch.nodeType != 1) continue;

			o = $Element(el.childNodes[i]);
			if (!pFunc || (pFunc && pFunc(o))) a[a.length] = o;
			if (lim != 0) f(el.childNodes[i], lim-1);
		}
	})(e, limit-1);

	return a;
};

/**
 * prev 메서드는 특정 노드의 이전에 나오는 형제 노드를 검색한다.
 * @param {String} [pFunc] 이전 형제 노드의 검색 조건을 지정한 콜백 함수. pFunc를 생략하면 바로 전의 형제 노드를 리턴한다.
 * @returns {$Element | Array} pFunc의 조건에 맞는 형제 $Element의 배열, 혹은 바로 전의 형제 노드를 가리키는 $Element.
 */
$Element.prototype.prev = function(pFunc) {
	var e = this._element;
	var a = [];
	var b = (typeof pFunc == "undefined");

	if (!e) return b?$Element(null):a;

	do {
		e = e.previousSibling;

		if (!e || e.nodeType != 1) continue;
		if (b) return $Element(e);
		if (!pFunc || pFunc(e)) a[a.length] = $Element(e);
	} while(e);

	return b?$Element(e):a;
};

/**
 * next 메서드는 특정 노드의 다음에 나오는 형제 노드를 검색한다.
 * @param {String} pFunc 다음 형제 노드의 검색 조건을 지정한 콜백 함수. pFunc를 생략하면 바로 다음의 형제 노드를 가리키는 $Element를 리턴한다.
 * @returns {$Element | Array} pFunc의 조건에 맞는 형제 $Element의 배열, 혹은 바로 다음의 형제 노드를 가리키는 $Element.
 */
$Element.prototype.next = function(pFunc) {
	var e = this._element;
	var a = [];
	var b = (typeof pFunc == "undefined");

	if (!e) return b?$Element(null):a;

	do {
		e = e.nextSibling;

		if (!e || e.nodeType != 1) continue;
		if (b) return $Element(e);
		if (!pFunc || pFunc(e)) a[a.length] = $Element(e);
	} while(e);

	return b?$Element(e):a;
};

/**
 * first 메소드는 현재 노드의 처음 자식 노드를 반환한다. 엘리먼트 노드에 대해서만 적용된다.
 * @since 1.2.0
 * @returns {$Element} 마지막 자식 노드
 */
$Element.prototype.first = function() {
	var el = this._element.firstElementChild||this._element.firstChild;
	if (!el) return null;
	while(el && el.nodeType != 1) el = el.nextSibling;

	return el?$Element(el):null;
}

/**
 * last 메소드는 현재 노드의 처음 자식 노드를 반환한다. 엘리먼트 노드에 대해서만 적용된다.
 * @since 1.2.0
 * @returns {$Element} 마지막 자식 노드
 */
$Element.prototype.last = function() {
	var el = this._element.lastElementChild||this._element.lastChild;
	if (!el) return null;
	while(el && el.nodeType != 1) el = el.previousSibling;

	return el?$Element(el):null;
}

/**
 * isChildOf 메서드는 현재 노드가 지정한 노드의 자식 노드인지 확인한다.
 * @param {HTMLElement | String | $Element} element 부모 노드인지 확인할 DOM 엘리먼트, 혹은 DOM 엘리먼트의 고유한 id
 * @returns {Boolean} 현재 노드가 인수로 지정한 노드의 자식 노드이면 true, 그렇지 않으면 false를 리턴한다.
 */
$Element.prototype.isChildOf = function(element) {
	var e  = this._element;
	var el = $Element(element).$value();

	while(e && e.parentNode) {
		e = e.parentNode;
		if (e == el) return true;
	}
	return false;
};

/**
 * isParentOf 메서드는 현재 노드가 지정한 노드의 부모인지 확인한다.
 * @param {HTMLElement,$Element} element	자식 노드인지 확인할 DOM 엘리먼트, 혹은 DOM 엘리먼트의 고유한 id.
 * @returns {Boolean} 현재 노드가 인수로 지정한 노드의 부모이면 true, 그렇지 않으면 false
 */
$Element.prototype.isParentOf = function(element) {
	var el = $Element(element).$value();

	while(el && el.parentNode) {
		el = el.parentNode;
		if (this._element == el) return true;
	}
	return false;
};

/**
 * isEqual 메서드는 현재 객체와 인자로 주어진 객체가 같은 엘리먼트인지 확인한다.
 * @param {HTMLElement, $Element} element 비교할 엘리먼트 객체
 * @returns {Boolean} 같은 노드이면 true, 그렇지 않으면 false
 */
$Element.prototype.isEqual = function(element) {
	try {
		return (this._element === $Element(element).$value());
	} catch(e) {
		return false;
	}
};

/**
 * fireEvent 메서드는 DOM 엘리먼트에 이벤트를 발생시킨다.
 * @param {String} sEvent 실행할 이벤트 이름. on 접두사는 생략한다.
 * @return {$Element} 이벤트를 발생시킨 DOM 엘리먼트
 */
$Element.prototype.fireEvent = function(sEvent) {
	function IE(sEvent) {
		sEvent = (sEvent+"").toLowerCase();
		this._element.fireEvent("on"+sEvent);
		return this;
	};

	function DOM2(sEvent) {
		var sType = "HTMLEvents";
		sEvent = (sEvent+"").toLowerCase();

		if (sEvent == "click" || sEvent.indexOf("mouse") == 0) {
			sType = "MouseEvents";
			if (sEvent == "mousewheel") sEvent = "dommousescroll";
		} else if (sEvent.indexOf("key") == 0) {
			sType = "KeyEvents";
		}

		var evt   = document.createEvent(sType);

		evt.initEvent(sEvent, true, true);

		this._element.dispatchEvent(evt);
		return this;
	};

	$Element.prototype.fireEvent = (typeof this._element.dispatchEvent != "undefined")?DOM2:IE;

	return this.fireEvent(sEvent);
};

/**
 * empty 메서드는 현재 노드의 자식 노드를 모두 제거한다.
 * @return {$Element} 자식 노드를 모두 제거한 현재 노드
 */
$Element.prototype.empty = function() {
	this.html("");
	return this;
};

/**
 * leave 메서드는 현재 노드를 부모 노드에서 제거한다.
 * @return {$Element} 부모 노드에서 제거한 $Element 개체
 */
$Element.prototype.leave = function() {
	var e = this._element;

	if (e.parentNode) {
		e.parentNode.removeChild(e);
	}

	return this;
};

/**
 * wrap 메서드는 인수로 지정한 노드로 현재 노드를 감싼다.
 * @param {String | HTMLElement} wrapper 감쌀 노드 혹은 노드의 고유한 id 값
 * @return {$Element} 새로운 노드로 감싸진 $Element 개체
 */
$Element.prototype.wrap = function(wrapper) {
	var e = this._element;

	wrapper = $(wrapper);
	if (e.parentNode) {
		e.parentNode.insertBefore(wrapper, e);
	}
	wrapper.appendChild(e);

	return this;
};

/**
 * ellipsis 메서드는 DOM 엘리먼트의 텍스트 노드가 브라우저에 한 줄로 보이도록 길이를 조절한다. 길이를 조절한다면 stringTail에서 지정한 문자열을 텍스트 노드의 맨 끝에 붙인다.
 * @remark 이 메서드는 엘리먼트가 텍스트 노드만 포함한다고 가정한다. 따라서, 이외의 상황에서의 동작은 보장하지 않는다.
 * @remark 브라우저에서의 엘리먼트 너비를 측정하고 그 너비를 기준으로 텍스트 노드의 길이를 정하므로 엘리먼트는 반드시 보이는 상태이어야 한다.
 * @remark 화면에 전체 텍스트 노드가 보였다가 줄어드는 경우가 있다. 따라서, 엘리먼트에서 overflow:hidden 속성을 활용하면 이런 현상을 줄일 수 있다. ??
 * @param {String} stringTail 말줄임 표시자. 생락하면 말줄임표('...')를 사용한다.
 */
$Element.prototype.ellipsis = function(stringTail) {
	stringTail = stringTail || "...";

	var txt   = this.text();
	var len   = txt.length;
	var cur_h = this.height();
	var i     = 0;
	var h     = this.text('A').height();

	if (cur_h < h * 1.5) return this.text(txt);

	cur_h = h;
	while(cur_h < h * 1.5) {
		i += Math.max(Math.ceil((len - i)/2), 1);
		cur_h = this.text(txt.substring(0,i)+stringTail).height();
	}

	while(cur_h > h * 1.5) {
		i--;
		cur_h = this.text(txt.substring(0,i)+stringTail).height();
	}
};

/**
 * indexOf 메소드는 주어진 엘리먼트가 이 객체의 몇 번째 자식노드인지 반환한다. 인덱스는 0부터 시작하므로 첫번째 자식 노드를 인자로 전달하면 0을 반환한다. 주어진 엘리먼트가 이 객체의 자식이 아니라면, 다시 말해, 주어진 엘리먼트를 자식 노드에서 찾지 못했다면 -1을 반환한다.
 * @param {HTMLElement, $Element} element 검색할 엘리먼트
 * @since 1.2.0
 * @return {Number} 검색 결과
 */
$Element.prototype.indexOf = function(element) {
	try {
		var e = $Element(element).$value();
		var n = this._element.childNodes;
		var c = 0;
		var l = n.length;

		for (var i=0; i < l; i++) {
			if (n[i].nodeType != 1) continue;

			if (n[i] === e) return c;
			c++;
		}
	}catch(e){}

	return -1;
};

/**
 * @fileOverview $Fn의 생성자 및 메서드를 정의한 파일
 * @name function.js
 */

/**
 * 함수 객체를 리턴한다.
 * @name $Fn
 * @extends core
 * @class $Fn 클래스는 Javascript 함수의 레퍼 클래스이다.  ??
 * @constructor
 * @param {Function | String} func 함수 객체 혹은 함수의 인자를 지정한 문자열
 * @param {Object | String} thisObject 함수 객체가 다른 객체의 메서드일 때, 해당 객체도 같이 전달한다. 혹은 함수 몸체를 지정하는 문자열.
 * @return {$Fn} $Fn 객체
 * @see $Fn#toFunction
 * @example
var someObject = {
    func : function() {
       // code here
   }
}

var fn = $Fn(someObject.func, someObject);
 * @author Kim, Taegon
 */
function $Fn(func, thisObject) {
	var cl = arguments.callee;
	if (func instanceof cl) return func;
	if (!(this instanceof cl)) return new cl(func, thisObject);

	this._events = [];
	this._tmpElm = null;
	this._key    = null;

	if (typeof func == "function") {
		this._func = func;
		this._this = thisObject;
	} else if (typeof func == "string" && typeof thisObject == "string") {
		this._func = new Function(func, thisObject);
	}
}

/**
 * $value 메서드는 원래의 Function 객체를 반환한다.
 * @return {Function} 함수 객체
 */
$Fn.prototype.$value = function() {
	return this._func;
};

/**
 * bind 메서드는 객체와 메서드를 묶어 하나의 Function으로 반환한다.
 * @return {Function} 메소드로 묶인 Funciton 객체
 * @see $Fn#bindForEvent
 */
$Fn.prototype.bind = function() {
	var a = $A(arguments).$value();
	var f = this._func;
	var t = this._this;

	var b = function() {
		var args = $A(arguments).$value();

		// fix opera concat bug
		if (a.length) args = a.concat(args);

		return f.apply(t, args);
	};

	return b;
};

/**
 * bingForEvent는 객체와 메서드를 묶어 하나의 이벤트 핸들러 Function으로 반환한다.
 * @param {Element, ...} [elementN] 이벤트 객체와 함께 전달할 값
 * @see $Fn#bind
 * @see $Event
 */
$Fn.prototype.bindForEvent = function() {
	var a = arguments;
	var f = this._func;
	var t = this._this;
	var m = this._tmpElm || null;

	var b = function(e) {
		var args = $A(a);
		if (typeof e == "undefined") e = window.event;

		if (typeof e.currentTarget == "undefined") {
			e.currentTarget = m;
		}

		args.unshift($Event(e));

		return f.apply(t, args.$value());
	};

	return b;
};

/**
 * attach 메서드는 함수를 특정 엘리먼트의 이벤트 헨들러로 등록한다.
 * @remark 이벤트 이름은 on 접두어를 사용하지 않는다.
 * @remark 마우스 휠 스크롤 이벤트는 mousewheel 로 사용한다.
 * @param {Element} oElement 이벤트를 추가할 엘리먼트
 * @param {String} sEvent 추가할 이벤트의 종류
 * @see $Fn#detach
 */
$Fn.prototype.attach = function(oElement, sEvent) {
	var fn = null, l, ev = sEvent, el = oElement, ua = navigator.userAgent;

	if ((el instanceof Array) || ($A && (el instanceof $A) && (el=el.$value()))) {
		for(var i=0; i < el.length; i++) this.attach(el[i], ev);
		return this;
	}

	if (!el || !ev) return this;
	if (typeof el.$value == "function") el = el.$value();

	el = $(el);
	ev = ev.toLowerCase();

	this._tmpElm = el;
	fn = this.bindForEvent();
	this._tmpElm = null;

	if (typeof el.addEventListener != "undefined") {
		if (ev == "domready") ev = "DOMContentLoaded";
		else if (ev == "mousewheel" && ua.indexOf("WebKit") < 0) ev = "DOMMouseScroll";
		el.addEventListener(ev, fn, false);
	} else if (typeof el.attachEvent != "undefined") {
		if (ev == "domready") {
			$Fn._domready(el, fn);
			return this;
		} else {
			el.attachEvent("on"+ev, fn);
		}
	}

	if (!this._key) {
		this._key = "$"+$Fn.gc.count++;
		$Fn.gc.pool[this._key] = this;
	}

	this._events[this._events.length] = {element:el, event:sEvent.toLowerCase(), func:fn};

	return this;
};

/**
 * detach 메서드는 함수를 특정 엘리먼트의 이벤트에서 제거한다.
 * @remark 이벤트 이름은 on 접두어를 사용하지 않는다.
 * @remark 마우스 휠 스크롤 이벤트는 mousewheel 로 사용한다.
 * @param {Element} oElement 이벤트를 제거할 엘리먼트
 * @param {String} sEvent 제거할 이벤트의 이름
 * @see $Fn#attach
 */
$Fn.prototype.detach = function(oElement, sEvent) {
	var fn = null, l, el = oElement, ev = sEvent, ua = navigator.userAgent;

	if ((el instanceof Array) || ($A && (el instanceof $A) && (el=el.$value()))) {
		for(var i=0; i < el.length; i++) this.detach(el[i], ev);
		return this;
	}

	if (!el || !ev) return this;
	if ($Element && el instanceof $Element) el = el.$value();

	el = $(el);
	ev = ev.toLowerCase();

	var e = this._events;
	for(var i=0; i < e.length; i++) {
		if (e[i].element !== el || e[i].event !== ev) continue;

		fn = e[i].func;
		this._events = $A(this._events).refuse(e[i]).$value();
		break;
	}

	if (typeof el.removeEventListener != "undefined") {
		if (ev == "domready") ev = "DOMContentLoaded";
		else if (ev == "mousewheel" && ua.indexOf("WebKit") < 0) ev = "DOMMouseScroll";
		el.removeEventListener(ev, fn, false);
	} else if (typeof el.detachEvent != "undefined") {
		if (ev == "domready") {
			$Fn._domready.list = $Fn._domready.list.refuse(fn);
			return this;
		} else {
			el.detachEvent("on"+ev, fn);
		}
	}

	return this;
};

/**
 * delay 메서드는 지정한 시간 이후에 지정한 인수로 함수를 호출한다.
 * @param {Number} nSec 함수를 호출할 때까지 기다릴 시간.
 * @param {Array} args 함수를 호출할 때 사용할 인수. 여러 개의 인수를 전달해기 위해 배열을 사용한다.
 * @see $Fn#bind
 * @example
function popup_sum(a, b) {
    alert(a + b);
}

$Fn(popup_sum).delay(5, [3, 5]);
 */
$Fn.prototype.delay = function(nSec, args) {
	if (typeof args == "undefined") args = [];
	setTimeout(this.bind.apply(this, args), nSec*1000);

	return this;
};

/**
 * 메모리에서 이 객체를 사용한 참조를 모두 해제한다(직접 호출 금지).
 * @ignore
 */
$Fn.prototype.free = function() {
	var len = this._events.length;
	while(len > 0) {
		this.detach(this._events[--len].element, this._events[len].event);
	}
	try { delete $Fn.gc.pool[this._key]; }catch(e){};
};

/**
 * IE에서 domready(=DOMContentLoaded) 이벤트를 에뮬레이션한다.
 * @ignore
 */
$Fn._domready = function(doc, func) {
	if (typeof $Fn._domready.list == "undefined") {
		var f = null, l  = $Fn._domready.list = $A([func]);

		(function (){
			// use the trick by Diego Perini
			// http://javascript.nwbox.com/IEContentLoaded/
			try {
				doc.documentElement.doScroll("left");
			} catch(e) {
				setTimeout(arguments.callee, 0);
				return;
			}

			var evt = {
				type : "domready",
				target : doc,
				currentTarget : doc
			};

			while(f = l.shift()) f(evt);
		})();
	} else {
		$Fn._domready.list.push(func);
	}
};

/**
 * gc 메서드는 Window에서 벗어날 때, DOM Element에 할당된 이벤트 핸들러를 제거한다. ??
 * @see $Fn#gcinit
 */
$Fn.gc = function() {
	var p = $Fn.gc.pool;

	for(var key in p) {
		try { p[key].free(); }catch(e){ };
	}

	// delete all missing references
	$Fn.gc.pool = p = {};
};

$Fn.gc.count = 0;

$Fn.gc.pool = {};
if (typeof window != "undefined") {
	$Fn($Fn.gc).attach(window, "unload");
}

/**
 * @fileOverview $Event의 생성자 및 메서드를 정의한 파일
 * @name event.js
 */

/**
 * $Event 클래스는 Event 객체의 레퍼(Wrapper) 클래스이다.
 * 사용자는 $Event.element 메서드를 사용하여 이벤트가 실행된 객체를 알 수 있다.
 * @class JavaScript Core 이벤트 객체로부터 $Event 객체를 생성한다.
 * @param {Event} e	DOM Event 객체
 * @constructor
 * @author Kim, Taegon
 */
function $Event(e) {
	var cl = arguments.callee;
	if (e instanceof cl) return e;
	if (!(this instanceof cl)) return new cl(e);

	if (typeof e == "undefined") e = window.event;
	if (e === window.event && document.createEventObject) e = document.createEventObject(e);

	this._event = e;
	this._globalEvent = window.event;

	/** 이벤트의 종류 */
	this.type = e.type.toLowerCase();
	if (this.type == "dommousescroll") {
		this.type = "mousewheel";
	} else if (this.type == "DOMContentLoaded") {
		this.type = "domready";
	}

	this.canceled = false;

	/** 이벤트가 발생한 엘리먼트 */
	this.element = e.target || e.srcElement;
	/** 이벤트가 정의된 엘리먼트 */
	this.currentElement = e.currentTarget;
	/** 이벤트의 연관 엘리먼트 */
	this.relatedElement = null;

	if (typeof e.relatedTarget != "undefined") {
		this.relatedElement = e.relatedTarget;
	} else if(e.fromElement && e.toElement) {
		this.relatedElement = e[(this.type=="mouseout")?"toElement":"fromElement"];
	}
}

/**
 * 마우스 이벤트의 버튼, 휠 정보를 리턴한다.
 * @example
function eventHandler(evt) {
   var mouse = evt.mouse();

   mouse.delta;   // Number. 휠이 움직인 정도. 휠을 위로 굴리면 양수, 아래로 굴리면 음수.
   mouse.left;    // Boolean. 마우스 왼쪽 버튼을 눌렸으면 true, 아니면 false
   mouse.middle;  // Boolean. 마우스 중간 버튼을 눌렸으면 true, 아니면 false
   mouse.right;   // Boolean. 마우스 오른쪽 버튼을 눌렸으면 true, 아니면 false
}
 * @return {Object} 마우스 정보를 가지는 객체. 리턴한 객체의 속성은 예제를 참조한다.
 */
$Event.prototype.mouse = function() {
	var e    = this._event;
	var delta = 0;
	var left  = (e.which&&e.button==0)||!!(e.button&1);
	var mid   = (e.which&&e.button==1)||!!(e.button&4);
	var right = (e.which&&e.button==2)||!!(e.button&2);
	var ret   = {};

	if (e.wheelDelta) {
		delta = e.wheelDelta / 120;
	} else if (e.detail) {
		delta = -e.detail / 3;
	}

	ret = {
		delta  : delta,
		left   : left,
		middle : mid,
		right  : right
	};

	// replace method
	this.mouse = function(){ return ret };

	return ret;
};

/**
 * 키보드 이벤트 정보를 리턴한다.
 * @example
function eventHandler(evt) {
   var key = evt.key();

   key.keyCode; // Number. 눌린 키보드의 키코드
   key.alt;     // Boolean. Alt 키를 눌렸으면 true.
   key.ctrl;    // Boolean. Ctrl 키를 눌렸으면 true.
   key.meta;    // Boolean. Meta 키를 눌렸으면 true. Meta키는 맥의 커맨드키를 검출할 때 사용합니다.
   key.shift;   // Boolean. Shift 키를 눌렸으면 true.
   key.up;      // Boolean. 위쪽 화살표 키를 눌렸으면 true.
   key.down;    // Boolean. 아래쪽 화살표 키를 눌렸으면 true.
   key.left;    // Boolean. 왼쪽 화살표 키를 눌렸으면 true.
   key.right;   // Boolean. 오른쪽 화살표 키를 눌렸으면 true.
   key.enter;   // Boolean. 리턴키를 눌렀으면 true
   }
}
 * @return {Object} 키보드 이벤트의 눌린 키값. 객체의 속성은 예제를 참조한다.
 */
$Event.prototype.key = function() {
	var e     = this._event;
	var k     = e.keyCode || e.charCode;
	var ret   = {
		keyCode : k,
		alt     : e.altKey,
		ctrl    : e.ctrlKey,
		meta    : e.metaKey,
		shift   : e.shiftKey,
		up      : (k == 38),
		down    : (k == 40),
		left    : (k == 37),
		right   : (k == 39),
		enter   : (k == 13)
	};

	this.key = function(){ return ret };

	return ret;
};

/**
 * 마우스 커서의 위치 정보를 리턴한다.
 * @param {Boolean} bGetOffset 현재 엘리먼트에 대한 마우스 커서의 상대위치인 offsetX, offsetY를 구할 것인지의 여부. true면 값을 구한다(offsetX, offsetY는 1.2.0버전부터 추가).
 * @example
function eventHandler(evt) {
   var pos = evt.pos();

   pos.clientX;  // Number. 현재 화면에 대한 X 좌표
   pos.clientY;  // Number. 현재 화면에 대한 Y 좌표
   pos.pageX;    // Number. 문서 전체에 대한 X 좌표
   pos.pageY;    // Number. 문서 전체에 대한 Y 좌표
   pos.layerX;   // Number. <b>deprecated.</b> 이벤트가 발생한 객체로부터의 상대적인 X 좌표
   pos.layerY;   // Number. <b>deprecated.</b> 이벤트가 발생한 객체로부터의 상대적인 Y 좌표
   pos.offsetX;  // Number. 현재 엘리먼트에 대한 마우스 커서의 상대적인 X좌표 (1.2.0 이상)
   pos.offsetY;  // Number. 현재 엘리먼트에 대한 마우스 커서의 상대적인 Y좌표 (1.2.0 이상)

}
 * @return {Object} 마우스 커서의 위치 정보. 객체의 속성은 예제를 참조한다.
 * @remark layerX, layerY는 차후 지원하지 않을(deprecated) 예정입니다.
 */
$Event.prototype.pos = function(bGetOffset) {
	var e   = this._event;
	var b   = (this.element.ownerDocument||document).body;
	var de  = (this.element.ownerDocument||document).documentElement;
	var pos = [b.scrollLeft || de.scrollLeft, b.scrollTop || de.scrollTop];
	var ret = {
		clientX : e.clientX,
		clientY : e.clientY,
		pageX   : 'pageX' in e ? e.pageX : e.clientX+pos[0]-b.clientLeft,
		pageY   : 'pageY' in e ? e.pageY : e.clientY+pos[1]-b.clientTop,
		layerX  : 'offsetX' in e ? e.offsetX : e.layerX - 1,
		layerY  : 'offsetY' in e ? e.offsetY : e.layerY - 1
	};

	// 오프셋을 구하는 메소드의 비용이 크므로, 요청시에만 구하도록 한다.
	if (bGetOffset) {
		var offset = $Element(this.element).offset();
		ret.offsetX = ret.pageX - offset.left;
		ret.offsetY = ret.pageY - offset.top;
	}

	return ret;
};

/**
 * 이벤트를 종료한다.
 * @remark 버블링은 특정 DOM 엘리먼트에서 이벤트가 발생했을 때 이벤트가 상위 노드로 전파되는 현상이다. 예를 들어, div 객체를 클릭할 때 div와 함께 상위 엘리먼트인 document에도 onclick 이벤트가 발생한다. stop() 메소드는 지정한 객체에서만 이벤트가 발생하도록 버블링을 차단한다.
 * @example
function eventHandler(evt) {
   someCode();
   evt.stop();
}
 * @example
// 디폴트 이벤트만 중지시키고 싶을 때 (1.1.3버전 이상)
function stopDefaultOnly(evt) {
	// Here is some code to execute

	// Stop default event only
	evt.stop($Event.CANCEL_DEFAULT);
}
 * @return {$Event} 이벤트 객체.
 * @param {Number} nCancel 이벤트를 정지할 때 버블링, 디폴트 액션 중 정지시킬 값을 지정한다. 기본값은 $Event.CANCEL_ALL 이다(1.1.3 버전 이상).
 */
$Event.prototype.stop = function(nCancel) {
	nCancel = nCancel || $Event.CANCEL_ALL;

	var e = (window.event && window.event == this._globalEvent)?this._globalEvent:this._event;
	var b = !!(nCancel & $Event.CANCEL_BUBBLE); // stop bubbling
	var d = !!(nCancel & $Event.CANCEL_DEFAULT); // stop default event

	this.canceled = true;

	if (typeof e.preventDefault != "undefined" && d) e.preventDefault();
	if (typeof e.stopPropagation != "undefined" && b) e.stopPropagation();

	if(d) e.returnValue = false;
	if(b) e.cancelBubble = true;

	return this;
};

/**
 * $Event#stop 메서드에서 버블링을 중지시킨다.
 * @final
 */
$Event.CANCEL_BUBBLE = 1;

/**
 * $Event#stop 메서드에서 디폴트 액션을 중지시킨다.
 * @final
 */
$Event.CANCEL_DEFAULT = 2;

/**
 * $Event#stop 메서드에서 버블리과 디폴트 액션 모두 중지시킨다.
 * @final
 */
$Event.CANCEL_ALL = 3;


/**
 * @fileOverview $Agent의 생성자 및 메서드를 정의한 파일
 */

/**
 * Agent 객체를 반환한다. Agent 객체는 브라우저와 OS에 대한 정보를 알 수 있도록 한다.
 * @class Agent 객체는 운영체제, 브라우저를 비롯한 사용자 시스템의 정보를 가져온다.
 * @constructor
 * @author Kim, Taegon
 */
function $Agent() {
	var cl = arguments.callee;
	var cc = cl._cached;

	if (cc) return cc;
	if (!(this instanceof cl)) return new cl;
	if (!cc) cl._cached = this;
}

/**
 * navigator 메서드는 웹 브라우저의 정보 객체를 리턴한다.
 * @return {Object} 웹 브라우저 정보를 저장하는 객체.
 * object는 브라우저 이름과 버전을 속성으로 가진다. 브라우저 이름은 영어 소문자로 표시하며, 사용자의 브라우저와 일치하는 브라우저 이름은 true를 가진다.
 * @example
oAgent = $Agent().navigator(); // 사용자가 파이어폭스 3를 사용한다고 가정한다.
oAgent.camino  // false
oAgent.firefox  // true
oAgent.konqueror // false
oAgent.mozilla  //true
oAgent.netscape  // false
oAgent.omniweb  //false
oAgent.opera  //false
oAgent.safari  //false
oAgent.version  //3
*/

$Agent.prototype.navigator = function() {
	var info = new Object;
	var ver  = -1;
	var u    = navigator.userAgent;
	var v    = navigator.vendor || "";

	function f(s,h){ return ((h||"").indexOf(s) > -1) };

	info.opera     = (typeof window.opera != "undefined") || f("Opera",u);
	info.ie        = !info.opera && f("MSIE",u);
	info.chrome    = f("Chrome",u);
	info.safari    = !info.chrome && f("Apple",v);
	info.mozilla   = f("Gecko",u) && !info.safari && !info.chrome;
	info.firefox   = f("Firefox",u);
	info.camino    = f("Camino",v);
	info.netscape  = f("Netscape",u);
	info.omniweb   = f("OmniWeb",u);
	info.icab      = f("iCab",v);
	info.konqueror = f("KDE",v);

	try {
		if (info.ie) {
			ver = u.match(/(?:MSIE) ([0-9.]+)/)[1];
		} else if (info.firefox||info.opera||info.omniweb) {
			ver = u.match(/(?:Firefox|Opera|OmniWeb)\/([0-9.]+)/)[1];
		} else if (info.mozilla) {
			ver = u.match(/rv:([0-9.]+)/)[1];
		} else if (info.safari) {
			ver = parseFloat(u.match(/Safari\/([0-9.]+)/)[1]);
			if (ver == 100) {
				ver = 1.1;
			} else {
				ver = [1.0,1.2,-1,1.3,2.0,3.0][Math.floor(ver/100)];
			}
		} else if (info.icab) {
			ver = u.match(/iCab[ \/]([0-9.]+)/)[1];
		} else if (info.chrome) {
			ver = u.match(/Chrome[ \/]([0-9.]+)/)[1];
		}

		info.version = parseFloat(ver);
		if (isNaN(info.version)) info.version = -1;
	} catch(e) {
		info.version = -1;
	}

	$Agent.prototype.navigator = function() {
		return info;
	};

	return info;
};

/**
 * os 메서드는 운영체제에 대한 정보 객체를 리턴한다.
 * @return {Object} 운영체제 정보 객체. 운영체제의 영문 이름을 속성으로 가지며, 사용자가 사용하는 운영체제와 동일한 이름의 속성은 true를 가진다.
 * @example
oOS = $Agent().os();  // 사용자의 운영체제가 Windows XP라고 가정한다.
oOS.linux  // false
oOS.mac  // false
oOS.vista  // false
oOS.win  // false
oOS.win2000  // false
oOS.winxp  // true
oOS.xpsp2  // false
 */
$Agent.prototype.os = function() {
	var info = new Object;
	var u    = navigator.userAgent;
	var p    = navigator.platform;
	var f    = function(s,h){ return (h.indexOf(s) > -1) };

	info.win     = f("Win",p);
	info.mac     = f("Mac",p);
	info.linux   = f("Linux",p);
	info.win2000 = info.win && (f("NT 5.0",p) || f("2000",p));
	info.winxp   = info.win && (f("NT 5.1",p) || f("Win32",p));
	info.xpsp2   = info.winxp && (f("SV1",u) || f("MSIE 7",u));
	info.vista   = f("NT 6.0",p);

	$Agent.prototype.os = function() {
		return info;
	};

	return info;
};

/**
 * flash 메서드는 플래시에 대한 정보 객체를 리턴한다.
 * @return {Object} Flash 정보 객체. object.installed는 플래시 플레이어 설치 여부를 boolean 값으로 가지고 object.version은 플래시 플레이어의 버전을 가진다. 플래시 버전을 탐지하지 못하면 flash.version은 -1의 값을 가진다.
 * @example
var oFlash = $Agent.flash();
oFlash.installed  // 플래시 플레이어를 설치했다면 true
oFlash.version  // 플래시 플레이어의 버전.
 */
$Agent.prototype.flash = function() {
	var info = new Object;
	var p    = navigator.plugins;
	var m    = navigator.mimeTypes;
	var f    = null;

	info.installed = false;
	info.version   = -1;

	if (typeof p != "undefined" && p.length) {
		f = p["Shockwave Flash"];
		if (f) {
			info.installed = true;
			if (f.description) {
				info.version = parseFloat(f.description.match(/[0-9.]+/)[0]);
			}
		}

		if (p["Shockwave Flash 2.0"]) {
			info.installed = true;
			info.version   = 2;
		}
	} else if (typeof m != "undefined" && m.length) {
		f = m["application/x-shockwave-flash"];
		info.installed = (f && f.enabledPlugin);
	} else {
		for(var i=10; i > 1; i--) {
			try {
				f = new ActiveXObject("ShockwaveFlash.ShockwaveFlash."+i);

				info.installed = true;
				info.version   = i;
				break;
			} catch(e) {}
		}
	}

	$Agent.prototype.info = function() {
		return info;
	};

	return info;
};

/**
 * silverlight 메서드는 실버라이트(SilverLight)에 대한 정보 객체를 리턴한다.
 * @returns {Object} Silverlight 정보 객체. object.installed은 실버라이트 플레이어 설치 여부를 boolean 값으로 가지고 object.version은 실버라이트 플레이어의 버전을 가진다. 플레이어의 버전을 탐지하지 못하면 object.version의 값은 -1이 된다.
 * @example
var oSilver = $Agent.silverlight();
oSilver.installed  // SilverLight 플레이어를 설치했다면 true
oSilver.version  // SilverLight 플레이어의 버전.
 */
$Agent.prototype.silverlight = function() {
	var info = new Object;
	var p    = navigator.plugins;
	var s    = null;

	info.installed = false;
	info.version   = -1;

	if (typeof p != "undefined" && p.length) {
		s = p["Silverlight Plug-In"];
		if (s) {
			info.installed = true;
			info.version = parseInt(s.description.split(".")[0]);
			if (s.description == "1.0.30226.2") info.version = 2;
		}
	} else {
		try {
			s = new ActiveXObject("AgControl.AgControl");
			info.installed = true;

			if (s.isVersionSupported("2.0")) {
				info.version = 2;
			} else if (s.isVersionSupported("1.0")) {
				info.version = 1;
			}
		} catch(e) {}
	}

	$Agent.prototype.silverlight = function() {
		return info;
	};

	return info;
};


/**
 * @fileOverview $Ajax의 생성자 및 메서드를 정의한 파일
 * @name Ajax.js
 */

/**
 * $Ajax 객체를 생성 및 리턴한다.
 * @extends core
 * @class $Ajax는 ajax 요청과 응답을 처리한다.
 * @param {String}   url			  서버측 URL. URL이 "http://" 혹은 "https://로 시작하면 자동으로 Cross-domain ajax를 호출한다. Cross-domain ajax에 대한 자세한 정보는 http://wiki.nhncorp.com/display/lsuit/Jindo2+Cross-domain+Ajax 를 참조한다.
 * @param {Object}   option		      HTTP 요청에 사용할 매개변수.
 * @param {String}   option.type	  요청 타입. "xhr", "iframe", "jsonp" 중에 선택할 수 있다. 하위 호환성을 위해 "post" 혹은 "get"을 지원하며, "post", "get"으로 선택하면 type은 자동적으로 "xhr"이 된다. 기본값은 "xhr"이다.
 * @param {String}   option.method	  HTTP 요청에 사용할 방식. "post" 혹은 "get"을 이용한다. 단, 특정 type에서 요청 방식이 강제되는 경우가 있다. 예를 들어 type을 jsonp으로 설정했다면 method는 강제적으로 get이 된다. 기본값은 "get"이다.
 * @param {Number}   option.timeout   요청이 일정시간 동안 요청이 완료되지 않으면 중지시킨다. 단위는 초이다.
 * @param {Function} option.onload	  요청이 완료되면 실행할 콜백 함수
 * @param {Function} option.ontimeout 타임아웃이 되었을 때 실행할 콜백 함수
 * @param {String} option.proxy		  Ajax를 실행하는 현재 문서와 동일한 도메인에 존재하는 proxy 파일의 위치. Jindo와 함께 배포하는 ajax_local_callback.html을 사용한다. type을 iframe으로 설정했다면 반드시 proxy를 설정해야 한다.
 * @param {String} option.jsonp_charset	type을 jsonp로 설정했을 때 요청에 사용할 인코딩 방식. 보내는 문자셋은 항상 UTF-8이다(0.4.2 부터 지원).
 * @constructor
 * @author Kim, Taegon
 */
function $Ajax(url, option) {
	var cl = arguments.callee;
	if (!(this instanceof cl)) return new cl(url, option);

	function _getXHR() {
		if (window.XMLHttpRequest) {
			return new XMLHttpRequest();
		} else if (ActiveXObject) {
			try { return new ActiveXObject('MSXML2.XMLHTTP'); }
			catch(e) { return new ActiveXObject('Microsoft.XMLHTTP'); }
			return null;
		}
	}

	var loc    = location.toString();
	var domain = '';
	try { domain = loc.match(/^https?:\/\/([a-z0-9_\-\.]+)/i)[1]; } catch(e) {}

	this._url = url;
	this._options  = new Object;
	this._headers  = new Object;
	this._options = {
		type   :"xhr",
		method :"post",
		proxy  :"",
		timeout:0,
		onload :function(req){},
		ontimeout:function(req){},
		jsonp_charset : "utf-8"
	};

	this.option(option);

	var _opt = this._options;

	_opt.type   = _opt.type.toLowerCase();
	_opt.method = _opt.method.toLowerCase();

	if (typeof window.__jindo2_callback == "undefined") {
		window.__jindo2_callback = new Array();
	}

	switch (_opt.type) {
		case "get":
		case "post":
			_opt.method = _opt.type;
			_opt.type   = "xhr";
		case "xhr":
			this._request = _getXHR();
			break;
		case "flash":
			this._request = new $Ajax.SWFRequest();
			break;
		case "jsonp":
			_opt.method = "get";
			this._request = new $Ajax.JSONPRequest();
			this._request.charset = _opt.jsonp_charset;
			break;
		case "iframe":
			this._request = new $Ajax.FrameRequest();
			this._request._proxy = _opt.proxy;
			break;
	}
};

/**
 * @ignore
 */
$Ajax.prototype._onload = function() {
	if (this._request.readyState == 4) {
		this._options.onload($Ajax.Response(this._request));
	}
};

/**
 * request 메서드는 ajax 요청을 전송한다. ajax 요청에 사용할 매개변수는 생성자에서 설정하거나 option 메서드에서 변경할 수 있다.  요청 타입이 'flash'이면 이 메소드를 실행하기 전에 body 태그 내부에서 $Ajax.SWFRequest.write() 명령어를 반드시 실행해야 한다.
 * @param {Object} oData 서버로 전송할 데이터.
 * @example
var ajax = $Ajax("http://www.remote.com", {
   onload : function(res) {
      // onload 핸들러
   }
});

ajax.request( {key1:"value1", key2:"value2"} );
 */
$Ajax.prototype.request = function(oData) {
	var t   = this;
	var req = this._request;
	var opt = this._options;
	var data, v,a = [], data = "";
	var _timer = null;

	if (typeof oData == "undefined" || !oData) {
		data = null;
	} else {
		for(var k in oData) {
			v = oData[k];
			if (typeof v == "function") v = v();
			a[a.length] = k+"="+encodeURIComponent(v);
		}
		data = a.join("&");
	}

	req.open(opt.method.toUpperCase(), this._url, true);
	req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
	req.setRequestHeader("charset", "utf-8");
	for(var x in this._headers) {
		if (typeof this._headers[x] == "function") continue;
		req.setRequestHeader(x, String(this._headers[x]));
	}

	if (typeof req.onload != "undefined") {
		req.onload = function(rq){ clearTimeout(_timer); t._onload(rq) };
	} else {
		req.onreadystatechange = function(rq){ clearTimeout(_timer); t._onload(rq) };
	}

	if (opt.timeout > 0) {
		_timer = setTimeout(function(){ try{ req.abort(); }catch(e){}; opt.ontimeout(req); }, opt.timeout * 1000);
	}

	req.send(data);

	return this;
};

/**
 * abort 메서드는 서버로 전송한 ajax 요청을 취소한다. abort 메서드를 사용하면 네트워크 연결을 닫으며 대기중이던 요청은 취소된다. ajax 요청의 응답이 너무 오래 걸려서 취소하고 싶을 경우 abort 메서드를 사용할 수 있다.
 * @return 전송을 취소한 $Ajax 객체
 * @example
var ajax = $Ajax("http://www.remote.com", {
   onload : function(res) {
      // onload 핸들러
   }
}).request( {key1:"value1", key2:"value2"} );

function stopRequest() {
    ajax.abort();
}
 */
$Ajax.prototype.abort = function() {
	this._request.abort();

	return this;
};

/**
 * option 메서드는 ajax 요청에서 사용할 매개변수를 가져오거나 설정한다.
 * 매개변수를 설정하려면 매개 변수의 이름과 값, 혹은 매개변수의 이름과 값을 원소로 가지는 하나의 객체를 인수로 사용한다.
 * 만약 여러 개의 원소를 가진 객체를 사용하면 두 개 이상의 매개변수를 한 번에 설정할 수 있다.
 * @param {string|object} name 매개변수로 설정할 객체, 혹은 가지고 올 매개변수의 이름
 * @param {String}  [value] 새로 설정할 매개변수의 값
 * @return {String|$Ajax}  매개변수 값을 가져올 때는 문자열을, 매개변수 값을 설정할 때는 $Ajax 객체를 리턴한다.
 * @example
var request_type = ajax.option("type");
// ajax의 type 매개변수 값을 리턴한다.

ajax.option("method", "post");
// ajax의 method 매개변수를 post로 초기화한다.

ajax.option( { timeout : 0, onload : handler_func } );
// ajax의 timeout, onload 매개변수를 각각 0와 handler_func로 초기화한다.
 */
$Ajax.prototype.option = function(name, value) {
	if (typeof name == "undefined") return "";
	if (typeof name == "string") {
		if (typeof value == "undefined") return this._options[name];
		this._options[name] = value;
		return this;
	}

	try { for(var x in name) this._options[x] = name[x] } catch(e) {};

	return this;
};

/**
 * header 메서드는 ajax 요청에서 사용할 HTTP 요청 헤더를 가져오거나 설정한다.
 * 헤더를 설정하려면 헤더의 이름과 값, 혹은 헤더의 이름과 값을 원소로 가지는 하나의 객체를 인수로 사용한다.
 * 만약 여러 개의 원소를 가진 객체를 사용하면 두 개 이상의 헤더를 한 번에 설정할 수 있다.
 * 매개 변수의 값을 가져오려면 단순히 가지고 올 매개변수의 이름을 지정한다.
 * @param {String} name 가지고 오거나 설정할 헤더 이름
 * @param {Value} [value] 값을 설정할 헤더 값
 * @return {String|$Ajax} 설정된 헤더값 혹은 $Ajax 객체
 * @example
var customheader = ajax.header("myHeader");
// HTTP 요청 헤더에서 myHeader의 값

ajax.header( "myHeader", "someValue" );
// HTTP 요청 헤더의 myHeader = someValue

ajax.header( { anotherHeader : "someValue2" } );
// HTTP 요청 헤더의 anotherHeader = someValue2
 */
$Ajax.prototype.header = function(name, value) {
	if (typeof name == "undefined") return "";
	if (typeof name == "string") {
		if (typeof value == "undefined") return this._headers[name];
		this._headers[name] = value;
		return this;
	}

	try { for(var x in name) this._headers[x] = name[x] } catch(e) {};

	return this;
};

/**
 * Ajax 응답 객체
 * @class
 * @constructor
 * @param {Object} req 요청 객체
 */
$Ajax.Response  = function(req) {
	if (this === $Ajax) return new $Ajax.Response(req);
	this._response = req;
};

/**
 * 응답을 XML 객체로 반환한다.
 * @return {Object} 응답 XML 객체. XHR의 responseXML과 유사하다.
 */
$Ajax.Response.prototype.xml = function() {
	return this._response.responseXML;
};

/**
 * 응답을 문자열로 반환한다.
 * @return {String} 응답 문자열. XHR의 responseText와 유사하다.
 */
$Ajax.Response.prototype.text = function() {
	return this._response.responseText;
};

/**
 * 응답을 Json객체로 반환한다.
 * @return {Object} 응답 JSON 객체. 응답 문자열을 자동으로 JSON 객체로 변환 후 반환한다. 변환 과정에서 오류가 발생하면 빈 객체를 반환한다.
 */
$Ajax.Response.prototype.json = function() {
	if (this._response.responseJSON) {
		return this._response.responseJSON;
	} else if (this._response.responseText) {
		try {
			return eval("("+this._response.responseText+")");
		} catch(e) {
			return {};
		}
	}

	return {};
};

/**
 * 응답헤더를 가져온다. 인자를 전달하지 않으면 모든 헤더를 반환한다.
 * @param {String} name 가져올 응답헤더의 이름
 * @return {String|Object} 인자가 있을 때는 해당하는 헤더 값을, 그렇지 않으면 전체 헤더를 반환한다.
 */
$Ajax.Response.prototype.header = function(name) {
	if (typeof name == "string") return this._response.getResponseHeader(name);
	return this._response.getAllResponseHeaders();
};

/**
 * @class
 */
$Ajax.RequestBase = $Class({
	_headers : {},
	_respHeaders : {},
	_respHeaderString : "",
	responseXML  : null,
	responseJSON : null,
	responseText : "",
	$init  : function(){},
	onload : function(){},
	abort  : function(){},
	open   : function(){},
	send   : function(){},
	setRequestHeader  : function(sName, sValue) {
		this._headers[sName] = sValue;
	},
	getResponseHeader : function(sName) {
		return this._respHeaders[sName] || "";
	},
	getAllResponseHeaders : function() {
		return this._respHeaderString;
	},
	_getCallbackInfo : function() {
		var id = "";

		do {
			id = "$"+Math.floor(Math.random()*10000);
		} while(window.__jindo2_callback[id]);

		return {id:id,name:"window.__jindo2_callback."+id};
	}
});

/**
 * @class
 */
$Ajax.JSONPRequest = $Class({
	charset : "utf-8",
	_script : null,
	_callback : function(data) {
		var self = this;

		this.readyState = 4;
		this.responseJSON = data;
		this.onload(this);

		setTimeout(function(){ self.abort() }, 10);
	},
	abort : function() {
		if (this._script) {
			try { this._script.parentNode.removeChild(this._script) }catch(e){};
		}
	},
	open  : function(method, url) {
		this.responseJSON = null;

		this._url = url;
	},
	send  : function(data) {
		var t    = this;
		var info = this._getCallbackInfo();
		var head = document.getElementsByTagName("head")[0];

		this._script = $("<script>");
		this._script.type    = "text/javascript";
		this._script.charset = this.charset;

		if (head) {
			head.appendChild(this._script);
		} else if (document.body) {
			document.body.appendChild(this._script);
		}

		window.__jindo2_callback[info.id] = function(data){
			try {
				t._callback(data);
			} finally {
				delete window.__jindo2_callback[info.id];
			}
		};

		this._script.src = this._url+"?_callback="+info.name+"&"+data;
	}
}).extend($Ajax.RequestBase);

/**
 * @class
 */
$Ajax.SWFRequest = $Class({
	_callback : function(success, data){
		this.readyState = 4;
		if (success) {
			if (typeof data == "string") {
				try {
					this.responseText = decodeURIComponent(data);
				} catch(e) {}
			}
			this.onload(this);
		}
	},
	open : function(method, url) {
		var re  = /https?:\/\/([a-z0-9_\-\.]+)/i;

		this._url    = url;
		this._method = method;
	},
	send : function(data) {
		this.responseXML  = false;
		this.responseText = "";

		var t    = this;
		var dat  = {};
		var info = this._getCallbackInfo();
		var swf  = window.document[$Ajax.SWFRequest._tmpId];
		var header = [];

		function f(arg) {
			switch(typeof arg){
				case "string":
					return '"'+arg.replace(/\"/g, '\\"')+'"';
					break;
				case "number":
					return arg;
					break;
				case "object":
					var ret = "", arr = [];
					if (arg instanceof Array) {
						for(var i=0; i < arg.length; i++) {
							arr[i] = f(arg[i]);
						}
						ret = "["+arr.join(",")+"]";
					} else {
						for(var x in arg) {
							arr[arr.length] = f(x)+":"+f(arg[x]);
						}
						ret = "{"+arr.join(",")+"}";
					}
					return ret;
				default:
					return '""';
			}
		}

		data = (data || "").split("&");

		for(var i=0; i < data.length; i++) {
			pos = data[i].indexOf("=");
			key = data[i].substring(0,pos);
			val = data[i].substring(pos+1);

			dat[key] = decodeURIComponent(val);
		}

		window.__jindo2_callback[info.id] = function(success, data){
			try {
				t._callback(success, data);
			} finally {
				delete window.__jindo2_callback[info.id];
			}
		};

		swf.requestViaFlash(f({
			url  : this._url,
			type : this._method,
			data : dat,
			charset  : "UTF-8",
			callback : info.name,
			headers_json : this._headers
		}));
	}
}).extend($Ajax.RequestBase);

/**
 * $Ajax.SWFRequest.write는 요청 타입이 flash일 때, request 메소드가 호출되기 전 반드시 한 번 실행해야 한다(두 번 이상 실행해도 문제가 발생한다). 이 명령어가 호출되면 통신을 위한 swf 객체를 문서 내에 추가된다.
 * @param {String} [swf_path] 통신을 담당할 swf 파일의 경로. 기본값은 "./ajax.swf" 이다.
 */
$Ajax.SWFRequest.write = function(swf_path) {
	if(typeof swf_path == "undefined") swf_path = "./ajax.swf";
	$Ajax.SWFRequest._tmpId = 'tmpSwf'+(new Date).getMilliseconds()+Math.floor(Math.random()*100000);

	document.write('<div style="position:absolute;top:-1000px;left:-1000px"><object id="'+$Ajax.SWFRequest._tmpId+'" width="1" height="1" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0"><param name="movie" value="'+swf_path+'"><param name = "allowScriptAccess" value = "always" /><embed name="'+$Ajax.SWFRequest._tmpId+'" src="'+swf_path+'" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" width="1" height="1" allowScriptAccess="always" swLiveConnect="true"></embed></object></div>');
};

/**
 * @class
 */
$Ajax.FrameRequest = $Class({
	_frame  : null,
	_proxy  : "",
	_domain : "",
	_callback : function(id, data, header) {
		var self = this;

		this.readyState   = 4;
		this.responseText = data;

		this._respHeaderString = header;
		header.replace(/^([\w\-]+)\s*:\s*(.+)$/m, function($0,$1,$2) {
			self._respHeaders[$1] = $2;
		});

		this.onload(this);

		setTimeout(function(){ self.abort() }, 10);
	},
	abort : function() {
		if (this._frame) {
			try {
				this._frame.parentNode.removeChild(this._frame);
			} catch(e) {}
		}
	},
	open : function(method, url) {
		var re  = /https?:\/\/([a-z0-9_\-\.]+)/i;
		var dom = document.location.toString().match(re);

		this._method = method;
		this._url    = url;
		this._remote = String(url).match(/(https?:\/\/[a-z0-9_\-\.]+)(:[0-9]+)?/i)[0];
		this._frame = null;
		this._domain = (dom[1] != document.domain)?document.domain:"";
	},
	send : function(data) {
		this.responseXML  = "";
		this.responseText = "";

		var t      = this;
		var re     = /https?:\/\/([a-z0-9_\-\.]+)/i;
		var info   = this._getCallbackInfo();
		var url    = this._remote+"/ajax_remote_callback.html?method="+this._method;
		var header = new Array;

		window.__jindo2_callback[info.id] = function(id, data, header){
			try {
				t._callback(id, data, header);
			} finally {
				delete window.__jindo2_callback[info.id];
			}
		};

		for(var x in this._headers) {
			header[header.length] = "'"+x+"':'"+this._headers[x]+"'";
		}

		header = "{"+header.join(",")+"}";

		url += "&id="+info.id;
		url += "&header="+encodeURIComponent(header);
		url += "&proxy="+encodeURIComponent(this._proxy);
		url += "&domain="+this._domain;
		url += "&url="+encodeURIComponent(this._url.replace(re, ""));
		url += "#"+encodeURIComponent(data);

		var fr = this._frame = $("<iframe>");
		fr.style.position = "absolute";
		fr.style.visibility = "hidden";
		fr.style.width = "1px";
		fr.style.height = "1px";

		var body = document.body || document.documentElement;
		if (body.firstChild) body.insertBefore(fr, body.firstChild);
		else body.appendChild(fr);

		fr.src = url;
	}
}).extend($Ajax.RequestBase);


/**
 * @fileOverview $Json의 생성자 및 메서드를 정의한 파일
 * @name json.js
 */

/**
 * $Json 객체를 반환한다.
 * @class $Json 객체는 Javascipt에서 JSON(JavaScript Object Notation)을 다루기 위한 다양한 메서드를 제공한다.
 * @param {Object | String} sObject 객체, 혹은 JSON으로 인코딩 가능한 문자열.
 * @return {$Json} 인수를 인코딩한 $Json 객체.
 * @remark XML 문자를 사용하여 $Json 객체를 생성하려면 $Json#fromXML 메서드를 사용한다.
 * @example
var oStr = $Json ('{ zoo: "myFirstZoo", tiger: 3, zebra: 2}');

var d = {name : 'nhn', location: 'Bundang-gu'}
var oObj = $Json (d);

 * @constructor
 * @author Kim, Taegon
 */
function $Json(sObject) {
	var cl = arguments.callee;
	if (typeof sObject == "undefined") sObject = new Object;
	if (sObject instanceof cl) return sObject;
	if (!(this instanceof cl)) return new cl(sObject);

	if (typeof sObject == "string") {
		try {
			sObject = eval("("+sObject+")");
		} catch(e) {
			sObject = new Object;
		}
	}

	this._object = sObject;
}

/**
 * fromXML 메서드는 XML 문자열을 $Json 객체로 인코딩한다.
 * @param {String} sXML $Json  객체로 인코딩할 XML 문자열
 * @returns {Object} $Json 객체
 * @remark 속성과 CDATA를 가지는 태그는 CDATA를 '$cdata' 속성과 값으로 인코딩한다.
 * @example
var j1 = $Json.fromXML('<data>only string</data>');
// {"data":"only string"}

var j2 = $Json.fromXML('<data><id>Faqh%$</id><str attr="123">string value</str></data>');
{"data":{"id":"Faqh%$","str":{"attr":"123","$cdata":"string value"}}}
  */
$Json.fromXML = function(sXML) {
	var o  = new Object;
	var re = /\s*<(\/?[\w:\-]+)((?:\s+[\w:\-]+\s*=\s*"[^"]*")*)\s*>(?:(<\/\1>)|\s*)|\s*<!\[CDATA\[([\w\W]*?)\]\]>\s*|\s*([^<]*)\s*/ig;
	var re2= /^[0-9]+(?:\.[0-9]+)?$/;
	var ec = {"&amp;":"&","&nbsp;":" ","&quot;":"\"","&lt;":"<","&gt;":">"};
	var fg = {tags:["/"],stack:[o]};
	var es = function(s){return s.replace(/&[a-z]+;/g, function(m){ return (typeof ec[m] == "string")?ec[m]:m; })};
	var at = function(s,c){s.replace(/([\w\:\-]+)\s*=\s*"([^"]*)"/g, function($0,$1,$2){ c[$1] = es($2) }) };
	var em = function(o){for(var x in o){if(Object.prototype[x])continue;return false;};return true};
	var cb = function($0,$1,$2,$3,$4,$5) {
		var cur, cdata = "";
		var idx = fg.stack.length - 1;

		if (typeof $1 == "string" && $1) {
			if ($1.substr(0,1) != "/") {
				var has_attr = (typeof $2 == "string" && $2);
				var closed   = (typeof $3 == "string" && $3);
				var newobj   = (!has_attr && closed)?"":{};

				cur = fg.stack[idx];

				if (typeof cur[$1] == "undefined") {
					cur[$1] = newobj;
					cur = fg.stack[idx+1] = cur[$1];
				} else if (cur[$1] instanceof Array) {
					var len = cur[$1].length;
					cur[$1][len] = newobj;
					cur = fg.stack[idx+1] = cur[$1][len];
				} else {
					cur[$1] = [cur[$1], newobj];
					cur = fg.stack[idx+1] = cur[$1][1];
				}

				if (has_attr) at($2,cur);

				fg.tags[idx+1] = $1;

				if (closed) {
					fg.tags.length--;
					fg.stack.length--;
				}
			} else {
				fg.tags.length--;
				fg.stack.length--;
			}
		} else if (typeof $4 == "string" && $4) {
			cdata = $4;
		} else if (typeof $5 == "string" && $5) {
			cdata = es($5);
		}

		if (cdata.length > 0) {
			var par = fg.stack[idx-1];
			var tag = fg.tags[idx];

			if (re2.test(cdata)) cdata = parseFloat(cdata);
			else if (cdata == "true" || cdata == "false") cdata = new Boolean(cdata);

			if (par[tag] instanceof Array) {
				var o = par[tag];
				if (typeof o[o.length-1] == "object" && !em(o[o.length-1])) {
					o[o.length-1].$cdata = cdata;
					o[o.length-1].toString = function(){ return cdata; }
				} else {
					o[o.length-1] = cdata;
				}
			} else {
				if (typeof par[tag] == "object" && !em(par[tag])) {
					par[tag].$cdata = cdata;
					par[tag].toString = function(){ return cdata; }
				} else {
					par[tag] = cdata;
				}
			}
		}
	};

	sXML = sXML.replace(/<(\?|\!-)[^>]*>/g, "");
	sXML.replace(re, cb);

	return $Json(o);
};

/**
 * get 메서드는 $Json 객체의 값을 path 형태로 리턴한다.
 * @param {String} sPath path 문자열
 * @return {Array} 객체의 배열
 * @example
var j = $Json.fromXML('<data><id>Faqh%$</id><str attr="123">string value</str></data>');
var r = j.get ("/data/id");
// Faqh%$

 */
$Json.prototype.get = function(sPath) {
	var o = this._object;
	var p = sPath.split("/");
	var re = /^([\w:\-]+)\[([0-9]+)\]$/;
	var stack = [[o]], cur = stack[0];
	var len = p.length, c_len, idx, buf, j, e;

	for(var i=0; i < len; i++) {
		if (p[i] == "." || p[i] == "") continue;
		if (p[i] == "..") {
			stack.length--;
		} else {
			buf = [];
			idx = -1;
			c_len = cur.length;

			if (c_len == 0) return [];
			if (re.test(p[i])) idx = +RegExp.$2;

			for(j=0; j < c_len; j++) {
				e = cur[j][p[i]];
				if (typeof e == "undefined") continue;
				if (e instanceof Array) {
					if (idx > -1) {
						if (idx < e.length) buf[buf.length] = e[idx];
					} else {
						buf = buf.concat(e);
					}
				} else if (idx == -1) {
					buf[buf.length] = e;
				}
			}

			stack[stack.length] = buf;
		}

		cur = stack[stack.length-1];
	}

	return cur;
};

/**
 * toString 메서드는 $Json 객체를 JSON 문자열로 변환한다.
 * @return {String} JSON 문자열
 * @example
var j = $Json({foo:1, bar: 31});
document.write (j.toString());
document.write (j);

 */
$Json.prototype.toString = function() {
	var func = {
		$ : function($) {
			if (typeof $ == "undefined") return '""';
			if (typeof $ == "boolean") return $?"true":"false";
			if (typeof $ == "string") return this.s($);
			if (typeof $ == "number") return $;
			if ($ instanceof Array) return this.a($);
			if ($ instanceof Object) return this.o($);
		},
		s : function(s) {
			var e = {'"':'\\"',"\\":"\\\\","\n":"\\n","\r":"\\r","\t":"\\t"};
			var c = function(m){ return (typeof e[m] != "undefined")?e[m]:m };
			return '"'+s.replace(/[\\"'\n\r\t]/g, c)+'"';
		},
		a : function(a) {
			var s = "[",c = "",n=a.length;
			for(var i=0; i < n; i++) {
				if (typeof a[i] == "function") continue;
				s += c+this.$(a[i]);
				if (!c) c = ",";
			}
			return s+"]";
		},
		o : function(o) {
			var s = "{",c = "";
			for(var x in o) {
				if (typeof o[x] == "function") continue;
				s += c+this.s(x)+":"+this.$(o[x]);
				if (!c) c = ",";
			}
			return s+"}";
		}
	}

	return func.$(this._object);
};

/**
 * toXML 메서드는 $Json 객체를 XML 문자열로 변환한다.
 * @return {String} XML 문자열
 * @example
var json = $Json({foo:1, bar: 31});
json.toXML();
// <foo>1</foo><bar>31</bar>
 */
$Json.prototype.toXML = function() {
	var f = function($,tag) {
		var t = function(s,at) { return "<"+tag+(at||"")+">"+s+"</"+tag+">" };

		switch (typeof $) {
			case "undefined":
			case "null":
				return t("");
			case "number":
				return t($);
			case "string":
				if ($.indexOf("<") < 0) return t($.replace(/&/g,"&amp;"));
				else return t("<![CDATA["+$+"]]>");
			case "boolean":
				return t(String($));
			case "object":
				var ret = "";
				if ($ instanceof Array) {
					var len = $.length;
					for(var i=0; i < len; i++) { ret += f($[i],tag); };
				} else {
					var at = "";

					for(var x in $) {
						if (x == "$cdata" || typeof $[x] == "function") continue;
						ret += f($[x], x);
					}

					if (tag) ret = t(ret, at);
				}
				return ret;
		}
	};

	return f(this._object, "");
};

/**
 * toObject 메서드는 $Json 객체 내부의 JSON 데이터 객체를 반환한다.
 * @return {Object} 데이터 객체
 * @example
var json = $Json({foo:1, bar: 31});
json.toObject();
// {foo: 1, bar: 31}
 */
$Json.prototype.toObject = function() {
	return this._object;
};

/**
 * $value 메서드는 $Json.toObject의 별칭(Alias)이다.
 * @return {Object} 데이터 객체
 */
$Json.prototype.$value = $Json.prototype.toObject;

/**
 * @fileOverview $Template의 생성자 및 메서드를 정의한 파일
 * @name template.js
 */

/**
 * 템플릿을 실행한다. 첫번째 인수로 템플릿 문자열 또는 문자열을 포함한 HTML 엘리먼트 혹은 엘리먼트의 아이디를 전달할 수 있다.
 * 엘리먼트는 <textarea> 또는 <script type="text/template"> 이 가능하다(<script> 태그를 사용하려면 반드시 type 속성을 지정해주어야 한다).
 * @class $Template 클래스는 템플릿 문자열에 동적으로 문자열을 삽입한다.
 * @constructor
 * @author Kim, Taegon
 * @example
 * <script type="text/javascript">
 * // Template 문자열을 전달하는 경우
 * var tpl = $Template("Value : {=test}");
 * </script>
 * @example
 * <textarea id="tpl1">
 * Value : {=test}
 * </textarea>
 *
 * <script type="text/template" id="tpl2">
 Value : {=test}
 * </script>
 *
 * <script type="text/javascript">
 * // Textarea 엘리먼트를 사용하는 경우
 * var template1 = $Template("tpl1");
 *
 * // Script 엘리먼트를 사용하는 경우
 * var template2 = $Template("tpl2");
 * </script>
 */
function $Template(str) {
    var obj = null, tag = "";
    var cl  = arguments.callee;

    if (str instanceof cl) return str;
    if (!(this instanceof cl)) return new cl(str);

    if(typeof str == "undefined") str = "";
    else if( (obj=$(str)) && (tag=obj.tagName.toUpperCase()) && (tag == "TEXTAREA" || (tag == "SCRIPT" && obj.getAttribute("type") == "text/template")) ) {
        str = (obj.value||obj.innerHTML).replace(/^\s+|\s+$/g,"");
    }

    this._str = str+"";
}
$Template.splitter = /(?!\\)[\{\}]/g;
$Template.pattern  = /^(?:if (.+)|elseif (.+)|for (?:(.+)\:)?(.+) in (.+)|(else)|\/(if|for)|=(.+))$/;

 /**
 * process 메서드는 템플릿을 해석하고 해석된 결과 문자열을 반환한다.
 * @param {Object} data 변수 및 함수 데이터
 * @return {String} 해석을 마친 새로운 문자열
 * @example
 * // 단순 문자열 치환
 * var tpl  = $Template("Value1 : {=val1}, Value2 : {=val2}")
 * var data = {val1:"first value", val2:"second value"};
 * document.write( tpl.parse(data) );
 *
 * // 결과 :
 * // Value1 : first value, Value2 : second value
 *
 * @example
 * // 조건문
 * var tpl= $Template("{{if num >= 7}7보다 크거나 같다.{elseif num =< 5}5보다 작거나 같다.{else}아마 6?{/if}");
 * var data = { num:5 };
 * document.write( tpl.parse(data) );
 *
 * // 결과 :
 * 5보다 작거나 같다.
 *
 * @example
 * // 반복문 - 인덱스 사용안함
 * var tpl  = $Template("<h1>포탈 사이트</h1>\n<ul> {for site in potals}\n<li><a href="{=site.url}">{=site.name}</a></li>{/for} \n</ul>");
 * var data = {potals:[
 *		{ name : "네이버", url : "http://www.naver.com"  },
 *		{ name : "다음",  url : "http://www.daum.net"  },
 *		{ name : "야후",  url : "http://www.yahoo.co.kr"  }
 * ]};
 *
 * // 결과:
 * <h1>포탈 사이트 목록</h1> <ul> <li>네이버</li><li>다음</li><li>야후</li>
 * <ul>
 * <li><a href="http://www.naver.com">네이버</a></li>
 * <li><a href="http://www.daum.net">다음</a></li>
 * <li><a href="http://www.yahoo.co.kr">야후</a></li>
 * </ul>
 *
 * @example
 * // 반복문 - 인덱스 사용
 * var tpl  = $Template("{for num:word in numbers}{=word}({=num}) {/for}");
 * var data = { numbers : ["zero", "one", "two", "three"] };
 * document.write( tpl.parse(data) );
 *
 * // 결과
 * zero(0) one(1) two(2) three(3)
 */
$Template.prototype.process = function(data) {
	var key = "\x01";
    var tpl = (" "+this._str+" ").replace(/(?!\\)\}\{/g, "}"+key+"{").split($Template.splitter), i = tpl.length;
    var map = {'"':'\\"','\\':'\\\\','\n':'\\n','\r':'\\r','\t':'\\t','\f':'\\f'};
    var reg = [/("(?:(?:\\.)+|[^\\"]+)*"|[a-zA-Z_][\w\.]*)/g, /[\n\r\t\f"\\]/g, /^\s+/, /\s+$/, /#/g];
    var cb  = [function(m){ return (m.substring(0,1)=='"')?m:"d."+m; }, function(m){return map[m]||m}, "", ""];
    var stm = [];
	var lev = 0;

	// remove " "
	tpl[0] = tpl[0].substr(1);
	tpl[i-1] = tpl[i-1].substr(0, tpl[i-1].length-1);

    // no pattern
    if(i<2) return tpl;

    while(i--) {
        if(i%2) {
            tpl[i] = tpl[i].replace($Template.pattern, function(){
                var m = arguments;

                // variables
                if(m[8]) return 's[i++]=d.'+m[8]+';';

                // if
                if(m[1]) {
                    return 'if('+m[1].replace(reg[0],cb[0])+'){';
                }

                // else if
                if(m[2]) return '}else if('+m[2].replace(reg[0],cb[0])+'){';

                // for loop
                if(m[5]) {
					return ('var t#=d.'+m[5]+'||{},p#=isArray(t#),i#=0;'+
							'for(var x# in t#){'+
							'	if( (p# && isNaN(i#=parseInt(x#))) || (!p# && !t#.propertyIsEnumerable(x#)) ) continue;'+
							'	d.'+m[4]+'=t#[x#];'+
							(m[3]?'d.'+m[3]+'=p#?i#:x#;':'')
						).replace(reg[4], lev++ );
                }

                // else
                if(m[6]) return '}else{';

                // end if, end for
                if(m[7]) {
                    return '};';
                }

                return m[0];
            });
        }else if(tpl[i] == key) {
			tpl[i] = "";
        }else if(tpl[i]){
            tpl[i] = 's[i++]="'+tpl[i].replace(reg[1],cb[1])+'";';
        }
    }

    tpl = (new Function("d",''+
		'var s=[],i=0;'+
		'function isArray(o){ return Object.prototype.toString.call(o) == "[object Array]" };'+
		tpl.join('')+
		'return s.join("");'
	))(data);

    return tpl;
};

/**
 * @fileOverview $Form 생성자 및 메서드를 정의한 파일
 * @name form.js
 */

/**
 * $Document 객체를 생성 및 리턴한다.
 * @class $Document 클래스는 문서와 관련된 여러가지 기능의 메서드를 제공한다
 * @param {Document} doc	기능에 사용된 document 객체, 지정하지 않으면 기본적으로 현재 문서의 document 가 지정된다.
 * @constructor
 * @author Hooriza
 */
function $Document(el) {
	var cl = arguments.callee;
	if (el instanceof cl) return el;
	if (!(this instanceof cl)) return new cl(el);

	this._doc = el || document;
	this._docKey = this.renderingMode() == 'Standards' ? 'documentElement' : 'body';
};

/**
 * $value 메서드는 래핑된 원래의 document 객체를 리턴한다.
 * @return {HTMLDocument} document 객체
 */
$Document.prototype.$value = function() {
	return this._document;
};

/**
 * scrollSize 메서드는 문서의 실제 가로, 세로 크기를 구한다
 * @return {Object} 가로크기는 width, 세로크기는 height 라는 키값으로 리턴된다.
 * @example
var size = $Document().scrollSize();
alert('가로 : ' + size.width + ' / 세로 : ' + size.height);
 */
$Document.prototype.scrollSize = function() {

	var oDoc = this._doc[this._docKey];

	return {
		width : Math.max(oDoc.scrollWidth, oDoc.clientWidth),
		height : Math.max(oDoc.scrollHeight, oDoc.clientHeight)
	};

};

/**
 * clientSize 메서드는 스크롤바로 인해 가려진 부분을 제외한 문서 중 보이는 부분의 가로, 세로 크기를 구한다
 * @return {Object} 가로크기는 width, 세로크기는 height 라는 키값으로 리턴된다
 * @example
var size = $Document(document).clientSize();
alert('가로 : ' + size.width + ' / 세로 : ' + size.height);
 */
$Document.prototype.clientSize = function() {

	var oDoc = this._doc[this._docKey];

	return {
		width : oDoc.clientWidth,
		height : oDoc.clientHeight
	};

};

/**
 * renderingMode 메서드는 문서의 렌더링 방식을 얻는다
 * @return {String} 렌더링 모드
 * <dl>
 *	<dt>Standards</dt>
 *	<dd>표준 렌더링 모드</dd>
 *	<dt>Almost</dt>
 *	<dd>유사 표준 렌더링 모드 (IE 외의 브라우저에서 DTD 을 올바르게 지정하지 않았을때 리턴)</dd>
 *	<dt>Quirks</dt>
 *	<dd>비표준 렌더링 모드</dd>
 * </dl>
 * @example
var mode = $Document().renderingMode();
alert('렌더링 방식 : ' + mode);
 */
$Document.prototype.renderingMode = function() {

	var oBrowser = $Agent().navigator();
	var sRet;

	if ('compatMode' in this._doc)
		sRet = this._doc.compatMode == 'CSS1Compat' ? 'Standards' : (oBrowser.ie ? 'Quirks' : 'Almost');
	else
		sRet = oBrowser.safari ? 'Standards' : 'Quirks';

	return sRet;

};


/**
 * @fileOverview $Date의 생성자 및 메서드를 정의한 파일
 * @name date.js
 */

/**
 * $Date 객체를 생성하고 리턴한다.
 * @extends core
 * @class $Date 클래스는 날짜를 처리하기 위한 Date 타입의 레퍼(Wrapper) 클래스이다.
 * @constructor
 * @author Kim, Taegon
 * @example
$Date();
$Date(milliseconds);
$Date(dateString);
$Date(year, month, date[, hours, minitues, seconds, milliseconds]);
 */
function $Date(src) {
	var a=arguments,t="";
	var cl=arguments.callee;

	if (src && src instanceof cl) return src;
	if (!(this instanceof cl)) return new cl(a[0],a[1],a[2],a[3],a[4],a[5],a[6]);

	if ((t=typeof src) == "string") {
		this._date = cl.parse(src).$value();
	} else if (t == "number") {
		if (typeof a[1] == "undefined") this._date = new Date(src);
		else this._date = new Date(a[0],a[1],a[2],a[3],a[4],a[5],a[6]);
	} else if (t == "object" && src.constructor == Date) {
		(this._date = new Date).setTime(src.getTime());
		this._date.setMilliseconds(src.getMilliseconds());
	} else {
		this._date = new Date;
	}
}

/**
 * names 속성은 $Date에서 사용할 달, 요일, 오전/오후의 이름을 저장한 문자열이다. s_ 를 접두어로 가지는 이름들은 약어(abbreviation)이다.
 */
$Date.names = {
	month   : ["January","Febrary","March","April","May","June","July","August","September","October","Novermber","December"],
	s_month : ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],
	day     : ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],
	s_day   : ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],
	ampm    : ["AM", "PM"]
};

/**
 * now 메서드는 현재 시간을 밀리초 단위의 정수로 리턴한다.
 * @return {Number} 밀리초 단위의 정수인 현재 시간
 */
$Date.now = function() {
	return Date.now();
};

/**
 * parse 메서드는 인수로 지정한 문자열을 파싱하여 문자열의 형식에 맞는 Date 객체를 생성한다.
 * @param {String} strDate 날짜, 혹은 시간 형식을 지정한 파싱 대상 문자열
 * @return {Object} Date 객체.
 * @remark parse 메서드는 Javascript Core의 Date.parse와 같은 결과값을 리턴한다.
 */
$Date.parse = function(strDate) {
	return Date.parse(strDate);
};

/**
 * $value 메서드는 $Date가 감싸고 있던 원본 Date 객체를 반환한다.
 * @returns {Object} Date 객체
 */
$Date.prototype.$value = function(){
	return this._date;
};

/**
 * format 메서드는 $Date 객체가 저장하고 있는 시간을 인수로 지정한 형식 문자열에 맞추어 변환한다. 형식 문자열은 PHP의 date 함수와 동일하게 사용한다. ??
 * @param {Date} strFormat  형식 문자열
 * @returns {String} 시간을 형식 문자열에 맞추어 변환한 문자열.
 */
$Date.prototype.format = function(strFormat){
	var o = {};
	var d = this._date;

	return (strFormat||"").replace(/[a-z]/ig, function callback(m){
		if (typeof o[m] != "undefined") return o[m];

		switch(m) {
			case"d":
			case"j":
				o.j = d.getDate();
				o.d = (o.j>9?"":"0")+o.j;
				return o[m];
			case"l":
			case"D":
			case"w":
			case"N":
				o.w = d.getDay();
				o.N = o.w?o.w:7;
				o.D = $Date.names.s_day[o.w];
				o.j = $Date.names.day[o.w];
				return o[m];
			case"S":
				return (!!(o.S=["st","nd","rd"][d.getDate()]))?o.S:(o.S="th");
			case"z":
				o.z = Math.floor((d.getTime() - (new Date(d.getFullYear(),0,1)).getTime())/(3600*24*1000));
				return o.z;
			case"m":
			case"n":
				o.n = d.getMonth()+1;
				o.m = (o.n>9?"":"0")+o.n;
				return o[m];
			case"L":
				o.L = this.isLeapYear();
				return o.L;
			case"o":
			case"Y":
			case"y":
				o.o = o.Y = d.getFullYear();
				o.y = (o.o+"").substr(2);
				return o[m];
			case"a":
			case"A":
			case"g":
			case"G":
			case"h":
			case"H":
				o.G = d.getHours();
				o.g = (o.g=o.G%12)?o.g:12;
				o.A = o.G<12?$Date.names.ampm[0]:$Date.names.ampm[1];
				o.a = o.A.toLowerCase();
				o.H = (o.G>9?"":"0")+o.G;
				o.h = (o.g>9?"":"0")+o.g;
				return o[m];
			case"i":
				o.i = (((o.i=d.getMinutes())>9)?"":"0")+o.i;
				return o.i;
			case"s":
				o.s = (((o.s=d.getSeconds())>9)?"":"0")+o.i;
				return o.s;
			case"u":
				o.u = d.getMilliseconds();
				return o.u;
			case"U":
				o.U = this.time();
				return o.U;
			default:
				return m;
		}
	});
};

/**
 * time 메서드는 GMT 1970/01/01 00:00:00을 기준으로 경과한 시간을 설정하거나 가져온다.
 * @param {Number} nTime 밀리 초 단위의 시간 값.
 * @return {$Date | Number} 인수를 지정했다면 GMT 1970/01/01 00:00:00 에서 부터 인수만큼 지난 시간을 설정한 $DAte 객체. 인수를 지정하지 않았다면 GMT 1970/01/01 00:00:00에서 부터 $Date 객체에 지정된 시각까지 경과한 시간(밀리 초).
 */
$Date.prototype.time = function(nTime) {
	if (typeof nTime == "number") {
		this._date.setTime(nTime);
		return this;
	}

	return this._date.getTime();
};

/**
 * year 메서드는 년도를 설정하거나 가져온다.
 * @param {Number} nYear 설정할 년도값
 * @return {$Date | Number} 인수를 지정하였다면 새로 년도 값을 설정한 $Date 객체. 인수를 지정하지 않았다면 $Date 객체가 지정하고 있는 시각의 년도를 리턴한다.
 */
$Date.prototype.year = function(nYear) {
	if (typeof nYear == "number") {
		this._date.setFullYear(nYear);
		return this;
	}

	return this._date.getFullYear();
};

/**
 * month 메서드는 달을 설정하거나 가져온다.
 * @param {Number} nMon 설정할 달의 값
 * @return {$Date | Number} 인수를 지정하였다면 새로 달을 설정한 $Date 객체. 인수를 지정하지 않았다면 $Date 객체가 지정하고 있는 시각의 달을 리턴한다.
 * @remark 리턴 값의 범위는 0(1월)에서 11(12월)이다.
 */
$Date.prototype.month = function(nMon) {
	if (typeof nMon == "number") {
		this._date.setMonth(nMon);
		return this;
	}

	return this._date.getMonth();
};

/**
 * date 메서드는 날짜를 설정하거나 가져온다.
 * @param {nDate} nDate	설정할 날짜 값
 * @return {$Date | Number} 인수를 지정하였다면 새로 날짜를 설정한 $Date 객체. 인수를 지정하지 않았다면 $Date 객체가 지정하고 있는 시각의 날짜를 리턴한다.
 */
$Date.prototype.date = function(nDate) {
	if (typeof nDate == "number") {
		this._date.setDate(nDate);
		return this;
	}

	return this._date.getDate();
};

/**
 * day 메서드는 요일을 가져온다.
 * @return {Number} 요일 값. 0(일요일)에서 6(토요일)을 리턴한다.
 */
$Date.prototype.day = function() {
	return this._date.getDay();
};

/**
 * hours 메서드는 시(時)를 설정하거나 가져온다.
 * @param {Number} nHour 설정할 시 값
 * @return {$Date | Number} 인수를 지정하였다면 새로 시 값을 설정한 $Date 객체. 인수를 지정하지 않았다면 $Date 객체가 지정하고 있는 시각의 시 값.
 */
$Date.prototype.hours = function(nHour) {
	if (typeof nHour == "number") {
		this._date.setHours(nHour);
		return this;
	}

	return this._date.getHours();
};

/**
 * seconds 메서드는 초을 설정하거나 가져온다.
 * @param {Number} nSec 설정할 초 값
 * @return {Number} 인수를 지정하였다면 새로 초 값을 설정한 $Date 객체. 인수를 지정하지 않았다면 $Date 객체가 지정하고 있는 시각의 초 값.
 */
$Date.prototype.seconds = function(nSec) {
	if (typeof nSec == "number") {
		this._date.setSeconds(nSec);
		return this;
	}

	return this._date.getSeconds();
};

/**
 * minutes 메서드는 분을 설정하거나 가져온다.
 * @param {Number} nMin 설정할 분 값
 * @return {Number} 인수를 지정하였다면 새로 분 값을 설정한 $Date 객체. 인수를 지정하지 않았다면 $Date 객체가 지정하고 있는 시각의 분 값.
 */
$Date.prototype.minutes = function(nMin) {
	if (typeof nMin == "number") {
		this._date.setMinutes(nMin);
		return this;
	}

	return this._date.getMinutes();
};

/**
 * isLeapYear 메서드는 시각의 윤년 여부를 확인한다.
 * @returns {Boolean} $Date가 가리키고 있는 시각이 윤년이면 True, 그렇지 않다면 False
 */
$Date.prototype.isLeapYear = function() {
	var y = this._date.getFullYear();

	return !(y%4)&&!!(y%100)||!(y%400);
};

/**
 * 선택영역 $Selection 객체를 반환한다.
 * @id selection.$Selection
 */
function $Selection() {
}

/**
 * 현재의 Selection 영역에 해당하는 $Range 객체를 반환한다.
 * 첫번째 파라미터로 $Range 객체를 전달하면 주어진 $Range를 선택영역으로 설정한다.
 * @id core.$Range.range
 * @param {$Range} 선택영역에 대한 $Range 객체
 */
$Selection.prototype.range = function(oRange) {
};

/**
 * 선택영역 $Range 객체를 반환한다
 * @id selection.$Range
 */
function $Range() {
};

/**
 * 주어진 Text Range에 있는 HTML 문자열을 가져온다.
 * @id selection.$Range.getHTML
 */
$Range.prototype.getHTML = function() {
};

/**
 * 주어진 Text Range에 HTML을 대체한다.
 * @id selection.$Range.setHTML
 */
$Range.prototype.setHTML = function() {
};

/**

 * @fileOverview $Cookie의 생성자 및 메서드를 정의한 파일

 * @name cookie.js

 */



/**

 * $Cookie 객체를 생성한다.

 * @class $Cookie 클래스는 쿠키(Cookie)를 추가, 수정, 혹은 삭제하거나 쿠키의 값을 가져온다.

 * @constructor

 * @author Kim, Taegon

 * @example

var cookie = $Cookie();

cookie.set("session_id", "eac312d1d51ab", 1);



document.write (cookie.keys());

 */

function $Cookie() {

	var cl = arguments.callee;

	var cached = cl._cached;



	if (cl._cached) return cl._cached;

	if (!(this instanceof cl)) return new cl;

	if (typeof cl._cached == "undefined") cl._cached = this;

};



/**

 * 쿠키 이름의 배열을 반환한다.

 * @return {Array} 쿠키 이름의 배열

 */

$Cookie.prototype.keys = function() {

	var ca = document.cookie.split(";");

	var re = /^\s+|\s+$/g;

	var a  = new Array;



	for(var i=0; i < ca.length; i++) {

		a[a.length] = ca[i].substr(0,ca[i].indexOf("=")).replace(re, "");

	}



	return a;

};



/**

 * 이름에 해당하는 쿠키 값을 가져온다. 값이 존재하지 않는다면 null을 반환한다.

 * @param {String} sName 쿠키 이름

 * @return {String} 쿠키 값

 * @example

var cookie = $Cookie();

cookie.set("session_id", "eac312d1d51ab", 1);



// ...



document.write (cookie.get ("session_id"));

 */

$Cookie.prototype.get = function(sName) {

	var ca = document.cookie.split(/\s*;\s*/);

	var re = new RegExp("^(\\s*"+sName+"\\s*=)");



	for(var i=0; i < ca.length; i++) {

		if (re.test(ca[i])) return unescape(ca[i].substr(RegExp.$1.length));

	}



	return null;

};



/**

 * 이름에 해당하는 쿠키 값을 설정한다.

 * @param {String} sName 쿠키 이름.

 * @param {String} sV체lue 쿠키 값.

 * @param {Number} [nDays] 쿠키 유효 시간. 유효 시간은 일단위로 설정한다. 유효시간을 생략했다면 쿠키는 웹 브라우저가 종료되면서 없어진다.

 * @param {String} [sDomain] 쿠키 도메인.

 * @return {$Cookie} $Cookie 객체

 */

$Cookie.prototype.set = function(sName, sValue, nDays, sDomain) {

	var sExpire = "";



	if (typeof nDays == "number") {

		sExpire = ";expires="+(new Date((new Date()).getTime()+nDays*1000*60*60*24)).toGMTString();

	}

	if (typeof sDomain == "undefined") sDomain = "";



	document.cookie = sName+"="+escape(sValue)+sExpire+"; path=/"+(sDomain?"; domain="+sDomain:"");



	return this;

};



/**

 * 이름에 해당하는 쿠키 값을 제거한다.

 * @param {String} sName 쿠키 이름

 * @return {$Cookie} $Cookie 객체

 */

$Cookie.prototype.remove = function(sName) {

	if (this.get(sName) != null) this.set(sName, "", -1);



	return this;

};



/**
 * @fileOverview $Form 생성자 및 메서드를 정의한 파일
 * @name form.js
 */

/**
 * $Form 객체를 생성 및 리턴한다.
 * @class $Form 클래스는 <form> 엘리먼트와 하위 입력요소들을 다루기 위한 여러 가지 메서드를 제공한다.
 * @param {Element | String} el	$Form를 사용할 <form> 엘리먼트, 혹은 <form> 엘리먼트의 고유한 id. 만약 고유하지 않은 id를 지정하면 가장 먼저 나오는 엘리먼트를 반환한다.
 * @constructor
 * @author Hooriza
 */
function $Form(el) {
	var cl = arguments.callee;
	if (el instanceof cl) return el;
	if (!(this instanceof cl)) return new cl(el);

	el = $(el);

	if (!el.tagName || el.tagName.toUpperCase() != 'FORM') throw new Error('The element should be a FORM element');
	this._form = el;
}

/**
 * $value 메서드는 랩핑된 원래 <form> 엘리먼트를 리턴한다
 * @return {HTMLElement} <form> 엘리먼트
 * @example

var el = $('<form>');
var form = $Form(el);

alert(form.$value() === el); // true

 */
$Form.prototype.$value = function() {
	return this._form;
};

/**
 * serialize 메서드는 특정 또는 전체 엘리멘트  입력요소를 문자열 형태로 리턴한다.
 * @param {Mixed} Mixed 인수를 지정하지 않거나 인수를 하나 이상 설정할 수 있다.
 * <dl>
 *	<dt>인수를 지정하지 않을 경우</dt>
 *	<dd>인수를 지정하지 않으면 쿼리 형태의 문자열을 리턴한다.</dd>
 *	<dt>{String} one_element_name</dt>
 *	<dd>인수로 하나의 문자열만 지정하면 문자열과 동일한 name 속성을 가지는 엘리먼트와 그 값을 리턴한다.</dd>
 *	<dt>{String, String... } element_names</dt>
 *	<dd>두 개 이상의 엘리먼트와 그 값을 리턴한다. </dd>
 * </dl>
 * @return {String} 쿼리 문자열 형태로 변환한 엘리먼트와 그 값.
 * @example

<form id="TEST">
	<input name="ONE" value="1" type="text" />
	<input name="TWO" value="2" checked="checked" type="checkbox" />
	<input name="THREE" value="3_1" type="radio" />
	<input name="THREE" value="3_2" checked="checked" type="radio" />
	<input name="THREE" value="3_3" type="radio" />
	<select name="FOUR">
		<option value="4_1">..</option>
		<option value="4_2">..</option>
		<option value="4_3" selected="selected">..</option>
	</select>
</form>
<script type="text/javascript">
	var form = $Form('TEST');

	var allstr = form.serialize();
	alert(allstr == 'ONE=1&TWO=2&THREE=3_2&FOUR=4_3'); // true

	var str = form.serialize('ONE', 'THREE');
	alert(str == 'ONE=1&THREE=3_2'); // true
</script>

 */
$Form.prototype.serialize = function() {

 	var self = this;
 	var oRet = {};

 	var nLen = arguments.length;
 	var fpInsert = function(sKey) {
 		var sVal = self.value(sKey);
 		if (typeof sVal != 'undefined') oRet[sKey] = sVal;
 	};

 	if (nLen == 0)
	 	$A(this.element()).forEach(function(o) { if (o.name) fpInsert(o.name); });
 	else
 		for (var i = 0; i < nLen; i++) fpInsert(arguments[i]);

	return $H(oRet).toQueryString();

};

/**
 * element 메서드는 특정 또는 전체 입력요소를 리턴한다.
 * @param {String} sKey 얻고자 하는 입력요소 엘리먼트의 name 문자열, 생략시에는 모든 입력요소들을 배열로 리턴한다.
 * @return {HTMLElement | Array} 입력 요소 엘리먼트
 */
$Form.prototype.element = function(sKey) {

	if (arguments.length > 0)
		return this._form[sKey];

	return this._form.elements;

};

/**
 * enable 메서드는 입력 요소의 활성화 여부를 얻거나 설정한다.
 * @param {Mixed} mixed 정확한 인수는 다음과 같다.
 * <dl>
 *	<dt>{String}fieldName</dt>
 *	<dd>인수로 입력요소의 이름만 넣어주면 해당 입력요소의 활성화 여부를 true, false 로 반환한다</dd>
 *	<dt>{String}fieldName, {Boolean}flag</dt>
 *	<dd>입력요소와 Boolean 형의 상태값을 함께 넣어주면 해당 입력요소를 지정한 상태로 활성화 여부를 변경한다</dd>
 *	<dt>{Object}objectProperties</dt>
 *	<dd>동시에 여러개의 입력요소에 대해 활성화 여부를 지정하고 싶을때 사용한다</dd>
 * </dl>
 * @return {Boolean|$Form} 엘리먼트의 활성화 여부를 가져오거나 엘리먼트의 활성화 여부를 설정한 $Form 객체.
 * @example

<form id="TEST">
	<input name="ONE" disabled="disabled" type="text" />
	<input name="TWO" type="checkbox" />
</form>
<script type="text/javascript">
	var form = $Form('TEST');

	var one_enabled = form.enable('ONE');
	alert(one_enabled === false); // true

	form.enable('TWO', false);

	form.enable({
		'ONE' : true,
		'TWO' : false
	});
</script>

 */
$Form.prototype.enable = function() {

	var sKey = arguments[0];

	if (typeof sKey == 'object') {

		var self = this;
		$H(sKey).forEach(function(bFlag, sKey) { self.enable(sKey, bFlag); });
		return this;

	}

	var aEls = this.element(sKey);
	if (!aEls) return this;
	aEls = aEls.nodeType == 1 ? [ aEls ] : aEls;

	if (arguments.length < 2) {

		var bEnabled = true;
		$A(aEls).forEach(function(o) { if (o.disabled) {
			bEnabled = false;
			$A.Break();
		}});
		return bEnabled;

	} else { // setter

		var sFlag = arguments[1];
		$A(aEls).forEach(function(o) { o.disabled = !sFlag; });

		return this;

	}

};

/**
 * value 메서드는 폼 엘리먼트의 값을 얻거나 설정한다.
 * @param {Mixed} Mixed 정확안 인수 정보는 다음과 같다.
 * <dl>
 *	<dt>{String}fieldName</dt>
 *	<dd>인수로 엘리먼트의 이름만 지정하면 엘리먼트의 값을 리턴한다</dd>
 *	<dt>{String}fieldName, {String}value</dt>
 *	<dd>엘리먼트의 이름과 엘리먼트에 설정할 값을 함께 지정하면 엘리먼트의 값을 지정한 값으로 바꾼다.</dd>
 *	<dt>{Object}objectProperties</dt>
 *	<dd>두 개 이상의 엘리먼트 값을 동시에 지정하고 싶으면 '엘리먼트 이름 : 엘리먼트 값'을 원소로 가지는 객체를 지정한다. </dd>
 * </dl>
 * @return {String|$Form} 인수로 엘리먼트만 지정했다면 지정한 엘리먼트의 값을, 인수로 폼 엘리먼와 엘리먼트의 값을 지정했다면 $Form 객체를 리턴한다.
 * @example

<form id="TEST">
	<input name="ONE" value="1" type="text" />
	<input name="TWO" value="2" type="checkbox" />
</form>
<script type="text/javascript">
	var form = $Form('TEST');

	var one_value = form.value('ONE');
	alert(one_value === '1'); // true

	var two_value = form.value('TWO');
	alert(two_value === undefined); // true

	form.value('TWO', 2);
	alert(two_value === '2'); // true

	form.value({
		'ONE' : '1111',
		'TWO' : '2'
	});
	// form.value('ONE') -> 1111
	// form.value('ONE') -> 2
</script>

 */
$Form.prototype.value = function(sKey) {

	if (typeof sKey == 'object') {

		var self = this;
		$H(sKey).forEach(function(bFlag, sKey) { self.value(sKey, bFlag); });
		return this;

	}

	var aEls = this.element(sKey);
	if (!aEls) throw new Error('The element is not exist');
	aEls = aEls.nodeType == 1 ? [ aEls ] : aEls;

	if (arguments.length > 1) { // setter

		var sVal = arguments[1];

		$A(aEls).forEach(function(o) {

			switch (o.type) {
			case 'radio':
			case 'checkbox':
				o.checked = (o.value == sVal);
				break;

			case 'select-one':
				var nIndex = -1;
				for (var i = 0, len = o.options.length; i < len; i++)
					if (o.options[i].value == sVal) nIndex = i;
				o.selectedIndex = nIndex;

				break;

			default:
				o.value = sVal;
				break;
			}

		});

		return this;
	}

	// getter

	var aRet = [];

	$A(aEls).forEach(function(o) {

		switch (o.type) {
		case 'radio':
		case 'checkbox':
			if (o.checked) aRet.push(o.value);
			break;

		case 'select-one':
			if (o.selectedIndex != -1) aRet.push(o.options[o.selectedIndex].value);
			break;

		default:
			aRet.push(o.value);
			break;
		}

	});

	return aRet.length > 1 ? aRet : aRet[0];

};

/**
 * submit 메서드는 폼의 데이터를 웹으로 제출(submit) 한다.
 * @param {String} sTargetName 제출할 폼이 있는 윈도우의 이름. sTargetName을 생략하면 기본 타겟
 * @return {$Form} 데이터를 제출한 $Form 객체.
 * @example
var form = $Form(el);
form.submit();
form.submit('foo');
 */
$Form.prototype.submit = function(sTargetName) {

	var sOrgTarget = null;

	if (typeof sTargetName != 'undefined') {
		sOrgTarget = this._form.target;
		this._form.target = sTargetName;
	}

	this._form.submit();

	if (sOrgTarget !== null)
		this._form.target = sOrgTarget;

	return this;

};

/**
 * submit 메서드는 폼을 초기화(reset)한다.
 * @return {$Form} 초기화한 $Form 객체.
 * @example
var form = $Form(el);
form.reset();
 */
$Form.prototype.reset = function() {

	this._form.reset();
	return this;

};


/**
 * @fileOverview $Json의 생성자 및 메서드를 정의한 파일
 * @name json.js
 */

/**
 * 인수로 지정한 문자열을 사용하여 $S 클래스를 생성한다.
 * @class $S 클래스는문자열을 처리하기 위한 레퍼(Wrapper) 클래스이다.
 * @constructor
 * @author Kim, Taegon
 */
function $S(str) {
	var cl = arguments.callee;

	if (typeof str == "undefined") str = "";
	if (str instanceof cl) return str;
	if (!(this instanceof cl)) return new cl(str);

	this._str = str+"";
}

/**
 * $value 메서드는 문자열을 리턴한다.
 * @return {String} $S의 내부 문자열
 * @see $S#toString
 */
$S.prototype.$value = function() {
	return this._str;
};

/**
 * toString 메서드는 문자열을 리턴한다.
 * @return {String} $S의 내부 문자열
 * @remark 내부적으로 $value를 사용한다.
 */
$S.prototype.toString = $S.prototype.$value;

/**
 * trim 메서드는 문자열의 양 끝 공백을 제거한다.
 * @return {$S} 문자열의 양 끝을 제거한 새로운 $S 객체
 * @example
 * var str = "   I have many spaces.   ";
 * document.write ( $S(str).trim() );
 *
 * // 결과 :
 * // I have many spaces.
 */
$S.prototype.trim = function() {
	return $S(this._str.replace(/^\s+|\s+$/g, ""));
};

/**
 * escapeHTML 메서드는 HTML 특수 문자를 HTML 엔티티 형식으로 변환한다.
 * @return {$S} HTML 특수 문자를 엔티티 형식으로 변환한 새로운 $S 객체
 * @see $S#unescapeHTML
 * @remark  ", &, <, > 를 각각 &quot;, &amp;, &lt;, &gt; 로 변경한다.
 * @example
 * var str = ">_<;;";
 * document.write( $S(str).escapeHTML() );
 *
 * // 결과 :
 * // &gt;_&lt;;;
 */
$S.prototype.escapeHTML = function() {
	var entities = {'"':'quot','&':'amp','<':'lt','>':'gt'};
	var s = this._str.replace(/[<>&"]/g, function(m0){
		return entities[m0]?'&'+entities[m0]+';':m0;
	});
	return $S(s);
};

/**
 * stripTags 메서드는 문자열에서 XML 혹은 HTML 태그를 제거한다.
 * @return {$S} XML 혹은 HTML 태그를 제거한 새로운 $S 객체
 * @example
 * var str = "Meeting <b>people</b> is easy.";
 * document.write( $S(str).stripTags() );
 *
 * // 결과 :
 * // Meeting people is easy.
 */
$S.prototype.stripTags = function() {
	return $S(this._str.replace(/<\/?(?:h[1-5]|[a-z]+(?:\:[a-z]+)?)[^>]*>/ig, ''));
};

/**
 * times 메서드는 문자열을 인수로 지정한 숫자만큼 반복한다.
 * @param {Number} nTimes 반복할 횟수
 * @return {$S} 문자열을 지정한 숫자만큼 반복한 새로운 $S 객체
 * @example
 * document.write ( $S("Abc").times(3) );
 *
 * // 결과 : AbcAbcAbc
 */
$S.prototype.times = function(nTimes) {
	var buf = [];
	for(var i=0; i < nTimes; i++) {
		buf[buf.length] = this._str;
	}

	return $S(buf.join(''));
};

/**
 * unescapeHTML 메서드는 이스케이프된 HTML을 원래의 HTML로 변환한다.
 * @return {$S} 이스케이프된 HTML을 원래의 HTML로 변환한 새로운 $S 객체
 * @remark  &quot;, &amp;, &lt;, &gt;를 각각 ", &, <, > 으로 변경한다.
 * @see $S#escapeHTML
 * @example
 * var str = "&lt;a href=&quot;http://naver.com&quot;&gt;Naver&lt;/a&gt;";
 * document.write( $S(str).unescapeHTML() );
 *
 * // 결과 :
 * // <a href="http://naver.com">Naver</a>
 */
$S.prototype.unescapeHTML = function() {
	var entities = {'quot':'"','amp':'&','lt':'<','gt':'>'};
	var s = this._str.replace(/&([a-z]+);/g, function(m0,m1){
		return entities[m1]?entities[m1]:m0;
	});
	return $S(s);
};

/**
 * escape 메서드는 문자열을 겹따옴표에 포함될 수 있는 ASCI 문자열로 이스케이프 처리한다.
 * @remark \r, \n, \t, ', ", non-ASCII 문자를 이스케이프 처리한다.
 * @return {$S} 문자열을 이스케이프 처리한 새로운 $S 객체
 * @see $S#escapeHTML
 * @example
 * var str = '가"\'나\\';
 * document.write( $S(str).escape() );
 *
 * // 결과 :
 * \uAC00\"\'\uB098\\
 */
$S.prototype.escape = function() {
	var s = this._str.replace(/([\u0080-\uFFFF]+)|[\n\r\t"'\\]/g, function(m0,m1,_){
		if(m1) return escape(m1).replace(/%/g,'\\');
		return (_={"\n":"\\n","\r":"\\r","\t":"\\t"})[m0]?_[m0]:"\\"+m0;
	});

	return $S(s);
};

/**
 * bytes 메서드는 문자열의 실제 바이트(byte) 수를 반환한다.
 * @return 문자열의 바이트 수. 단, 첫번째 파라미터를 설정하면 자기 객체를 반환한다.
 * @param {Number} nBytes 맞출 글자 수
 * @remark 문서의 charset을 해석해서 인코딩 방식에 따라 한글을 비롯한 유니코드 문자열의 바이트 수를 가변적으로 계산한다. 유니코드 문서가 아니라면, 유니코드 문자열은 항상 2byte로 계산한다.
 * @example
 * // 문서가 euc-kr 환경임을 가정합니다.
 * var str = "한글과 English가 섞인 문장...";
 * document.write( $S(str).bytes() );
 *
 * // 결과 : 29
 */
$S.prototype.bytes = function(nBytes) {
	var code = 0, bytes = 0, i = 0, len = this._str.length;
	var charset = ((document.charset || document.characterSet || document.defaultCharset)+"").toLowerCase();
	var cut = (typeof nBytes == "number");

	if (charset == "utf-8") {
		// 유니코드 문자열의 바이트 수는 위키피디아를 참고했다(http://ko.wikipedia.org/wiki/UTF-8).
		for(i=0; i < len; i++) {
			code = this._str.charCodeAt(i);
			if (code < 128) bytes += 1;
			else if (code < 2048) bytes += 2;
			else if (code < 65536) bytes += 3;
			else bytes += 4;

			if (cut && bytes > nBytes) {
				this._str = this._str.substr(0,i);
				break;
			}
		}
	} else {
		for(i=0; i < len; i++) {
			bytes += (this._str.charCodeAt(i) > 128)?2:1;

			if (cut && bytes > nBytes) {
				this._str = this._str.substr(0,i);
				break;
			}
		}
	}

	return cut?this:bytes;
};

/**
 * parseString 메서드는 URL 쿼리 스트링을 객체로 파싱한다.
 * @return {Object} 문자열을 파싱한 객체
 * @example
 * var str = "aa=first&bb=second";
 * var obj = $S(str).parseString();
 *
 * // obj => { aa : "first", bb : "second" }
 */
$S.prototype.parseString = function() {
	var str = this._str.split(/&/g), pos, key, val, buf = {};

	for(var i=0; i < str.length; i++) {
		key = str[i].substring(0, pos=str[i].indexOf("="));
		val = decodeURIComponent(str[i].substring(pos+1));

		if (key.substr(key.length-2,2) == "[]") {
			key = key.substring(0, key.length-2);
			if (typeof buf[key] == "undefined") buf[key] = [];
			buf[key][buf[key].length] = val;
		} else {
			buf[key] = val;
		}
	}

	return buf;
};

/**
 * 정규식에 사용할 수 있도록 문자열을 이스케이프 한다.
 * @since 1.2.0
 * @return {String} 이스케이프된 문자열
 * @example
 * var str = "Slash / is very important. Backslash \ is more important. +_+";
 * document.write( $S(str).escapeRegex() );
 *
 * // 결과 :
 * // Slash \/ is very important\. Backslash \\ is more important\. \+_\+
 */
$S.prototype.escapeRegex = function() {
	var s = this._str;
	var r = /([\?\.\*\+\-\/\(\)\{\}\[\]\:\!\^\$\\\|])/g;

	return $S(s.replace(r, "\\$1"));
};

/**
 * format 메서드는 문자열을 형식 문자열에 대입한 새로운 문자열을 만든다. 형식 문자열은 %로 시작하며, 형식 문자열의 종류는 <a href="http://www.php.net/manual/en/function.sprintf.php">PHP</a>와 동일하다.
 * @param {String} formatString 형식 문자열
 * @return {String} 문자열을 형식 문자열에 대입하여 만든 새로운 문자열.
 * @example
var str = $S("%4d년 %02d월 %02d일").format(2008, 2, 13);
// str == "2008년 02월 13일"

var str = $S("패딩 %5s 빈공백").format("값");
// str == "패딩     값 빈공백"

var str = $S("%b").format(10);
// str == "1010"

var str = $S("%x").format(10);
// str == "a"

var str = $S("%X").format(10);
// str == "A"
 * @see $S#times
 */
$S.prototype.format = function() {
	var args = arguments;
	var idx  = 0;
	var s = this._str.replace(/%([ 0])?(-)?([1-9][0-9]*)?([bcdsoxX])/g, function(m0,m1,m2,m3,m4){
		var a = args[idx++];
		var ret = "", pad = "";

		m3 = m3?+m3:0;

		if (m4 == "s") {
			ret = a+"";
		} else if (" bcdoxX".indexOf(m4) > 0) {
			if (typeof a != "number") return "";
			ret = (m4 == "c")?String.fromCharCode(a):a.toString(({b:2,d:10,o:8,x:16,X:16})[m4]);
			if (" X".indexOf(m4) > 0) ret = ret.toUpperCase();
		}

		if (ret.length < m3) pad = $S(m1||" ").times(m3 - ret.length).toString();
		(m2 == '-')?(ret+=pad):(ret=pad+ret);

		return ret;
	});

	return $S(s);
};


/**
 * @fileOverview $ElementList의 생성자 및 메서드를 정의한 파일
 * @name elementlist.js
 */

/**
 * $ElementList 객체를 생성 및 리턴한다.
 * @class $ElementList 클래스는 id 배열, 혹은 CSS 쿼리 등을 사용하여 DOM 엘리먼트의 배열을 만든다.
 * @param {String | Array} els 문서에서 DOM 엘리먼트를 찾기 위한 CSS 선택자 혹은 id, HTMLElement, $Element의 배열
 * @constructor
 * @borrows $Element#show as this.show
 * @borrows $Element#hide as this.hide
 * @borrows $Element#toggle as this.toggle
 * @borrows $Element#addClass as this.addClass
 * @borrows $Element#removeClass as this.removeClass
 * @borrows $Element#toggleClass as this.toggleClass
 * @borrows $Element#fireEvent as this.fireEvent
 * @borrows $Element#leave as this.leave
 * @borrows $Element#empty as this.empty
 * @borrows $Element#appear as this.appear
 * @borrows $Element#disappear as this.disappear
 * @borrows $Element#className as this.className
 * @borrows $Element#width as this.width
 * @borrows $Element#height as this.height
 * @borrows $Element#text as this.text
 * @borrows $Element#html as this.html
 * @borrows $Element#css as this.css
 * @borrows $Element#attr as this.attr
 * @author Kim, Taegon
 */
function $ElementList(els) {
	var cl = arguments.callee;
	if (els instanceof cl) return els;
	if (!(this instanceof cl)) return new cl(els);

	if (els instanceof Array || ($A && els instanceof $A)) {
		els = $A(els);
	} else if (typeof els == "string" && cssquery) {
		els = $A(cssquery(els));
	} else {
		els = $A();
	}

	this._elements = els.map(function(v,i,a){ return $Element(v) });
}

/**
 * get 메서드는 $ElementList에서 인덱스에 해당하는 엘리먼트를 가져온다.
 * @param {Number} idx 가져올 엘리먼트의 인덱스. 인덱스는 0에서 부터 시작한다.
 * @return {$Element} 인덱스에 해당하는 엘리먼트
*/
$ElementList.prototype.get = function(idx) {
	return this._elements.$value()[idx];
};

/**
 * getFirst 메서드는 $ElementList의 첫번째 엘리먼트를 가져온다.
 * @remark getFirst 메서드의 리턴값은 $ElementList.get(0)의 리턴값과 동일하다.
 * @return {$Element} 첫번째 엘리먼트
*/
$ElementList.prototype.getFirst = function() {
	return this.get(0);
};

/**
 * getLast 메서드는 $ElementList의 마지막 엘리먼트를 가져온다.
 * @return {$Element} 마지막 엘리먼트
*/
$ElementList.prototype.getLast = function() {
	return this.get(Math.Max(this._elements.length-1,0));
};

(function(proto){
	var setters = 'show,hide,toggle,addClass,removeClass,toggleClass,fireEvent,leave,';
	setters += 'empty,appear,disappear,className,width,height,text,html,css,attr';

	$A(setters.split(',')).forEach(function(name){
		proto[name] = function() {
			var args = $A(arguments).$value();
			this._elements.forEach(function(el){
				el[name].apply(el, args);
			});

			return this;
		}
	});

	$A(['appear','disapeear']).forEach(function(name){
		proto[name] = function(duration, callback) {
			var len  = this._elements.length;
			var self = this;
			this._elements.forEach(function(el,idx){
				if(idx == len-1) el[name](duration, function(){callback(self)});
				else el[name](duration);
			});
		}
	});
})($ElementList.prototype);

/**
 * @fileOverView $Window의 생성자 및 메서드를 정의한 파일
 * @name window.js
 */

/**
 * $Window 객체를 생성하고 생성한 객체를 반환한다.
 * @class $Window 객체는 자바스크립트 네이티브 객체인 window 객체를 래핑하고, 이를 다루기 위한 여러가지 메서드를 제공한다.
 * @param {HTMLWidnow} el $Window로 감쌀 window 엘리먼트.
 * @author gony
 */
function $Window(el) {
	var cl = arguments.callee;
	if (el instanceof cl) return el;
	if (!(this instanceof cl)) return new cl(el);

	this._win = el || window;
}

/**
 * $value 메서드는 $Window 객체로 감싼 원래의 window 객체를 반환한다.
 * @return {HTMLWindow} window 엘리먼트
 */
$Window.prototype.$value = function() {
	return this._win;
};

/**
 * resizeTo 메서드는 window 객체의 크기를 주어진 크기로 변경한다.
 * 이 크기는 프레임을 포함한 창 전체의 크기를 나타내므로 실제로 표현하는 컨텐트 사이즈는
 * 브라우저 종류와 설정에 따라 달라질 수 있다.
 * @remark 브라우저에 따라 보안 문제 때문에, 창 크기가 화면의 가시 영역을 벗어나서 커지지 못하도록 막는 경우도 있다. 이 경우에는 지정한 크기보다 작게 창이 커진다.
 * @param {Number} nWidth 창의 너비
 * @param {Number} nHeight 창의 높이
 * @return {$Window} 창 크기가 변경된 $Window 객체 this
 * @see $Window#resizeBy
 * @example
 * 	// 현재 창의 너비를 400, 높이를 300으로 변경한다.
 *  $Window.resizeTo(400, 300);
 */
$Window.prototype.resizeTo = function(nWidth, nHeight) {
	this._win.resizeTo(nWidth, nHeight);
	return this;
};

/**
 * resizeBy 메서드는 window 객체의 크기를 주어진 크기만큼 변경한다.
 * @param {Number} nWidth 늘어날 창의 너비
 * @param {Number} nHeight 늘어날 창의 높이
 * @see $Window#resizeTo
 * @example
 *   // 현재 창의 너비를 100, 높이를 50 만큼 늘린다.
 *   $Window().resize(100, 50);
 */
$Window.prototype.resizeBy = function(nWidth, nHeight) {
	this._win.resizeBy(nWidth, nHeight);
	return this;
};

/**
 * moveTo 메서드는 창을 주어진 위치로 이동시킨다. 좌표는 프레임을 포함 윈도우의 좌측 상단을 기준으로 한다.
 * @param {Number} nLeft 이동할 x좌표 (pixel 단위)
 * @param {Number} nTop 이동할 y좌표 (pixel 단위)
 * @see $Window#moveBy
 * @example
 *  // 현재 창을 (15, 10) 으로 이동시킨다.
 *  $Window().moveTo(15, 10);
 */
$Window.prototype.moveTo = function(nLeft, nTop) {
	this._win.moveTo(nLeft, nTop);
	return this;
};

/**
 * moveTo 메서드는 창을 주어진 위치만큼 이동시킨다.
 * @param {Number} nLeft x좌표로 이동할 양 (pixel 단위)
 * @param {Number} nTop y좌표 이동할 양 (pixel 단위)
 * @see $Window#moveTo
 * @example
 *  // 현재 창을 좌측으로 15px, 아래로 10px만큼 이동시킨다.
 *  $Window().moveBy(15, 10);
 */
 $Window.prototype.moveBy = function(nLeft, nTop) {
	this._win.moveBy(nLeft, nTop);
	return this;
};

/**
 * sizeToContent 메서드는 내부 문서 크기에 맞추어 객체의 크기를 변경한다.
 * @remark 메서드의 특성상 내부 문서가 완전히 로딩된 다음에 실행되어야 한다. 또한, 창이 내부 문서보다 큰 경우에는 내부 문서를 구할 수 없으므로, 반드시 창 크기를 내부 문서보다 작게 만든다.
 * @example
 * // 새 창을 띄우고 자동으로 창 크기를 컨텐트에 맞게 변경하는 함수
 * function winopen(url) {
 *		try {
 *			win = window.open(url, "", "toolbar=0,location=0,status=0,menubar=0,scrollbars=0,resizable=0,width=250,height=300");
 *			win.moveTo(200, 100);
 *			win.focus();
 *		} catch(e){}
 *
 *		setTimeout(function() {
 *			$Window(win).sizeToContent();
 *		}, 1000);
 *
 *	}
 *
 * winopen('/samples/popup.html');
 */
$Window.prototype.sizeToContent = function() {
	if (typeof this._win.sizeToContent == "function") {
		this._win.sizeToContent();
	} else {
		var doc = $Document(this._win.document);
		var clientSize = doc.clientSize();
		var scrollSize = doc.scrollSize();

		this.resizeBy(scrollSize.width - clientSize.width, scrollSize.height - clientSize.height);
	}

	return this;
};
/**
 * @author hooriza (ajaxUI team)
 */

var SPS = { __inst : {} };
SPS.debugMode = true; // ?debug=true 일 경우에 디버깅 모드를 쓰려면

SPS.learningLang = ''; // 학습언어 (en/zh)
SPS.autoCompleteURL = 'http://devac.nciku.com:8080';

// 각종 메세지
SPS.text = function(sKey) {
	var sMsg = SPS.text[sKey];
	sMsg = sMsg || 'The language file is not included';

	for (var i = 1, sVal; sVal = arguments[i]; i++)
		sMsg = sMsg.replace('%' + i, sVal);

	// 맨 앞에 오는 영어는 대문자로 변경
	return sMsg.replace(/^(.)/, function(e) { return e.toUpperCase(); });;
};

SPS.text.set = function(oData) {
	for (var k in oData) SPS.text[k] = oData[k];
};

// NHN Flash UI common - Flash Contents
// v1.0 lastUpdate : 2007. 6. 14 (modified by hooriza - 20071019)
(function() {

	var fc_isIE  = (navigator.appVersion.indexOf("MSIE") != -1) ? true : false;
	var fc_isWin = (navigator.appVersion.toLowerCase().indexOf("win") != -1) ? true : false;
	var fc_isOpera = (navigator.userAgent.indexOf("Opera") != -1) ? true : false;

	window.checkFlashPlayerVersion = function(){
		var _version;
		var _e;
		try {
			var _axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.5");		// check flash player ver. 5 or higher
			_version = _axo.GetVariable("$version");
		} catch (_e) {
			_version = -1;
		}
		return _version;
	};

	window.showFlash = function(_swfURL_,_flashID_,_width_,_height_,_wmode_,_flashVars_,_bgColor_){
		_wmode_ = (_wmode_ == undefined)? "transparent" : _wmode_;		// wmode = "window/ opaque/ transparent"
		_bgColor_ = (_bgColor_ == undefined)? "#FFFFFF" : _bgColor_;	// default "#000000" -> "#FFFFFF" _change 2007. 6. 14

		if(fc_isIE && fc_isWin && !fc_isOpera){
			_object_ ='<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" width="'+_width_+'" height="'+_height_+'" id="'+_flashID_+'" align="middle">';
			_object_ += '<param name="allowScriptAccess" value="always" />';
			_object_ += '<param name="quality" value="high" />';
			_object_ += '<param name="movie" value="'+_swfURL_+'" />';
			_object_ += '<param name="wmode" value="'+_wmode_+'" />';
			_object_ += '<param name="bgcolor" value="'+_bgColor_+'" />';
			_object_ += '<param name="FlashVars" value="'+_flashVars_+'">';
			_object_ += '</object>';
		}else{
			_object_ = '<embed src="'+_swfURL_+'" quality="high" wmode="'+_wmode_+'" FlashVars="'+_flashVars_+'" bgcolor="'+_bgColor_+'" width="'+_width_+'" height="'+_height_+'" name="'+_flashID_+'" align="middle" allowScriptAccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" />';
		}

		document.write(_object_);

	};

	window.findFlashObj = function(_flashID_){
		var tarObj = fc_isIE ? document.all[_flashID_] : document[_flashID_];
		return tarObj;
	};

	// for 'Out of memory line at 56' error _add 2007. 6. 12
	window.flashExternalCleanup = function() {
		__flash_unloadHandler = function(){
			externalProbSet = true;
			obj = document.getElementsByTagName('OBJECT');
		  	for (i=0;i<obj.length;i++){
				var theObj = eval(obj[i]);
				theObj.style.display = "none";
		   	for (var prop in theObj){
		    		if (typeof(theObj[prop]) == "function"){
		     			try { theObj[prop]=null } catch(e) {}
		    		}
		   	}
		  	}
		}
		if (window.onunload != __flash_unloadHandler){
			__flash_savedUnloadHandler = window.onunload;
			window.onunload = __flash_unloadHandler;
		}
	};

	window.onbeforeunload=flashExternalCleanup;

	/////////////////////////

})();

/**
 * debug hotkey
 */
/*
(function() {

	var __DEBUG = {
		codes : [ 68, 69, 66, 85, 71, 38, 39, 40, 37, 39, 37, 39, 37, 16, 192 ],
		index : 0
	};

	function getMatched(keyCode) {

		var matched = __DEBUG.codes[__DEBUG.index] == keyCode;
		if (matched) __DEBUG.index++;

		return matched;

	}

	$Fn(function(oEvent) {

		var keyCode = oEvent.key().keyCode;
		var matched = getMatched(keyCode);
		if (!matched) {
			__DEBUG.index = 0;
			matched = getMatched(keyCode);
		}

		if (__DEBUG.index == __DEBUG.codes.length) {

			SPS.debugMode = true;
			alert('DEBUG MODE turn on!');

			__DEBUG.index = 0;
		}

	}).attach(document, 'keydown');

})();
*/

// prototype override
(function() {

	// toHTML
	String.prototype.toHTML = function() {
		return this.replace(/</g, '&lt;').replace(/>/g, '&gt;');
	};

	// toValue
	String.prototype.toValue = function() {
		return this.replace(/&lt;/g, '<').replace(/&gt;/g, '>');
	};

	// repeat
	String.prototype.repeat = function(n) {
		var r = '';
		for (var i = 0; i < n; i++) r += this;
		return r;
	};

	// stripTags
	String.prototype.stripTags = function() {
		return this.replace(/<[^>]*>/g, '');
	};

	// trim
	String.prototype.trim = function() {
		return this.replace(/^\s+/, '').replace(/\s+$/, '');
	};

	// bytesLength
	String.prototype.bytesLength = function() {

		var nRet = 0;

		for (var i = 0, nLen = this.length; i < nLen; i++)
			nRet += this.charCodeAt(i) > 128 ? 2 : 1;

		return nRet;

	};

	// bytesCut
	String.prototype.bytesCut = function(nMaxLength) {

		var nRet = 0;

		for (var i = 0, nLen = this.length; i < nLen; i++) {
			var nCharLen = this.charCodeAt(i) > 128 ? 2 : 1;
			if (nMaxLength < nRet + nCharLen)
				return this.substr(0, i);
			nRet += nCharLen;
		}

		return this;

	};

	// 받침이 있는지
	String.prototype.hasFinalConsonant = function() {

		var datas = "가나다라마바사아자차카타파하";

		var first, last;
		var code = this.charCodeAt(this.length - 1);

		for (var i = 0; i < datas.length; i++) {

			first = datas.charCodeAt(i);
			last = first + 560;

			if (code >= first && code <= last && (code - first) % 14 == 0)
				return false;
		}

		return true;

	};

	// 조사 붙히기
	String.prototype.addPostword = function(sPostword) {

		var case1, case2;

		switch(sPostword)
		{
		case "를":
		case "을":
			case1 = "를";
			case2 = "을";
			break;

		case "가":
		case "이":
			case1 = "가";
			case2 = "이";
			break;

		case "는":
		case "은":
			case1 = "는";
			case2 = "은";
			break;

		case "와":
		case "과":
			case1 = "와";
			case2 = "과";
			break;

		default:
			case1 = case2 = what;
		}

		return this + (this.hasFinalConsonant() ? case2 : case1);

	};

	// Jindo2 기능 확장
	$Element.prototype.has = function(oElement) {

		var n = this.$value();
		var o = $Element(oElement).$value();

		for (; o; o = o.parentNode)
			if (o == n) return true;

		return false;
	};

	/*
	$Element.prototype.hasClass = function(className) {
		var oEl = this.$value();
		return (" " + oEl.className + " ").indexOf(" " + className + " ") != -1;
	};

	$Element.prototype.addClass = function(className) {
		var oEl = this.$value();
		if (!this.hasClass(className)) oEl.className = (oEl.className+' '+className).replace(/^\s+/,'');
	};

	$Element.prototype.removeClass = function(className) {
		var oEl = this.$value();
		for (var i = 0, className; className = arguments[i]; i++)
			oEl.className = oEl.className.replace(new RegExp('\\b' + className + '(\\s+|$)', 'g'), '');
	};
	*/

	$Element.prototype.findElement = function(sRelative, fCallback, bIncl) {

		var oEl = this.$value();
		fCallback = fCallback || function(oEl) { return true; }

		if (!bIncl) oEl = oEl[sRelative];

		for (; oEl; oEl = oEl[sRelative])
			if (oEl.nodeType == 1 && fCallback(oEl))
				return $Element(oEl);

		return null;

	};

	$Event.prototype.getRelatedElement = function() {

		var e = this._event;
		return e.relatedTarget || (e.type == "mouseover" ? e.fromElement : e.toElement);

	};

	$Event.prototype.of = function(parent) {

		var el;
		var ret = true;

		var bMustHaveRelated = this._event.type == 'mouseover' || this._event.type == 'mouseout';

		if (el = this.element)
			ret &= $Element(parent).has(el);

		if (el = this.getRelatedElement())
			ret &= $Element(parent).has(el);
		else if (bMustHaveRelated)
			ret = false;

		return ret;
	};

	$Fn.prototype.owner = function(oThis) {

		var f = this.$value();

		return $Fn(function() { return f.apply(oThis, arguments); });

	};

	$Element.prototype._orgOffset = $Element.prototype.offset;

	$Element.prototype.offset = function(top, left) {
		var e = this._element;
		var t = 0, l = 0;

		if (typeof top == "number" && typeof left == "number") {
			return this._orgOffset(top, left);
		}

		while(typeof e != "undefined" && e != null) {
			t += e.offsetTop;
			l += e.offsetLeft;

			e = e.offsetParent;

			if (e && e.tagName) {

				var sTagName = e.tagName.toUpperCase();
				if (sTagName != 'BODY' && sTagName != 'HTML') {
					t -= e.scrollTop;
					l -= e.scrollLeft;
				}

			}

		}

		return {top:t, left:l};
	}
})();

/**
 * 기타 잡다구리한 Misc 함수들
 * @author hooriza (ajaxUI)
 */
(function() {

	SPS.uniqid = function() {
		return (new Date().getTime() % 31536000000) * 100 + parseInt(Math.random() * 100);
	};

	SPS.dirname = function(sPath) {

		var nPos = sPath.lastIndexOf('/');
		if (nPos == -1) return '';

		return sPath.substr(0, nPos + 1);

	};

	SPS.setCookie = function(sKey, sValue, nExpire, sPath, sDomain) {

		var sOptions = '';

		sPath = sPath || '/'; // 사이트 전체에 적용되게 하자

		if (typeof nExpire != 'undefined') {

			var oExpire = new Date();
			oExpire.setDate(oExpire.getDate() + nExpire);

			sOptions += ';expires=' + oExpire.toGMTString();

		}

		if (typeof sPath != 'undefined') sOptions += ';path=' + sPath;
		if (typeof sDomain != 'undefined') sOptions += ';domain=' + sDomain;

		document.cookie = sKey + '=' + escape(sValue) + sOptions;

	};

	SPS.getCookie = function(sKey) {

		var sCookie = document.cookie;
		if (sCookie.length < 1) return;

		var rReg = new RegExp('\\b' + sKey + '=([^;]*)(;|$)');
		var aMatch = sCookie.match(rReg);

		return aMatch && aMatch[1];

	};

	/**
	 * 사파리에서 input[type=text] 의 border 가 사라지지 않는 문제 다른 방법으로 회피
	 * @author hooriza (ajaxUI)
	 */
	SPS.borderNoneSafari = function(oEl) {

		if (!$Agent().navigator().safari) return;

		var eWrap;
		var eEl = $Element(oEl);
		if (eEl.hasClass('_border_none_safari')) {

			eWrap = $Element(oEl.parentNode);

		} else {

			eWrap = $Element($("<div>"));
			eEl.addClass('_border_none_safari');

			eEl.before(eWrap);

			eEl.css({
				margin : '0px',
				position : 'relative',
				left : '-1px',
				top : '-2px'
			});

			eWrap.append(eEl);

		}

		eWrap.css({
			width : oEl.offsetWidth - 6 + 'px',
			height : oEl.offsetHeight - 5 + 'px',
			padding : '0',
			margin : '0',
			overflow : 'hidden',
			display : 'inline-block',
			'float' : 'left'
		});

		return eWrap;

	};

	SPS.getParentForm = function(oEl) {

		for (; oEl; oEl = oEl.parentNode)
	 		if (oEl.tagName && oEl.tagName.toUpperCase() == 'FORM') return oEl;

		return oEl;

	};

	SPS.setBeforeSubmit = function(oForm, sButtonSelector, fValidator) {

		fValidator = fValidator || function() { return true; };

		var oSubmit = $$(sButtonSelector, oForm)[0];
		var fCheck = function(oEvent) {

			if (!fValidator()) {
				oEvent.stop();
				return false;
			}

			SPS.protectLossData.setChanged(false);
			return true;

		};

		$Fn(fCheck).attach(oForm, 'submit');

		$Fn(function(oEvent) {

			if (fCheck(oEvent))
				oForm.submit();

			oEvent.stop();

		}).attach(oSubmit, 'click');

	};

	SPS.getJsonList = function(oForm, aKeys) {

		var aList = [];
		var oObj = null;

		var bSingle = typeof aKeys == 'string';

		for (var i = 0, oEl; oEl = oForm[i]; i++) {

			var sName = oEl.name;
			if (oEl.type == 'radio' && !oEl.checked) continue;

			if (sName && (/\[[0-9]*\]$/).test(sName)) {

				sName = sName.replace(/\[[0-9]*\]$/, '');

				if (bSingle) {

					if (aKeys != sName) continue;
					aList.push(oEl.value);

				} else {

					if ($A(aKeys).indexOf(sName) < 0) continue;
					if (!oObj) oObj = {};

					if (typeof oObj[sName] != 'undefined') {
						aList.push(oObj);
						oObj = {};
					}

					oObj[sName] = oEl.value;

				}

			}

		}

		if (!bSingle) aList.push(oObj);
		return $Json(aList).toString();

	};

	SPS.moveLocationToValue = function(oEl) {

		var sVal = SPS.getFormValue(oEl);
		if (sVal) location.href = sVal;

	};

	SPS.autoPosition = function(oEl, oLayer, aGap, bUpper) {

		aGap = aGap || [ 0, 0 ];

		var oPos = $Element(oEl).offset();
		var nPitch = bUpper ? -(oLayer.offsetHeight + aGap[1]) : oEl.offsetHeight + aGap[1];

		$Element(oLayer).css({ left : oPos.left + aGap[0] + 'px', top : oPos.top + nPitch + 'px' });

	};

	SPS.parseQueryString = function(sQuery) {

		var aQuery = sQuery.split('&');
		var oQuery = {};

		for (var i = 0, nLen = aQuery.length; i < nLen; i++) {
			var aPart = aQuery[i].split('=', 2);

			try { aPart[0] = decodeURIComponent(aPart[0]); } catch(e) {}
			try { aPart[1] = decodeURIComponent(aPart[1]); } catch(e) {}

			oQuery[aPart[0]] = aPart[1];
		}

		return oQuery;

	};

	SPS.makeQueryString = function(oQuery) {

		var aQuery = [];

		$H(oQuery).forEach(function(sValue, sKey) {
			aQuery.push(encodeURIComponent(sKey) + '=' + encodeURIComponent(sValue));
		});

		return aQuery.join('&');

	};

	SPS.query = SPS.parseQueryString(location.search.substr(1));
	SPS.debugMode = SPS.debugMode ? SPS.query.debug == 'true' : false;

	SPS.decToHex = function(nNum) {

		var sHex = '';
		var aHexes = [ 'A', 'B', 'C', 'D', 'E', 'F' ];

		do {
			var n = nNum % 16;
			sHex = (n >= 10 ? aHexes[n - 10] : String(n)) + sHex;
			nNum = parseInt(nNum / 16);
		} while (nNum);

		return sHex;

	};

	SPS.isCharCode = function(nCode) {

		if (
				(nCode >= 8 && nCode <= 46) ||
				(nCode >= 91 && nCode <= 105) ||
				(nCode == 110) ||
				(nCode >= 112 && nCode <= 123) ||
				(nCode == 144)
		) {
			return false;
		}

		return true;

	};

	SPS.getParentByClassName = function(oEl, sClassName) {

		var eEl = $Element(oEl);

		eEl = eEl.hasClass(sClassName) ? eEl : eEl.findElement('parentNode', function(oEl) { return $Element(oEl).hasClass(sClassName); });
		return eEl;

	};

	SPS.addOption = function(oSelectbox, sText, sValue) {

		var oOption = $('<option>');

		oOption.innerHTML = sText.toHTML();
		oOption.value = sValue;

		oSelectbox.appendChild(oOption);

		return oSelectbox.length - 1;

	};

	SPS.extend = function(oSrc, oExt) {
		$H(oExt).forEach(function(oVal, sKey) { oSrc[sKey] = oVal; });
		return oSrc;
	};

	/** 폼을 Ajax 로 submit 함
	 *
	 * @author hooriza (ajaxUI team)
	 */
	SPS.ajaxSubmit = function(oForm, onLoad) {

		onLoad = onLoad || function() {};

		var sUrl = oForm.action;

		var oAjax = $Ajax(sUrl, {
			type : oForm.method || 'get',
			onload : onLoad
		});

		var oData = {};
		var oEl;

		for (var i = 0, oEl; oEl = oForm[i]; i++) {
			if (!oEl.name) continue;
			oData[oEl.name] = oEl.value;
		}

		oAjax.request(oData);

	};

	SPS.getFormValue = function(oEl) {

		var sValue = null;

		var sTagName = oEl.tagName.toUpperCase();
		if (sTagName != 'INPUT' && sTagName != 'TEXTAREA' && sTagName != 'SELECT') return null;

		if (sTagName == 'SELECT' && oEl.selectedIndex > -1) {

			sValue = oEl.options[oEl.selectedIndex].getAttribute('value') || null;

		} else {

			var sType = oEl.type.toUpperCase();

			if (sType == 'CHECKBOX') {
				sValue = oEl.checked ? oEl.value : null;
			} else if (sType == 'RADIO') {

				var oDoc = oEl.ownerDocument || document;
				var aRadios = oDoc.getElementsByName(oEl.name);
				for (var i = 0, oRadio; oRadio = aRadios[i]; i++)
					if (oRadio.checked) sValue = oRadio.value;

			} else {
				sValue = oEl.value;
			}

		}

		return sValue;

	};

	SPS.login = function (bEl,sGoURL,sSubURL){

		var sCurLocation = document.location.href;
		var sGenLocation = new String();
		var sGoLocation = new String();

		switch (bEl){
			case true:
				// Login
					sGentLocation = sSubURL + encodeURIComponent(sCurLocation);
				break;
			case false:
				// Logout
					sGentLocation = sSubURL;
				break;
		}

		sGentLocation = encodeURIComponent(sGentLocation);
		sGoLocation = sGoURL + "?go=" + sGentLocation;

		document.location.href = sGoLocation;

	}

})();

/**
 * @author hooriza (ajaxUI team)
 * 엘리먼트의 클래스명을 이용한 Form Validator
 *
 * 사용방법 :
 *	엘리먼트의 class 어트리뷰트에 지정된 클래스를 추가
 *	예) <input type="text" class="notnull" />
 *
 * 조건 클래스 종류
 *	notnull										: 반드시 채워져 있어야 함
 *  notspace									: 반드시 채워져 있고 빈칸만으로만 이루어지지 않아야 함
 *	name(STRING)							: 유효성 검사에서 걸렸을때 onValidate 로 전달되는 값
 *
 * 판단 클래스 종류 :
 *	length(INTEGER~INTEGER)					: 허용 문자열 수
 *	group(STRING)										: 상호 동일해야 하는 값의 그룹
 *	number(NUMBER~NUMBER)						: 숫자만 가능 (영역 지정은 옵션)
 *	integer(INTEGER~INTEGER)				: 정수만 가능 (영역 지정은 옵션)
 *	only(E1,E2,E3)									: E1, E2, E3 로만 구성되어야 함
 *	deny(E1,E2,E3)									: E1, E2, E3 는 하나도 없어야 함
 *	TODO : any(E1,E2,E3)							: E1, E2, E3 가 하나라도 있어야함
 *		chinese (한자)
 *		korean (한글)
 *		english (영문)
 *		number (숫자)
 *
 *	TODO : callback(FUNCTION_NAME)	: 유효성 검사를 위한 콜백함수 지정
 */
(function() {

	SPS.validator = {

		_prefix : '-', // 클래스 prefix

		_rules : 'notnull,notspace,length,group,only,deny,any,number,integer,callback'.split(','),
		_defaultOnInvalid : function(oEl, sType, sName, sError, oParam) {

			if (sError) {

				switch (sType) {
				case 'integer':

					if (sError == 'INTEGER_TEST')
						if (oParam.value < 1 || isNaN(oParam.value))
							sError = 'INTEGER_TEST_NONZERO';

					alert(SPS.text(sError, sName, oParam.from, oParam.to));
					break;

				case 'length':
					alert(SPS.text(sError, sName, oParam.from, oParam.to));
					break;

				default:
					alert(SPS.text(sError, sName));
					break;
				}

			} else {

				switch (sType) {
				case 'notnull':
				case 'notspace':
					alert(SPS.text('NOTNULL', sName));
					break;

				case 'integer':
					alert(SPS.text('INTEGER', sName, oParam.from, oParam.to));
					break;

				case 'length':
					alert(SPS.text('LENGTH', sName, parseInt(oParam.to / 2), oParam.to));
					break;

				case 'any':

					var dummy = SPS.validator;

					var sValue = SPS.getFormValue(oEl);
					var rRegExp = new RegExp('(' + dummy._filters.chinese + '|' + dummy._filters.english + ')');

					if (oParam['catch'] == 'english') {

						// 영어도 중국어도 모두 없으면 true
						if (sValue.search(rRegExp) == -1) return true;

						alert(SPS.text('ANY_ENGLISH', sName));
						break;

					} else if (oParam['catch'] == 'chinese') {

						// 영어도 중국어도 모두 없으면 true
						if (sValue.search(rRegExp) == -1) return true;

						alert(SPS.text('ANY_CHINESE', sName));
						break;

					}

				default:
					alert(SPS.text('OTHER', sName));
				}

			}

			try {
				oEl.focus();
				oEl.select();
			} catch(e) {}

			return false;

		},

		_getRange : function(sArg, bFloat) {

			var aRange = sArg.split('~');

			if (bFloat) aRange = [ parseFloat(aRange[0]), parseFloat(aRange[1]) ];
			else aRange = [ parseInt(aRange[0], 10), parseInt(aRange[1], 10) ];

			if (isNaN(aRange[0])) aRange[0] = -Infinity;
			if (isNaN(aRange[1])) aRange[1] = Infinity;

			return aRange;

		},

		_filters : {
			'chinese' : '[一-龥]', // '[\\u4E00-\\u9FFF]', for Safari
			'korean' : '[ㄱ-힣]',
			'english' : '[A-Z|a-z]',
			'number' : '[0-9]',
			'url' : '(http|https|ftp)://\\w+(\.\\w+)+(/.*)?'
		},

		_functions : {

			'notnull' : function(oEl, sValue) {
				return (typeof sValue == 'string' && sValue.length > 0) ? false : {};
			},

			'notspace' : function(oEl, sValue) {
				return (typeof sValue == 'string' && sValue.trim().length > 0) ? false : {};
			},

			'length' : function(oEl, sValue, sArg) {

				var aRange = SPS.validator._getRange(sArg);
				var nLen = sValue.bytesLength();

				return (aRange[0] <= nLen && nLen <= aRange[1]) ? false : {
					'from' : aRange[0],
					'to' : aRange[1]
				};

			},

			'only' : function(oEl, sValue, sArg) {

				var aTypes = sArg.split(',');
				var aFilters = [];

				$A(aTypes).forEach(function(sType) {
					aFilters.push(SPS.validator._filters[sType]);
				});

				var rRegex = new RegExp('^(' + aFilters.join('|') + ')*$');
				return (rRegex.test(sValue)) ? false : {
					'types' : aTypes
				};

			},

			'deny' : function(oEl, sValue, sArg) {

				var aTypes = sArg.split(',');
				var bPassed = true;
				var rRegex;

				var sCatch = '';
				var bCatch;

				$A(aTypes).forEach(function(sType) {

					rRegex = new RegExp(SPS.validator._filters[sType]);
					bCatch = (sValue.search(rRegex) > -1 ? false : true);
					bPassed = bPassed && bCatch;

					if (!bCatch) {
						sCatch = sType;
						$A.Break();
					}
				});

				return bPassed ? false : {
					'type' : aTypes,
					'catch' : sCatch
				};

			},

			'any' : function(oEl, sValue, sArg) {

				var aTypes = sArg.split(',');
				var bPassed = true;
				var rRegex;

				var sCatch = '';

				$A(aTypes).forEach(function(sType) {
					rRegex = new RegExp(SPS.validator._filters[sType]);
					if (sValue.search(rRegex) == -1) {
						bPassed = false;
						sCatch = sType;
						$A.Break();
					}
				});

				return bPassed ? false : {
					'type' : aTypes,
					'catch' : sCatch
				};

			},

			'number' : function(oEl, sValue, sArg) {

				var rExp = /^[-+]?[0-9]+(\.[0-9]+)?$/;
				if (!rExp.test(sValue)) sValue = 'NaN';

				var nValue = parseFloat(sValue);

				if (typeof sArg != 'undefined') {

					var aRange = SPS.validator._getRange(sArg, true);
					return (!isNaN(nValue) && aRange[0] <= nValue && nValue <= aRange[1]) ? false : {
						'value' : nValue,
						'from' : aRange[0],
						'to' : aRange[1]
					};

				}

				return false;

			},

			'integer' : function(oEl, sValue, sArg) {

				var rExp = /^[-+]?[0-9]+(\.[0-9]+)?$/;
				if (!rExp.test(sValue)) sValue = 'NaN';

				var nValue = parseInt(sValue, 10);

				if (typeof sArg != 'undefined') {

					var aRange = SPS.validator._getRange(sArg);

					return (!isNaN(nValue) && aRange[0] <= nValue && nValue <= aRange[1]) ? false : {
						'value' : nValue,
						'from' : aRange[0],
						'to' : aRange[1]
					};

				}

				return false;

			},

			'group' : function(oEl, sValue, sArg, oVars) {

				if (oVars.oGroups[sArg]) return false;
				oVars.oGroups[sArg] = true;

				var bMatch = true;
				var rExp = new RegExp('\\bgroup\\(' + sArg + '\\)(\\s|$)');

				for (var i = 0, oDst; oDst = oVars.aEls[i]; i++) {

					if (oDst.nodeType != 1 || oEl === oDst) continue;
					if (rExp.test(oDst.className))
						if (oEl.value != oDst.value) bMatch = false;

				}

				return bMatch ? false : {};

			}

		},

		_fireEvent : function(fHandler, oEl, sType, sError, oParam) {

			var bFlag;
			var sClassName = oEl.className;

			var aMatch = sClassName.match(/\bname\(([^)]*)\)(\s|$)/);
			var sName = aMatch ? aMatch[1] : 'undefined';

			fHandler = fHandler || this._defaultOnInvalid;
			return fHandler(oEl, sType, sName, sError, oParam);

		},

		validate : function(oForm, oOptions) {

			var bValid = true;

			if (oForm instanceof Array) {

				for (var i = 0, len = oForm.length; i < len; i++)
					if (!this.validate(oForm[i], oOptions))
						return false;

				return true;

			}

			var aEls = oForm.all || oForm.getElementsByTagName('*');
			oOptions = oOptions || {};

			var oVars = {
				'aEls' : aEls,
				'oGroups' : { },
				'oRadios' : { }
			};

			aEls[aEls.length] = oForm;

			for (var i = 0, oEl; oEl = aEls[i]; i++) {

				if (oEl.nodeType != 1) continue;

				var bFlag = this._validateElement(oEl, oOptions, oVars);
				bValid &= bFlag;

				if (bFlag === null) return bValid; // validate 을 바로 멈춤

			}

			return bValid;

		},

		_validateElement : function(oEl, oOptions, oVars) {

			var sTagName = oEl.tagName.toUpperCase();
			if (sTagName != 'INPUT' && sTagName != 'TEXTAREA' && sTagName != 'SELECT') return true;

			var bValid = true;

			var sValue = SPS.getFormValue(oEl);
			var sClassName = oEl.className;

			if (sTagName == 'INPUT' && oEl.type.toUpperCase() == 'RADIO') { // 라디오 버튼에서 같은 name 에서의 체크는 한번만

				var sName = oEl.name;

				if (oVars.oRadios[sName]) return true;
				oVars.oRadios[sName] = true;

			}

			for (var i = 0, sKey; sKey = this._rules[i]; i++) {

				// var rExp = new RegExp('\\b(' + sKey + ')(\\(([^)]*)\\))?(\\s|$)');
				var rExp = new RegExp('\\b(' + sKey + ')(\\((([^:)]+):)?([^)]*)\\))?(\\s|$)');
				var aMatch = sClassName.match(rExp);
				if (!aMatch) continue;

				var oParam;

				var fFunc = this._functions[aMatch[1]];
				if (typeof fFunc == 'function') { // validator 함수가 있으면 검사

					oParam = fFunc(oEl, sValue, aMatch[5], oVars);
					var bFlag = oParam ? false : true;

					if (!bFlag) { // validator 함수에 걸렸으면
						if (!this._fireEvent(oOptions.onInvalid, oEl, aMatch[1], aMatch[4], oParam)) // 이벤트 날리고 false 이면 validate 중지
							return null;
						else // onInvalid 가 true 리턴하면 성공한걸로 간주
							bFlag = true;
					}

					bValid &= bFlag;
				}

			}

			return bValid;

		}

	};

})();

/**
 * 폼에서 변경 사항 있을때 자동 경고창
 *
 * @author hooriza (ajaxUI team)
 */
SPS.protectLossData = function(oEl, sWarnMsg) {

	if (typeof SPS.protectLossData._changed == 'undefined') {
		SPS.protectLossData._changed = false;
		SPS.protectLossData._message = sWarnMsg || 'Warning!';
	}

	oEl = oEl || document;

	var aInputs = oEl.getElementsByTagName('input');
	var aTextareas = oEl.getElementsByTagName('textarea');
	var aSelects = oEl.getElementsByTagName('select');

	var fChanged = $Fn(function() {
		SPS.protectLossData.setChanged(true);
	});

	var fDetectEls = function(aEls) {

		$A(aEls).forEach(function(oEl) {

			var eEl = $Element(oEl);
			if (eEl.hasClass('_protect_loss')) return;

			fChanged.attach(oEl, 'change');

			eEl.addClass('_protect_loss');

		});

	};

	fDetectEls(aInputs);
	fDetectEls(aTextareas);
	fDetectEls(aSelects);

};

SPS.protectLossData.setChanged = function(bFlag) {
	SPS.protectLossData._changed = bFlag;

	if (bFlag) {
		window.onbeforeunload = function() { return SPS.protectLossData._message; };
	} else {
		window.onbeforeunload = null;
	}

};

/**
 * 타이머
 *
 * @author hooriza (ajaxUI team)
 */
SPS.timer = $Class({

	_bInterval : false,
	_oInstance : null,

	$init : function(sType) {
		this._bInterval = sType.toLowerCase() == "interval";
	},

	start : function(nTime, fCallback) {

		this.stop();

		if (this._bInterval) {

			this._oInstance = window.setInterval(fCallback, nTime);

		} else {

			var self = this;

			this._oInstance = window.setTimeout(function() {
				fCallback();
				self.stop();
			}, nTime);

		}

	},

	stop : function() {

		var fFunc = window[this._bInterval ? "clearInterval" : "clearTimeout"];

		if (this._oInstance)
			fFunc(this._oInstance);

		this._oInstance = null;

	}

});

/**
 * 지연된 요청
 *
 * @author hooriza (ajaxUI team)
 */
SPS.delayedRequest = $Class({

	_timer : null,

	_delay : null,
	_count : null,

	_remained : null,

	$init : function(nDelay, nCount) {

		this._timer = new SPS.timer('timeout');

		this._delay = nDelay;
		this._count = nCount;

		this._remained = this._count;

	},

	request : function(fFunc, oOwner) {

		var aArgs = [];

		for (var i = 2, len = arguments.length; i < len; i++)
			aArgs.push(arguments[i]);

		// 지금 바로 실행해야 되면
		if (this._remained-- < 1) {

			this._remained = this._count;

			this._timer.stop();
			return fFunc.apply(oOwner, aArgs);

		}

		this._timer.start(this._delay, function() { fFunc.apply(oOwner, aArgs); });

	},

	stop : function() {
		this._timer.stop();
	}

});

/**
 * TTS 플레이
 *
 * @author hooriza (ajaxUI team)
 */
function thisMovie(movieName) {
   if (navigator.appName.indexOf("Microsoft") != -1) {
       return window[movieName];
   }
   else {
       return document[movieName];
   }
}
SPS.tts = $Class({

	_sUniq : null,

	_oFrame : null,
	_oOptions : null,

	_oTimer : null,
	_sOrgUrl : null,

	$init : function(oOptions) {

		this._sUniq = "TTS" + parseInt(Math.random() * 10000000);
		SPS.tts._instances[this._sUniq] = this;

		this._oOptions = oOptions || {};

		// iframe 만들기
/*
		var oBody = document.body;

		this._oFrame = $('<iframe>');
		this._oFrame.src = oOptions.frameSrc;

		with (this._oFrame.style) {
			position = "absolute";

			if (SPS.debugMode) {
				right = "0";
				top = "0";
				position = "fixed";
				width = "400px";
				height = "400px";
			} else {
				left = "-999999px";
				top = "-999999px";
				width = "1px";
				height = "1px";

			}

		}

		oBody.insertBefore(this._oFrame, oBody.firstChild);
*/

	},

	_execute : function(sMethod) {

		var aArgs = [];
		for (var i = 1, nLen = arguments.length; i < nLen; i++)
			aArgs.push(arguments[i]);

		var oWnd = this._oFrame.contentWindow;

		try {
			return oWnd[sMethod].apply(oWnd, aArgs);
		} catch(e) { }

	},

	_sReservedUrl : null,

	playDictation : function(sText, bEnglish, nCount) {

		if (!SPS.tts._ttsEnable) {
			alert(SPS.text('TTS_NOT_SUPPORTED'));
			return;
		}

		this.play(SPS.tts.textToUrl(sText, bEnglish), false, nCount);
	},

	_status : null,

	play : function(sUrl, bSuspend, nCount) {

		if (!nCount) nCount = 3;

		if (sUrl) this._sReservedUrl = sUrl;
		if (!bSuspend) {
			//this._execute('play', this._sUniq, sUrl || this._sReservedUrl, nCount);
			//this._status = 'play';
			var oBody = document.body;
			try {
				thisMovie("nciku_mp3_flash_player").stopTTS();
			}catch(e) {  };
			var oElem = document.getElementById(this._sUniq);
			if (oElem && oElem.parentNode && oElem.parentNode.removeChild) {
			try {
				oElem.parentNode.removeChild(oElem);
				;
			} catch (e) {
				;
			}
			} else {
				;
			}

			var ttsSpan = $('<span>');
			ttsSpan.id = this._sUniq;
			with (ttsSpan.style) {
			position = "absolute";

				if (SPS.debugMode) {
					right = "0";
					top = "0";
					position = "fixed";
					width = "400px";
					height = "400px";
				} else {
					left = "-999999px";
					top = "-999999px";
					width = "0px";
					height = "0px";
				}

			}

			oBody.insertBefore(ttsSpan, oBody.firstChild);
			var fo = new FlashObject("/flash/musicplayer.swf", "nciku_mp3_flash_player", "0", "0", "0", "#FFFFFF");
			fo.addVariable("song_url", this._sReservedUrl);
			fo.addVariable("song_title","nciku");
			fo.addVariable("autostart", "1");
			fo.addVariable("autoplay","true");
			fo.addVariable("repeatCount",nCount);
			fo.addVariable("repeat","false");
			fo.write(this._sUniq);
			this._status = 'play';
		}
	},

	stop : function() {
		if (this._status == 'stop') return;
		try {
			thisMovie("nciku_mp3_flash_player").stopTTS();
		}catch(e) {  };
		var oElem = document.getElementById(this._sUniq);
		if (oElem && oElem.parentNode && oElem.parentNode.removeChild) {
		try {
			oElem.parentNode.removeChild(oElem);
			;
		} catch (e) {
			;
		}
		} else {
			;
		}

		this._status = 'stop';
	}

});

SPS.tts.textToUrl = function(sText, bEnglish) {

	var fUcssafeutf8 = function(str) {

		var rtn = "";
		var for_i = 0;
		var str_length = str.length;
		var u;
		var s;
		for(i=0; i < str_length ; i++)
		{
			u = str.charCodeAt(i);
			if		( (u >= 0x00) && (u <= 0x7f) )
			{
				s= "0" + u.toString(16);
				rtn += "%" + s.substr(s.length-2);
			}
			else if	( u > 0x1fffff )
			{
				rtn += "%" + (oxf0 + ((u & 0x1c0000) >> 18)).toString(16);
				rtn += "%" + (0x80 + ((u & 0x3f000) >> 12)).toString(16);
				rtn += "%" + (0x80 + ((u & 0xfc0) >> 6)).toString(16);
				rtn += "%" + (0x80 + (u & 0x3f)).toString(16);
			}
			else if	( u > 0x7ff )
			{
				rtn += "%" + (0xe0 + ((u & 0xf000) >> 12)).toString(16);
				rtn += "%" + (0x80 + ((u & 0xfc0) >> 6)).toString(16);
				rtn += "%" + (0x80 + (u & 0x3f)).toString(16);
			}
			else
			{
				rtn += "%" + (0xc0 + ((u & 0x7c0) >> 6)).toString(16);
				rtn += "%" + (0x80 + (u & 0x3f)).toString(16);
			}
		}
		return rtn;

	};

	var nId = bEnglish ? 100 : 250; // 100 이면 영어발음, 200 이면 중국어 발음
	var sSafeText = fUcssafeutf8(sText.trim());
	var spd = 100;
	var tts_url = "";
	if(nId==100){
		tts_url = "http://tts.nciku.com/tts/mp3tts.cgi?text=" + sSafeText + "&spk_id=" + nId + "&text_fmt=0&pitch=100&volume=100&speed=" + spd + "&wrapper=0&enc=0&lb_ip=218.240.10.38&lb_port=80";
	}else{
		tts_url = "http://tts.nciku.com/tts/mp3tts2.cgi?domain_id=15&url=chinese.nciku.com&text="+sSafeText+"&spk_id="+nId+"&text_fmt=0&pitch=100&volume=100&speed=65&wrapper=0&enc=0&lb_ip=218.240.10.38&lb_port=80";
	}
	return tts_url;
};

SPS.tts._ttsEnable = true;
if ($Agent().navigator().ie) {
	try { new ActiveXObject('WMPlayer.OCX'); } catch(e) {
		SPS.tts._ttsEnable = false;
	}
}

SPS.tts._instances = [];
SPS.tts._getInstance = function(sKey) { return this._instances[sKey]; };

/**
 * 탭 UIO 와 Panel 과 연결하는거
 *
 * @author hooriza (ajaxUI team)
 */
SPS.tab = function(sTabID, aPanelIDs, oOptions) {

	sTabID = $(sTabID);
	oOptions = oOptions || {};

	var aTabs = [];

	if (sTabID.tagName.toUpperCase() == 'UL') {

		for (var oEl = sTabID.firstChild; oEl; oEl = oEl.nextSibling)
			if (oEl.tagName && oEl.tagName.toUpperCase() == 'LI') aTabs.push(oEl);

	} else {

		aTabs = $$('ul>li', sTabID);

	}

	var aPanels = [];
	var nSelected = 0;

	$A(aPanelIDs).forEach(function(sPanelID) {
		aPanels.push($Element($(sPanelID)));
	});

	var showPanel = function(nIdx) {

		var oRet = { 'tab' : null, 'panel' : null };

		$A(aPanels).forEach(function(ePanel, nI) {

			if (nI == nIdx) {

				oRet.tab = aTabs[nI];
				oRet.panel = ePanel.$value();

				ePanel.show();

				/* 20071221 modify */
				if ($Agent().navigator().ie) {
				    ePanel.css('zoom', '1');
				    setTimeout(function() { ePanel.css('zoom', ''); }, 0);
				}
				/* 20071221 modify end */
				Element.addClass(aTabs[nI], 'selected');

			} else {

				ePanel.hide();
				Element.removeClass(aTabs[nI], 'selected');

			}

		});

		return oRet;

	};

	$A(aTabs).forEach(function(oTab, nIdx) {

		if (Element.hasClass(oTab, 'selected')) nSelected = nIdx;

		$Fn(function(oEvent) {

			var oRet = showPanel(this);

			if (typeof oOptions.onChange == 'function')
				oOptions.onChange(oRet.tab, oRet.panel);

			oEvent.stop();

		}).owner(nIdx).attach(oTab, 'click');

	});

	showPanel(nSelected);

};

/**
 * 셀렉트박스
 *
 * @author hooriza (ajaxUI team)
 */
SPS.selectbox = $Class({

	_agent : $Agent().navigator(),

	_o : null,
	_timer : null,

	_options : null,

	$init : function(oEl, oOptions) {

		/*
		var o = this._o = {
			wrap : oEl,
			label : $$('.label', oEl)[0],
			layer : $$('.layer', oEl)[0],
			list : $$('.list', oEl)[0],
			resource : $$('.resource', oEl)[0]
		};
		*/

		var o = this._o = {
			wrap : oEl,
			label : $$('p', oEl)[0],
			layer : $$('ul', oEl)[0],
			list : $$('ul', oEl)[0],
			button : $$('.btn', oEl)[0]
		};

		var eSrc = $Element(o.wrap).findElement('nextSibling', function(oEl) { return oEl.tagName && oEl.tagName.toUpperCase() == 'SELECT'; });
		if (!eSrc) return;

		$Element(oEl).addClass('selectbox-skip');
		SPS.selectbox._instances.push(this);

		o.resource = eSrc.$value();
		eSrc.css({
			position : 'absolute',
			left : '-999999px'
		});

		o.button.removeAttribute('href');

		o.wrap.style.display = 'block';
		o.list.style.display = 'none';

		/////////

		this._options = oOptions || { showNULL : true };

		this._timer = new SPS.timer('timeout');

		this._bindEvents();
		this._touchLabel();

	},

	_findAbove : function(nIndex) {

		var o = this._o;

		for (; nIndex > -1; nIndex--)
			if (o.resource.options[nIndex].getAttribute('value'))
				return nIndex;

		return -1;

	},

	_findBelow : function(nIndex) {

		var o = this._o;
		var nLen = o.resource.options.length;

		for (; nIndex < nLen; nIndex++)
			if (o.resource.options[nIndex].getAttribute('value'))
				return nIndex;

		return -1;

	},

	_showList : function(bFlag) {

		var o = this._o;
		if (o.resource.selectedIndex == -1) return;

		var sMethod = bFlag ? 'show' : 'hide';
		if (typeof bFlag == 'undefined') sMethod = 'toggle';

		var eLayer = $Element(o.layer);
		eLayer[sMethod]();

		var bVisible = eLayer.visible();
		if (bVisible) this._touchList();

		// SPS.debug(m + ' : ' + bVisible);

	},

	_fireOnChangeEvent : function() {

		var o = this._o;

		if (document.createEvent) {

			var e = document.createEvent('HTMLEvents');
			e.initEvent('change', true, true);
			o.resource.dispatchEvent(e);

		} else {

			var e = document.createEventObject();
			o.resource.fireEvent('onchange', e);

		}

	},

	_bindEvents : function() {

		var o = this._o;

		// 진짜 셀렉트 박스에서 휠 for IE6 below
		$Fn(function(oEvent) {
			var nDelta = oEvent.mouse().delta;
			o.list.scrollTop -= nDelta * 15;
			//alert(nDelta);
			//o.list.fireEvent('onmousewheel', oEvent._event);
			oEvent.stop();
		}).owner(this).attach(o.resource, 'mousewheel');

		// 진짜 셀렉트 박스에 포커스
		$Fn(function(oEvent) {

			var eWrap = $Element(o.wrap);

			if (!eWrap.hasClass('selectbox-focus'))
				eWrap.addClass('selectbox-focus');

			this._timer.stop();
			// this._showList(true);

		}).owner(this).attach(o.resource, 'focus');

		// 진짜 셀렉트 박스에 포커스 잃음
		$Fn(function(oEvent) {

			var eWrap = $Element(o.wrap);
			var self = this;

			this._timer.start(50, function() {

				if (eWrap.hasClass('selectbox-focus'))
					eWrap.removeClass('selectbox-focus');

				self._showList(false);

			});

		}).owner(this).attach(o.resource, 'blur');

		// 셀렉트 박스에서 키보드 눌렀을때
		$Fn(function(oEvent) {

			var self = this;

			var nKeyCode = oEvent.key().keyCode;
			var nSrcIndex = this._getSelectedIndex();

			switch (nKeyCode) {
			case 33, 36, 37, 38: // 감소
			case 34, 39, 40, 45: // 증가
				break;

			case 32: // space
				this._showList();
				oEvent.stop();
				return;
			}

			setTimeout(function() {

				if (!self._options.showNULL) {

					var nIndex = self._getSelectedIndex();
					var bMustFind = o.resource.options[nIndex].getAttribute('value') ? false : true;

					if (nIndex > nSrcIndex) { // 밑으로 내려가는 거면

						var nLen = o.resource.options.length;

						nIndex = self._findBelow(nIndex);
						if (nIndex == -1) nIndex = self._findAbove(nLen - 1);

					} else { // 위로 올라가는 거면

						nIndex = self._findAbove(nIndex);
						if (nIndex == -1) nIndex = self._findBelow(0);

					}

					if (self._agent.firefox || self._agent.safari) bMustFind = true;

					if (nIndex != -1) {
						o.resource.selectedIndex = nIndex;

						if (nIndex != nSrcIndex && bMustFind)
							self._fireOnChangeEvent();
					}

				}

				self._touchLabel();

			}, 0);

		}).owner(this).attach(o.resource, this._agent.opera ? 'keypress' : 'keydown');

		// 가짜 셀렉트 박스 클릭했을때
		$Fn(function(oEvent) {

			var self = this;
			setTimeout(function() { self._showList(); o.resource.focus(); }, 1);

		}).owner(this).attach(o.wrap, 'mousedown');

		// 목록의 아이템 위에 마우스 올렸을때
		$Fn(function(oEvent) {

			var eItem = $Element(oEvent.element).findElement('parentNode', function(e) { return $Element(e).hasClass('item'); }, true);
			if (!eItem) return;

			this._setHighlight(eItem);

		}).owner(this).attach(o.list, 'mouseover');

		// 목록의 아이템을 클릭했을때
		$Fn(function(oEvent) {

			var self = this;

			var eItem = $Element(oEvent.element).findElement('parentNode', function(e) { return $Element(e).hasClass('item'); }, true);
			if (!eItem) {
				SPS.debug("FOO");
				setTimeout(function() { self._timer.stop(); o.resource.focus(); }, 1);
				oEvent.stop();
				return;
			}

			var oItem = eItem.$value();
			var nIndex = parseInt(oItem.className.match(/__au_index_([0-9]+)/)[1]);

			o.resource.selectedIndex = nIndex;
			this._setSelect(eItem);
			this._touchLabel();

			if (this._agent.firefox || this._agent.ie) self._fireOnChangeEvent();
			setTimeout(function() { self._showList(false); }, 50);

		}).owner(this).attach(o.list, 'mousedown');

	},

	_setHighlight : function(eItem) {

		if (this._highlighted) this._highlighted.removeClass('item-highlight');
		eItem.addClass('item-highlight');
		this._highlighted = eItem;

		var oItem = eItem.$value();
		var oList = this._o.list;

		var oTop = oItem.offsetTop;
		if (oList.scrollTop > oTop) oList.scrollTop = oTop;

		var oBottom = oTop + oItem.offsetHeight;
		if (oList.scrollTop + oList.clientHeight < oBottom) oList.scrollTop = oBottom - oList.clientHeight;

	},

	_setSelect : function(eItem) {
		// 设置笔画数下拉框的位置 20080731
		var oSelect = this._o.wrap,
				oList = this._o.list,
				wHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight,
				wScroll = document.documentElement.scrollTop || document.body.scrollTop,
				nHeight, oList_height = oList.offsetHeight;

		if(wScroll === 0){
			nHeight = wHeight-oSelect.offsetTop-oSelect.offsetHeight;
		}
		else{
			nHeight = Math.abs(wHeight-(oSelect.offsetTop-wScroll)-oSelect.offsetHeight);
		}
		if(nHeight < oList_height){
			oList.style.top = -oList_height + 'px';
		}
		else{
			oList.style.top = 20 + 'px';
		}

		if (this._selected) this._selected.removeClass('item-select');
		eItem.addClass('item-select');
		this._selected = eItem;

		this._setHighlight(eItem);

	},

	_highlighted : null,
	_selected : null,

	_getSelectedIndex : function() {

		var o = this._o;
		var nIndex = o.resource && o.resource.selectedIndex;

		if (nIndex > -1) return nIndex;
		return -1;

	},

	_touchLabel : function() {

		var o = this._o;

		var nIndex = this._getSelectedIndex();
		if (nIndex == -1) {
			o.label.innerHTML = '';
			return;
		}

		var sText = o.resource.options[nIndex].text;
		o.label.innerHTML = sText;

		var oItem = $$('.__au_index_' + nIndex, o.list)[0];
		if (oItem) this._setSelect($Element(oItem));

	},

	_touchList : function() {

		var o = this._o;
		var aOptions = o.resource.options;
		var oItem;

		var sText, sValue;

		o.list.innerHTML = '';

		this._highlighted = null;
		this._selected = null;

		for (var nIndex = 0, nLen = aOptions.length; nIndex < nLen; nIndex++) {

			sText = aOptions[nIndex].text;
			sValue = aOptions[nIndex].getAttribute('value');

			if (this._options.showNULL || sValue) {

				oItem = $('<li>');
				oItem.className = 'item __au_index_' + nIndex;

				oItem.innerHTML = aOptions[nIndex].text;
				o.list.appendChild(oItem);

			}

		}

		this._touchLabel();

	},

	touch : function() {
		this._touchLabel();
	}

});

SPS.selectbox._version = 3;
SPS.selectbox._instances = [];

SPS.selectbox.touchAll = function(sClassName, oDoc) {

	if (!sClassName) {
		$A(this._instances).forEach(function(oInst) { oInst.touch(); });
		return;
	}

	var aEls = $$('.' + sClassName, oDoc || document);
	$A(aEls).forEach(function(oEl) {
		if ($Element(oEl).hasClass('selectbox-skip')) return;
		var oInst = new SPS.selectbox(oEl);
	});

};

/**
 * 팝업
 *
 * sample code :
 *

 SPS.popup('http://naver.com', 400, 300, {
	canResize : true,
	canScroll : true,
	name : 'hello',
	center : true
 });

 SPS.popup('http://naver.com', 400, 300);

 */
SPS.newWin = function(sUrl, sName) { // 무조건 새창 띄우기

	var a = [];

	a.push('chanellmode=1');
	a.push('directories=1');
	a.push('fullscreen=0');
	a.push('location=1');
	a.push('menubar=1');
	a.push('scrollbars=1');
	a.push('status=1');
	a.push('toolbar=1');
	a.push('resizable=1');

	return window.open(sUrl, sName || SPS.uniqid(), a.join(','), false);

};

SPS.popup = function(sUrl, nWidth, nHeight, oOptions) {

	nWidth = nWidth || 300;
	nHeight = nHeight || 200;

	oOptions = oOptions || {};

	oOptions.canResize = oOptions.canResize || false;
	oOptions.canScroll = oOptions.canScroll || false;

	oOptions.name = oOptions.name || 'sps_popup';
	oOptions.center = oOptions.center || false; // 듀얼모니터일때 정상작동 안됨

	var a = [];
	a.push('chanellmode=0');
	a.push('directories=0');
	a.push('fullscreen=0');
	a.push('location=0');
	a.push('menubar=0');
	a.push('scrollbars=' + (oOptions.canScroll ? '1' : '0'));
	a.push('status=0');
	a.push('toolbar=0');
	a.push('resizable=' + (oOptions.canResize ? '1' : '0'));
	a.push('width=' + nWidth);
	a.push('height=' + nHeight);

	var aScrSize = [ window.screen.availWidth, window.screen.availHeight ];
	var aPos = [ parseInt((aScrSize[0] - nWidth) / 2), parseInt((aScrSize[1] - nHeight) / 2) ];

	a.push('left=' + aPos[0]);
	a.push('top=' + aPos[1]);

	return window.open(sUrl, oOptions.name, a.join(','), false);

};

SPS.popup.image = function(sUrl, nWidth, nHeight, oOptions) {

	if (typeof sUrl == 'object')
		sUrl = sUrl.getAttribute('src');

	nWidth = nWidth || 30;
	nHeight = nHeight || 30;

	return SPS.popup('/html/popup.htm?img=' + encodeURIComponent(sUrl), nWidth, nHeight, {
		canResize : true,
		name : 'image_pop'
	});

};

SPS.popup.mini = function(oEl) {

	var oAgent = $Agent().navigator();
	var nWidth = 450;
	var nHeight = 570;

	if (oAgent.ie) {
		nWidth += 5;
	} else if (oAgent.firefox || oAgent.safari) {
		nHeight += 20;
	}

	return SPS.popup(oEl.href, nWidth, nHeight, {
		canResize : false,
		canScroll : false,
		name : 'mini_pop',
		center : true
	});

};

SPS.popup.stroke = function(oEl) {

	return SPS.popup(oEl.href, 350, 360, {
		canResize : false,
		canScroll : false,
		name : 'stroke_pop',
		center : true
	});

};

/**
 * 한번에 하나의 엘리먼트만 숨기기
 *
 * @author hooriza (ajaxUI team)
 */
SPS.hideOne = {

	_beforeEl : null,

	hide : function(oEl, sClassName) {

		var eEl = null;

		if (sClassName) eEl = SPS.getParentByClassName(oEl, sClassName);
		else eEl = $Element(oEl);

		if (!eEl) return;

		this.show();

		eEl.hide();
		this._beforeEl = eEl;

	},

	show : function() {

		if (this._beforeEl)
			this._beforeEl.show();

		this._beforeEl = null;

	}

};

/**
 * 한번에 하나의 엘리먼트만 보여주기
 *
 * @author hooriza (ajaxUI team)
 */
SPS.showOne = {

	_beforeEl : null,

	show : function(oEl, sClassName) {

		var eEl = null;

		if (sClassName) eEl = SPS.getParentByClassName(oEl, sClassName);
		else eEl = $Element(oEl);

		if (!eEl) return;

		this.hide();

		eEl.show();
		this._beforeEl = eEl;

	},

	hide : function() {

		if (this._beforeEl)
			this._beforeEl.hide();

		this._beforeEl = null;

	}

};

/**
 * 엘리먼트 내에서 적용된 값 얻고 셋팅하기
 *
 * @author hooriza (ajaxUI team)
 */
SPS.data = {

	setValue : function(sPrefix, oWrap, oData, bHtml) {

		$H(oData).forEach(function(sValue, sKey) {

			var oEl = $$(sPrefix + sKey, oWrap)[0];
			var sTag = oEl.tagName.toUpperCase();

			if (sTag == 'INPUT' || sTag == 'TEXTAREA' || sTag == 'SELECT')
				oEl.value = sValue;
			else
				oEl.innerHTML = bHtml ? sValue : new String(sValue).toHTML();

		});

	},

	getValue : function(sPrefix, oWrap, aKeys, bHtml) {

		var oRet = {};

		$A(aKeys).forEach(function(sKey) {

			var oEl = $$(sPrefix + sKey, oWrap)[0];
			var sTag = oEl.tagName.toUpperCase();

			if (sTag == 'INPUT' || sTag == 'TEXTAREA' || sTag == 'SELECT')
				oRet[sKey] = SPS.getFormValue(oEl);
			else
				oRet[sKey] = bHtml ? (oEl.innerText || oEl.textContent) : oEl.innerHTML;

		});

		return oRet;

	}

};

/**
 * 팝업 레이어
 *
 * @author hooriza (ajaxUI team)
 */
SPS.layer = {

	oLayer : null,

	oBlind : null,
	eBlind : null,

	bPoped : false,
	bCenter : false,

	orgPopup : null,
	orgZIndex : null,

	createBlind : function() {

		if (this.oBlind) return;

		var oAgent = $Agent().navigator();

		var bIE55 = oAgent.ie && oAgent.version < 6;
		var bIE6 = oAgent.ie && oAgent.version < 7;

		var bFF = oAgent.firefox;

		if (bIE55) { // IE5.5 이하면

			this.oBlind = document.createElement('<table border="0" cellpadding="0" cellspacing="0"></table>');
			this.oBlind.insertRow(0).insertCell(0).innerHTML = '&nbsp;';

		} else {
			this.oBlind = $("<div>");
		}

		this.eBlind = $Element(this.oBlind);

		document.body.insertBefore(this.oBlind, document.body.firstChild);

		this.eBlind.css({
			position : 'fixed',
			background : '#000',
			left : (bIE55 ? 0 : -0) + 'px', // body 의 padding:10px 이므로
			top : (bIE55 ? 0 : -0) + 'px',
			width : '100%', // 100%
			height : '100%',
			display : 'none',
			zIndex : 999
		});

		if (bIE6 || bFF) { // IE6 이하 브라우저면

			this.eBlind.css('position', 'absolute');

			if (bIE55) $Fn(this.fitBlindSize).owner(this).attach(window, 'scroll');
			$Fn(this.fitBlindSize).owner(this).attach(window, 'resize');
		}

		$Fn(this.moveToCenter).owner(this).attach(window, 'scroll');
		$Fn(this.moveToCenter).owner(this).attach(window, 'resize');

		this.eBlind.opacity(0.3);

	},

	fitBlindSize : function() {

		if (!this.bPoped) return;

		var oAgent = $Agent().navigator();

		var bIE55 = oAgent.ie && oAgent.version < 6;
		var bIE6 = oAgent.ie && oAgent.version < 7;

		// if (!bIE6) return; // IE7 이상이면 필요없음

		var aClientSize = this.getClientSize();

		if (bIE55) { // IE5.5 이하면

			var oDoc = document.body;

			this.eBlind.css({
				width : aClientSize[0] + 'px',
				height : aClientSize[1] + 'px',
				left : oDoc.scrollLeft + 'px',
				top : oDoc.scrollTop + 'px'
			});

		} else {

			this.eBlind.css({ width : '0px', height : '0px' });

			var self = this;
			var oDoc = document.documentElement || document;

			setTimeout(function() {

				var aScrollSize = [
					oDoc.scrollWidth,
					oDoc.scrollHeight
				];

				var aSize = [
					Math.max(aScrollSize[0], aClientSize[0]),
					Math.max(aScrollSize[1], aClientSize[1])
				];

				self.eBlind.css({
					width : aSize[0] + 'px', height : aSize[1] + 'px'
				});

			}, 1);

		}

	},

	getClientSize : function() {

		var oAgent = $Agent().navigator();
		var bIE55 = oAgent.ie && oAgent.version < 6;

		var oDoc = document.documentElement || document;
		if (bIE55) oDoc = document.body;

		return [ oDoc.clientWidth, oDoc.clientHeight ];

	},

	moveToCenter : function() {

		if (!this.bCenter) return;

		var aScrSize = this.getClientSize();
		var aPopSize = [ this.orgPopup.width(), this.orgPopup.height() ];

		var aPos = [ parseInt((aScrSize[0] - aPopSize[0]) / 2), parseInt((aScrSize[1] - aPopSize[1]) / 2) ];

		var oDoc = document.documentElement || document;
		this.orgPopup.css({ left : aPos[0] + oDoc.scrollLeft + 'px', top : aPos[1] + oDoc.scrollTop + 'px' });

	},

	pop : function(oEl) {

		this.createBlind();

		this.bPoped = true;
		this.fitBlindSize();

		if (this.orgPopup)
			this.orgPopup.css('zIndex', this.orgZIndex);

		this.orgPopup = $Element(oEl);
		this.orgZIndex = this.orgPopup.css('zIndex');

		var sPosition = this.orgPopup.css('position');

		if (sPosition != 'absolute' && sPosition != 'relative')
			this.orgPopup.css('position', 'relative');

		this.orgPopup.css('zIndex', 1000);

	},

	unpop : function() {

		this.bPoped = false;

		if (this.orgPopup) {

			if (this.orgZIndex !== null)
				this.orgPopup.css('zIndex', this.orgZIndex);

			this.orgPopup = null;
			this.orgZIndex = null;
		}

	},

	show : function(oEl, bCenter, bNoBlind) {

		var oLayer = this.oLayer = oEl;
		var eLayer = $Element(oLayer);

		eLayer.show();
		this.pop(oLayer);

		if (!bNoBlind)
			this.eBlind.show();

		var sPosition = eLayer.css('position');

		var oNavigator = $Agent().navigator();
		if (oNavigator.ie && oNavigator.version < 7) {

			eLayer.css('position', 'static');
			eLayer.css('position', sPosition);

		}

		if (sPosition == 'absolute') { // absolute 일때만 sFlag 셋팅 먹이기

			this.bCenter = bCenter;
			this.moveToCenter();

		}

	},

	hide : function(bHideOne) {

		if (!this.oLayer) return;

		this.bCenter = null;

		this.unpop();
		this.eBlind.hide();

		$Element(this.oLayer).hide();
		this.oLayer = null;

		if (bHideOne) SPS.hideOne.show();

	}

};

SPS.fontSize = $Class({

	_o : null,
	_size : 0,

	_dragging : false,

	$init : function(sEl) {

		var oEl = $(sEl);

		var aAnchors = $$('.txtsize_ctr a', oEl);

		var o = this._o = {
			smaller : aAnchors[0],
			bigger : aAnchors[1],
			track : $$('.txtsize_slide', oEl)[0],
			grap : $$('.move', oEl)[0]
		};

		$Fn(function() {

			o.panels = $$('.font_reslzable');
			this._setSize(1);

			this._bindEvents();

		}).owner(this).attach(window, 'load');

	},

	_setSize : function(nSize) {

		var o = this._o;

		this._size = nSize;
		if (this._size > 2) this._size = 2;
		if (this._size < 0) this._size = 0;

		this._applySize();

	},

	_applySize : function() {

		var o = this._o;

		var nLeft = this._size * 15;
		var nSize = this._size * 3 + 12;

		$Element(o.grap).css({ 'left' : nLeft + 'px', 'display' : 'block' });

		$A(o.panels).forEach(function(oPanel) {
			oPanel.style.fontSize = nSize + 'px';
		});

	},

	_handlerDrag : function(oEvent) {

		var o = this._o;

		if (!this._dragging) return;

		var oPos = $Element(o.track).offset();
		var nLeft = oEvent.pos().pageX - oPos.left;

		this._setSize(parseInt((nLeft + 6) / 16));

	},

	_bindEvents : function() {

		var o = this._o;

		$Fn(function(oEvent) {

			this._setSize(this._size - 1);
			oEvent.stop();

		}).owner(this).attach(o.smaller, 'click');

		$Fn(function(oEvent) {

			this._setSize(this._size + 1);
			oEvent.stop();

		}).owner(this).attach(o.bigger, 'click');

		// 드래그 시작
		$Fn(function(oEvent) {
			this._dragging = true;
			this._handlerDrag(oEvent);
			oEvent.stop();
		}).owner(this).attach(o.track, 'mousedown');

		$Fn(function(oEvent) { oEvent.stop(); }).owner(this).attach(o.track, 'dragstart');
		$Fn(function(oEvent) { oEvent.stop(); }).owner(this).attach(o.track, 'selectstart');

		// 드래그 진행
		$Fn(this._handlerDrag).owner(this).attach(document, 'mousemove');

		// 드래그 끝
		$Fn(function(oEvent) { this._dragging = false; }).owner(this).attach(document, 'mouseup');

	}

});

SPS.selection = {

	_holded : false,

	block : function() {
		if (this._holded) return;
		document.body.onselectstart = function() { return false; };
	},

	allow : function() {
		if (this._holded) return;
		document.body.onselectstart = null;
	},

	hold : function(bFlag) {
		this._holded = bFlag;
	}

};

/**
 * Dynamic Script Including
 *
 * @author hooriza at nhncorp.com
 * @version 0.95
 *
 * @created Nov.1.2007.
 */
SPS.Include = function() {

	var self = this;

	this._uniqID = SPS.uniqid();
	this._requests = {};

	if (!SPS.Include._instance) SPS.Include._instance = {};
	SPS.Include._instance[this._uniqID] = this;

	this._gcTimer = setInterval(function() { self._gc(); }, this._gcInterval);

};

SPS.Include.prototype = {

	_uniqID : null,
	_requests : null,

	_trID : null,
	_callback : null,

	_gcTimer : null,
	_gcInterval : 30000, // 30초마다 gc 실행

	_head : null,

	request : function(sUrl, pCallback) {

		var nTrID = SPS.uniqid();

		var sMethod = 'SPS.Include._response(' + this._uniqID + ', ' + nTrID + ')';

		if (sUrl.indexOf('?') == -1) sUrl += '?';
		sUrl += '&callback=' + encodeURIComponent(sMethod);

		this._trID = nTrID;
		this._callback = pCallback;

		if (SPS.debugMode) window.console.debug(sUrl);

		this._createScript(nTrID, sUrl);

	},

	abort : function() {
		this._trID = null;
	},

	_createScript : function(nTrID, sUrl) {

		if (!this._head) this._head = document.getElementsByTagName('head')[0];

		var oScript = document.createElement('script');
		var nTime = SPS.uniqid();

		with (oScript) {
			src = sUrl;
			id = nTime; // 생성시간
			language = 'javascript';
			type = 'text/javascript';
		};

		this._requests[nTrID] = oScript;
		this._head.appendChild(oScript);

		return oScript;

	},

	_destroyScript : function(nTrID, nExpires, nNow) {

		var oScript = this._requests[nTrID];

		// 생성된지 지정된 시간이 지난 것만 삭제
		if (nExpires) {

			nNow = nNow || new Date().getTime();

			if (parseInt(oScript.id) > nNow - nExpires)
				return;

		}

		var self = this;

		setTimeout(function() { self._head.removeChild(oScript); }, 10); // IE5.5 버그 회피
		delete this._requests[nTrID];

	},

	_gc : function() {

		var nNow = new Date().getTime();

		for (var nTrID in this._requests)
			this._destroyScript(nTrID, this._gcInterval, nNow);

	}

};

SPS.Include._response = function(nUniqID, nTrID) {

	var pIgnore = function() { };

	var oInst = this._instance[nUniqID];
	if (!oInst) return pIgnore;

	oInst._destroyScript(nTrID);
	if (oInst._trID != nTrID) return pIgnore;

	var pCallback = oInst._callback;

	return function(oData) { return pCallback(oData); };

};

SPS.findDo = function(oEl, oEvent, pFindFunc, sMethod) {

	var eEvent = $Event(oEvent);
	var oEventEl = eEvent.element;

	var bTTSButton = SPS.getParentByClassName(oEventEl, 'tts_button');
	if (bTTSButton) return;

	if (typeof pFindFunc == 'string')
		pFindFunc = SPS.findDo[pFindFunc];

	var oTar = pFindFunc(oEl);
	if (oTar) return oTar[sMethod]();
};

SPS.findDo.translation = function(oEl) {

	var eEl = $Element(oEl.parentNode).findElement('nextSibling', function(o) {
		return o.tagName.toUpperCase() == 'TR';
	});

	if (eEl) eEl.toggle();

};

/**
 * @author hooriza
 */
SPS.autohide = $Class({

	_panel : null,
	_areas : null,

	_options : null,

	$init : function(oPanel, aAreas, oOptions) {

		this._panel = oPanel;
		this._areas = aAreas || [];

		this._options = oOptions || {};

		this._bindEvents();

	},

	_bindEvents : function() {

		$Fn(function(oEvent) {

			var oElement = oEvent.element;

			if (this._panel && oEvent.of(this._panel)) return;

			for (var i = 0, oArea; oArea = this._areas[i]; i++)
				if (oArea && oEvent.of(oArea)) return;

			if (this._panel){
				$Element(this._panel).hide();
			}
				// add handwrite bar style 20080715
				var handwriteBar = this._areas[0].parentNode;
				if(handwriteBar.className.indexOf('handwrite') != -1){
					handwriteBar.className = 'handwrite';
				}
		}).owner(this).attach(document, 'click');

	},

	toggle : function() {

		var bVisible = $Element(this._panel).visible();
		this[bVisible ? 'hide' : 'show']();

		// add handwrite bar style 20080715
		var handwriteBar = this._areas[0].parentNode;
		if(handwriteBar.className != 'inputbox_style'){
			if(bVisible){
				handwriteBar.className = 'handwrite';
			}
			else{
				handwriteBar.className = 'handwrite open';
			}
		}
	},

	show : function() {

		$Element(this._panel).show();
		if (typeof this._options.onShow == 'function')
			this._options.onShow();

	},

	hide : function() {

		$Element(this._panel).hide();
		if (typeof this._options.onHide == 'function')
			this._options.onHide();

	}

});

SPS.debug = function(sMsg) {

	if (!SPS.debugMode) return;

	if (!this._wnd) {

		this._wnd = $Element($('<ul>'));
		this._wnd.css({
			'position' : 'absolute',
			'right' : '10px',
			'bottom' : '10px',
			'border' : '5px solid #f00',
			'padding' : '10px',
			'background' : '#fff'
		});

		document.body.insertBefore(this._wnd.$value(), document.body.firstChild);

	}

	var oLog = $('<li>');
	oLog.innerHTML = sMsg;

	this._wnd.append(oLog);

};

SPS.sortTable = function(oEl) {

	var eTH = $Element(oEl).findElement('parentNode', function(e) { var t = e.tagName.toUpperCase(); return (t == 'TD' || t == 'TH'); }, true);
	var eTable = eTH.findElement('parentNode', function(e) { return e.tagName.toUpperCase() == 'TABLE'; });
	var oTable = eTable.$value();

	var oTH = eTH.$value();
	var nColIndex = oTH.cellIndex;

	var aTRs = $$('.sortable', oTable);
	var aDatas = [];

	var oBtnImg = $$('img.sorting', oTH)[0];
	var sBtnSrc = oBtnImg.src;

	var bDesc = sBtnSrc.indexOf('btn_usorting.gif') == -1;

	var pFillZero = function(sStr, nLen) {

		var sStr = new String(sStr);
		for (var i = 0, len = sStr.length; i < nLen - len; i++)
			sStr = '0' + sStr;

		return sStr;

	};

	$A(aTRs).forEach(function(oTR) {

		var oTD = oTR.cells[nColIndex];
		var sSortKey = oTD.className.match(/\bsortkey\((\w+)\)(\b|$)/);

		if (sSortKey && sSortKey[1]) {
			var aSortKey = sSortKey[1].split('_');
			var sSortKey = '';

			for (var i = 0, len = aSortKey.length; i < len; i++)
				sSortKey += pFillZero(aSortKey[i], 15) + '_';

		} else {
			sSortKey = (oTD.innerText || oTD.textContent).trim();
		}

		aDatas.push({ key : sSortKey, object : oTR });
	});

	if (bDesc) sBtnSrc = sBtnSrc.replace('btn_sorting.gif', 'btn_usorting.gif');
	else sBtnSrc = sBtnSrc.replace('btn_usorting.gif', 'btn_sorting.gif');

	oBtnImg.src = sBtnSrc;

	$A($$('img.sorting', oTable)).forEach(function(oImg) {

		if (oImg == oBtnImg) return;

		if (bDesc) oImg.src = oImg.src.replace('btn_sorting.gif', 'btn_usorting.gif');
		else oImg.src = oImg.src.replace('btn_usorting.gif', 'btn_sorting.gif');

	});

	var pAsc = function(a, b) { return a.key < b.key ? -1 : (a.key == b.key ? 0 : 1); };
	var pDesc = function(a, b) { return a.key < b.key ? 1 : (a.key == b.key ? 0 : -1); };

	aDatas.sort(!bDesc ? pDesc : pAsc);

	for (var i = 0; oData = aDatas[i]; i++) {

		var oNewTR = oData.object.cloneNode(true);
		var oOldTR = aTRs[i];

		oOldTR.parentNode.insertBefore(oNewTR, oOldTR);
		oOldTR.parentNode.removeChild(oOldTR);

	}

};

SPS.switchMemorized = function(oEl) {

	var sTrID = SPS.uniqid();
	SPS.switchMemorized.trID = sTrID;

	var oAjax = $Ajax(oEl.href, {
		onload : function(oRes) {

			var oData = null;
			try { eval('oData = ' + oRes.text() + ';'); } catch(e) {}

			if (!oData) return;

			var oEl;
			location.reload();
			return;

			/*
			trID:'3010374439012',
			text : 'Not Memorized',
			memorizedCount : '-4',
			notMemorizedCount : '21',
			percentage : '-23'
			*/

			oEl = $('SM' + oData.trID);
			if (oEl) oEl.innerHTML = oData.text;

			// 가장 최근 trID 것만 적용하게
			if (oData.trID != SPS.switchMemorized.trID) return;

			var eAnchor;

			oEl = $$('.mem_percentage')[0];
			oEl.innerHTML = oData.percentage + '%';

			oEl = $$('.mem_memorized')[0];
			if (oEl) {
				if (oEl) oEl.innerHTML = oData.memorizedCount;
				eAnchor = $Element(oEl).findElement('parentNode', function(o) { return o.tagName.toUpperCase() == 'A'; });
				if (eAnchor) {
					var bNoLink = oData.memorizedCount == '0';
					var oAnchor = eAnchor.$value();
					eAnchor[bNoLink ? 'addClass' : 'removeClass']('nolink');
					if (bNoLink) {
						oAnchor.setAttribute('_href', oAnchor.href);
						oAnchor.removeAttribute('href');
					} else {
						oAnchor.href = oAnchor.getAttribute('_href');
					}
				}
			}

			oEl = $$('.mem_notmemorized')[0];
			if (oEl) {
				if (oEl) oEl.innerHTML = oData.notMemorizedCount;
				eAnchor = $Element(oEl).findElement('parentNode', function(o) { return o.tagName.toUpperCase() == 'A'; });
				if (eAnchor) {
					var bNoLink = oData.notMemorizedCount == '0';
					var oAnchor = eAnchor.$value();
					eAnchor[bNoLink ? 'addClass' : 'removeClass']('nolink');
					if (bNoLink) {
						oAnchor.setAttribute('_href', oAnchor.href);
						oAnchor.removeAttribute('href');
					} else {
						oAnchor.href = oAnchor.getAttribute('_href');
					}
				}
			}

		}
	});

	oEl.id = 'SM' + sTrID;
	oAjax.request({ trID : sTrID });

};

SPS.DomLoaded = {

	_callbacks : [],
	_loaded : false,

	_onLoaded : function() {

		var self = SPS.DomLoaded;

		for (var i = 0, pCallback; pCallback = self._callbacks[i]; i++)
			pCallback();
	},

	_monitorInterval : function() {

		var self = SPS.DomLoaded;

		if (self._loaded) return;
		if (document.readyState == 'loaded' || document.readyState == 'complete') {
			self._onLoaded();
			self._loaded = true;
		} else {
			setTimeout(self._monitorInterval, 100);
		}

	},

	addCallback : function(pCallback) {
		this._callbacks.push(pCallback);
	},

	monitor : function() {

		if (document.addEventListener && !$Agent().navigator().safari) {
			document.addEventListener("DOMContentLoaded", SPS.DomLoaded._onLoaded, false);
		} else {
			setTimeout(this._monitorInterval, 100);
		}
	}

};

SPS.DomLoaded.monitor();

SPS.banWords = '';

SPS.checkBanWords = $Fn(function(oEvent) {

	var oEl = oEvent.element;
	var sTagName = (oEl.tagName || '').toUpperCase();

	if (sTagName != 'INPUT' && sTagName != 'TEXTAREA') return;

	// class="skip_banword" 가 되어있으면 욕 체크 안하기
	if ($Element(oEl).hasClass('skip_banword')) return;

	var sValue = oEl.value;
	var bExist = false;

	for (var i = 0; i < SPS.banWords.length; i++) {

		var banWord = Base64.decode(SPS.banWords[i]);

		if (banWord == '' || sValue.indexOf(banWord) == -1) continue;

		bExist = true;
		sValue = sValue.replace(new RegExp(banWord, 'gi'), '');

	}

	if (bExist) {
		oEl.value = sValue;
		alert(SPS.text('BANWORDS'));
	}

});

SPS.checkBanWords.attach(document, 'keyup');
SPS.checkBanWords.attach(document, 'blur');

SPS.transition = function(oEl, nCount, nInterval, nLeft, nTop, oOptions) {

	var nPercent = 0;

	var nStep = 1 / nCount;
	var aOrgPos = [ parseInt(oEl.style.left), parseInt(oEl.style.top) ];

	var pProgress = function() {

		nPercent += nStep;

		var nP = (function(v) { var v = Math.sin(v * (Math.PI / 2)); return v * v; })(nPercent);

		var aNewPos = [
						(nLeft - aOrgPos[0]) * nP + aOrgPos[0],
						(nTop - aOrgPos[1]) * nP + aOrgPos[1]
		];

		oEl.style.left = aNewPos[0] + 'px';
		oEl.style.top = aNewPos[1] + 'px';

		nCount--;

		if (nCount > 0) setTimeout(pProgress, nInterval);
		else if (oOptions.onEnd) oOptions.onEnd();

	};

	pProgress();

};

eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('7 22={3b:8(10){7 m=X 10.1k;q(7 x 24 10)m[x]=10[x];9 m},1t:8(10,1s){7 m=10;q(7 x 24 1s)m[x]=1s[x];9 m}};7 1P=8(){7 1k=8(){a(d.2j)d.2j.2T(d,O)};a(O[0])1k.1A=O[0];9 1k};1P.1t=8(26){7 m=1P();m.1A=X 26;q(7 i=1;i<O.1g;i++){a(O[i])22.1t(m.1A,O[i])}9 m};7 u={1W:/^(2z|p|3a|38|36|33|2Z|2Y|2X|2W|2S|h[1-6]|2N|2K|2I|2G)$/i,28:8(){[].1z(O).1n(8(v){v=$(v);a(!u.1x(v))v.1h.1w=u.1W.20(v.1v)?\'2B\':\'2A\'})},1V:8(){[].1z(O).1n(8(v){$(v).1h.1w=\'1U\'})},2y:8(){[].1z(O).1n(8(v){u[u.1x(v)?\'1V\':\'28\'](v)})},1x:8(5){9(d.1S($(5),\'1w\')!=\'1U\')},2v:8(2s,1d){q(;1d;1d=1d.1b)a(1d==2s)9 J;9 11},2p:8(5){a(5.2o){7 p=d.2p(5.2o);9{1L:5.2l+p.1L,1I:5.2k+p.1I}}1F{9{1L:5.2l,1I:5.2k}}},1S:8(5,S){7 1D;2V{a(5.1u&&(1D=5.1u.2R)){S=S.Y(/([A-Z])/g,8(s){9"-"+s.1i()});9 1D.2L(5,2c).2J(S)}1F a(5.2a)9 5.2a[S]}2H(e){}9 u.29(5,S)},2F:8(5,27){$H(27).1n(8(25,23){5.1h[23]=25})},1y:8(5,B){9(X 21("\\\\b"+B+"\\\\b")).20(5.B)},2D:8(5,B){a(!d.1y(5,B))$(5).B=($(5).B+\' \'+B).Y(/^\\s+/,\'\')},2C:8(5,B){q(7 i=1,B;B=O[i];i++)$(5).B=$(5).B.Y(X 21(\'\\\\b\'+B+\'(\\\\s+|$)\',\'g\'),\'\')},29:8(5,S){9 5.1h[S]},14:8(5){q(5=5.14;5&&5.13!=1;5=5.19);9 5},1l:8(5){q(5=5.1l;5&&5.13!=1;5=5.1l);9 5},19:8(5){q(5=5.19;5&&5.13!=1;5=5.19);9 5},1o:{},M:{1Y:/([^>|\\+|\\s]+)\\s*([>|\\+]?)\\s*/g,1X:/\\s*([>|\\+]?)\\s*([^>|\\+|\\s]+)/g,y:/^([\\w-]+)/,D:/#([\\w-]+)/g,12:/\\.([\\w-]+)/g,R:/:([\\w-]+(\\(.*\\))?)/g,1Z:/([\\w-]+)(\\((.*)\\))?/,r:/\\[([^\\]]+)]/g,1T:/^(\\[)([\\w-]+)([!|\\^|\\$|\\*]?=)(.*)(\\])$/},1r:$2x().2w(),1q:8(5,1R){a(d.1r.2u&&d.1r.2t<6&&1R=="*")9 5.1Q;9 5.39(1R)},1O:8(o){2r(o&&(o=o.1l)&&o.13!=1);9 o},17:8(o){2r(o&&(o=o.19)&&o.13!=1);9 o},37:8(o){7 C=1;q(7 k=o.1b.14;k&&k!=o;k=d.17(k),C++);9 C},2q:{35:8(o){9 u.1O(o)?11:J},34:8(o){9 u.17(o)?11:J},32:8(o){9 o.14?11:J},31:8(o,2n){9 o.30.1J(2n)>-1}},1H:8(t,U){7 c={};7 1G={},E=[\'J\'];7 L,I=\'\';7 D,y,1E,1p,1C;a(D=t.Q(d.M.D)){a(D[1])9 11;c.D=D[0].1f(1)}y=t.Q(d.M.y);c.y=(y&&y[1])||\'*\';a(1E=t.Q(d.M.12)){q(7 12,i=0;12=1E[i];i++)E.l(\'u.1y(o,"\'+12.1f(1)+\'")\')}a(1p=t.Q(d.M.R)){q(7 R,i=0;R=1p[i];i++){R=R.1f(1).Q(d.M.1Z);E.l(\'u.2q.\'+R[1].Y(\'-\',\'2U\')+\'(o, "\'+R[3]+\'")\')}}a(1C=t.Q(d.M.r)){q(7 r,i=0;r=1C[i];i++){r=r.Q(d.M.1T);L=\'2Q\'+r[2];a(!1G[r[2]]){I+=\'7 \'+L+\' = o.2P("\'+r[2]+\'") || "";\\n\';1G[r[2]]=J}1B(r[3]){G\'=\':E.l(L+\' == "\'+r[4]+\'"\');F;G\'!=\':E.l(L+\' != "\'+r[4]+\'"\');F;G\'^=\':E.l(L+\'.1J("\'+r[4]+\'") == 0\');F;G\'$=\':E.l(L+\'.1f(\'+L+\'.1g - "\'+r[4]+\'".1g) == "\'+r[4]+\'"\');F;G\'*=\':E.l(L+\'.1J("\'+r[4]+\'") > -1\');F;2O:E.l(L+\' != 2c\')}}}a(U){a(c.y&&c.y!=\'*\')E.l(\'(f ? o.1v.1i() == "\'+c.y.1i()+\'" : o.1v == "\'+c.y+\'")\');a(c.D)E.l(\'o.D == "\'+c.D+\'"\')}c.I=X 2e(\'o\',\'f\',I+\'\\2M (\'+E.2d(" && ")+\')\');9 c},16:8(z,o,15,C,f){a(C==-1)9 J;7 t=15[C];1B(t.T){G\'\':q(o=o.1b;o!=z;o=o.1b)a(t.c.I(o,f))a(d.16(z,o,15,C-1,f))9 J;F;G\'>\':o=o.1b;a(o&&o!=z&&t.c.I(o,f))a(d.16(z,o,15,C-1,f))9 J;F;G\'+\':o=d.1O(o);a(o&&t.c.I(o,f))a(d.16(z,o,15,C-1,f))9 J;F}9 11},2b:8(N,2f,2g){a(!d.1o[N]){7 K={U:[],1e:[]};7 1K=d;2f.Y(d.M.1Y,8(1Q,t,T){K.U.l({\'T\':T,\'c\':1K.1H(t,J)})});7 i=0,18;7 1j=[];2g.Y(d.M.1X,8(1Q,T,t){K.1e.l({\'T\':T,\'c\':1K.1H(t)});1B(T){G\'>\':18="2h";F;G\'\':18="2i";F;G\'+\':18="2m";F}1j.l(\'V = u.\'+18+\'(V, 1e[\'+(i++)+\'].c, f);\')});1j.l(\'9 V;\');K.c=X 2e(\'V\',\'1e\',\'f\',1j.2d(\'\\n\'));d.1o[N]=K}9 d.1o[N]},2h:8(W,c,f){7 P=[];7 k;q(7 i=0,m;m=W[i];i++){q(k=m.14;k;k=d.17(k))a(c.I(k,f))P.l(k)}9 P},2i:8(W,c,f){7 P=[];7 1M;q(7 i=0,m;m=W[i];i++){1M=d.1q(m,c.y);q(7 j=0,k;k=1M[j];j++)a(c.I(k,f))P.l(k)}9 P},2m:8(W,c,f){7 P=[];7 k;q(7 i=0,m;m=W[i];i++){a(k=d.17(m))a(c.I(k,f))P.l(k)}9 P},N:8(N,z){z=z||1a;7 1N=N.Q(/(.*#[\\w]+[^>|\\+|\\s]*)([>|\\+|\\s].*)/)||[];7 K=u.2b(N,1N[1]||N,1N[2]||\'\');7 C=K.U.1g-1;7 c=K.U[C].c;7 1m=[];7 V=[];7 f=(z==1a||(z.1u||z.1a)==1a);a(c.D)1m.l(1a.2E(c.D));1F 1m=u.1q(z,f?c.y.1i():c.y);q(7 i=0,1c;1c=1m[i];i++){a(c.I(1c,f))a(u.16(z,1c,K.U,C-1,f))V.l(1c)}9 K.c(V,K.1e,f)}};$$=u.N;',62,198,'|||||oEl||var|function|return|if||filter|this||casei|||||child|push|obj||||for|attr||selector|Element||||tag|root||className|idx|id|cond|break|case||func|true|cache|varname|_regexp|query|arguments|ret|match|pseudo|name|type|backward|result|objs|new|replace||source|false|cls|nodeType|firstChild|selectors|_traceNode|_nextNode|method|nextSibling|document|parentNode|sand|oChild|forward|substr|length|style|toLowerCase|code|constructor|previousSibling|sands|each|_cache|pseudoes|_getElementsByTagName|_agent|append|extend|ownerDocument|tagName|display|visible|hasClass|load|prototype|switch|attres|view|classes|else|declare|_filter|left|indexOf|self|top|childs|parts|_previousNode|Class|all|sTagName|getCSS|attrparse|none|hide|_blockTags|fparse|bparse|pseudoparse|test|RegExp|JINDO|key|in|val|superClass|css|show|getInlineCSS|currentStyle|_compile|null|join|Function|bquery|fquery|_getChildren|_getOffspring|__init|offsetLeft|offsetTop|_getBrother|arg|offsetParent|realPos|_pseudo|while|oParent|version|ie|has|navigator|Agent|toggle|div|inline|block|removeClass|addClass|getElementById|setCSS|dd|catch|dt|getPropertyValue|dl|getComputedStyle|nreturn|fieldset|default|getAttribute|v_|defaultView|br|apply|_|try|center|blockquote|hr|xmp|innerHTML|contains|empty|pre|last_child|first_child|li|_nodeIndex|ol|getElementsByTagName|ul|clone'.split('|'),0,{}));

/**
 * @author shgraph (ajaxUI team)
 */
(function(className) {

	SPS[className] = $Class({
		_o : null,

		$init : function(sEl) {

			var oEl = $(sEl);

			this._o = {

				miniBtn : $$('.nciku_mini_pop', oEl)[0]
			};

			this._bindEvents();


		},
		_bindEvents : function () {

			var o = this._o;

			if (o.miniBtn) {

				$Fn(function(oEvent) {

					if (!o.miniBtn) return;

					SPS.popup.mini(o.miniBtn);
					oEvent.stop();

				}).attach(o.miniBtn, 'click');

			}

		}

	});

})('cm_hmenu03');

/**
 * @author hooriza (ajaxUI team)
 */

(function(className) {

	SPS[className] = $Class({

		_instance : null,

		_o : null,

		_layers : null,
		_befChar : null,

		_hwData : null,
		_curPage : 1,
		_totalPage : 0,
		_axis : null,
		_drawingData : null,

		_ajax : null,
		_ffTimer : null,

		_autohideAC : null, // 자동완성 autohide
		_autohideHW : null, // HandWrite autohide

		_orgTitle : null, // 브라우저 제목

		_bLockedList : false,
		_sDefaultQuery : '',

		$init : function(sEl, oArg) {

			var self = this;

			this._instance = 'SPS.__inst.' + sEl;
			var oEl = $(sEl);

			// 각 엘리먼트 얻어내기
			var o = this._o = {
				wrap : oEl,
				//初始状态
				bChanged : false,

				form : $$('form', oEl)[0],
				inputbox : $$('input[type=text]', oEl)[0], // 검색어 입력폼

				handWriteBtn : $$('.hand', oEl)[0], // 필기인식 버튼
				handWrite : $$('.handwrite_layer', oEl)[0], // 필기인식 레이어
				close : $$('.txtbtn01', oEl)[0], // 필기인식 Close 버튼
				areaNoList : $$('.area_nolist', oEl)[0],
				chars : $$('.area_list', oEl)[0], // 글자 목록
				overlayer : $$('.overlayer', oEl)[0], // 오버레이어
				pinyin : $$('.pinyin', oEl)[0], // 병음표시 레이어
				zoom : $$('.zoom td', oEl)[0], // 한자 크게 보기
				paging : $$('.paging', oEl)[0], // 페이징 부분

				suggestionBtn : $$('.down', oEl)[0], // 자동완성 레이어 버튼
				suggestion : $$('.auto_complete_layer', oEl)[0], // 자동완성 레이어

				listPinyin : $$('.lst_pinyin', oEl)[0],
				listEnglish : $$('.lst_english', oEl)[0],
				listPrefix : $$('.lst_prefix', oEl)[0],
				listPostfix : $$('.lst_postfix', oEl)[0],

				areaPinyin : $$('.pinyin_area', oEl)[0],
				areaEnglish : $$('.english_area', oEl)[0],
				areaPrefix : $$('.prefix_area', oEl)[0],
				areaPostfix : $$('.postfix_area', oEl)[0],

				seperator : $$('.dot', oEl)[0],

				viewOthers : $$('.viewother', oEl),

				enableLink : $$('.enable_link', oEl)[0],
				disableLink : $$('.disable_link', oEl)[0],

				searchButton : $$('.inputbox em', oEl)[0]
			};

			// window.console.debug(o);

			if (o.chars) o.charsTable = $$('table', o.chars)[0];

			this._showHandwriteStep2(true);

			if (o.paging) {
				o.curPage = $$('.curpage', o.paging)[0];
				o.totalPage = $$('.totalpage', o.paging)[0];

				o.prev = $$('.prev', o.paging)[0];
				o.next = $$('.next', o.paging)[0];
			}

			/////

			this._orgTitle = document.title;

			this._ffTimer = new SPS.timer('interval');
			this._ajax = new SPS.Include();

			var self = this;

			if (o.handWriteBtn) {

				this._autohideHW = new SPS.autohide(o.handWrite, [ o.handWriteBtn ], {

					onShow : function() {

						self._setHandwriteData([]);
						self._showHandwritePage(1);

						self._showHandwriteStep2(true);

						try { findFlashObj('hanja_encoder_new').clearData(); } catch(e) {}
						// findFlashObj('hanja_encoder_new').clearData();
					}

				});

				o.handWriteBtn.onclick = function() { self._autohideHW.toggle(); return false; };

			}

			if (o.suggestionBtn) {

				this._autohideAC = new SPS.autohide(o.suggestion, [ o.suggestionBtn, o.inputbox ], {

					onHide : function() {
						// self._requestedValue = null;
					}

				});

				o.suggestionBtn.onclick = function() {

					var eLayer = $Element(o.suggestion);
					eLayer.addClass('message');

					self._requestedValue = null
					self._autohideAC.toggle();

					return false;

				};

			}

			try { this._hideOverLayer(); } catch(e) {}

			this._bindEvents();

			// 사파리2 에서의 텍스트필드 border 없애지 못하는 문제 해결
			this._setInputBoxForSafari();
			$Fn(function() {
				this._setInputBoxForSafari();
			}).owner(this).attach(window, 'load');

			if (!window['hanja_encoder_showHanja'])
				window['hanja_encoder_showHanja'] = function(sData) { self._requestHandwrite(sData) };

			if (!window['hanja_encoder_clearHanja'])
				window['hanja_encoder_clearHanja'] = function(sData) { self._requestHandwrite('=R 0') };

			this._setEnable(this._getEnable(), true);

			// 메인페이지인지 판달할때 HTML 에 learn 이라는 클래스명을 가진 엘리먼트가 있는지
			// 확인하므로 메인페이지 수정시에 주의할것
			var bIsMainPage = $$('.learn', o.wrap)[0] ? true : false;

			if (SPS.learningLang == 'zh' && bIsMainPage && this._autohideHW)
				this._autohideHW.show();

			if (($Element(o.inputbox).hasClass('needfocus') && self._getInputValue().length > 0) || $Element(o.inputbox).hasClass('mustfocus')){
					//搜索框初始化
					self._bLockedList = true;

					o.inputbox.focus();
					setTimeout(function(){
						o.inputbox.value = o.inputbox.defaultValue;
						o.inputbox.select();
						self._bLockedList = true;
					}, 500);

					// NGDTW-507 搜索一个单词后，光标未在搜索框时用鼠标双击，或者光标已经在搜索框后鼠标双击，该单词被选中
					document.ondblclick = function(){
						o.inputbox.select();
					};

					//点击的时候
					o.inputbox.onmousedown = function(){
						if(!o.bChanged){

// NGDTW-507 点击搜索框后仍然保留搜索内容且不显示提示框
//							var vDefault = o.inputbox.defaultValue;
//							if(o.inputbox.value == vDefault){
//								o.inputbox.value = "";
//							}

							o.bChanged = true;
							o.inputbox.style.color = "";
						}
						o.suggestion.style.display = 'none';
					}
				self._sDefaultQuery = self._getInputValue().replace('?', '');
			}
		},

		_getInputValue : function() {
			return this._o.inputbox.value.replace(/\u3000/g, '');
		},

		_setInputBoxForSafari : function() {

			var o = this._o;

			var oSfr = SPS.borderNoneSafari(o.inputbox);
			if (oSfr) oSfr.css('margin', '6px 0 0 6px');

		},

		_showHandwriteStep2 : function(bFlag) {

			var o = this._o;

			if (o.chars) $Element(o.chars)[bFlag ? 'hide' : 'show']();
			if (o.areaNoList) $Element(o.areaNoList)[bFlag ? 'show' : 'hide']();

		},

		_aItemList : null,
		_eBefItem : null,
		_curIndex : null,

		_makeSuggestionList : function(oData, nNum) {

			var o = this._o;
			var self = this;

			var sHtml = '';
			var sPrefix = this._getInputValue().toHTML();

			$A(oData).forEach(function(sItem) {

				if (--nNum < 0) return;

				var rPrefix = rPrefix_bak = [];
				for (var i = 0, len = sPrefix.length; i <len; i++){
					rPrefix[i] = sPrefix.charAt(i);
					rPrefix_bak[i] = sPrefix.charAt(i);
				}

				try{
                    rPrefix = new RegExp('(' + rPrefix.join('\\s*').replace('[','\\[').replace(']','\\]') + ')', 'i');
				}catch(e){ return;}

				sItem = sItem.replace(rPrefix, '<span class="tc_point02">$1</span>');
				sHtml += '<li><a href="#" onmouseover="' + self._instance + '._selectSuggestItem(this);" onclick="' + self._instance + '._inputSuggestionItem(this); return false;">' + sItem + '</a></li>\n';

			});

			return sHtml;

		},

		_setEnable : function(bFlag, bNoFocus) {

			var o = this._o;
			var aMethod = bFlag ? [ 'addClass', 'removeClass' ] : [ 'removeClass', 'addClass' ];

			if (!o.enableLink || !o.disableLink) return;

			$Element(o.enableLink)[aMethod[0]]('tc_sub');
			$Element(o.disableLink)[aMethod[1]]('tc_sub');

			SPS.setCookie('_nciku_ac_disable', !bFlag);
			$Element(o.suggestion).hide();

			$Element(o.suggestion)[aMethod[1]]('ac_off');

			if (!bNoFocus) {
				o.inputbox.focus();
				o.inputbox.select();
			}

			if (!bFlag) this._suggestionData = null;

		},

		_getEnable : function() {
			return (SPS.getCookie('_nciku_ac_disable') != 'true')
		},

		_viewPinyinOnly : function() {

			var o = this._o;
			var oData = this._suggestionData;

			this._setSuggestionHTML([ oData.py_list ]);
			o.inputbox.focus();

		},

		_viewEnglishOnly : function() {

			var o = this._o;
			var oData = this._suggestionData;

			this._setSuggestionHTML([ null, oData.en_list ]);
			o.inputbox.focus();

		},

		_viewAll : function(bSetOnlyOne) {

			var o = this._o;
			var oData = this._suggestionData;

			var aoPrList = $A(oData.pr_list);
			var aSuList = [];
			for (var i = 0, t; t = oData.su_list[i]; i++)
				if (aoPrList.indexOf(t) == -1)
					aSuList.push(t);

			this._setSuggestionHTML([ oData.py_list, oData.en_list, oData.pr_list, aSuList ], bSetOnlyOne);
			o.inputbox.focus();

		},

		_getItems : function() {

			var o = this._o;
			this._aItemList = $$('div ul a', o.suggestion);

			this._eBefItem = null;
			this._curIndex = null;

		},

		_setSuggestionData : function(oData) {

			if (SPS.debugMode) window.console.debug('AC response : ' + oData.q + ' ' + '-'.repeat(50));

			if (!this._getEnable()) return;
			//if (SPS.debugMode) window.console.debug(1);

			var self = this;
			var o = this._o;

			var eLayer = $Element(o.suggestion);
			eLayer.removeClass('message');

			var sPrefix = this._getInputValue();

			//if (SPS.debugMode) window.console.debug(2 + ' : ' + sPrefix.length);

			if (sPrefix.length == 0) {
				$Element(o.suggestion).hide();
				return;
			}

			this._suggestionData = oData;
			if (oData.q != sPrefix.replace(/\s/g, '')) return;// 080107 park js

			this._viewAll(true);
			// this._selectSuggestItem();

		},

		_setSuggestionHTML : function(lists, bSetOnlyOne) {

			var o = this._o;
			var eLayer = $Element(o.suggestion);

			var oLists = {
				'pinyin' : lists[0] || [],
				'english' : lists[1] || [],
				'prefix' : lists[2] || [],
				'postfix' : lists[3] || []
			};

			if (SPS.learningLang == 'en') oLists.pinyin = [];

			var oMethod = { 'true' : 'addClass', 'false' : 'removeClass' };

			var oExist = {
				'pinyin' : oLists.pinyin.length > 0,
				'english' : oLists.english.length > 0,
				'prefix' : oLists.prefix.length > 0,
				'postfix' : oLists.postfix.length > 0
			};

			var bExist = oExist.pinyin || oExist.english || oExist.prefix || oExist.postfix;
			var bOnlyOne = 1 == ((oExist.pinyin ? 1 : 0) + (oExist.english ? 1 : 0) + (oExist.prefix ? 1 : 0) + (oExist.postfix ? 1 : 0));

			var nNum = bOnlyOne ? 10 : 5;

			o.listPinyin.innerHTML = this._makeSuggestionList(oLists.pinyin, nNum);
			o.listEnglish.innerHTML = this._makeSuggestionList(oLists.english, nNum);
			o.listPrefix.innerHTML = this._makeSuggestionList(oLists.prefix, nNum);
			o.listPostfix.innerHTML = this._makeSuggestionList(oLists.postfix, nNum);

			$Element(o.suggestion)[oMethod[oExist.pinyin]]('pinyin');
			$Element(o.suggestion)[oMethod[oExist.english]]('english');
			$Element(o.suggestion)[oMethod[oExist.prefix]]('prefix');
			$Element(o.suggestion)[oMethod[oExist.postfix]]('postfix');

			$Element(o.suggestion)[oMethod[bOnlyOne]]('show_one');
			if (bSetOnlyOne) $Element(o.suggestion)[oMethod[bOnlyOne]]('only_one');

			// 검색결과가 하나라도 있으면 보여주기
			if (bExist) {
				if (!this._bLockedList) {
					eLayer.show();
				}
			} else {
				eLayer.hide();
				eLayer.addClass('message');
			}

			this._getItems();

		},

		_deEscapeSuggestItemValue : function( val ) {

		    val = val.replace("&amp;", "&");
		    val = val.replace("&lt;", "<");
		    val = val.replace("&gt;", ">");
		    val = val.replace("&apos;", "'");
		    val = val.replace("&quot;", "\"");
		    return val;

		},
		_selectSuggestItem : function(nIdx, bFill) {

			if (!this._aItemList || !this._aItemList.length) return;

			var nLen = this._aItemList.length;

			if (typeof nIdx != 'number')
				nIdx = $A(this._aItemList).indexOf(nIdx);

			if (nIdx < 0) nIdx = nLen + nIdx;
			nIdx = nIdx % nLen;

			var oEl = this._aItemList[nIdx];

			if (this._eBefItem) this._eBefItem.removeClass('selected');
			var eEl = this._eBefItem = $Element(oEl);

			eEl.addClass('selected');

			this._curIndex = nIdx;

			if (bFill) this._o.inputbox.value = this._deEscapeSuggestItemValue (oEl.innerHTML.stripTags());

		},

		_onKeyUp : function(oEvent) {

			if (oEvent.keyCode == 38 || oEvent.keyCode == 40) return; // UP, DOWN

		},

		_onKeyDown : function(oEvent) {
			var self = this;

			if (oEvent.keyCode == 38 || oEvent.keyCode == 40) { // UP, DOWN

				var nOffset = oEvent.keyCode - 39;
				this._selectSuggestItem(this._curIndex === null ? 0 : this._curIndex + nOffset, true);

				this._requestedValue = this._getInputValue();
				$Event(oEvent).stop();

				return;

			} else if (oEvent.keyCode == 13 ) { // Enter

				this._requestSearch();
				$Event(oEvent).stop();

				return;

			}

			setTimeout(function() { self._requestSuggestion(self._getInputValue()); }, 1);

		},

		_inputSuggestionItem : function(oEl) {

			this._selectSuggestItem(oEl, true);
			this._requestSearch();

		},

		_requestedValue : null,

		_requestSuggestion : function(sValue) {

			var self = this;
			var o = this._o;

			if (!this._getEnable()) return;
			if (!o.listPinyin) return;

			var sUniq = 'S' + parseInt(Math.random() * 100000000);

			if (sValue == this._requestedValue) return;
			this._requestedValue = sValue;

			// window.console.debug('req : ' + sValue);

			if (sValue.length) {

				var sUrl = SPS.autoCompleteURL + '/autocompl_ngd?m=s&q=' + encodeURIComponent(sValue);
				if (SPS.debugMode) window.console.debug('AC request : ' + sValue);
				this._ajax.request(sUrl, function(o) { self._setSuggestionData(o); });

			} else {

				var bHasData = this._suggestionData ? true : false;

				this._ajax.abort();
				this._suggestionData = null;

				if (bHasData) $Element(o.suggestion).hide();

			}

		},

		_trID_hw : null,

		_requestHandwrite : function(sTrace) {

			var self = this;
			if(sTrace != "=R 0")
				this._drawingData = sTrace;

			document.title = this._orgTitle;

			var oAjax = $Ajax('/proxy.nhn', {
				onload : function(res) {

					var aList = [];
					var aParts = res.text().split(';');

					if (aParts[0] != self._trID_hw) return;

					$A(aParts).forEach(function(sPart, nIdx) {
						if (nIdx && sPart.indexOf(',') > -1) aList.push(sPart.split(','));
					});

					self._showHandwriteStep2(false);

					self._setHandwriteData(aList);
					self._showHandwritePage(1);

				}
			});

			this._trID_hw = SPS.uniqid();

			oAjax.request({
				'ReqNum' : this._trID_hw,
				'InputStr' : sTrace,
				'auto' : 1,
				'charset' : 3
			});

		},
/**
 * Edited by sundongguo on 20080509.
 * Replace the method "_requestSearch".
 *
		_requestSearch : function() {

			var o = this._o;
			var sValue = this._getInputValue();

			var eInputbox = $Element(o.inputbox);

			if (eInputbox.hasClass('regard_null')) sValue = '';

			if (!eInputbox.hasClass('skip_nullcheck') && sValue.length == 0) {
				alert(SPS.text('FILL_SEARCHQUERY'));
				o.inputbox.focus();
				return;
			}

			var oSearchButton = o.searchButton;
			if( oSearchButton.href.match('detailpageEn.nhn') ){
				document.location.href = oSearchButton.href + "&query=" + encodeURIComponent(sValue);
			} else {
				var searchKeyword = encodeURIComponent(sValue);
				document.location.href = oSearchButton.href + searchKeyword;
			}

		},
*/
		_requestSearch:function()
		{
			var o=this._o;
			var sValue=this._getInputValue();
			var eInputbox=$Element(o.inputbox);
			var oSearchButton_href = '';
			if(eInputbox.hasClass('regard_null'))sValue='';
			if(!eInputbox.hasClass('skip_nullcheck')&&sValue.length==0)
			{
				alert(SPS.text('FILL_SEARCHQUERY'));
				o.inputbox.focus();
				return;
			}
			var oSearchButton=o.searchButton;
			oSearchButton_href = oSearchButton.getAttribute('href');

			if(oSearchButton_href.match('detailpageEn.nhn'))
			{
				document.location.href=oSearchButton_href+"/"+encodeURIComponent(sValue);
			}
			else
			{
				var sRange="";
				var elRadios=document.getElementsByName("search_range");
				for(var i=0;i<elRadios.length;i++)
				{
					var elRadio=elRadios[i];
					if(elRadio.checked)
					{
						sRange=elRadio.value==""?"":"/"+elRadio.value;
						break;
					}
				}
				document.location.href=oSearchButton_href+sRange+"/"+encodeURIComponent(sValue);
			}
		},

		_setHandwriteData : function(aList) {
			this._hideOverLayer();
			this._hwData = aList;
		},

		_getHandwriteData : function(sKey) {

			var aRet = null;

			$A(this._hwData).forEach(function(aItem) {
				if (aItem[0] == sKey) aRet = aItem;
			});

			return aRet;

		},

		_showHandwritePage : function(nPage) {

			var o = this._o;

			if (!o.charsTable) return;

			var aCells = $$('a', o.charsTable);
			var nIdx = (nPage - 1) * 25; // 한페이지에 25개씩 보임

			var self = this;

			$A(aCells).forEach(function(oCell) {

				var bHasData = self._hwData[nIdx] && self._hwData[nIdx][0] ? true : false;

				oCell.innerHTML = bHasData ? self._hwData[nIdx][0] : '';
				$Element(oCell).css('cursor', bHasData ? 'pointer' : 'default');

				nIdx++;

			});

			var oCurPage = o.curPage;
			var oTotalPage = o.totalPage;

			var eCurPage = $Element(oCurPage);
			var eTotalPage = $Element(oTotalPage);

			var nNum = this._hwData.length;

			var nCurPage = nPage; // n페이지로 이동
			var nTotalPage = parseInt((nNum - 1) / 25) + 1; // 한페이지에 25개씩 보임

			if (nTotalPage < 1) nTotalPage = 1;

			this._curPage = nCurPage;
			this._totalPage = nTotalPage;

			eCurPage.text(nCurPage + '');
			eTotalPage.text(nTotalPage + '');

			eTotalPage[nCurPage == nTotalPage ? 'addClass' : 'removeClass']('tc_point01');

		},

		_hideOverLayer : function() {

			var o = this._o;

			this._befChar = null;
			$Element(o.overlayer).hide();

		},

		_bindEvents : function() {

			var self = this;
			var o = this._o;

			// 입력폼에 포커스가 갔을때
			$Fn(function() {

				if ($Agent().navigator().firefox) {

					self._ffTimer.start(100, function() {
						self._requestSuggestion(self._getInputValue());
					});

				}

				var inputbox = o.inputbox;
				if (!$Element(inputbox).hasClass('cleared')) {
					inputbox.value = '';
					$Element(inputbox).addClass('cleared');
					$Element(inputbox).removeClass('regard_null');
				}

//				self._requestSuggestion(self._getInputValue());

			}).attach(o.inputbox, 'focus');

/* -sugg
			$Fn(function() {
				self._bLockedList = false;
				self._requestSuggestion(self._getInputValue());
			}).attach(o.inputbox, 'click');

			$Fn(function() {
				self._bLockedList = false;
			}).attach(o.inputbox, 'keydown');

			$Fn(function() {
				self._bLockedList = false;
				self._ffTimer.stop();
			}).attach(o.inputbox, 'blur');

			$Fn(function() {

				if (!o.suggestion) return;

				self._bLockedList = false;

				var eLayer = $Element(o.suggestion);
				var bMethod = eLayer.visible() ? 'hide' : 'show';

				// 보여줄 데이터가 없으면
				if (bMethod == 'show' && !self._suggestionData)
					eLayer.addClass('message');

				eLayer[bMethod]();

			}).attach(o.inputbox, 'mousedown');
*/
			if (o.close) {

				// 필기인식창의 close 버튼을 눌렀을때
				$Fn(function(oEvent) {

					if (!o.handWrite) return;

					$Element(o.handWrite).hide();
					oEvent.stop();

				}).attach(o.close, 'click');

			}

			if (o.chars) {

				// 글자 목록에 마우스를 올렸을때 병음 표시하도록
				$Fn(function(oEvent) {

					if (!this._hwData) return;

					// 어느 글자 위에 마우스를 올렸는지 좌표를 통해 계산해서 알아내자
					var oPos = oEvent.pos();
					var oGap = $Element(o.chars).offset();

					var oOffset = { col : oPos.pageX - oGap.left, row : oPos.pageY - oGap.top };

					// 한글자가 차지하고 있는 영역은 가로x세로 : 25x25 니까 나눠주자
					oOffset.col = parseInt(oOffset.col / 25);
					oOffset.row = parseInt(oOffset.row / 25);

					this._axis = oOffset;

					var oTable = o.charsTable;
					var oCell = null;

					// 얻어낸 행/열로 해당하는 TD 태그 찾기
					try { oCell = oTable.rows[oOffset.row].cells[oOffset.col]; } catch(e) {}

					if (!oCell) this._hideOverLayer();
					if (!oCell || oCell == this._befChar) return;

					var eCell = $Element(oCell);
					var eHandWrite = $Element(o.handWrite);

					// 말풍선의 글자 바꿔주기
					var sChar = eCell.text().replace(/(^\s+|\s+$)/g, '');
					if (!sChar) {
						this._hideOverLayer();
						return;
					}

					this._befChar = oCell;

					var sPinyins = this._getHandwriteData(sChar).join('<br />');
					sPinyins = sPinyins.replace(/^.<br\s?\/>/, '');

					o.pinyin.style.display = "none";
					$Element(o.overlayer)[oCell ? 'show' : 'hide']();

					if (sPinyins) {

						$$('.text', o.pinyin)[0].innerHTML = sPinyins;
						o.zoom.innerHTML = sChar;

						var oPos = eCell.offset();
						var oGap = eHandWrite.offset();

						// handwrite提示信息
						$Element(o.overlayer).css({
							left : (eCell._element.offsetLeft) + 'px',
							top : (eCell._element.offsetTop) + 'px'
							//left : (eCell._element.offsetLeft) + 'px',
							//top : (eCell._element.offsetTop) + 'px'
//							left : (oPos.left - oGap.left - 3) + 'px',
//							top : (oPos.top - oGap.top - o.overlayer.offsetHeight - 10) + 'px'
						});

					}

					$Element(o.overlayer)[sPinyins ? 'show' : 'hide']();

				}).owner(this).attach(o.chars, 'mousemove');

				// 문자 선택 영역 밖으로 마우스 이동하면 말풍선 숨기기
				$Fn(function(oEvent) {

					if (oEvent.of(o.chars)) return;
					this._hideOverLayer();

				}).owner(this).attach(o.chars, 'mouseout');

				// 필기인식 글자 클릭하면 입력되게
				$Fn(function(oEvent) {

					var trackingData = '/handWriteTrack.gif?drawing='+this._drawingData+'&page='+this._curPage+'&col='+this._axis.col+'&row='+this._axis.row;


					var o = this._o;
					var self = this;

					if (!$Element(o.overlayer).visible()) return;

					var sChar = $Element(o.zoom).text();

					o.inputbox.focus();

					try { findFlashObj('hanja_encoder_new').clearData(); } catch(e) {}
					self._setHandwriteData([]);
					self._showHandwritePage(1);

					//手写输入
					setTimeout(function() {
						var vDefault = o.inputbox.defaultValue;
						if(o.inputbox.value == vDefault && !o.bChanged){
							o.inputbox.value = sChar;
							o.bChanged = true;
						}
						else{
							o.inputbox.value += sChar;
						}
						self._requestSuggestion(self._getInputValue());
					}, 10);

					var oAjaxTracker = $Ajax(trackingData, {
						onload : function(res) {

						}
					});

					oAjaxTracker.request({
						'tracker' : 'nciku'
					});

					oEvent.stop();

				}).owner(this).attach(o.chars, 'click');

			}

			if (o.paging) {

				$Fn(function(oEvent) {

					if (this._curPage > 1)
						this._showHandwritePage(this._curPage - 1);

					oEvent.stop();

				}).owner(this).attach(o.prev, 'click');

				$Fn(function(oEvent) {

					if (this._curPage < this._totalPage)
						this._showHandwritePage(this._curPage + 1);

					oEvent.stop();

				}).owner(this).attach(o.next, 'click');

			}

		}

	});

})('cm_search01');

/**
 * @author hooriza (ajaxUI team)
 */

SPS.commonLayer = {

	_o : null,

	_tts : null,

	_pinyinTimer : null,

	_ttsTimer : null,
	_infoTimer : null,
	_userTimer : null,

	_init : function(oEl) {

		if (this._o) return;

		var o = this._o = {

			cn_tts : {
				layer : $$('#layer_tip_cn_tts', oEl)[0],
				pinyin : $$('.txt_pinyin', oEl)[0],
				chinese : $$('.txt_chinese', oEl)[0],
				status : $$('img.cn_status', oEl)[0],
				autoplay : $$('#layer_tip_cn_tts input', oEl)[0],
				autoplayArea : $$('.autoplay', oEl)[0]
			},

			en_tts : {
				layer : $$('#layer_tip_en_tts', oEl)[0],
				status : $$('img.en_status', oEl)[0],
				autoplay : $$('#layer_tip_en_tts input', oEl)[0]
			},

			info : {
				layer : $$('#layer_tip_wordinfo', oEl)[0],
				content : $$('.has', oEl)[0]
			},

			userinfo : {
				layer : $$('#layer_tip_userinfo', oEl)[0],
				links : $$('.userlink', oEl),
				theirNciku : $$('.their_nciku', oEl)[0],
				myNciku : $$('.my_nciku', oEl)[0]
			}

		};

		var oBody = document.body;

		oBody.insertBefore(o.cn_tts.layer, oBody.firstChild);
		oBody.insertBefore(o.en_tts.layer, oBody.firstChild);
		oBody.insertBefore(o.info.layer, oBody.firstChild);
		oBody.insertBefore(o.userinfo.layer, oBody.firstChild);

		var oOption = {

			frameSrc : '/html/tts.html',

			onStateChange : function(nState) {

				var sFilename;

				switch (nState) {
				case 0: // stop
					sFilename = 'tts_autoplay1_off.gif';
					break;

				case 2: // play
					sFilename = 'tts_autoplay1.gif';
					break;

				case 3: // buffering
					sFilename = 'tts_autoplayload1.gif';
					break;
				}

				if (sFilename) {

					o.cn_tts.status.src = SPS.dirname(o.cn_tts.status.src) + sFilename;

					/*
						en TTS 레이어 에서는
							tts_autoplay2_off.gif
							tts_autoplay2.gif
							tts_autoplayload2.gif

						로 셋팅
					*/
					o.en_tts.status.src = SPS.dirname(o.en_tts.status.src) + sFilename.replace('1', '2');
				}

			}

		};

		this._tts = new SPS.tts(oOption);

		this._pinyinTimer = new SPS.timer('timeout');
		this._ttsTimer = new SPS.timer('timeout');
		this._infoTimer = new SPS.timer('timeout');
		this._userTimer = new SPS.timer('timeout');

		$Element(o.cn_tts.layer).css({ position : 'absolute', left : '-99999px' });
		$Element(o.en_tts.layer).css({ position : 'absolute', left : '-99999px' });

		$Element(o.cn_tts.layer).show();
		$Element(o.en_tts.layer).show();

		$Element(o.info.layer).css({ position : 'absolute', left : '-99999px' });

		// IE bug 버그 때문에 hide 를 못 씀
		$Element(o.userinfo.layer).css('left', '-99999px');
		$Element(o.userinfo.layer).css('top', '-99999px');

		this._bindEvents();

	},

	_bindEvents : function() {

		var o = this._o;

		$Fn(function() { SPS.setCookie('autoPlayFlag', this.checked); }).owner(o.cn_tts.autoplay).attach(o.cn_tts.autoplay, 'click');
		$Fn(function() { SPS.setCookie('autoPlayFlag', this.checked); }).owner(o.en_tts.autoplay).attach(o.en_tts.autoplay, 'click');

		var self = this;

		//o.info.layer.onmouseover = function() { self._infoTimer.stop(); };
		//o.info.layer.onmouseout = function() { self._hideInfo(); };

		o.userinfo.layer.onmouseover = function() { self._userTimer.stop(); };
		o.userinfo.layer.onmouseout = function() { self._hideUser(); };

		this._bindTTSEvent();

	},

	_buttonClick : function(oEl) {

		var oImg = $$('img', oEl)[0];

		var sSrc = oImg.src;
		var sState = sSrc.match(/\/(\w+)\.gif/)[1];

		var sMethod = sState.search(/tts_autoplay[0-9]_off/) > -1 ? 'play' : 'stop';
		this._tts[sMethod]();

	},

	_ucssafeutf8 : function(str) {

		var rtn = "";
		var for_i = 0;
		var str_length = str.length;
		var u;
		var s;
		for(i=0; i < str_length ; i++)
		{
			u = str.charCodeAt(i);
			if		( (u >= 0x00) && (u <= 0x7f) )
			{
				s= "0" + u.toString(16);
				rtn += "%" + s.substr(s.length-2);
			}
			else if	( u > 0x1fffff )
			{
				rtn += "%" + (oxf0 + ((u & 0x1c0000) >> 18)).toString(16);
				rtn += "%" + (0x80 + ((u & 0x3f000) >> 12)).toString(16);
				rtn += "%" + (0x80 + ((u & 0xfc0) >> 6)).toString(16);
				rtn += "%" + (0x80 + (u & 0x3f)).toString(16);
			}
			else if	( u > 0x7ff )
			{
				rtn += "%" + (0xe0 + ((u & 0xf000) >> 12)).toString(16);
				rtn += "%" + (0x80 + ((u & 0xfc0) >> 6)).toString(16);
				rtn += "%" + (0x80 + (u & 0x3f)).toString(16);
			}
			else
			{
				rtn += "%" + (0xc0 + ((u & 0x7c0) >> 6)).toString(16);
				rtn += "%" + (0x80 + (u & 0x3f)).toString(16);
			}
		}
		return rtn;
	},

	_oWord : null,
	_oLayer : null,

	_onlyPinyin : false,

	info : function(oEl, sText) {

		var o = this._o;
		var eEl = $Element(oEl);

		var oPos = eEl.offset();
		var oLayer = o.info.layer;

		o.info.content.innerHTML = sText;

		this._infoTimer.start(200, function() {

			if ($Agent().navigator().ie) { // IE 버그 회피
				oLayer.style.left = oPos.left + 'px';
				oLayer.style.top = oPos.top - oLayer.offsetHeight + 'px';
			} else {
				oLayer.style.top = oPos.top - oLayer.offsetHeight + 'px';
				oLayer.style.left = oPos.left + 'px';
			}

		});

		if (!eEl.hasClass('__info')) {

			// 이벤트 부여
			$Fn(function(oEvent) {
				if (oEvent.of(oEl)) return;
				this._hideInfo();
			}).owner(this).attach(oEl, 'mouseout');

			eEl.addClass('__info');
		}

	},

	_hideInfo : function() {

		var o = this._o;
		var oLayer = o.info.layer;

		this._infoTimer.start(100, function() {
			oLayer.style.left = '-99999px';
		});

	},

	tts : function(oEl, sText, sChinese, sPinyin) {

		var self = this;

		if (!sText && !sChinese && !sPinyin) return;

		var o = this._o;

		var eEl = $Element(oEl);
		var sUrl = '';
		var bAutoPlay = false;

		var bEng = typeof sChinese == 'undefined' && typeof sPinyin == 'undefined'; // sChinese, sPinyin 둘다 없으면 영어
		var sKey = sPinyin ? 'cn_tts' : 'en_tts'; // sPinyin 유무에 따라 레이어 모양 결정
		var sUrl = SPS.tts.textToUrl(sText, bEng);

		var o = this._o;
		var bAutoPlay = (SPS.getCookie('autoPlayFlag') != 'false');

		o[sKey].autoplay.checked = bAutoPlay ? true : false;

		if (!bEng) SPS.data.setValue('.txt_', o[sKey].layer, { chinese : sChinese, pinyin : sPinyin }, true);

		this._onlyPinyin = !sText && !sChinese && sPinyin;

		if (bEng || sChinese) {
			$Element(o.cn_tts.chinese).show();
			$Element(o.cn_tts.autoplayArea).show();
		} else {
			$Element(o.cn_tts.chinese).hide();
			$Element(o.cn_tts.autoplayArea).hide();
		}

		// 레이어 보여주기
		var oLayer = o[sKey].layer;
		var eLayer = $Element(oLayer);

		////////

		if (!eEl.hasClass('__tts')) {

			// 이벤트 부여
			$Fn(function(oEvent) {
				if (oEvent.of(oEl)) return;
				this._hide();
			}).owner(this).attach(oEl, 'mouseout');

			eEl.addClass('__tts');
		}

		this._show(eEl, eLayer, sUrl, !bAutoPlay);

	},

	_show : function(eEl, eLayer, sUrl, bSuspend) {

		var self = this;

		try { if (eEl.$value() == this._oWord.$value()) eEl = null; } catch(e) {}

		if (!eEl) {
			this._pinyinTimer.stop();
			return;
		}

		this._hide(true);

		this._oWord = eEl;
		this._oWord.addClass('tts_on');

		this._oLayer = eLayer;

		this._pinyinTimer.start(500, function() {

			// 레이어 보여주기
			var oPos = eEl.offset();
			var oLayer = self._oLayer.$value();

			if ($Agent().navigator().ie) { // IE 버그 회피
				oLayer.style.left = oPos.left + 'px';
				oLayer.style.top = oPos.top - oLayer.offsetHeight + 'px';
			} else {
				oLayer.style.top = oPos.top - oLayer.offsetHeight + 'px';
				oLayer.style.left = oPos.left + 'px';
			}

			if (!self._onlyPinyin) self._tts.play(sUrl, bSuspend);

		});

	},

	_hide : function(bRightNow) {

		var self = this;

		var pFunc = function() {
			if (self._oWord) self._oWord.removeClass('tts_on');
			self._oWord = null;

			if (self._oLayer) self._oLayer.css('left', '-99999px');
			self._oLayer = null;

			if (!self._onlyPinyin) self._tts.stop();
		};

		this._pinyinTimer.stop();

		if (bRightNow) pFunc();
		else this._pinyinTimer.start(500, pFunc); // 사라질땐 0.5초 딜레이 후 사라짐

	},

	_currentUser : null,

	// NGDTW-254 对应页面：p127_popup_vocab.ftl
	_user : function(oEl, aLinks, sTarget) {

		var o = this._o;

		var eEl = $Element(oEl);
		var oOffset = eEl.offset();

		var oLayer = o.userinfo.layer;
		var eLayer = $Element(oLayer);

		var aPos = [ oOffset.left + parseInt(oEl.offsetWidth / 2) - 14, oOffset.top - oLayer.offsetHeight - 1 ];
		var sTop = document.documentElement.scrollTop || document.body.scrollTop;
		var sTop = document.documentElement.scrollTop || document.body.scrollTop;
		var diffValue = oOffset.top - sTop;
		var hLayer = oLayer.offsetHeight;

		this._currentUser = oEl;
		this._userTimer.stop();

		var aClientSize = SPS.layer.getClientSize();

		if (aClientSize[0] < aPos[0] + 110)
			aPos[0] = aClientSize[0] - 110;

		eLayer.css('left', aPos[0] + 'px');

		if (hLayer < diffValue) {	// 在上面显示提示layer
			eLayer.removeClass('tip_layer2');
			eLayer.addClass('tip_layer');
			eLayer.css('top', aPos[1] + 'px');
		} else {	// 在下面显示提示layer
			eLayer.removeClass('tip_layer');
			eLayer.addClass('tip_layer2');
			eLayer.css('top', oOffset.top + oEl.offsetHeight + 1 + 'px');
		}




		if (!eEl.hasClass('__user')) {

			// 이벤트 부여
			$Fn(function(oEvent) {
				if (oEvent.of(oEl)) return;
				this._hideUser();
			}).owner(this).attach(oEl, 'mouseout');

			$Fn(function(oEvent) {
				if (oEvent.of(oEl)) return;
				if (oEl != this._currentUser) return;

				this._userTimer.stop();
			}).owner(this).attach(oEl, 'mouseover');

			eEl.addClass('__user');
		}


		var oLinks = $$('a.userlink', oLayer);
		$A(oLinks).forEach(function(oLink, nIdx) {

			if (aLinks[nIdx]) {

				oLink.href = aLinks[nIdx];

				if (sTarget) {

					oLink.onclick = function() {

						if (sTarget.indexOf('_') == 0) {

							if (sTarget == '_new')
								SPS.newWin(this.href);
							else
								window.open(this.href, sTarget);

							return false;

						} else {

							var oWnd = null;

							try { oWnd = eval(sTarget); } catch(e) {}

							if (oWnd) {

								oWnd.location.href = this.href;

								// IE bug 버그 때문에 hide 를 못 씀
								eLayer.css('left', '-99999px');
								eLayer.css('top', '-99999px');

								return false;

							}

						}

						return true;

					};

				} else {
					oLink.onclick = null;
				}
			}

		});

	},

	user : function(oEl, aLinks, sTarget) {
		var o = this._o;

		$Element(o.userinfo.theirNciku).show();
		$Element(o.userinfo.myNciku).hide();

		return this._user(oEl, aLinks, sTarget);
	},

	myself : function(oEl, aLinks, sTarget) {
		var o = this._o;

		$Element(o.userinfo.theirNciku).hide();
		$Element(o.userinfo.myNciku).show();

		return this._user(oEl, aLinks, sTarget);
	},

	_hideUser : function() {

		var o = this._o;
		var oLayer = o.userinfo.layer;

		this._userTimer.start(100, function() {
			oLayer.style.left = '-999999px';
		});

	},

	_ttsElement : null,
	_ttsLayer : null,

	_ttsShow : function(oEl, oLayer) {
		this._ttsHide();
		if(curOver){
			//------------- LW modify 2008-06-05 for Pinyin -----------
			try{
				var over = curOver._event.target || curOver._event.srcElement;
				if(over.tagName.toLowerCase() == 'a' || over.tagName.toLowerCase() == 'span'){
					clearTimeout(time);
				}
			}catch(e){
					clearTimeout(time);
			}
		}
		this._ttsElement = oEl;
		this._ttsLayer = oLayer;

		oLayer.style.width = 'auto';
		if (oLayer.offsetWidth > 880)
			oLayer.style.width = '880px';

		var oPos = $Element(oEl).offset();

		var nRight = oPos.left + oLayer.offsetWidth;
		var aClientSize = SPS.layer.getClientSize();

		if (aClientSize[0] < nRight) oPos.left = aClientSize[0] - oLayer.offsetWidth;

		if ($Agent().navigator().ie) { // IE 버그 회피
			oLayer.style.left = oPos.left + 'px';
			oLayer.style.top = oPos.top - oLayer.offsetHeight + 'px';
		} else {
			oLayer.style.top = oPos.top - oLayer.offsetHeight + 'px';
			oLayer.style.left = oPos.left + 'px';
		}

		if ($Element(oEl).hasClass('tts_button')) oEl = oEl.parentNode;
		$Element(oEl).addClass('tts_on');

	},

	_ttsHide : function(bDelay) {

		var self = this;

		if (bDelay) {
			this._ttsTimer.start(100, function() { self._ttsHide(); });
			return;
		}

		if (this._ttsElement) {

			var oEl = this._ttsElement;

			if ($Element(oEl).hasClass('tts_button')) oEl = oEl.parentNode;
			$Element(oEl).removeClass('tts_on');

			this._ttsElement = null;
		}

		if (this._ttsLayer) $Element(this._ttsLayer).css('left', '-9999999px');
		this._ttsLayer = null;

		this._ttsTimer.stop();
		this._tts.stop();

	},

	_bindTTSEvent : function() {

		var self = this;
		var o = this._o;

		$Fn(function(oEvent) {
			self._ttsTimer.stop();
		}).attach(o.cn_tts.layer, 'mouseover');

		$Fn(function(oEvent) {
			self._ttsTimer.stop();
		}).attach(o.en_tts.layer, 'mouseover');

		$Fn(function(oEvent) {
			self._ttsHide(true);
		}).attach(o.cn_tts.layer, 'mouseout');

		$Fn(function(oEvent) {
			self._ttsHide(true);
		}).attach(o.en_tts.layer, 'mouseout');

	},

	pinyin : function(oEl, sPinyin) {
		if (this._oldPinyin != sPinyin)
			this._ttsHide();

		this._oldPinyin = sPinyin;
		this.newTTS(oEl, '', sPinyin, 0); // 设置从喇叭到文字上时，拼音层显示的间隔，默认为300
	},

	//------------------ auto play function ---------------------
	/**
	 * Parameter：
	 *	@oEl: clicked icon
	 *	@sText: text to speech
	 *	@sPinyin: pin yin
	 */
	newTTS : function(oEl, sText, sPinyin, nDelay) {

		if (!SPS.tts._ttsEnable) {
			alert(SPS.text('TTS_NOT_SUPPORTED'));
			return;
		}

		var self = this;
		if (!sText && !sPinyin) return;

		nDelay = nDelay || 0;

		var o = this._o;
		var eEl = $Element(oEl);

		var sKey = sPinyin ? 'cn_tts' : 'en_tts'; // sPinyin 유무에 따라 레이어 모양 결정

		if (sText) {
			var bIsChinese = sText.search(/[\u4E00-\u9FFF]/) > -1;
			var sUrl = SPS.tts.textToUrl(sText, !bIsChinese);			// 080707 player's url
			var bAutoPlay = (SPS.getCookie('autoPlayFlag') != 'false');		// 080707 match keyword(autoPlayFlag) from cookie it will return true or false,if true then auto play, if false then stop

			o[sKey].autoplay.checked = bAutoPlay ? true : false;	// 080707 true is auto play; false is stopping
		}

		if (sPinyin) SPS.data.setValue('.txt_', o[sKey].layer, { chinese : sText, pinyin : sPinyin }, true);

		if (sText) {
			$Element(o.cn_tts.chinese).show();
			$Element(o.cn_tts.autoplayArea).show();
		} else {
			$Element(o.cn_tts.chinese).hide();
			$Element(o.cn_tts.autoplayArea).hide();
		}

		// 레이어 보여주기
		var oLayer = o[sKey].layer;

		////////

		var bind = function(oEl) {

			var eEl = $Element(oEl);
			if (eEl.hasClass('__tts')) return;
			eEl.addClass('__tts');

			$Fn(function(oEvent) {
				curOver = oEvent;	// LW modify 2008-06-05 for Pinyin
				if (oEl != self._ttsElement) return;
				self._ttsTimer.stop();
			}).attach(oEl, 'mouseover');

			$Fn(function(oEvent) {
				if (oEvent.of(oEl)) return;
				curOut = oEvent;	// LW modify 2008-06-05 for Pinyin
				self._ttsHide(true);
			}).attach(oEl, 'mouseout');

		};

		bind(oEl);

		this._ttsTimer.start(nDelay, function() {
			self._ttsShow(oEl, oLayer);
			if (sText) self._tts.play(sUrl, !bAutoPlay);
		});

	}

};

(function(className) {

	SPS[className] = $Class({

		$init : function(oEl) { SPS.commonLayer._init($(oEl)); }

	});

})('cm_layer');


if(typeof com == "undefined") var com = new Object();
if(typeof com.deconcept == "undefined") com.deconcept = new Object();
if(typeof com.deconcept.util == "undefined") com.deconcept.util = new Object();
if(typeof com.deconcept.FlashObjectUtil == "undefined") com.deconcept.FlashObjectUtil = new Object();
com.deconcept.FlashObject = function(swf, id, w, h, ver, c, useExpressInstall, quality, xiRedirectUrl, redirectUrl, detectKey){
  if (!document.createElement || !document.getElementById) return;
  this.DETECT_KEY = detectKey ? detectKey : 'detectflash';
  this.skipDetect = com.deconcept.util.getRequestParameter(this.DETECT_KEY);
  this.params = new Object();
  this.variables = new Object();
  this.attributes = new Array();
  this.useExpressInstall = useExpressInstall;

  if(swf) this.setAttribute('swf', swf);
  if(id) this.setAttribute('id', id);
  if(w) this.setAttribute('width', w);
  if(h) this.setAttribute('height', h);
  if(ver) this.setAttribute('version', new com.deconcept.PlayerVersion(ver.toString().split(".")));
  this.installedVer = com.deconcept.FlashObjectUtil.getPlayerVersion(this.getAttribute('version'), useExpressInstall);
  if(c) this.addParam('bgcolor', c);
  var q = quality ? quality : 'high';
  this.addParam('quality', q);
  var xir = (xiRedirectUrl) ? xiRedirectUrl : window.location;
  this.setAttribute('xiRedirectUrl', xir);
  this.setAttribute('redirectUrl', '');
  if(redirectUrl) this.setAttribute('redirectUrl', redirectUrl);
};
com.deconcept.FlashObject.prototype = {
  setAttribute: function(name, value){
    this.attributes[name] = value;
  },
  getAttribute: function(name){
    return this.attributes[name];
  },
  addParam: function(name, value){
    this.params[name] = value;
  },
  getParams: function(){
    return this.params;
  },
  addVariable: function(name, value){
	  if(name == "url") alert(encodeURIComponent(value));
    this.variables[name] = encodeURIComponent(value);
  },
  getVariable: function(name){
    return this.variables[name];
  },
  getVariables: function(){
    return this.variables;
  },
  createParamTag: function(n, v){
    var p = document.createElement('param');
    p.setAttribute('name', n);
    p.setAttribute('value', v);
    return p;
  },
  getVariablePairs: function(){
    var variablePairs = new Array();
    var key;
    var variables = this.getVariables();
    for(key in variables){
      variablePairs.push(key +"="+ variables[key]);
    }
    return variablePairs;
  },
  getFlashHTML: function() {
    var flashNode = "";
    if (navigator.plugins && navigator.mimeTypes && navigator.mimeTypes.length) { // netscape plugin architecture
      if (this.getAttribute("doExpressInstall")) this.addVariable("MMplayerType", "PlugIn");
      flashNode = '<embed type="application/x-shockwave-flash" src="'+ this.getAttribute('swf') +'" width="'+ this.getAttribute('width') +'" height="'+ this.getAttribute('height') +'"';
      flashNode += ' id="'+ this.getAttribute('id') +'" name="'+ this.getAttribute('id') +'" ';
      var params = this.getParams();
       for(var key in params){ flashNode += [key] +'="'+ params[key] +'" '; }
      var pairs = this.getVariablePairs().join("&");
       if (pairs.length > 0){ flashNode += 'flashvars="'+ pairs +'"'; }
      flashNode += '/>';
    } else { // PC IE
      if (this.getAttribute("doExpressInstall")) this.addVariable("MMplayerType", "ActiveX");
      flashNode = '<object id="'+ this.getAttribute('id') +'" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="'+ this.getAttribute('width') +'" height="'+ this.getAttribute('height') +'">';
      flashNode += '<param name="movie" value="'+ this.getAttribute('swf') +'" />"';
      var params = this.getParams();
       for(var key in params) {
        flashNode += '<param name="'+ key +'" value="'+ params[key] +'">';
       }
      var pairs = this.getVariablePairs().join("&");
       if(pairs.length > 0) flashNode += '<param name="flashvars" value="'+ pairs +'">';
    }
    return flashNode;
  },
  write: function(elementId){
    if(this.useExpressInstall) {
      // check to see if we need to do an express install
      var expressInstallReqVer = new com.deconcept.PlayerVersion([6,0,65]);
      if (this.installedVer.versionIsValid(expressInstallReqVer) && !this.installedVer.versionIsValid(this.getAttribute('version'))) {
        this.setAttribute('doExpressInstall', true);
        this.addVariable("MMredirectURL", escape(this.getAttribute('xiRedirectUrl')));
        document.title = document.title.slice(0, 47) + " - Flash Player Installation";
        this.addVariable("MMdoctitle", document.title);
      }
    } else {
      this.setAttribute('doExpressInstall', false);
    }
    if(this.skipDetect || this.getAttribute('doExpressInstall') || this.installedVer.versionIsValid(this.getAttribute('version'))){
      var n = (typeof elementId == 'string') ? document.getElementById(elementId) : elementId;
      n.innerHTML = this.getFlashHTML();
    }else{
      if(this.getAttribute('redirectUrl') != "") {
        document.location.replace(this.getAttribute('redirectUrl'));
      }
    }
  }
};

/* ---- detection functions ---- */
com.deconcept.FlashObjectUtil.getPlayerVersion = function(reqVer, xiInstall){
  var PlayerVersion = new com.deconcept.PlayerVersion(0,0,0);
  if(navigator.plugins && navigator.mimeTypes.length){
    var x = navigator.plugins["Shockwave Flash"];
    if(x && x.description) {
      PlayerVersion = new com.deconcept.PlayerVersion(x.description.replace(/([a-z]|[A-Z]|\s)+/, "").replace(/(\s+r|\s+b[0-9]+)/, ".").split("."));
    }
  }else{
    try{
      var axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
      for (var i=3; axo!=null; i++) {
        axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash."+i);
        PlayerVersion = new com.deconcept.PlayerVersion([i,0,0]);
      }
    }catch(e){}
    if (reqVer && reqVer.major > PlayerVersion.major) return PlayerVersion; // version is ok, skip minor detection
    // this only does the minor rev lookup if the user's major version
    // is not 6 or we are checking for a specific minor or revision number
    // see http://blog.deconcept.com/2006/01/11/getvariable-setvariable-crash-internet-explorer-flash-6/
    if (!reqVer || ((reqVer.minor != 0 || reqVer.rev != 0) && PlayerVersion.major == reqVer.major) || PlayerVersion.major != 6 || xiInstall) {
      try{
        PlayerVersion = new com.deconcept.PlayerVersion(axo.GetVariable("$version").split(" ")[1].split(","));
      }catch(e){}
    }
  }
  return PlayerVersion;
};
com.deconcept.PlayerVersion = function(arrVersion){
  this.major = parseInt(arrVersion[0]) || 0;
  this.minor = parseInt(arrVersion[1]) || 0;
  this.rev = parseInt(arrVersion[2]) || 0;
};
com.deconcept.PlayerVersion.prototype.versionIsValid = function(fv){
  if(this.major < fv.major) return false;
  if(this.major > fv.major) return true;
  if(this.minor < fv.minor) return false;
  if(this.minor > fv.minor) return true;
  if(this.rev < fv.rev) return false;
  return true;
};
/* ---- get value of query string param ---- */
com.deconcept.util = {
  getRequestParameter: function(param){
    var q = document.location.search || document.location.href.hash;
    if(q){
      var startIndex = q.indexOf(param +"=");
      var endIndex = (q.indexOf("&", startIndex) > -1) ? q.indexOf("&", startIndex) : q.length;
      if (q.length > 1 && startIndex > -1) {
        return q.substring(q.indexOf("=", startIndex)+1, endIndex);
      }
    }
    return "";
  },
  removeChildren: function(n){
    while (n.hasChildNodes()) n.removeChild(n.firstChild);
  }
};

/* add Array.push if needed (ie5) */
if (Array.prototype.push == null) { Array.prototype.push = function(item) { this[this.length] = item; return this.length; }}

/* add some aliases for ease of use/backwards compatibility */
var getQueryParamValue = com.deconcept.util.getRequestParameter;
var FlashObject = com.deconcept.FlashObject;




//Add DOMLoad function
SPS.DOMLoad = (function() {
            var DOMLoad = {
                        _handler : [],
                        done : false,

                        attach : function(fpHandler) {
                                    this._handler.push(fpHandler);
                        },

                        _fire : function() {
                                    this.done = true;
                                    var aHandlers = this._handler;

                                    setTimeout(function() {
                                                for (var i = 0, fpHandler; fpHandler = aHandlers[i]; i++)
                                                            fpHandler();
                                    }, 0);
                        }
            };

            if (document.addEventListener) {
                        document.addEventListener("DOMContentLoaded", function() { DOMLoad._fire(); }, false);
    }
            else if (document.attachEvent) {
                        var oDummy = document.createElement('document:ready');
                        var oTimer = setInterval(function() {
                                    try {
                                                oDummy.doScroll('left');
                                                oDummy = null;

                                                clearInterval(oTimer);
                                                DOMLoad._fire();
                                    } catch(e) { }
                        }, 10);
            }
            return DOMLoad;
})();



var cssquery = (function() {

            var sVersion = '2.3';

            var debugOption = { repeat : 1 };

            // ?? ??? ?? ???? ??? ? ??
            var UID = 1;

            var cost = 0;
            var validUID = {};

            var bSupportByClassName = document.createElement("DIV").getElementsByClassName ? true : false;
            var safeHTML = false;

            var getUID4HTML = function(oEl) {

                        var nUID = safeHTML ? (oEl._cssquery_UID && oEl._cssquery_UID[0]) : oEl._cssquery_UID;
                        if (nUID && validUID[nUID] == oEl) return nUID;

                        nUID = UID++;
                        oEl._cssquery_UID = safeHTML ? [ nUID ] : nUID;
                        validUID[nUID] = oEl;

                        return nUID;

            };

            var getUID4XML = function(oEl) {

                        var oAttr = oEl.getAttribute('_cssquery_UID');
                        var nUID = safeHTML ? (oAttr && oAttr[0]) : oAttr;

                        if (!nUID) {
                                    nUID = UID++;
                                    oEl.setAttribute('_cssquery_UID', safeHTML ? [ nUID ] : nUID);
                        }

                        return nUID;

            };

            var getUID = getUID4HTML;

            var uniqid = function(sPrefix) {
                        return (sPrefix || '') + new Date().getTime() + parseInt(Math.random() * 100000000);
            };

            function getElementsByClass(searchClass,node,tag) {
        var classElements = new Array();
        if ( node == null )
                node = document;
        if ( tag == null )
                tag = '*';
        var els = node.getElementsByTagName(tag);
        var elsLen = els.length;
        var pattern = new RegExp("(^|\\s)"+searchClass+"(\\s|$)");
        for (i = 0, j = 0; i < elsLen; i++) {
                if ( pattern.test(els[i].className) ) {
                        classElements[j] = els[i];
                        j++;
                }
        }
        return classElements;
            }

            var getChilds_dontShrink = function(oEl, sTagName, sClassName) {
                        if (bSupportByClassName && sClassName) {
                                    if(oEl.getElementsByClassName)
                                                return oEl.getElementsByClassName(sClassName);
                                    if(oEl.querySelectorAll)
                                                return oEl.querySelectorAll(sClassName);
                                    return getElementsByClass(sClassName, oEl, sTagName);
                        }
                        else if (sTagName == '*') return oEl.all || oEl.getElementsByTagName(sTagName);
                        return oEl.getElementsByTagName(sTagName);
            };

            var clearKeys = function() {
                         backupKeys._keys = {};
            };

            var oDocument_dontShrink = document;

            var bXMLDocument = false;

            // ???, [] ? ??? ??? ? ? ?? ?? replace ????
            var backupKeys = function(sQuery) {

                        var oKeys = backupKeys._keys;

                        // ?? ??? ????
                        sQuery = sQuery.replace(/'(\\'|[^'])*'/g, function(sAll) {
                                    var uid = uniqid('QUOT');
                                    oKeys[uid] = sAll;
                                    return uid;
                        });

                        // ? ??? ????
                        sQuery = sQuery.replace(/"(\\"|[^"])*"/g, function(sAll) {
                                    var uid = uniqid('QUOT');
                                    oKeys[uid] = sAll;
                                    return uid;
                        });

                        // [ ] ?? ????
                        sQuery = sQuery.replace(/\[(.*?)\]/g, function(sAll, sBody) {
                                    if (sBody.indexOf('ATTR') == 0) return sAll;
                                    var uid = '[' + uniqid('ATTR') + ']';
                                    oKeys[uid] = sAll;
                                    return uid;
                        });

                        // ( ) ?? ????
                        var bChanged;

                        do {

                                    bChanged = false;

                                    sQuery = sQuery.replace(/\(((\\\)|[^)|^(])*)\)/g, function(sAll, sBody) {
                                                if (sBody.indexOf('BRCE') == 0) return sAll;
                                                var uid = '_' + uniqid('BRCE');
                                                oKeys[uid] = sAll;
                                                bChanged = true;
                                                return uid;
                                    });

                        } while(bChanged);

                        return sQuery;

            };

            // replace ???? ?? ????
            var restoreKeys = function(sQuery, bOnlyAttrBrace) {

                        var oKeys = backupKeys._keys;

                        var bChanged;
                        var rRegex = bOnlyAttrBrace ? /(\[ATTR[0-9]+\])/g : /(QUOT[0-9]+|\[ATTR[0-9]+\])/g;

                        do {

                                    bChanged = false;

                                    sQuery = sQuery.replace(rRegex, function(sKey) {

                                                if (oKeys[sKey]) {
                                                            bChanged = true;
                                                            return oKeys[sKey];
                                                }

                                                return sKey;

                                    });

                        } while(bChanged);

                        // ( ) ? ???? ????
                        sQuery = sQuery.replace(/_BRCE[0-9]+/g, function(sKey) {
                                    return oKeys[sKey] ? oKeys[sKey] : sKey;
                        });

                        return sQuery;

            };

            // replace ???? ????? Quot ? ???? ??
            var restoreString = function(sKey) {

                        var oKeys = backupKeys._keys;
                        var sOrg = oKeys[sKey];

                        if (!sOrg) return sKey;
                        return eval(sOrg);

            };

            var wrapQuot = function(sStr) {
                        return '"' + sStr.replace(/"/g, '\\"') + '"';
            };

            var getStyleKey = function(sKey) {

                        if (/^@/.test(sKey)) return sKey.substr(1);
                        return null;

            };

            var getCSS = function(oEl, sKey) {

                        if (oEl.currentStyle) {

                                    if (sKey == "float") sKey = "styleFloat";
                                    return oEl.currentStyle[sKey] || oEl.style[sKey];

                        } else if (window.getComputedStyle) {

                                    return oDocument_dontShrink.defaultView.getComputedStyle(oEl, null).getPropertyValue(sKey.replace(/([A-Z])/g,"-$1").toLowerCase()) || oEl.style[sKey];

                        }

                        if (sKey == "float" && /MSIE/.test(window.navigator.userAgent)) sKey = "styleFloat";
                        return oEl.style[sKey];

            };

            var oCamels = {
                        'accesskey' : 'accessKey',
                        'cellspacing' : 'cellSpacing',
                        'cellpadding' : 'cellPadding',
                        'class' : 'className',
                        'colspan' : 'colSpan',
                        'for' : 'htmlFor',
                        'maxlength' : 'maxLength',
                        'readonly' : 'readOnly',
                        'rowspan' : 'rowSpan',
                        'tabindex' : 'tabIndex',
                        'valign' : 'vAlign'
            };

            var getDefineCode = function(sKey) {

                        var sVal;
                        var sStyleKey;

                        if (bXMLDocument) {

                                    sVal = 'oEl.getAttribute("' + sKey + '")';

                        } else {

                                    if (sStyleKey = getStyleKey(sKey)) {

                                                sKey = '$$' + sStyleKey;
                                                sVal = 'getCSS(oEl, "' + sStyleKey + '")';

                                    } else {

                                                switch (sKey) {
                                                case 'checked':
                                                            sVal = 'oEl.checked + ""';
                                                            break;

                                                case 'disabled':
                                                            sVal = 'oEl.disabled + ""';
                                                            break;

                                                case 'enabled':
                                                            sVal = '!oEl.disabled + ""';
                                                            break;

                                                case 'readonly':
                                                            sVal = 'oEl.readOnly + ""';
                                                            break;

                                                case 'selected':
                                                            sVal = 'oEl.selected + ""';
                                                            break;

                                                default:
                                                            if (oCamels[sKey]) sVal = 'oEl.' + oCamels[sKey];
                                                            else sVal = 'oEl.getAttribute("' + sKey + '")';
                                                }

                                    }

                        }

                        return '_' + sKey + ' = ' + sVal;
            };

            var getReturnCode = function(oExpr) {

                        var sStyleKey = getStyleKey(oExpr.key);

                        var sVar = '_' + (sStyleKey ? '$$' + sStyleKey : oExpr.key);
                        var sVal = oExpr.val ? wrapQuot(oExpr.val) : '';

                        switch (oExpr.op) {
                        case '~=':
                                    return '(' + sVar + ' && (" " + ' + sVar + ' + " ").indexOf(" " + ' + sVal + ' + " ") > -1)';
                        case '^=':
                                    return '(' + sVar + ' && ' + sVar + '.indexOf(' + sVal + ') == 0)';
                        case '$=':
                                    return '(' + sVar + ' && ' + sVar + '.substr(' + sVar + '.length - ' + oExpr.val.length + ') == ' + sVal + ')';
                        case '*=':
                                    return '(' + sVar + ' && ' + sVar + '.indexOf(' + sVal + ') > -1)';
                        case '!=':
                                    return '(' + sVar + ' != ' + sVal + ')';
                        case '=':
                                    return '(' + sVar + ' == ' + sVal + ')';
                        }

                        return '(' + sVar + ')';

            };

            var getNodeIndex = function(oEl) {

                        var nUID = getUID(oEl);
                        var nIndex = oNodeIndexes[nUID] || 0;

                        // ?? ???? ?? ? ???
                        if (nIndex == 0) {

                                    for (var oSib = (oEl.parentNode || oEl._IE5_parentNode).firstChild; oSib; oSib = oSib.nextSibling) {

                                                if (oSib.nodeType != 1) continue;
                                                nIndex++;

                                                setNodeIndex(oSib, nIndex);

                                    }

                                    nIndex = oNodeIndexes[nUID];

                        }

                        return nIndex;

            };

            // ??? ???? ???? ??
            var oNodeIndexes = {};

            var setNodeIndex = function(oEl, nIndex) {
                        var nUID = getUID(oEl);
                        oNodeIndexes[nUID] = nIndex;
            };

            var unsetNodeIndexes = function() {
                        setTimeout(function() { oNodeIndexes = {}; }, 0);
            };

            // ?? ???
            var oPseudoes_dontShrink = {

                        'contains' : function(oEl, sOption) {
                                    return (oEl.innerText || oEl.textContent || '').indexOf(sOption) > -1;
                        },

                        'last-child' : function(oEl, sOption) {
                                    for (oEl = oEl.nextSibling; oEl; oEl = oEl.nextSibling)
                                                if (oEl.nodeType == 1)
                                                            return false;

                                    return true;
                        },

                        'first-child' : function(oEl, sOption) {
                                    for (oEl = oEl.previousSibling; oEl; oEl = oEl.previousSibling)
                                                if (oEl.nodeType == 1)
                                                            return false;

                                    return true;
                        },

                        'only-child' : function(oEl, sOption) {
                                    var nChild = 0;

                                    for (var oChild = (oEl.parentNode || oEl._IE5_parentNode).firstChild; oChild; oChild = oChild.nextSibling) {
                                                if (oChild.nodeType == 1) nChild++;
                                                if (nChild > 1) return false;
                                    }

                                    return nChild ? true : false;
                        },

                        'empty' : function(oEl, _) {
                                    return oEl.firstChild ? false : true;
                        },

                        'nth-child' : function(oEl, nMul, nAdd) {
                                    var nIndex = getNodeIndex(oEl);
                                    return nIndex % nMul == nAdd;
                        },

                        'nth-last-child' : function(oEl, nMul, nAdd) {
                                    var oLast = (oEl.parentNode || oEl._IE5_parentNode).lastChild;
                                    for (; oLast; oLast = oLast.previousSibling)
                                                if (oLast.nodeType == 1) break;

                                    var nTotal = getNodeIndex(oLast);
                                    var nIndex = getNodeIndex(oEl);

                                    var nLastIndex = nTotal - nIndex + 1;
                                    return nLastIndex % nMul == nAdd;
                        }

            };

            // ?? part ? body ?? expression ???
            var getExpression = function(sBody) {

                        var oRet = { defines : '', returns : 'true' };

                        var sBody = restoreKeys(sBody, true);

                        var aExprs = [];
                        var aDefineCode = [], aReturnCode = [];
                        var sId, sTagName;

                        // ????? ?? ????
                        var sBody = sBody.replace(/:([\w-]+)(\(([^)]*)\))?/g, function(_, sType, _, sOption) {

                                    switch (sType) {
                                    case 'not':
                                                var oInner = getExpression(sOption); // ?? ?? ??? ??????

                                                var sFuncDefines = oInner.defines;
                                                var sFuncReturns = oInner.returnsID + oInner.returnsTAG + oInner.returns;

                                                aReturnCode.push('!(function() { ' + sFuncDefines + ' return ' + sFuncReturns + ' })()');
                                                break;

                                    case 'nth-child':
                                    case 'nth-last-child':
                                                sOption =  restoreString(sOption);

                                                if (sOption == 'even') sOption = '2n';
                                                else if (sOption == 'odd') sOption = '2n+1';

                                                var nMul, nAdd;

                                                if (/([0-9]*)n([+-][0-9]+)*/.test(sOption)) {
                                                            nMul = parseInt(RegExp.$1) || 1;
                                                            nAdd = parseInt(RegExp.$2) || 0;
                                                } else {
                                                            nMul = Infinity;
                                                            nAdd = parseInt(sOption);
                                                }

                                                aReturnCode.push('oPseudoes_dontShrink[' + wrapQuot(sType) + '](oEl, ' + nMul + ', ' + nAdd + ')');
                                                break;

                                    case 'first-of-type':
                                    case 'last-of-type':
                                                sType = (sType == 'first-of-type' ? 'nth-of-type' : 'nth-last-of-type');
                                                sOption = 1;

                                    case 'nth-of-type':
                                    case 'nth-last-of-type':
                                                sOption =  restoreString(sOption);

                                                if (sOption == 'even') sOption = '2n';
                                                else if (sOption == 'odd') sOption = '2n+1';

                                                var nMul, nAdd;

                                                if (/([0-9]*)n([+-][0-9]+)*/.test(sOption)) {
                                                            nMul = parseInt(RegExp.$1) || 1;
                                                            nAdd = parseInt(RegExp.$2) || 0;
                                                } else {
                                                            nMul = Infinity;
                                                            nAdd = parseInt(sOption);
                                                }

                                                oRet.nth = [ nMul, nAdd, sType ];
                                                break;

                                    default:
                                                sOption = sOption ? restoreString(sOption) : '';
                                                aReturnCode.push('oPseudoes_dontShrink[' + wrapQuot(sType) + '](oEl, ' + wrapQuot(sOption) + ')');
                                                break;
                                    }

                                    return '';

                        });

                        // [key=value] ?? ?? ????
                        var sBody = sBody.replace(/\[(@?[\w-]+)(([!^~$*]?=)([^\]]*))?\]/g, function(_, sKey, _, sOp, sVal) {

                                    sKey = restoreString(sKey);
                                    sVal = restoreString(sVal);

                                    if (sKey == 'checked' || sKey == 'disabled' || sKey == 'enabled' || sKey == 'readonly' || sKey == 'selected') {

                                                if (!sVal) {
                                                            sOp = '=';
                                                            sVal = 'true';
                                                }

                                    }

                                    aExprs.push({ key : sKey, op : sOp, val : sVal });
                                    return '';

                        });

                        var sClassName = null;

                        // ??? ?? ????
                        var sBody = sBody.replace(/\.([\w-]+)/g, function(_, sClass) {
                                    aExprs.push({ key : 'class', op : '~=', val : sClass });
                                    if (!sClassName) sClassName = sClass;
                                    return '';
                        });

                        // id ?? ????
                        var sBody = sBody.replace(/#([\w-]+)/g, function(_, sIdValue) {
                                    if (bXMLDocument) aExprs.push({ key : 'id', op : '=', val : sIdValue });
                                    else sId = sIdValue;
                                    return '';
                        });

                        sTagName = sBody == '*' ? '' : sBody;

                        // match ?? ?? ??? ??
                        var oVars = {};

                        for (var i = 0, oExpr; oExpr = aExprs[i]; i++) {

                                    var sKey = oExpr.key;

                                    if (!oVars[sKey]) aDefineCode.push(getDefineCode(sKey));
                                    aReturnCode.unshift(getReturnCode(oExpr)); // ????? ?? ??? ? ?? ??? unshift ??
                                    oVars[sKey] = true;

                        }

                        if (aDefineCode.length) oRet.defines = 'var ' + aDefineCode.join(',') + ';';
                        if (aReturnCode.length) oRet.returns = aReturnCode.join('&&');

                        oRet.quotID = sId ? wrapQuot(sId) : '';
                        oRet.quotTAG = sTagName ? wrapQuot(bXMLDocument ? sTagName : sTagName.toUpperCase()) : '';

                        if (bSupportByClassName) oRet.quotCLASS = sClassName ? wrapQuot(sClassName) : '';

                        oRet.returnsID = sId ? 'oEl.id == ' + oRet.quotID + ' && ' : '';
                        oRet.returnsTAG = sTagName && sTagName != '*' ? 'oEl.tagName == ' + oRet.quotTAG + ' && ' : '';

                        return oRet;

            };

            // ??? ??? ???? ???
            var splitToParts = function(sQuery) {

                        var aParts = [];
                        var sRel = ' ';

                        var sBody = sQuery.replace(/(.*?)\s*(!?[+>~ ]|!)\s*/g, function(_, sBody, sRelative) {

                                    if (sBody) aParts.push({ rel : sRel, body : sBody });

                                    sRel = sRelative.replace(/\s+$/g, '') || ' ';
                                    return '';

                        });

                        if (sBody) aParts.push({ rel : sRel, body : sBody });

                        return aParts;

            };

            var isNth_dontShrink = function(oEl, sTagName, nMul, nAdd, sDirection) {

                        var nIndex = 0;
                        for (var oSib = oEl; oSib; oSib = oSib[sDirection])
                                    if (oSib.nodeType == 1 && (!sTagName || sTagName == oSib.tagName))
                                                            nIndex++;

                        return nIndex % nMul == nAdd;

            };

            // ??? part ? ??? ??? ??
            var compileParts = function(aParts) {

                        var aPartExprs = [];

                        // ??? ??? ?? ???
                        for (var i = 0, oPart; oPart = aParts[i]; i++)
                                    aPartExprs.push(getExpression(oPart.body));

                        //////////////////// BEGIN

                        var sFunc = '';
                        var sPushCode = 'aRet.push(oEl); if (oOptions.single) { bStop = true; }';

                        for (var i = aParts.length - 1, oPart; oPart = aParts[i]; i--) {

                                    var oExpr = aPartExprs[i];
                                    var sPush = (debugOption.callback ? 'cost++;' : '') + oExpr.defines;

                                    // console.log(oExpr);

                                    var sReturn = 'if (bStop) {' + (i == 0 ? 'return aRet;' : 'return;') + '}';

                                    if (oExpr.returns == 'true') sPush += (sFunc ? sFunc + '(oEl);' : sPushCode) + sReturn;
                                    else sPush += 'if (' + oExpr.returns + ') {' + (sFunc ? sFunc + '(oEl);' : sPushCode ) + sReturn + '}';

                                    var sCheckTag = 'oEl.nodeType != 1';
                                    if (oExpr.quotTAG) sCheckTag = 'oEl.tagName != ' + oExpr.quotTAG;

                                    var sTmpFunc =
                                                '(function(oBase' +
                                                            (i == 0 ? ', oOptions) { var bStop = false; var aRet = [];' : ') {');

                                    if (oExpr.nth) {
                                                sPush =
                                                            'if (isNth_dontShrink(oEl, ' +
                                                            (oExpr.quotTAG ? oExpr.quotTAG : 'false') + ',' +
                                                            oExpr.nth[0] + ',' +
                                                            oExpr.nth[1] + ',' +
                                                            '"' + (oExpr.nth[2] == 'nth-of-type' ? 'previousSibling' : 'nextSibling') + '")) {' + sPush + '}';
                                    }

                                    switch (oPart.rel) {
                                    case ' ':
                                                if (oExpr.quotID) {

                                                            sTmpFunc +=
                                                                        'var oEl = oDocument_dontShrink.getElementById(' + oExpr.quotID + ');' +
                                                                        'var oCandi = oEl;' +
                                                                        'for (; oCandi; oCandi = (oCandi.parentNode || oCandi._IE5_parentNode)) {' +
                                                                                    'if (oCandi == oBase) break;' +
                                                                        '}' +
                                                                        'if (!oCandi || ' + sCheckTag + ') return aRet;' +
                                                                        sPush;

                                                } else {

                                                            sTmpFunc +=
                                                                        'var aCandi = getChilds_dontShrink(oBase, ' + (oExpr.quotTAG || '"*"') + ', ' + (oExpr.quotCLASS || 'null') + ');' +
                                                                        'for (var i = 0, oEl; oEl = aCandi[i]; i++) {' +
                                                                                    (oExpr.quotCLASS ? 'if (' + sCheckTag + ') continue;' : '') +
                                                                                    sPush +
                                                                        '}';

                                                }

                                                break;

                                    case '>':
                                                if (oExpr.quotID) {

                                                            sTmpFunc +=
                                                                        'var oEl = oDocument_dontShrink.getElementById(' + oExpr.quotID + ');' +
                                                                        'if ((oEl.parentNode || oEl._IE5_parentNode) != oBase || ' + sCheckTag + ') return aRet;' +
                                                                        sPush;

                                                } else {

                                                            sTmpFunc +=
                                                                        'for (var oEl = oBase.firstChild; oEl; oEl = oEl.nextSibling) {' +
                                                                                    'if (' + sCheckTag + ') { continue; }' +
                                                                                    sPush +
                                                                        '}';

                                                }

                                                break;

                                    case '+':
                                                if (oExpr.quotID) {

                                                            sTmpFunc +=
                                                                        'var oEl = oDocument_dontShrink.getElementById(' + oExpr.quotID + ');' +
                                                                        'var oPrev;' +
                                                                        'for (oPrev = oEl.previousSibling; oPrev; oPrev = oPrev.previousSibling) { if (oPrev.nodeType == 1) break; }' +
                                                                        'if (!oPrev || oPrev != oBase || ' + sCheckTag + ') return aRet;' +
                                                                        sPush;

                                                } else {

                                                            sTmpFunc +=
                                                                        'for (var oEl = oBase.nextSibling; oEl; oEl = oEl.nextSibling) { if (oEl.nodeType == 1) break; }' +
                                                                        'if (!oEl || ' + sCheckTag + ') { return aRet; }' +
                                                                        sPush;

                                                }

                                                break;

                                    case '~':

                                                if (oExpr.quotID) {

                                                            sTmpFunc +=
                                                                        'var oEl = oDocument_dontShrink.getElementById(' + oExpr.quotID + ');' +
                                                                        'var oCandi = oEl;' +
                                                                        'for (; oCandi; oCandi = oCandi.previousSibling) { if (oCandi == oBase) break; }' +
                                                                        'if (!oCandi || ' + sCheckTag + ') return aRet;' +
                                                                        sPush;

                                                } else {

                                                            sTmpFunc +=
                                                                        'for (var oEl = oBase.nextSibling; oEl; oEl = oEl.nextSibling) {' +
                                                                                    'if (' + sCheckTag + ') { continue; }' +
                                                                                    'if (!markElement_dontShrink(oEl, ' + i + ')) { break; }' +
                                                                                    sPush +
                                                                        '}';

                                                }

                                                break;

                                    case '!' :

                                                if (oExpr.quotID) {

                                                            sTmpFunc +=
                                                                        'var oEl = oDocument_dontShrink.getElementById(' + oExpr.quotID + ');' +
                                                                        'for (; oBase; oBase = (oBase.parentNode || oBase._IE5_parentNode)) { if (oBase == oEl) break; }' +
                                                                        'if (!oBase || ' + sCheckTag + ') return aRet;' +
                                                                        sPush;

                                                } else {

                                                            sTmpFunc +=
                                                                        'for (var oEl = (oBase.parentNode || oBase._IE5_parentNode); oEl; oEl = (oEl.parentNode || oEl._IE5_parentNode)) {'+
                                                                                    'if (' + sCheckTag + ') { continue; }' +
                                                                                    sPush +
                                                                        '}';

                                                }

                                                break;

                                    case '!>' :

                                                if (oExpr.quotID) {

                                                            sTmpFunc +=
                                                                        'var oEl = oDocument_dontShrink.getElementById(' + oExpr.quotID + ');' +
                                                                        'var oRel = (oBase.parentNode || oBase._IE5_parentNode);' +
                                                                        'if (!oRel || oEl != oRel || (' + sCheckTag + ')) return aRet;' +
                                                                        sPush;

                                                } else {

                                                            sTmpFunc +=
                                                                        'var oEl = (oBase.parentNode || oBase._IE5_parentNode);' +
                                                                        'if (!oEl || ' + sCheckTag + ') { return aRet; }' +
                                                                        sPush;

                                                }

                                                break;

                                    case '!+' :

                                                if (oExpr.quotID) {

                                                            sTmpFunc +=
                                                                        'var oEl = oDocument_dontShrink.getElementById(' + oExpr.quotID + ');' +
                                                                        'var oRel;' +
                                                                        'for (oRel = oBase.previousSibling; oRel; oRel = oRel.previousSibling) { if (oRel.nodeType == 1) break; }' +
                                                                        'if (!oRel || oEl != oRel || (' + sCheckTag + ')) return aRet;' +
                                                                        sPush;

                                                } else {

                                                            sTmpFunc +=
                                                                        'for (oEl = oBase.previousSibling; oEl; oEl = oEl.previousSibling) { if (oEl.nodeType == 1) break; }' +
                                                                        'if (!oEl || ' + sCheckTag + ') { return aRet; }' +
                                                                        sPush;

                                                }

                                                break;

                                    case '!~' :

                                                if (oExpr.quotID) {

                                                            sTmpFunc +=
                                                                        'var oEl = oDocument_dontShrink.getElementById(' + oExpr.quotID + ');' +
                                                                        'var oRel;' +
                                                                        'for (oRel = oBase.previousSibling; oRel; oRel = oRel.previousSibling) { ' +
                                                                                    'if (oRel.nodeType != 1) { continue; }' +
                                                                                    'if (oRel == oEl) { break; }' +
                                                                        '}' +
                                                                        'if (!oRel || (' + sCheckTag + ')) return aRet;' +
                                                                        sPush;

                                                } else {

                                                            sTmpFunc +=
                                                                        'for (oEl = oBase.previousSibling; oEl; oEl = oEl.previousSibling) {' +
                                                                                    'if (' + sCheckTag + ') { continue; }' +
                                                                                    'if (!markElement_dontShrink(oEl, ' + i + ')) { break; }' +
                                                                                    sPush +
                                                                        '}';

                                                }

                                                break;
                                    }

                                    sTmpFunc +=
                                                (i == 0 ? 'return aRet;' : '') +
                                    '})';

                                    sFunc = sTmpFunc;

                        }

                        // alert(sFunc);
                        //console.log(sFunc);
                        eval('var fpCompiled = ' + sFunc + ';');
                        //alert(fpCompiled);
                        return fpCompiled;

            };

            // ??? match ??? ??
            var parseQuery = function(sQuery) {

                        var sCacheKey = sQuery;

                        var fpSelf = arguments.callee;
                        var fpFunction = fpSelf._cache[sCacheKey];

                        if (!fpFunction) {

                                    sQuery = backupKeys(sQuery);

                                    var aParts = splitToParts(sQuery);

                                    fpFunction = fpSelf._cache[sCacheKey] = compileParts(aParts);
                                    fpFunction.depth = aParts.length;

                        }

                        return fpFunction;

            };

            parseQuery._cache = {};

            // test ??? match ??? ??
            var parseTestQuery = function(sQuery) {

                        var fpSelf = arguments.callee;

                        var aSplitQuery = backupKeys(sQuery).split(/\s*,\s*/);
                        var aResult = [];

                        var nLen = aSplitQuery.length;
                        var aFunc = [];

                        for (var i = 0; i < nLen; i++) {

                                    aFunc.push((function(sQuery) {

                                                var sCacheKey = sQuery;
                                                var fpFunction = fpSelf._cache[sCacheKey];

                                                if (!fpFunction) {

                                                            sQuery = backupKeys(sQuery);
                                                            var oExpr = getExpression(sQuery);

                                                            eval('fpFunction = function(oEl) { ' + oExpr.defines + 'return (' + oExpr.returnsID + oExpr.returnsTAG + oExpr.returns + '); };');

                                                }

                                                return fpFunction;

                                    })(restoreKeys(aSplitQuery[i])));

                        }

                        return aFunc;

            };

            parseTestQuery._cache = {};

            var distinct = function(aList) {

                        var aDistinct = [];
                        var oDummy = {};

                        for (var i = 0, oEl; oEl = aList[i]; i++) {

                                    var nUID = getUID(oEl);
                                    if (oDummy[nUID]) continue;

                                    aDistinct.push(oEl);
                                    oDummy[nUID] = true;
                        }

                        return aDistinct;

            };

            var markElement_dontShrink = function(oEl, nDepth) {

                        var nUID = getUID(oEl);
                        if (cssquery._marked[nDepth][nUID]) return false;

                        cssquery._marked[nDepth][nUID] = true;
                        return true;

            };

            var oResultCache = null;
            var bUseResultCache = false;

            /**
             * CSS ???? ???? ???? ??? ????
             * @param {String} selector         CSS ???
             * @param {Document | Element} el   ??? ???? ??? ?? ???? ?? ?? (??? ?? ??? document ??)
             * @remark el ?? XMLDocument ?? XMLElement ? ??? ? ??.
             * @return {Array} ??? ???? ??? ??
             */
            var cssquery = function(sQuery, oParent, oOptions) {

                        if (typeof sQuery == 'object') {

                                    var oResult = {};

                                    for (var k in sQuery)
                                                oResult[k] = arguments.callee(sQuery[k], oParent, oOptions);

                                    return oResult;
                        }

                        cost = 0;

                        var executeTime = new Date().getTime();
                        var aRet;

                        for (var r = 0, rp = debugOption.repeat; r < rp; r++) {

                                    aRet = (function(sQuery, oParent, oOptions) {

                                                oOptions = oOptions || {};

                                                if (!oParent) oParent = document;

                                                // ownerDocument ????
                                                oDocument_dontShrink = oParent.ownerDocument || oParent.document || oParent;

                                                // ???? ??? IE5.5 ??
                                                if (/\bMSIE\s([0-9]+(\.[0-9]+)*);/.test(navigator.userAgent) && parseFloat(RegExp.$1) < 6) {

                                                            try { oDocument_dontShrink.location; } catch(e) { oDocument_dontShrink = document; }

                                                            oDocument_dontShrink.firstChild = oDocument_dontShrink.getElementsByTagName('html')[0];
                                                            oDocument_dontShrink.firstChild._IE5_parentNode = oDocument_dontShrink;
                                                }

                                                // XMLDocument ?? ??
                                                bXMLDocument = (typeof XMLDocument != 'undefined') ? (oDocument_dontShrink.constructor === XMLDocument) : (!oDocument_dontShrink.location);
                                                getUID = bXMLDocument ? getUID4XML : getUID4HTML;

                                                clearKeys();

                                                // ??? ??? ???
                                                var aSplitQuery = backupKeys(sQuery).split(/\s*,\s*/);
                                                var aResult = [];

                                                var nLen = aSplitQuery.length;

                                                for (var i = 0; i < nLen; i++)
                                                            aSplitQuery[i] = restoreKeys(aSplitQuery[i]);

                                                // ??? ??? ?? ??
                                                for (var i = 0; i < nLen; i++) {

                                                            var sSingleQuery = aSplitQuery[i];
                                                            var aSingleQueryResult = null;

                                                            var sResultCacheKey = sSingleQuery + (oOptions.single ? '_single' : '');

                                                            // ?? ?? ??
                                                            var aCache = bUseResultCache ? oResultCache[sResultCacheKey] : null;
                                                            if (aCache) {

                                                                        // ???? ??? ??? parent ? ???? ???? aSingleQueryResult ? ??
                                                                        for (var j = 0, oCache; oCache = aCache[j]; j++) {
                                                                                    if (oCache.parent == oParent) {
                                                                                                aSingleQueryResult = oCache.result;
                                                                                                break;
                                                                                    }
                                                                        }

                                                            }

                                                            if (!aSingleQueryResult) {

                                                                        var fpFunction = parseQuery(sSingleQuery);
                                                                        // alert(fpFunction);

                                                                        cssquery._marked = [];
                                                                        for (var j = 0, nDepth = fpFunction.depth; j < nDepth; j++)
                                                                                    cssquery._marked.push({});

                                                                        aSingleQueryResult = distinct(fpFunction(oParent, oOptions));

                                                                        // ?? ??? ????? ??? ??
                                                                        if (bUseResultCache) {
                                                                                    if (!(oResultCache[sResultCacheKey] instanceof Array)) oResultCache[sResultCacheKey] = [];
                                                                                    oResultCache[sResultCacheKey].push({ parent : oParent, result : aSingleQueryResult });
                                                                        }

                                                            }

                                                            aResult = aResult.concat(aSingleQueryResult);

                                                }

                                                unsetNodeIndexes();

                                                return aResult;

                                    })(sQuery, oParent, oOptions);

                        }

                        executeTime = new Date().getTime() - executeTime;

                        if (debugOption.callback) debugOption.callback(sQuery, cost, executeTime);

                        return aRet;

            };

            /**
             * ?? ????? ?? CSS ???? ???? ?????? ????
             * @remark CSS ???? ???? ??? ? ??? ????.
             * @param {Element} element         ????? ?? ????
             * @param {String} selector         CSS ???
             * @return {Boolean} ??? ??? ???? true, ???? ??? false
             * @example

// oEl ? div ?? ?? p ??, ?? align=center ? ??????
if (cssquery.test(oEl, 'div, p, [align=center]')) alert('?? ?? ??');// oEl ? div ?? ?? p ??, ?? align=center ? ??????
if (cssquery.test(oEl, 'div, p, [align=center]')) alert('?? ?? ??');

             */
            cssquery.test = function(oEl, sQuery) {

                        clearKeys();

                        var aFunc = parseTestQuery(sQuery);

                        for (var i = 0, nLen = aFunc.length; i < nLen; i++)
                                    if (aFunc[i](oEl)) return true;

                        return false;

            };

            /**
             * cssquery ? ?? ??? ??? ??? ????? ????.
             * @remark ?? ??? ???? ??? ???? ???? ?? ?? ??? ?? ?? ?? ?? ??? ??? ???? ??? ???? ?? ??? ???? ?? ??? ??? ? ? ?? ??? ??? ??? ???? ?? DOM ? ??? ??? ?? ????? ???? ??.
             * @param {Boolean} flag            ??? ? ?? ?? (??? ?? ??? ??)
             * @return {Boolean} ?? ??? ????? ??
             */
            cssquery.useCache = function(bFlag) {

                        if (typeof bFlag != 'undefined') {
                                    bUseResultCache = bFlag;
                                    cssquery.clearCache();
                        }

                        return bUseResultCache;

            };

            /**
             * ?? ??? ?? ?? DOM ? ??? ??? ?? ??? ??? ?? ???? ??? ????.
             * @return {Void} ??? ??
             */
            cssquery.clearCache = function() {
                        oResultCache = {};
            };

            /**
             * CSS ???? ???? DOM ?? ??? ????? ??? ????. ???? ?? ??? ?? ?? ?? null ??.
             * @remark ??? ??? ???? ??? ?? ?? ??? ???? ??? ??? ???? ??? ??? ?? ??? ??? ??? ? ??.
             * @param {String} selector         CSS ???
             * @param {Document | Element} el   ??? ???? ??? ?? ???? ?? ?? (??? ?? ??? document ??)
             * @return {Element} ??? ????
             */
            cssquery.getSingle = function(sQuery, oParent) {
                        return cssquery(sQuery, oParent, { single : true })[0] || null;
            };

            /**
             * XPath ??? ???? ????? ????.
             * @remark ???? ??? ?? ????? ??? ????? ???? ?? ????.
             * @param {String} xpath            XPath
             * @param {Document | Element} el   ??? ???? ??? ?? ???? ?? ?? (??? ?? ??? document ??)
             * @return {Array} ??? ???? ??? ??
             */
            cssquery.xpath = function(sXPath, oParent) {

                        var sXPath = sXPath.replace(/\/(\w+)(\[([0-9]+)\])?/g, function(_, sTag, _, sTh) {
                                    sTh = sTh || '1';
                                    return '>' + sTag + ':nth-of-type(' + sTh + ')';
                        });

                        return cssquery.getSingle(sXPath, oParent);

            };

            /**
             * cssquery ? ??? ?? ??? ???? ?? ??? ???? ????.
             * @param {Function} callback       ??? ??? ??? ??? ??? ????? ?? (false ? ?? debug ??? ?)
             * @param {Number} repeat           ??? ???? ???? ????? ?? ????? ?? ??? ??
             * @remark callback ??? ??? ??? ????.
             * callback : function({String}query, {Number}cost, {Number}executeTime)
             * <dl>
             *          <dt>query</dt>
             *          <dd>??? ??? ???</dd>
             *          <dt>cost</dt>
             *          <dd>??? ??? ?? (?? ??)</dd>
             *          <dt>executeTime</dt>
             *          <dd>??? ??? ??</dd>
             * </dl>
             * @return {Void} ??? ??
             * @example

cssquery.debug(function(sQuery, nCost, nExecuteTime) {
            if (nCost > 5000)
                        console.warn('5000 ? ?? ????! ????? -> ' + sQuery + '/' + nCost);
            else if (nExecuteTime > 200)
                        console.warn('0.2?? ?? ????! ????? -> ' + sQuery + '/' + nExecuteTime);
}, 20);

....

cssquery.debug(false);

             */
            cssquery.debug = function(fpCallback, nRepeat) {

                        debugOption.callback = fpCallback;
                        debugOption.repeat = nRepeat || 1;

            };

            /**
             * IE ?? innerHTML ? ?? _cssquery_UID ??? ??? ?? ????.
             * true ? ???? ???? ???? ??? ???? innerHTML ? _cssquery_UID ? ??? ??? ??? ????? ?? ??? ? ??.
             * @param {Boolean} flag            true ? ???? _cssquery_UID ? ??? ??
             * @return {Boolean}    _cssquery_UID ? ??? ?? ???? true ??
             */
            cssquery.safeHTML = function(bFlag) {

                        var bIE = /MSIE/.test(window.navigator.userAgent);

                        if (arguments.length > 0)
                                    safeHTML = bFlag && bIE;

                        return safeHTML || !bIE;

            };

            /**
             * cssquery ? ????? ?? ?? ?????.
             */
            cssquery.version = sVersion;

            return cssquery;

})();

if(typeof window.$$=="undefined") window.$$=cssquery;

