/*
	sky.base.js
	2007-03-30 ~ 2008-12-08

	Seo, Jaehan <daddyofsky@gmail.com>

	Referred to
		ui javascript framework - gony <gonom9@gmail.com>
		Prototype javascript framework - Sam Stephenson <sam@conio.net>
		Swaf javascript framework - reizes <reizes@nate.com>
*/

// Class //////////////////////////////////////////////////////////////////////

	var Class = function() {
		var obj = function() {
			if (this.init) this.init.apply(this, arguments);
		}
		if (arguments[0]) Class.extend(obj.prototype, arguments[0]);
		return obj;
	}

	Class.extend = function(obj) {
		for(var i=1; i<arguments.length; i++) {
			if (arguments[i]) {
				for (var x in arguments[i]) {
					obj[x] = arguments[i][x];
				}
			}
		}
		return obj;
	}

// Extend Function ////////////////////////////////////////////////////////////

	Class.extend(Function.prototype, {
		bind : function(obj) {
			var func = this;
			var arg = $a(arguments); arg.shift();
			return function() {
				return func.apply(obj, arg.concat($a(arguments)));
			}
		},
		bindForEvent : function(obj) {
			var func = this;
			return function(e, el) {
				return func.call(obj, e, el);
			}
		}
	});

// Extend Array ///////////////////////////////////////////////////////////////

	Class.extend(Array.prototype, {
		exists : function(value) {
			for (var i=0; i<this.length; i++) {
				if (this[i] == value) return true;
			}
			return false;
		},
		search : function(value) {
			for (var i=0; i<this.length; i++) {
				if (this[i] == value) return i;
			}
			return -1;
		},
		copy : function(obj) {
			if (typeof obj == 'undefined') obj = this;
			if (typeof obj == 'string' || typeof obj.length == 'undefined') obj = [obj];
			var ret = [];
			for (var i=0; i<obj.length; i++) {
				ret.push(obj[i]);
			}
			return ret;
		},
		filter : function(func, applyResult) {
			var ret = [];
			if (typeof func == 'boolean') {
				for (var i=0; i<this.length; i++) {
					if (!this[i] == !func) ret.push(this[i]);
				}
			} else if (typeof func == 'function') {
				if (applyResult) {
					for (var i=0; i<this.length; i++) {
						var result = func(this[i], i);
						if (result !== false) ret.push(result);
					}
				} else {
					for (var i=0; i<this.length; i++) {
						if (func(this[i], i)) ret.push(this[i]);
					}
				}
			} else {
				ret = this;
			}
			return ret;
		},
		each : function(func) {
			if (typeof func == 'function') {
				for (var i=0; i<this.length; i++) {
					this[i] = func(this[i], i);
				}
			} else {
				for (var i=0; i<this.length; i++) {
					this[i] = func;
				}
			}
			return this;
		},
		merge : function(obj) {
			for (var i=0; i<obj.length; i++) {
				if (!this.exists(obj[i])) {
					this.push(obj[i]);
				}
			}
			return this;
		},
		intersect : function(obj) {
			var ret = [];
			for (var i=0; i<obj.length; i++) {
				if (this.exists(obj[i])) {
					ret.push(obj[i]);
				}
			}
			return ret;
		},
		diff : function(obj) {
			var ret = [];
			for (var i=0; i<obj.length; i++) {
				if (!this.exists(obj[i])) {
					ret.push(obj[i]);
				}
			}
			return ret;
		}
	});
	Array.prototype.has = Array.prototype.exists;

// Extend String //////////////////////////////////////////////////////////////

	Class.extend(String.prototype, {
		trim : function() {
			return this.replace(/^\s+/, '').replace(/\s+$/, '');
		},
		stripTags : function() {
			return this.replace(/<\/?[^>]+>/gi, '');
		},
		validHTML : function() {
			var div = document.createElement('div');
			div.innerHTML = this;
			return div.innerHTML;
		},
		escapeHTML: function() {
			var div = document.createElement('div');
			var text = document.createTextNode(this);
			div.appendChild(text);
			return div.innerHTML;
		},
		unescapeHTML: function() {
			var div = document.createElement('div');
			div.innerHTML = this.stripTags();
			return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
		},
		decodeQuery : function(isUrl) {
			if (isUrl) {
				var tmp = this.substring(this.indexOf('?')+1).match(/^\??(.*)$/)[1].split('&');
			} else {
				var tmp = this.match(/^\??(.*)$/)[1].split('&');
			}
			var ret = {};
			for (var i=0; i<tmp.length; i++) {
				var pair = tmp[i].split('=');
				if (pair[0]) ret[pair[0]] = pair[1];
			}
			return ret;
		},
		encodeQuery : function(param, base) {
			var tmp = [];
			for (var x in param) {
				var key = base ? base+'['+x+']' : x;
				if (typeof param[x] == 'function') {
					continue;
				} else if (typeof param[x] == 'string' || typeof param[x] == 'number') {
					tmp.push(key+'='+param[x]);
				} else {
					tmp.push(this.encodeQuery(param[x], key));
				}
			}
			return tmp.join('&');
		}
	});
	String.prototype.parseQuery = String.prototype.decodeQuery;

// Event //////////////////////////////////////////////////////////////////////

	var Event = {
		__funcIdx : 0,
		__objIdx : 0,
		__eventCache : {},
		bind : function(func, obj) {
			if (typeof func.__event_idx == 'undefined') func.__event_idx = this.__funcIdx++;
			if (obj.__event_idx == null) obj.__event_idx = this.__objIdx++;
			var evtIdx = func.__event_idx + '_' + obj.__event_idx;
			if (typeof this.__eventCache[evtIdx] != 'function') {
				this.__eventCache[evtIdx] = function(evt) {
					func(evt, obj);
				}
			}
			return this.__eventCache[evtIdx];
		},
		addListener : function(obj, handler, func) {
			obj = $(obj);
			if (!obj) return;
			if (!(obj instanceof Array)) obj = [obj];
			for (var i=0; i<obj.length; i++) {
				if (!obj[i]) continue; // error safe for rainbow
				var evtFunc = Event.bind(func, obj[i]);
				if (obj[i].addEventListener) {
					obj[i].addEventListener(handler, evtFunc, false);
				} else if(obj[i].attachEvent) {
					obj[i].attachEvent('on'+handler, evtFunc);
				}
			}
		},
		delListener : function(obj, handler, func) {
			obj = $(obj);
			if (!obj) return;
			if (!(obj instanceof Array)) obj = [obj];
			for (var i=0; i<obj.length; i++) {
				if (!obj) continue; // error safe for rainbow
				var evtFunc = Event.bind(func, obj[i]);
				if (obj[i].removeEventListener) {
					obj[i].removeEventListener(handler, evtFunc, false);
				} else if(obj[i].detachEvent) {
					obj[i].detachEvent('on'+handler, evtFunc);
				}
			}
		},
		stop : function(evt) {
			var e = evt || window.event;
			if (e.preventDefault) {
				e.preventDefault();
				e.stopPropagation();
			} else {
				e.returnValue = false;
				e.cancelBubble = true;
			}
		},
		element : function(evt) {
			var e = evt || window.event;
			return e.currentTarget || e.target || e.srcElement;
		},
		getPosition : function(evt, from) {
			var e = evt || window.event;
			var b = document.body;
			var scroll = Util.getScrollOffset();
			var pos = {
				x : e.pageX || e.clientX+scroll.x-b.clientLeft,
				y : e.pageY || e.clientY+scroll.y-b.clientTop
			}
			if (from) {
				var p = Element.getPosition(from);
				pos.x -= p.x;
				pos.y -= p.y;
			}
			return pos;
		},
		extend : function(evt) {
			if (typeof evt.initEvent != 'function' && typeof evt.keyCode != 'number') return false;
			if (typeof evt.extended != 'undefined') return evt;
			var e = evt || window.event;
			var b = document.body;
			Class.extend(e, {
				extended : true,
				element  : Element.extend(e.currentTarget || e.target || e.srcElement),
				mouse    : {
					isLeft   : (e.which && e.button==0) || (e.button&1 != 0),
					isMiddle : (e.which && e.button==1) || (e.button&4 != 0),
					isRight  : (e.which && e.button==2) || (e.button&2 != 0)
				},
				key      : {
					isAlt   : e.altKey,
					isCtrl  : e.ctrlKey,
					isShift : e.shiftKey,
					isUp    : [38,104].exists(e.keyCode),
					isDown  : [40,98].exists(e.keyCode),
					isLeft  : [37,100].exists(e.keyCode),
					isRight : [39,102].exists(e.keyCode),
					isEnter : (e.keyCode==13),
					isTab : (e.keyCode==9)
				},
				stop     : function() {
					if (this.preventDefault) {
						this.preventDefault();
						this.stopPropagation();
					} else {
						this.returnValue = false;
						this.cancelBubble = true;
					}
				}
			});

			// Opera safe
			if (typeof e.x == 'undefined') {
				Class.extend(e, {
					x : e.pageX || e.clientX+b.scrollLeft-b.clientLeft,
					y : e.pageY || e.clientY+b.scrollTop-b.clientTop
				});
			}
			return e;
		}
	}
	Event.pos = Event.getPosition;

// Element ////////////////////////////////////////////////////////////////////

	var Element = {
		show : function(obj, display) {
			if (display != 'block' && display != 'inline') display = '';
			$(obj).style.display = display;
			if (obj.blocker) this.updateBlocker(obj);
		},
		hide : function(obj) {
			$(obj).style.display = 'none';
			if (obj.blocker) this.updateBlocker(obj);
		},
		toggle : function(obj, display) {
			this.visible(obj) ? this.hide(obj) : this.show(obj, display);
		},
		visible : function(obj) {
			return ($(obj).style.display != 'none');
		},
		getPosition : function(obj, from) {
			obj = $(obj);
			var x = obj.offsetLeft;
			var y = obj.offsetTop;
			var p = obj.offsetParent;
			while (p && p != document.body) {
				x += p.offsetLeft;
				y += p.offsetTop;
				p = p.offsetParent;
			}
			var pos = { x:x, y:y };
			if (from) {
				var p = Element.getPosition(from);
				pos.x -= p.x;
				pos.y -= p.y;
			}
			return pos;
		},
		getCenter : function(obj, x, y) {
			var objPos = this.getPosition(obj);
			var objSize = this.getSize(obj);
			var docSize = Util.getClientSize();
			var docScroll = Util.getScrollOffset();
			var x = (typeof x == 'number') ? x : (x === false ? objPos.x : docScroll.x+(docSize.width-objSize.width)/2);
			var y = (typeof y == 'number') ? y : (y === false ? objPos.y : docScroll.y+(docSize.height-objSize.height)/2);
			return { x:x, y:y };
		},
		setPosition : function(obj, posX, posY) {
			obj = $(obj);
			if (!obj.style.position) obj.style.position = 'absolute';
			if (arguments.length == 2) {
				obj.style.left = (typeof arguments[1].x == 'number') ? arguments[1].x + 'px' : arguments[1].x;
				obj.style.top = (typeof arguments[1].y == 'number') ? arguments[1].y + 'px' : arguments[1].y;
			} else {
				obj.style.left = (typeof posX == 'number') ? posX+'px' : posX;
				obj.style.top = (typeof posY == 'number') ? posY+'px' : posY;
			}
			if (obj.blocker) this.updateBlocker(obj);
		},
		moveBy : function(obj, x, y) {
			obj = $(obj);
			obj.style.left = (parseInt(obj.style.left, 10) + x) + 'px';
			obj.style.top = (parseInt(obj.style.top, 10) + y) + 'px';
			if (obj.blocker) this.updateBlocker(obj);
		},
		getSize : function(obj) {
			obj = $(obj);
			var w = obj.offsetWidth || obj.scrollWidth;
			var h = obj.offsetHeight || obj.scrollHeight;
			return { width:w, height:h };
		},
		setSize : function(obj, width, height) {
			obj = $(obj);
			if (arguments.length == 2) {
				obj.style.width = (typeof arguments[1].width == 'number') ? arguments[1].width + 'px' : arguments[1].width;
				obj.style.height = (typeof arguments[1].height == 'number') ? arguments[1].height + 'px' : arguments[1].height;
			} else {
				width = parseInt(width, 10) || 0;
				height = parseInt(height, 10) || 0;
				obj.style.width = (typeof width == 'number') ? width + 'px' : width;
				obj.style.height = (typeof height == 'number') ? height + 'px' : height;
			}
			if (obj.blocker) this.updateBlocker(obj);
		},
		getStyle : function(obj, name) {
			obj = $(obj);
			if (name == 'opacity' && Util.isIE) {
				var filter = obj.style['filter'];
				if (filter) {
					var match = filter.match(/opacity=([0-9]+)/);
					if (match) {
						return '' + (match[1] / 100);
					}
				}
				return '1';
			} else {
				var value = obj.style[name];
				if (value == '') {
					if (obj.currentStyle) {
						value = obj.currentStyle[name];
					} else if (window.getComputedStyle) {
						value = window.getComputedStyle(obj, null).getPropertyValue(name.replace(/([A-Z])/, '-$1').toLowerCase());
						if (value.indexOf('rgb(') == 0) {
							value = Util.rgbToHex(value);
						}
					}
				}
				return value;
			}
		},
		setStyle : function(obj, style, value) {
			obj = $(obj);
			if (arguments.length == 3) {
				if (style == 'opacity' && Util.isIE) {
					obj.style['filter'] = 'alpha(opacity='+ (parseFloat(value)*100) + ')';
				} else {
					obj.style[style] = value;
				}
			} else {
				if (typeof style.opacity != 'undefined' && Util.isIE) {
					style['filter'] = 'alpha(opacity='+ (parseFloat(style.opacity)*100) + ')';
				}
				Class.extend(obj.style, style);
			}
			if (obj.blocker) this.updateBlocker(obj);
		},
		classExists : function(obj, className) {
			return $(obj).className.split(/\s+/).exists(className);
		},
		setClass : function(obj, className) {
			$(obj).className = className;
			if (obj.blocker) this.updateBlocker(obj);
		},
		addClass : function(obj, className) {
			obj = $(obj);
			if (!this.classExists(obj, className)) {
				(obj.className+=' '+className).replace(/^\s+/,'');
				if (obj.blocker) this.updateBlocker(obj);
			}
		},
		delClass : function(obj, className) {
			obj = $(obj);
			obj.className = obj.className.replace(new RegExp('(^|\\s+)'+className+'($|\\s+)','g'),'');
			if (obj.blocker) this.updateBlocker(obj);
		},
		getRect : function(obj) {
			var p = this.getPosition(obj);
			var s = this.getSize(obj);
			return { left:p.x, top:p.y, right:p.x+s.width, bottom:p.y+s.height };
		},
		hitTest : function(obj, target) {
			var o = this.getRect(obj);
			var t = this.getRect(target);
			return !(o.left > t.right || o.right < t.left || o.top > t.bottom || o.bottom < t.top);
		},
		inTest : function(obj, target) {
			var o = this.getRect(obj);
			var t = this.getRect(target);
			return (o.left >= t.left && o.right <= t.right && o.top >= t.top && o.bottom <= t.bottom);
		},
		addBefore : function(obj, src) {
			obj.parentNode.insertBefore(src, obj);
			if (obj.blocker) this.updateBlocker(obj);
		},
		addAfter : function(obj, src) {
			if (obj.nextSibling) {
				obj.parentNode.insertBefore(src, obj.nextSibling);
			} else {
				obj.parentNode.appendChild(src);
			}
			if (obj.blocker) this.updateBlocker(obj);
		},
		clone : function(obj, deep) {
			return obj.cloneNode(deep);
		},
		remove : function(obj) {
			if (obj.blocker) this.hide(obj.blocker);
			obj.parentNode.removeChild(obj);
		},
		attachBlocker : function(obj, group) {
			if (!Util.isIE) return; // IE only
			if (!obj.blocker) {
				var iframeId = '__blocker_' + (group || 'default');
				var iframe = $(iframeId);
				if (!iframe) {
					iframe = $c('iframe');
					iframe.setAttribute('src', 'about:blank');
					iframe.setAttribute('id', iframeId);
					iframe.setAttribute('frameBorder', '0');
					this.setStyle(iframe, {
						position : 'absolute',
						width : '0px',
						height : '0px',
						filter : 'alpha(opacity=0)'
					});
					document.body.appendChild(iframe);
				}
				obj.blocker = iframe;
			}
		},
		updateBlocker : function(obj) {
			var iframe = obj.blocker;
			if (!iframe) return;
			var display = this.getStyle(obj, 'display');
			if (display == 'none') {
				this.hide(iframe);
			} else {
				this.show(iframe);
				this.setPosition(iframe, this.getPosition(obj));
				this.setSize(iframe, this.getSize(obj));
				var zIndex = parseInt(this.getStyle(obj, 'zIndex'), 10);
				if (!zIndex) {
					zIndex = 1000;
					this.setStyle(obj, 'zIndex', zIndex)
				}
				this.setStyle(iframe, 'zIndex', zIndex-1);
			}
		},
		extend : function(obj) {
			obj = $(obj);
			if (typeof obj.extended != 'undefined') return obj;
			for (var func in this) {
				if (func == 'extend') continue;
				obj[func] = this[func].bind(this, obj);
			}
			obj.extended = true;
			return obj;
		}
	}
	Element.pos = Element.getPosition;
	Element.moveTo = Element.setPosition;

// Util ///////////////////////////////////////////////////////////////////////

	var Util = {
		// brower
		isIE : (window.navigator.userAgent.search(/msie/i) != -1),
		isFF : (window.navigator.userAgent.search(/firefox/i) != -1),
		isOpera : (window.navigator.userAgent.search(/opera/i) != -1),
		isSafari : (window.navigator.userAgent.search(/safari/i) != -1),
		userAgent : window.navigator.userAgent,
		appName : (new RegExp('(msie |firefox/|opera/|safari/)([0-9.]+)', 'i')).exec(window.navigator.userAgent)[1].slice(0, -1),
		appVersion : (new RegExp('(msie |firefox/|opera/|safari/)([0-9.]+)', 'i')).exec(window.navigator.userAgent)[2],
		// window, document
		getClientSize : function(win) {
			if (!win) win = self;
			var w = win.innerWidth || win.document.documentElement.clientWidth || win.document.body.clientWidth;
			var h = win.innerHeight || win.document.documentElement.clientHeight || win.document.body.clientHeight;
			return { width:w, height:h };
		},
		getDocSize : function(win) {
			if (!win) win = self;
			var docSize = Element.getSize(win.document.body);
			var w = Math.max(win.document.body.scrollWidth, docSize.width);
			var h = Math.max(win.document.body.scrollHeight, docSize.height);
			return { width:w, height:h };
		},
		getScrollOffset : function(win) {
			if (!win) win = self;
			var x = win.pageXOffset || win.document.body.scrollLeft || document.documentElement.scrollLeft || 0;
			var y = win.pageYOffset || win.document.body.scrollTop || document.documentElement.scrollTop || 0;
			return { x:x, y:y };
		},
		getDocumentWindow : function(doc) {
			return doc.defaultView || doc.parentWindow;
		},
		// iframe
		getIframeDoc : function(iframe) {
			if (iframe.contentWindow) {
				return iframe.contentWindow.document;
			} else if (iframe.contentDocument) {
				return iframe.contentDocument.documentElement;
			} else {
				return null;
			}
		},
		getIframeDocSize : function(iframe) {
			iframe = $(iframe);
			var doc = this.getIframeDoc(iframe);
			var win = this.getDocumentWindow(doc);
			return this.getDocSize(win);
		},
		autoResizeIframe : function(iframe, noWidth, noHeight) {
			this.resizeIframe(iframe, !noWidth, !noHeight);
		},
		resizeIframe : function(iframe, width, height) {
			iframe = $(iframe);
			if (width === true || height === true) {
				var docSize = this.getIframeDocSize(iframe);
			}
			if (width) {
				width = (width === true) ? docSize.width : width;
				Element.setStyle(iframe, 'width', (typeof width == 'number') ? width + 'px' : width);
			}
			if (height) {
				height = (height === true) ? docSize.height : height;
				Element.setStyle(iframe, 'height', (typeof height == 'number') ? height + 'px' : height);
			}
		},
		// math
		random : function(min, max) {
			if (typeof min == 'number' && typeof max == 'number') {
				return Math.floor(Math.random() * (max-min) + min);
			} else {
				return Math.floor(Math.random() * 10);
			}
		},
		randomId : function(cipher, prefix) {
			if (typeof cipher != 'number') {
				cipher = 7;
			} else if (cipher > 15) {
				cipher = 15;
			}
			var min = parseInt('100000000000000'.substr(0, cipher), 10);
			var max = parseInt('999999999999999'.substr(0, cipher), 10);
			return (prefix) ? prefix+'_'+Util.random(min, max) : Util.random(min, max);
		},
		// cookie
		getCookie : function(name) {
			var re = new RegExp(name + '=([^;]+)');
			var value = re.exec(document.cookie);
			return (value != null) ? unescape(value[1]) : null;
		},
		setCookie : function(name, value, expire, path, domain) {
			expire = parseInt(expire, 10);
			if (!path) path = '/';
			if (expire) {
				var today = new Date();
				var expiry = new Date(today.getTime() + expire * 1000);
				var cookie = name + '=' + escape(value) + '; expires=' + expiry.toGMTString() + '; path=' + path;
			} else {
				var cookie = name + '=' + escape(value) + '; path=' + path;
			}
			if (domain) cookie += '; domain=' + domain;
			document.cookie = cookie;
		},
		// link style sheet
		linkStyle : function(href, id) {
			var link = id ? ($(id) || $c('link')) : $c('link');
			if (link.id) {
				link.setAttribute('href', href);
			} else {
				if (id) link.setAttribute('id', id);
				link.setAttribute('type', 'text/css');
				link.setAttribute('rel', 'stylesheet');
				link.setAttribute('href', href);

				var head = document.getElementsByTagName('head')[0];
				head.appendChild(link);
			}
			return link;
		},
		// js path
		getJSPath : function(jsFile) {
			if (!jsFile) jsFile = 'sky.base.js';
			var scripts = document.getElementsByTagName('script');
			for (var i=0; i<scripts.length; i++) {
				var src = scripts[i].getAttribute('src');
				if (src && src.search(jsFile) != -1) {
					var path = src.replace(/\/[^\/]+$/, '');
					return path;
				}
			}
			return false;
		},
		// rgb() to #hex
		rgbToHex : function(str) {
			str = str.replace(/[^0-9,]/g, '').split(',');
			str[0] = ('0' + parseInt(str[0], 10).toString(16).toLowerCase()).slice(-2);
			str[1] = ('0' + parseInt(str[1], 10).toString(16).toLowerCase()).slice(-2);
			str[2] = ('0' + parseInt(str[2], 10).toString(16).toLowerCase()).slice(-2);

			return ('#' + str.join(''));
		},
		// debug
		dump : function(obj) {
			if (obj.toString && (typeof obj == 'string' || typeof obj == 'number' || obj instanceof Array)) {
				return obj.toString();
			} else {
				var str = '';
				for (x in obj) {
					str += x + ' : ' + obj[x] + "\n";
				}
				return str;
			}
		}
	}
	Util.linkCSS = Util.linkStyle;

// Special functions //////////////////////////////////////////////////////////

	// get element by id
	function $(obj) {
		if (obj && obj instanceof Array) {
			var ret = [];
			for (var i=0; i<obj.length; i++) {
				ret.push((typeof obj[i] == 'string') ? document.getElementById(obj[i]) : obj[i]);
			}
			return ret;
		} else {
			return (typeof obj == 'string') ? document.getElementById(obj) : obj;
		}
	}
	function $$(id, range) {
		var p = /([#]+)/.exec(id);
		if (p[1]) {
			var cipher = p[1].length;
			var pattern = p[1];
		} else{
			var cipher = 1;
			var pattern = '#';
		}
		var zero = pattern.replace(/#/g, '0');
		if (range instanceof Array) {
			var start = range[0];
			var end = range[1] || false;
		} else if (arguments.length == 3) {
			var start = arguments[1];
			var end = arguments[2];
		} else {
			var start = 0;
			var end = range;
		}
		var loop = (end) ? end : Math.pow(10, cipher) - 1;
		var ret = [];
		for (var i=start; i<=loop; i++) {
			var el = document.getElementById(id.replace(pattern, (zero+i).slice(-cipher)));
			if (el) {
				ret.push(el);
			} else if (!end && i>0) {
				break;
			}
		}
		return ret;
	}

	// extend
	function $e(obj) {
		if (typeof obj == 'string' || (typeof obj.nodeType == 'number' && obj.nodeType == 1)) {
			obj = Element.extend(obj);
		} else if (typeof obj.initEvent == 'function' || typeof obj.keyCode == 'number') {
			obj = Event.extend(obj);
		} else {
			obj = Class.extend.apply(Class, arguments);
		}
		return obj;
	}

	// array function
	$a = Array.prototype.copy;
	function $array(obj) {
		return (obj instanceof Array) ? obj : [obj];
	}

	// event function
	$l = Event.addListener;
	$lx = Event.delListener;

	var __window_load_func = [];
	function $load(func) {
		if (typeof func == 'function') {
			__window_load_func.push(func);
			if (__window_load_func.length == 1) {
				$l(window, 'load', __window_load_init);
			}
		}
	}
	function __window_load_init(evt, obj) {
		for (var i=0; i<__window_load_func.length; i++) {
			__window_load_func[i](evt, obj);
		}
	}

	// create element
	function $c(tag) {
		return document.createElement(tag);
	}

	// get form
	function $form(name) {
		if (typeof name == 'string') {
			return document.forms[name] || document.getElementById(name);
		} else {
			return name;
		}
	}

	// get form element value
	function $v(obj) {
		obj = $(obj);
		if (obj.type) {
			var type = obj.type;
		} else if (obj.length) {
			var type = obj[0] ? obj[0].type : false;
		} else {
			var type = false;
		}
		if (!type) return false;

		switch (type.toLowerCase()) {
			case 'radio' :
				var value = $a(obj).filter(function(e) { return e.checked ? e.value : false; }, true);
				value = (value.length) ? value[0] : '';
				break;
			case 'checkbox' :
				var value = $a(obj).filter(function(e) { return e.checked ? e.value : false; }, true);
				break;
			case 'select-one' :
				var value = $a(obj.options).filter(function(e) { return e.selected ? e.value : false; }, true);
				value = (value.length) ? value[0] : '';
				break;
			case 'select-multiple' :
				var value = $a(obj.options).filter(function(e) { return e.selected ? e.value : false; }, true);
				break;
			default :
				var value = $a(obj).each(function(e) { return e.value; });
				if (value.length == 1) value = value[0];
				break;
		}
		return value;
	}

	// set form element value
	function $vset(obj, value) {
		obj = $(obj);
		if (obj.type) {
			var type = obj.type;
		} else if (obj.length) {
			var type = obj[0] ? obj[0].type : false;
		} else {
			var type = false;
		}
		if (!type) return false;

		switch (type.toLowerCase()) {
			case 'radio' :
				$a(obj).each(function(e) { if (e.value == value) e.checked = true; });
				break;
			case 'checkbox' :
				$a(value).each(function(v) {
					var tmp = $a(obj);
					if (tmp[0].getAttribute('bitwise') != null) {
						v = parseInt(v, 10);
						tmp.each(function(e) { if (parseInt(e.value, 10) & v) e.checked = true; });
					} else {
						tmp.each(function(e) { if (e.value == v) e.checked = true; });
					}
				});
				break;
			case 'select-one' :
				$a(obj.options).each(function(e) { if (e.value == value) e.selected = true; });
				break;
			case 'select-multiple' :
				$a(value).each(function(v) {
					$a(obj.options).each(function(e) { if (e.value == v) e.selected = true; });
				});
				break;
			default :
				var e = $a(obj);
				$a(value).each(function(v, i) {
					if (typeof e[i] != 'undefined') e[i].value = v;
				});
				break;
		}
	}

	// get selected option object
	function $select(obj) {
		obj = $(obj);
		if (typeof obj.options == 'undefined') return false;
		var option = $a(obj.options).filter(function(e) { return e.selected ? e : false; }, true);
		if (obj.type.toLowerCase() == 'select-one') option = (option.length) ? option[0] : false;
		return option;
	}